From fa8c0d911a4f3a825aef0067e34e3b8be5740374 Mon Sep 17 00:00:00 2001 From: sai6855 Date: Tue, 19 Mar 2024 18:46:31 +0530 Subject: [PATCH] Merge branch 'next' into deprecate-slider-components --- .circleci/config.yml | 132 +- .codesandbox/ci.json | 8 +- .eslintignore | 12 +- .github/workflows/codeql.yml | 4 +- .github/workflows/no-response.yml | 6 +- .github/workflows/scorecards.yml | 2 +- .github/workflows/vale-action.yml | 11 +- .vale.ini | 35 +- CHANGELOG.md | 250 ++- CHANGELOG.old.md | 120 +- CONTRIBUTING.md | 14 +- README.md | 4 +- TYPESCRIPT_CONVENTION.md | 2 +- apps/local-ui-lib/package.json | 2 +- apps/local-ui-lib/tsconfig.json | 2 +- .../.eslintrc | 0 .../.gitignore | 0 .../README.md | 0 .../next.config.js | 0 .../package.json | 6 +- .../public/next.svg | 0 .../public/vercel.svg | 0 .../src/app/box/page.tsx | 0 .../src/app/favicon.ico | Bin .../src/app/globals.css | 0 .../src/app/layout.tsx | 0 .../src/app/material-ui/layout.tsx | 0 .../src/app/material-ui/page.tsx | 0 .../app/material-ui/react-accordion/page.tsx | 58 + .../src/app/material-ui/react-alert/page.tsx | 0 .../material-ui/react-autocomplete/page.tsx | 191 ++ .../src/app/material-ui/react-avatar/page.tsx | 0 .../src/app/material-ui/react-badge/page.tsx | 0 .../material-ui/react-breadcrumbs/page.tsx | 58 + .../src/app/material-ui/react-slider/page.tsx | 142 ++ .../src/app/material-ui/react-switch/page.tsx | 0 .../src/app/page.module.css | 0 .../src/app/page.tsx | 0 .../src/app/theme.ts | 0 .../src/augment.d.ts | 0 .../src/components/Box.jsx | 0 .../tsconfig.json | 2 +- .../.eslintrc | 0 .../.gitignore | 0 .../README.md | 0 .../index.html | 0 .../package.json | 4 +- .../postcss.config.cjs | 0 .../src/App.tsx | 0 .../src/Box.jsx | 0 .../src/Layout.tsx | 0 .../src/Slider/ZeroSlider.test.jsx | 0 .../src/Slider/ZeroSlider.tsx | 0 .../src/augment.ts | 0 .../src/component.tsx | 0 .../src/extend-zero.ts | 0 .../src/main.tsx | 0 .../src/pages/index.tsx | 0 .../src/pages/material-ui/react-accordion.tsx | 59 + .../src/pages/material-ui/react-alert.tsx | 0 .../pages/material-ui/react-autocomplete.tsx | 192 ++ .../src/pages/material-ui/react-avatar.tsx | 0 .../src/pages/material-ui/react-badge.tsx | 0 .../pages/material-ui/react-breadcrumbs.tsx | 59 + .../src/pages/material-ui/react-slider.tsx | 143 ++ .../src/pages/material-ui/react-switch.tsx | 0 .../tsconfig.json | 2 +- .../vite-env.d.ts | 0 .../vite.config.ts | 0 apps/pigment-next-app/next-env.d.ts | 5 + .../src/app/material-ui/react-modal/page.tsx | 51 + apps/pigment-next-app/src/app/slider/page.tsx | 100 - .../src/components/Slider/ZeroSlider.tsx | 945 --------- .../src/pages/material-ui/react-modal.tsx | 52 + apps/pnpm-lock.yaml | 182 +- apps/pnpm-workspace.yaml | 2 +- babel.config.js | 5 +- dangerfile.ts | 2 +- docs/data/base/components/badge/badge.md | 2 +- docs/data/base/components/button/button.md | 2 +- .../base/components/focus-trap/focus-trap.md | 2 +- .../components/form-control/form-control.md | 4 +- docs/data/base/components/input/input.md | 2 +- docs/data/base/components/menu/menu.md | 2 +- docs/data/base/components/no-ssr/no-ssr.md | 2 +- docs/data/base/components/select/select.md | 2 +- .../data/base/components/snackbar/snackbar.md | 2 +- docs/data/base/components/tabs/tabs.md | 2 +- .../customization/customization.md | 12 +- .../base/getting-started/overview/overview.md | 4 +- .../getting-started/quickstart/quickstart.md | 8 +- .../base/getting-started/roadmap/roadmap.md | 12 +- .../next-js-app-router/next-js-app-router.md | 12 +- .../working-with-tailwind-css.md | 20 +- docs/data/docs-infra/pages.ts | 1 + .../FreeSoloCreateOptionDialog.js | 2 +- .../FreeSoloCreateOptionDialog.tsx | 2 +- .../components/autocomplete/autocomplete.md | 4 +- .../components/button-group/button-group.md | 2 +- docs/data/joy/components/grid/grid.md | 2 +- docs/data/joy/components/list/list.md | 2 +- docs/data/joy/components/modal/modal.md | 2 +- .../components/radio-button/radio-button.md | 2 +- docs/data/joy/components/switch/switch.md | 4 +- docs/data/joy/components/textarea/textarea.md | 2 +- .../typography/TypographyTitleBody.js | 4 +- .../typography/TypographyTitleBody.tsx | 4 +- .../joy/components/typography/typography.md | 2 +- .../theme-colors/theme-colors.md | 2 +- .../theme-shadow/theme-shadow.md | 2 +- .../themed-components/themed-components.md | 2 +- .../using-css-variables.md | 2 +- .../ColorInversionNavigation.js | 64 +- .../ColorInversionNavigation.tsx | 64 +- .../joy/migration/TitleBodyIconExample.js | 4 +- .../joy/migration/TitleBodyIconExample.tsx | 4 +- .../FreeSoloCreateOptionDialog.js | 2 +- .../FreeSoloCreateOptionDialog.tsx | 2 +- .../components/autocomplete/autocomplete.md | 8 +- .../badges/BadgeMaterialYouPlayground.js | 48 - .../data/material/components/badges/badges.md | 15 - .../ButtonGroupMaterialYouPlayground.js | 51 - .../components/button-group/button-group.md | 13 - .../buttons/ButtonMaterialYouPlayground.js | 52 - .../material/components/buttons/buttons.md | 15 - .../components/checkboxes/checkboxes.md | 2 +- .../chips/ChipMaterialYouPlayground.js | 59 - docs/data/material/components/chips/chips.md | 17 +- .../material/components/dialogs/dialogs.md | 24 +- .../dividers/DividerMaterialYouPlayground.js | 80 - .../material/components/dividers/dividers.md | 15 - docs/data/material/components/lists/lists.md | 22 +- docs/data/material/components/modal/modal.md | 2 +- .../progress/CustomizedProgressBars.js | 24 +- .../progress/CustomizedProgressBars.tsx | 23 +- .../CustomizedProgressBars.tsx.preview | 1 + .../progress/ProgressMaterialYouPlayground.js | 74 - .../material/components/progress/progress.md | 16 - .../components/radio-buttons/radio-buttons.md | 2 +- .../slider/SliderMaterialYouPlayground.js | 49 - .../data/material/components/slider/slider.md | 17 +- .../material/components/switches/switches.md | 2 +- .../material/components/table/DataTable.js | 3 +- .../material/components/table/DataTable.tsx | 5 +- docs/data/material/components/table/table.md | 7 +- docs/data/material/components/tabs/tabs.md | 4 +- .../components/text-fields/text-fields.md | 6 +- .../use-media-query/use-media-query.md | 6 +- .../material/customization/color/color.md | 2 +- .../customization/typography/typography.md | 9 +- .../classname-generator.md | 2 +- .../css-theme-variables/customization.md | 2 +- .../css-theme-variables/migration.md | 2 +- .../css-theme-variables/usage/usage.md | 4 +- .../getting-started/overview/overview.md | 2 +- .../getting-started/templates/blog/README.md | 4 +- .../templates/checkout/InfoMobile.tsx.preview | 10 - .../templates/checkout/README.md | 4 +- .../templates/dashboard/README.md | 4 +- .../templates/landing-page/LandingPage.js | 2 +- .../templates/landing-page/LandingPage.tsx | 2 +- .../templates/landing-page/README.md | 4 +- .../templates/landing-page/components/Hero.js | 13 +- .../landing-page/components/Hero.tsx | 13 +- .../templates/pricing/README.md | 4 +- .../templates/sign-in-side/README.md | 4 +- .../templates/sign-in/README.md | 4 +- .../templates/sign-up/README.md | 4 +- .../templates/sticky-footer/README.md | 4 +- .../getting-started/templates/templates.md | 4 +- docs/data/material/guides/api/api.md | 12 +- .../guides/composition/composition.md | 8 +- .../guides/localization/localization.md | 2 +- .../material-3-components/MD3AndV5Usage.js | 22 - .../material-3-components/MD3AndV5Usage.tsx | 22 - .../MD3AndV5Usage.tsx.preview | 8 - .../material-3-components/MD3ButtonUsage.js | 11 - .../material-3-components/MD3ButtonUsage.tsx | 11 - .../MD3ButtonUsage.tsx.preview | 3 - .../material-3-components/MD3Theming.js | 38 - .../material-3-components/MD3Theming.tsx | 38 - .../MD3Theming.tsx.preview | 5 - .../material-3-components.md | 133 -- .../minimizing-bundle-size.md | 5 +- .../server-rendering/server-rendering.md | 18 +- .../material/guides/typescript/typescript.md | 20 +- .../interoperability/interoperability.md | 110 +- .../material/integrations/nextjs/nextjs.md | 28 +- .../styled-components/styled-components.md | 30 +- .../theme-scoping/theme-scoping.md | 4 +- .../migrating-from-deprecated-apis.md | 14 + .../migration-v4/migrating-from-jss.md | 2 +- .../migration/migration-v4/migration-v4.md | 4 +- .../migration-v4/v5-style-changes.md | 2 +- docs/data/styles/advanced/advanced.md | 8 +- .../system/getting-started/usage/usage.md | 4 +- docs/mui-vale.zip | Bin 0 -> 4897 bytes docs/mui-vale/.vale.ini | 2 + .../styles/MUI/CorrectReferenceAllCases.yml | 31 + .../styles/MUI/CorrectRerenceCased.yml | 13 + docs/mui-vale/styles/MUI/GoogleLatin.yml | 11 + .../styles/MUI/MuiBrandName.yml} | 7 +- docs/mui-vale/styles/MUI/NoBritish.yml | 112 ++ .../styles/MUI}/NoCompanyName.yml | 6 +- docs/next.config.mjs | 3 +- docs/package.json | 29 +- docs/pages/_app.js | 2 + docs/pages/_document.js | 2 +- .../blog/2019-developer-survey-results.md | 12 +- docs/pages/blog/2019.md | 4 +- .../blog/2020-developer-survey-results.md | 12 +- docs/pages/blog/2020.md | 2 +- .../blog/2021-developer-survey-results.md | 12 +- docs/pages/blog/2021-q1-update.md | 2 +- docs/pages/blog/2021-q3-update.md | 2 +- docs/pages/blog/2021.md | 2 +- .../blog/2023-material-ui-v6-and-beyond.md | 10 +- docs/pages/blog/base-ui-2024-plans.md | 2 +- ...tency-to-material-ui-customization-apis.js | 7 + ...tency-to-material-ui-customization-apis.md | 70 + .../callback-support-in-style-overrides.md | 2 +- docs/pages/blog/introducing-base-ui.md | 6 +- .../blog/making-customizable-components.md | 6 +- docs/pages/blog/material-ui-v4-is-out.md | 10 +- docs/pages/blog/mui-core-v5.md | 10 +- docs/pages/blog/mui-next-js-app-router.md | 2 +- docs/pages/blog/mui-product-comparison.md | 10 +- docs/pages/blog/mui-x-end-v6-features.md | 4 +- docs/pages/blog/mui-x-mid-v6-features.md | 2 +- docs/pages/blog/mui-x-v5.md | 2 +- docs/pages/blog/mui-x-v6.md | 4 +- docs/pages/blog/mui-x-v7-beta.md | 4 +- docs/pages/blog/toolpad-use-cases.md | 6 +- docs/pages/careers/design-engineer.md | 4 +- docs/pages/careers/designer.md | 6 +- docs/pages/careers/developer-advocate.md | 2 +- .../careers/developer-experience-engineer.md | 2 +- docs/pages/careers/engineering-manager.md | 8 +- docs/pages/careers/full-stack-engineer.md | 14 +- docs/pages/careers/head-of-operations.md | 2 +- docs/pages/careers/product-engineer.md | 2 +- docs/pages/careers/product-manager.md | 4 +- .../careers/product-marketing-manager.md | 2 +- docs/pages/careers/react-tech-lead-core.md | 2 +- docs/pages/careers/react-tech-lead-x-grid.md | 4 +- docs/pages/careers/senior-designer.md | 2 +- .../careers/technical-product-manager.md | 2 +- docs/pages/experiments/docs/codeblock.md | 35 +- .../docs/custom-components.js} | 2 +- .../experiments/docs/custom-components.md | 19 + docs/pages/experiments/material-next/menu.tsx | 339 ---- docs/pages/experiments/md3/buttons.tsx | 219 --- docs/pages/experiments/md3/index.tsx | 136 -- docs/pages/experiments/md3/inputs.tsx | 212 -- docs/pages/material-ui/api/autocomplete.json | 6 +- docs/public/_redirects | 2 +- .../card.png | Bin 0 -> 196042 bytes .../static/images/templates/checkout.png | Bin 21469 -> 22378 bytes .../static/images/templates/landing-page.png | Bin 30575 -> 34799 bytes docs/scripts/formattedTSDemos.js | 6 +- docs/src/MuiPage.ts | 2 +- docs/src/components/header/HeaderNavBar.tsx | 8 +- .../components/header/HeaderNavDropdown.tsx | 8 +- .../src/components/header/ThemeModeToggle.tsx | 85 +- .../src/components/home/XGridGlobalStyles.tsx | 15 +- docs/src/components/pricing/PricingTable.tsx | 2 +- .../src/components/productX/XGridFullDemo.tsx | 4 +- docs/src/components/productX/XHero.tsx | 28 +- docs/src/components/productX/XTheming.tsx | 4 +- docs/src/components/showcase/FolderTable.tsx | 4 +- .../components/showcase/NotificationCard.tsx | 5 +- .../components/showcase/ThemeDatePicker.tsx | 2 +- docs/src/layouts/AppFooter.tsx | 2 +- docs/src/layouts/AppHeader.tsx | 1 - docs/src/modules/components/Ad.js | 2 +- docs/src/modules/components/AdCarbon.js | 2 +- .../components/ApiPage/list/ClassesList.tsx | 2 +- .../ApiPage/sections/ClassesSection.tsx | 2 +- .../components/ApiPage/table/ClassesTable.tsx | 2 +- docs/src/modules/components/AppNavDrawer.js | 2 +- .../modules/components/AppNavDrawerItem.js | 2 +- docs/src/modules/components/AppSearch.js | 6 + .../modules/components/AppSettingsDrawer.js | 40 +- .../modules/components/AppTableOfContents.js | 2 +- .../components/HighlightedCodeWithTabs.tsx | 40 +- docs/src/modules/components/JoyUsageDemo.tsx | 2 +- docs/src/modules/components/MarkdownDocs.js | 2 +- docs/src/modules/components/MarkdownDocsV2.js | 2 +- .../src/modules/components/MarkdownElement.js | 53 + docs/src/modules/components/MarkdownLinks.js | 2 +- .../MaterialFreeTemplatesCollection.js | 128 +- .../MaterialDataDisplayComponents.js | 6 +- .../MaterialFeedbackComponents.js | 2 +- .../MaterialInputComponents.js | 4 +- .../components/MaterialUIExampleCollection.js | 32 +- .../components/MaterialYouUsageDemo.tsx | 377 ---- .../modules/components/MuiProductSelector.tsx | 4 +- docs/src/modules/components/Notifications.js | 2 +- docs/src/modules/components/ThemeContext.js | 145 +- docs/src/modules/components/TopLayoutBlog.js | 5 + docs/src/modules/sandbox/CodeSandbox.test.js | 6 +- docs/src/modules/sandbox/CodeSandbox.ts | 2 +- docs/src/modules/sandbox/Dependencies.test.js | 36 +- docs/src/modules/sandbox/Dependencies.ts | 3 +- docs/src/modules/sandbox/StackBlitz.test.js | 4 +- .../utils/getProductInfoFromUrl.test.js | 15 +- .../modules/utils/getProductInfoFromUrl.ts | 12 +- .../onepirate/modules/views/privacy.md | 6 +- docs/src/pages/versions/versions.md | 2 +- docs/src/route.ts | 1 + .../autocomplete/autocomplete.json | 2 +- .../api-docs/autocomplete/autocomplete.json | 8 +- .../circular-progress/circular-progress.json | 2 +- docs/translations/api-docs/icon/icon.json | 2 +- docs/writing-rules.zip | Bin 3538 -> 0 bytes .../ComponentNameConventions.yml | 16 - docs/writing-rules/ComposedWords.yml | 19 - docs/writing-rules/NamingConventions.yml | 24 - docs/writing-rules/Typos.yml | 15 - examples/base-ui-cra-ts/README.md | 6 +- examples/base-ui-cra/README.md | 6 +- examples/base-ui-nextjs-tailwind-ts/README.md | 10 +- examples/base-ui-vite-tailwind-ts/README.md | 12 +- examples/base-ui-vite-tailwind/README.md | 12 +- examples/joy-ui-cra-ts/README.md | 8 +- examples/joy-ui-nextjs-ts/README.md | 8 +- examples/joy-ui-vite-ts/README.md | 10 +- .../README.md | 6 +- .../README.md | 6 +- .../material-ui-cra-tailwind-ts/README.md | 12 +- examples/material-ui-cra-ts/README.md | 10 +- examples/material-ui-cra/README.md | 10 +- examples/material-ui-express-ssr/README.md | 12 +- examples/material-ui-gatsby/README.md | 6 +- .../README.md | 14 +- .../material-ui-nextjs-pages-router/README.md | 14 +- .../README.md | 12 +- .../pages/_document.tsx | 2 +- examples/material-ui-nextjs-ts/README.md | 8 +- examples/material-ui-nextjs/README.md | 10 +- examples/material-ui-preact/README.md | 6 +- examples/material-ui-remix-ts/README.md | 10 +- examples/material-ui-via-cdn/README.md | 8 +- examples/material-ui-vite-ts/README.md | 8 +- examples/material-ui-vite/README.md | 8 +- examples/pigment-css-nextjs-ts/README.md | 8 +- .../pigment-css-nextjs-ts/src/app/page.tsx | 2 +- examples/pigment-css-vite-ts/README.md | 8 +- examples/pigment-css-vite-ts/src/App.tsx | 2 +- ...-management.js => feedback-management.mts} | 60 +- package.json | 44 +- .../docs-utils/.npmignore | 0 .../docs-utils/CHANGELOG.md | 4 + .../docs-utils/README.md | 2 +- .../docs-utils/package.json | 6 +- .../src/ComponentClassDefinition.ts | 0 .../docs-utils/src/createTypeScriptProject.ts | 0 .../src/getPropsFromComponentNode.ts | 0 .../docs-utils/src/index.ts | 0 .../docs-utils/tsconfig.build.json | 0 .../docs-utils/tsconfig.json | 0 packages-internal/scripts/CHANGELOG.md | 2 +- packages-internal/scripts/README.md | 4 +- packages-internal/scripts/package.json | 8 +- .../scripts/tsconfig.typecheck.json | 2 +- .../typescript-to-proptypes/CHANGELOG.old.md | 2 +- .../src/getPropTypesFromFile.ts | 4 +- .../src/injectPropTypesInFile.ts | 4 +- .../test/typescript-to-proptypes.test.ts | 2 +- .../baseUi/generateBaseUiApiPages.ts | 2 +- packages/api-docs-builder-core/package.json | 2 +- .../ApiBuilders/ComponentApiBuilder.ts | 30 +- packages/api-docs-builder/ProjectSettings.ts | 6 +- packages/api-docs-builder/buildApiUtils.ts | 2 +- packages/api-docs-builder/package.json | 6 +- packages/api-docs-builder/tsconfig.json | 2 +- .../utils/parseSlotsAndClasses.ts | 2 +- packages/api-docs-builder/utils/parseTest.ts | 2 +- packages/eslint-plugin-material-ui/README.md | 2 +- .../rules/mui-name-matches-component-name.js | 2 +- packages/feedback/README.md | 2 +- packages/feedback/package.json | 2 +- packages/markdown/package.json | 4 +- packages/markdown/parseMarkdown.js | 49 +- packages/markdown/prepareMarkdown.js | 8 +- packages/mui-babel-macros/package.json | 4 +- packages/mui-base/README.md | 10 +- packages/mui-base/package.json | 2 +- packages/mui-base/src/FocusTrap/FocusTrap.tsx | 2 +- .../mui-base/src/Unstable_Popup/Popup.tsx | 6 +- .../src/useAutocomplete/useAutocomplete.js | 2 +- .../src/useCompound/useCompoundItem.ts | 2 +- .../src/useCompound/useCompoundParent.ts | 2 +- packages/mui-base/src/useList/useListItem.ts | 2 +- .../mui-base/src/useSelect/useSelect.test.tsx | 2 +- .../mui-base/src/utils/useRootElementName.ts | 6 +- packages/mui-codemod/CONTRIBUTING.md | 4 +- packages/mui-codemod/README.md | 196 +- packages/mui-codemod/package.json | 2 +- .../src/v5.0.0/jss-to-tss-react.js | 4 +- .../expected-todo-comments.js | 2 +- .../mui-core-downloads-tracker/package.json | 2 +- packages/mui-docs/package.json | 6 +- .../src/translations/translations.json | 8 +- packages/mui-envinfo/envinfo.js | 2 +- packages/mui-envinfo/package.json | 2 +- packages/mui-icons-material/README.md | 8 +- packages/mui-icons-material/builder.md | 2 +- packages/mui-icons-material/package.json | 2 +- .../mui-icons-material/scripts/download.mjs | 2 +- .../test/generated-types/tsconfig.json | 14 +- packages/mui-joy/README.md | 8 +- packages/mui-joy/package.json | 2 +- .../mui-joy/src/Autocomplete/Autocomplete.tsx | 4 +- .../src/Autocomplete/AutocompleteProps.ts | 2 +- packages/mui-joy/src/Button/Button.tsx | 6 +- packages/mui-joy/src/Chip/Chip.tsx | 6 +- .../mui-joy/src/ChipDelete/ChipDelete.tsx | 2 +- .../mui-joy/src/FormControl/FormControl.tsx | 2 +- .../mui-joy/src/IconButton/IconButton.tsx | 6 +- packages/mui-joy/src/ListItem/ListItem.tsx | 2 +- packages/mui-joy/src/Menu/Menu.tsx | 2 +- packages/mui-joy/src/Select/Select.tsx | 2 +- packages/mui-joy/src/SvgIcon/SvgIcon.tsx | 2 +- packages/mui-joy/src/Tooltip/Tooltip.test.tsx | 50 +- packages/mui-joy/src/Tooltip/Tooltip.tsx | 10 +- .../mui-joy/src/Typography/Typography.tsx | 2 +- packages/mui-joy/src/styles/types/theme.ts | 2 +- packages/mui-lab/README.md | 6 +- packages/mui-lab/package.json | 2 +- packages/mui-material-next/CONTRIBUTING.md | 34 - packages/mui-material-next/README.md | 14 - packages/mui-material-next/migration.md | 614 ------ packages/mui-material-next/package.json | 99 - .../src/Badge/Badge.spec.tsx | 79 - .../src/Badge/Badge.test.tsx | 342 ---- .../mui-material-next/src/Badge/Badge.tsx | 378 ---- .../src/Badge/Badge.types.ts | 133 -- .../src/Badge/badgeClasses.ts | 78 - packages/mui-material-next/src/Badge/index.ts | 5 - .../src/Button/Button.spec.tsx | 106 - .../src/Button/Button.test.js | 239 --- .../mui-material-next/src/Button/Button.tsx | 528 ----- .../src/Button/Button.types.ts | 115 -- .../src/Button/buttonClasses.ts | 76 - .../mui-material-next/src/Button/index.ts | 8 - .../src/ButtonBase/ButtonBase.test.tsx | 1089 ----------- .../src/ButtonBase/ButtonBase.tsx | 318 --- .../src/ButtonBase/ButtonBase.types.ts | 141 -- .../src/ButtonBase/Ripple.test.js | 88 - .../src/ButtonBase/Ripple.tsx | 89 - .../src/ButtonBase/TouchRipple.test.js | 271 --- .../src/ButtonBase/TouchRipple.tsx | 295 --- .../src/ButtonBase/TouchRipple.types.ts | 28 - .../src/ButtonBase/buttonBaseClasses.ts | 30 - .../mui-material-next/src/ButtonBase/index.ts | 5 - .../src/ButtonBase/touchRippleClasses.ts | 37 - .../src/ButtonBase/useTouchRipple.ts | 106 - .../src/ButtonGroup/ButtonGroup.test.tsx | 281 --- .../src/ButtonGroup/ButtonGroup.tsx | 371 ---- .../src/ButtonGroup/ButtonGroup.types.ts | 107 -- .../ButtonGroup/ButtonGroupButtonContext.ts | 14 - .../src/ButtonGroup/ButtonGroupContext.ts | 25 - .../src/ButtonGroup/buttonGroupClasses.ts | 69 - .../src/ButtonGroup/index.ts | 6 - .../mui-material-next/src/Chip/Chip.test.tsx | 754 -------- packages/mui-material-next/src/Chip/Chip.tsx | 560 ------ .../mui-material-next/src/Chip/Chip.types.ts | 108 -- .../mui-material-next/src/Chip/chipClasses.ts | 78 - packages/mui-material-next/src/Chip/index.ts | 5 - .../CircularProgress.test.tsx | 191 -- .../src/CircularProgress/CircularProgress.tsx | 359 ---- .../CircularProgress.types.ts | 83 - .../circularProgressClasses.ts | 51 - .../src/CircularProgress/index.ts | 5 - .../src/Divider/Divider.test.tsx | 145 -- .../mui-material-next/src/Divider/Divider.tsx | 306 --- .../src/Divider/Divider.types.ts | 66 - .../src/Divider/dividerClasses.ts | 54 - .../mui-material-next/src/Divider/index.ts | 5 - .../src/FilledInput/FilledInput.spec.tsx | 6 - .../src/FilledInput/FilledInput.test.tsx | 102 - .../src/FilledInput/FilledInput.tsx | 508 ----- .../src/FilledInput/FilledInput.types.ts | 75 - .../src/FilledInput/filledInputClasses.ts | 57 - .../src/FilledInput/index.ts | 5 - .../src/FormControl/FormControl.test.tsx | 481 ----- .../src/FormControl/FormControl.tsx | 366 ---- .../src/FormControl/FormControl.types.ts | 93 - .../src/FormControl/FormControlContext.ts | 36 - .../src/FormControl/formControlClasses.ts | 32 - .../src/FormControl/formControlState.js | 15 - .../src/FormControl/index.ts | 6 - .../src/FormControl/useFormControl.ts | 7 - .../FormHelperText/FormHelperText.spec.tsx | 62 - .../FormHelperText/FormHelperText.test.tsx | 127 -- .../src/FormHelperText/FormHelperText.tsx | 235 --- .../FormHelperText/FormHelperText.types.ts | 102 - .../FormHelperText/formHelperTextClasses.ts | 43 - .../src/FormHelperText/index.ts | 5 - .../src/FormLabel/FormLabel.test.tsx | 226 --- .../src/FormLabel/FormLabel.tsx | 225 --- .../src/FormLabel/FormLabel.types.ts | 101 - .../src/FormLabel/formLabelClasses.ts | 42 - .../mui-material-next/src/FormLabel/index.ts | 6 - .../src/IconButton/IconButton.d.ts | 91 - .../src/IconButton/IconButton.js | 251 --- .../src/IconButton/IconButton.test.js | 137 -- .../src/IconButton/iconButtonClasses.ts | 60 - .../src/IconButton/index.d.ts | 5 - .../mui-material-next/src/IconButton/index.js | 5 - .../src/InputAdornment/InputAdornment.d.ts | 69 - .../src/InputAdornment/InputAdornment.js | 194 -- .../src/InputAdornment/InputAdornment.test.js | 217 --- .../src/InputAdornment/index.d.ts | 5 - .../src/InputAdornment/index.js | 5 - .../InputAdornment/inputAdornmentClasses.ts | 45 - .../src/InputBase/InputBase.spec.tsx | 25 - .../src/InputBase/InputBase.test.tsx | 757 -------- .../src/InputBase/InputBase.tsx | 824 -------- .../src/InputBase/InputBase.types.ts | 257 --- .../src/InputBase/index.d.ts | 5 - .../mui-material-next/src/InputBase/index.js | 5 - .../src/InputBase/inputBaseClasses.ts | 78 - .../src/InputBase/utils.test.js | 39 - .../mui-material-next/src/InputBase/utils.ts | 36 - .../src/InputLabel/InputLabel.spec.tsx | 32 - .../src/InputLabel/InputLabel.test.tsx | 225 --- .../src/InputLabel/InputLabel.tsx | 255 --- .../src/InputLabel/InputLabel.types.ts | 83 - .../mui-material-next/src/InputLabel/index.ts | 5 - .../src/InputLabel/inputLabelClasses.ts | 57 - .../LinearProgress/LinearProgress.test.tsx | 250 --- .../src/LinearProgress/LinearProgress.tsx | 432 ----- .../LinearProgress/LinearProgress.types.ts | 66 - .../src/LinearProgress/index.ts | 5 - .../LinearProgress/linearProgressClasses.ts | 57 - packages/mui-material-next/src/List/List.d.ts | 75 - packages/mui-material-next/src/List/List.js | 135 -- .../mui-material-next/src/List/List.spec.tsx | 14 - .../mui-material-next/src/List/List.test.js | 82 - .../src/List/ListContext.d.ts | 4 - .../mui-material-next/src/List/ListContext.js | 13 - .../mui-material-next/src/List/index.d.ts | 5 - packages/mui-material-next/src/List/index.js | 5 - .../mui-material-next/src/List/listClasses.ts | 28 - .../src/ListItem/ListItem.d.ts | 191 -- .../src/ListItem/ListItem.js | 496 ----- .../src/ListItem/ListItem.spec.tsx | 42 - .../src/ListItem/ListItem.test.js | 249 --- .../mui-material-next/src/ListItem/index.d.ts | 5 - .../mui-material-next/src/ListItem/index.js | 5 - .../src/ListItem/listItemClasses.ts | 52 - .../src/ListItemAvatar/ListItemAvatar.d.ts | 31 - .../src/ListItemAvatar/ListItemAvatar.js | 88 - .../src/ListItemAvatar/ListItemAvatar.test.js | 24 - .../src/ListItemAvatar/index.d.ts | 5 - .../src/ListItemAvatar/index.js | 5 - .../ListItemAvatar/listItemAvatarClasses.ts | 22 - .../src/ListItemButton/ListItemButton.d.ts | 95 - .../src/ListItemButton/ListItemButton.js | 283 --- .../src/ListItemButton/ListItemButton.test.js | 260 --- .../src/ListItemButton/index.d.ts | 5 - .../src/ListItemButton/index.js | 5 - .../ListItemButton/listItemButtonClasses.ts | 40 - .../src/ListItemIcon/ListItemIcon.d.ts | 33 - .../src/ListItemIcon/ListItemIcon.js | 91 - .../src/ListItemIcon/ListItemIcon.test.js | 22 - .../src/ListItemIcon/index.d.ts | 5 - .../src/ListItemIcon/index.js | 5 - .../src/ListItemIcon/listItemIconClasses.ts | 22 - .../ListItemSecondaryAction.d.ts | 37 - .../ListItemSecondaryAction.js | 88 - .../ListItemSecondaryAction.test.js | 39 - .../src/ListItemSecondaryAction/index.d.ts | 5 - .../src/ListItemSecondaryAction/index.js | 5 - .../listItemSecondaryActionClasses.ts | 22 - .../src/ListItemText/ListItemText.d.ts | 77 - .../src/ListItemText/ListItemText.js | 184 -- .../src/ListItemText/ListItemText.spec.tsx | 99 - .../src/ListItemText/ListItemText.test.js | 201 -- .../src/ListItemText/index.d.ts | 5 - .../src/ListItemText/index.js | 5 - .../src/ListItemText/listItemTextClasses.ts | 34 - .../src/ListSubheader/ListSubheader.d.ts | 69 - .../src/ListSubheader/ListSubheader.js | 158 -- .../src/ListSubheader/ListSubheader.test.js | 61 - .../src/ListSubheader/index.d.ts | 5 - .../src/ListSubheader/index.js | 5 - .../src/ListSubheader/listSubheaderClasses.ts | 34 - .../mui-material-next/src/Menu/Menu.test.tsx | 801 -------- packages/mui-material-next/src/Menu/Menu.tsx | 442 ----- .../mui-material-next/src/Menu/Menu.types.ts | 126 -- packages/mui-material-next/src/Menu/index.ts | 6 - .../mui-material-next/src/Menu/menuClasses.ts | 23 - .../src/MenuItem/MenuItem.spec.tsx | 68 - .../src/MenuItem/MenuItem.test.tsx | 219 --- .../src/MenuItem/MenuItem.tsx | 323 ---- .../src/MenuItem/MenuItem.types.ts | 71 - .../mui-material-next/src/MenuItem/index.ts | 8 - .../src/MenuItem/menuItemClasses.ts | 39 - .../src/Option/Option.spec.tsx | 68 - .../src/Option/Option.test.tsx | 217 --- .../mui-material-next/src/Option/Option.tsx | 321 ---- .../src/Option/Option.types.ts | 71 - .../mui-material-next/src/Option/index.ts | 7 - .../src/Option/optionClasses.ts | 39 - .../src/OutlinedInput/NotchedOutline.d.ts | 18 - .../src/OutlinedInput/NotchedOutline.js | 118 -- .../src/OutlinedInput/NotchedOutline.test.js | 68 - .../src/OutlinedInput/OutlinedInput.d.ts | 41 - .../src/OutlinedInput/OutlinedInput.js | 414 ---- .../src/OutlinedInput/OutlinedInput.test.js | 97 - .../src/OutlinedInput/index.d.ts | 5 - .../src/OutlinedInput/index.js | 5 - .../src/OutlinedInput/outlinedInputClasses.ts | 53 - .../mui-material-next/src/Select/Select.d.ts | 173 -- .../mui-material-next/src/Select/Select.js | 287 --- .../src/Select/Select.spec.tsx | 80 - .../src/Select/Select.test.js | 1708 ----------------- .../src/Select/SelectInput.d.ts | 47 - .../src/Select/SelectInput.js | 727 ------- .../mui-material-next/src/Select/index.d.ts | 5 - .../mui-material-next/src/Select/index.js | 5 - .../src/Select/selectClasses.ts | 61 - .../src/Slider/Slider.spec.tsx | 49 - .../src/Slider/Slider.test.js | 1503 --------------- .../mui-material-next/src/Slider/Slider.tsx | 1012 ---------- .../src/Slider/Slider.types.ts | 277 --- .../mui-material-next/src/Slider/index.ts | 6 - .../src/Slider/sliderClasses.ts | 99 - .../src/Slider/useSliderElementsOverlap.ts | 99 - .../src/Snackbar/Snackbar.d.ts | 130 -- .../src/Snackbar/Snackbar.js | 312 --- .../src/Snackbar/Snackbar.test.js | 600 ------ .../mui-material-next/src/Snackbar/index.d.ts | 5 - .../mui-material-next/src/Snackbar/index.js | 5 - .../src/Snackbar/snackbarClasses.ts | 39 - .../src/SnackbarContent/SnackbarContent.d.ts | 41 - .../src/SnackbarContent/SnackbarContent.js | 135 -- .../SnackbarContent/SnackbarContent.test.js | 64 - .../src/SnackbarContent/index.d.ts | 5 - .../src/SnackbarContent/index.js | 5 - .../SnackbarContent/snackbarContentClasses.ts | 26 - .../src/Switch/Switch.test.tsx | 145 -- .../mui-material-next/src/Switch/Switch.tsx | 362 ---- .../src/Switch/Switch.types.ts | 72 - .../mui-material-next/src/Switch/index.ts | 5 - .../src/Switch/switchClasses.ts | 57 - packages/mui-material-next/src/Tab/Tab.d.ts | 83 - packages/mui-material-next/src/Tab/Tab.js | 272 --- .../mui-material-next/src/Tab/Tab.test.js | 170 -- packages/mui-material-next/src/Tab/index.d.ts | 5 - packages/mui-material-next/src/Tab/index.js | 5 - .../mui-material-next/src/Tab/tabClasses.ts | 46 - .../src/TabScrollButton/TabScrollButton.d.ts | 43 - .../src/TabScrollButton/TabScrollButton.js | 117 -- .../TabScrollButton/TabScrollButton.test.js | 55 - .../src/TabScrollButton/index.d.ts | 5 - .../src/TabScrollButton/index.js | 5 - .../TabScrollButton/tabScrollButtonClasses.ts | 24 - .../src/TablePagination/TablePagination.d.ts | 183 -- .../src/TablePagination/TablePagination.js | 399 ---- .../TablePagination/TablePagination.spec.tsx | 15 - .../TablePagination/TablePagination.test.js | 452 ----- .../src/TablePagination/index.d.ts | 5 - .../src/TablePagination/index.js | 5 - .../TablePagination/tablePaginationClasses.ts | 52 - .../src/Tabs/ScrollbarSize.js | 56 - .../src/Tabs/ScrollbarSize.test.js | 50 - packages/mui-material-next/src/Tabs/Tabs.d.ts | 159 -- packages/mui-material-next/src/Tabs/Tabs.js | 762 -------- .../mui-material-next/src/Tabs/Tabs.spec.tsx | 13 - .../mui-material-next/src/Tabs/Tabs.test.js | 1314 ------------- .../mui-material-next/src/Tabs/TabsList.js | 64 - .../src/Tabs/TabsListContext.js | 10 - .../mui-material-next/src/Tabs/index.d.ts | 5 - packages/mui-material-next/src/Tabs/index.js | 5 - .../mui-material-next/src/Tabs/tabsClasses.ts | 55 - packages/mui-material-next/src/index.test.js | 19 - packages/mui-material-next/src/index.ts | 93 - .../src/internal/svg-icons/ArrowDropDown.tsx | 8 - .../src/internal/svg-icons/CheckBox.tsx | 11 - .../src/internal/svg-icons/Clear.tsx | 11 - .../src/styles/CssVarsProvider.tsx | 47 - .../src/styles/Theme.types.ts | 302 --- .../src/styles/createDarkColorScheme.ts | 54 - .../src/styles/createLightColorScheme.ts | 53 - .../src/styles/defaultTheme.ts | 5 - .../mui-material-next/src/styles/elevation.ts | 17 - .../src/styles/extendTheme.test.ts | 34 - .../src/styles/extendTheme.ts | 476 ----- .../src/styles/identifier.ts | 1 - .../mui-material-next/src/styles/index.ts | 11 - .../src/styles/motion.test.js | 156 -- .../mui-material-next/src/styles/motion.ts | 118 -- .../mui-material-next/src/styles/palette.ts | 147 -- .../mui-material-next/src/styles/shape.ts | 19 - .../src/styles/shouldSkipGeneratingVar.ts | 7 - .../mui-material-next/src/styles/state.ts | 18 - .../src/styles/styled.test.js | 237 --- .../mui-material-next/src/styles/styled.ts | 11 - .../mui-material-next/src/styles/sxConfig.ts | 91 - .../mui-material-next/src/styles/typeface.ts | 11 - .../mui-material-next/src/styles/typescale.ts | 123 -- .../mui-material-next/src/styles/useTheme.ts | 18 - .../src/styles/useThemeProps.ts | 19 - .../src/utils/createSvgIcon.tsx | 28 - .../src/utils/shouldSpreadAdditionalProps.ts | 7 - .../test/describeConformance.ts | 20 - .../mui-material-next/tsconfig.build.json | 20 - packages/mui-material-next/tsconfig.json | 4 - packages/mui-material/README.md | 4 +- packages/mui-material/package.json | 2 +- .../mui-material/src/Accordion/Accordion.js | 51 +- .../src/AccordionActions/AccordionActions.js | 22 +- .../src/AccordionDetails/AccordionDetails.js | 5 +- .../src/AccordionSummary/AccordionSummary.js | 41 +- .../src/Autocomplete/Autocomplete.d.ts | 2 +- .../src/Autocomplete/Autocomplete.js | 72 +- .../src/Autocomplete/autocompleteClasses.ts | 6 +- .../src/Breadcrumbs/Breadcrumbs.js | 5 +- .../CircularProgress/CircularProgress.d.ts | 2 +- .../src/CircularProgress/CircularProgress.js | 2 +- packages/mui-material/src/Icon/Icon.d.ts | 2 +- packages/mui-material/src/Icon/Icon.js | 2 +- packages/mui-material/src/Modal/Modal.js | 19 +- .../src/OverridableComponent.d.ts | 2 +- packages/mui-material/src/Popper/Popper.tsx | 4 +- packages/mui-material/src/Select/Select.d.ts | 28 +- .../mui-material/src/Select/Select.spec.tsx | 122 +- packages/mui-material/src/Slider/Slider.js | 489 +++-- packages/mui-material/src/SvgIcon/SvgIcon.js | 2 +- packages/mui-material/src/Tooltip/Tooltip.js | 6 +- .../mui-material/src/Tooltip/Tooltip.test.js | 39 + .../src/styles/CssVarsProvider.test.js | 1 + .../src/styles/CssVarsProvider.tsx | 5 +- .../src/styles/experimental_extendTheme.d.ts | 1 + .../src/styles/experimental_extendTheme.js | 5 +- .../styles/experimental_extendTheme.test.js | 5 +- .../src/styles/rootShouldForwardProp.ts | 5 + .../src/styles/slotShouldForwardProp.ts | 6 + packages/mui-material/src/styles/styled.d.ts | 5 +- packages/mui-material/src/styles/styled.js | 8 +- .../src/usePagination/usePagination.js | 2 +- .../test/integration/Menu.test.js | 2 +- packages/mui-private-theming/package.json | 2 +- packages/mui-styled-engine-sc/README.md | 2 +- packages/mui-styled-engine-sc/package.json | 2 +- packages/mui-styled-engine/README.md | 2 +- packages/mui-styled-engine/package.json | 2 +- packages/mui-styles/package.json | 2 +- packages/mui-system/README.md | 4 +- packages/mui-system/package.json | 2 +- .../src/createTheme/createBreakpoints.d.ts | 8 +- .../src/cssVars/createCssVarsProvider.js | 45 +- .../src/cssVars/useCurrentColorScheme.ts | 65 +- packages/mui-types/index.d.ts | 2 +- packages/mui-types/package.json | 2 +- packages/mui-utils/package.json | 4 +- .../src/useLocalStorageState/index.ts | 1 + .../useLocalStorageState.ts | 150 ++ .../src/visuallyHidden/visuallyHidden.ts | 2 +- .../.eslintrc | 0 .../.gitignore | 0 .../loader.js | 0 .../next-font.js | 0 .../next-image.js | 0 .../package.json | 6 +- .../src/index.ts | 37 +- .../src/virtual-css-loader.js | 0 .../tsconfig.build.json | 0 .../tsconfig.json | 0 .../tsup.config.ts | 0 .../zero-virtual.css | 0 .../.eslintignore | 0 .../.eslintrc | 0 .../.gitignore | 0 packages/pigment-css-react/README.md | 767 ++++++++ .../exports/createUseThemeProps.js | 0 .../exports/css.js | 0 .../exports/generateAtomics.js | 0 .../exports/keyframes.js | 0 .../exports/styled.js | 0 .../exports/sx-plugin.js | 0 .../exports/sx.js | 0 .../package.json | 28 +- packages/pigment-css-react/src/Box.d.ts | 26 + packages/pigment-css-react/src/Box.jsx | 51 + .../src/base.d.ts | 29 + .../src/createUseThemeProps.d.ts | 0 .../src/createUseThemeProps.js | 0 .../src/css.d.ts | 0 .../src/css.js | 0 .../src/generateAtomics.d.ts | 0 .../src/generateAtomics.js | 0 .../src/index.ts | 0 .../src/keyframes.d.ts | 0 .../src/keyframes.js | 0 .../src/processors/base-processor.ts | 0 .../src/processors/createUseThemeProps.ts | 0 .../src/processors/css.ts | 0 .../src/processors/generateAtomics.ts | 0 .../src/processors/keyframes.ts | 4 +- .../src/processors/styled.ts | 4 +- .../src/processors/sx.ts | 6 +- .../src/styled.d.ts | 30 +- .../src/styled.jsx | 0 .../src/sx.d.ts | 0 .../src/sx.js | 0 .../src/theme.ts | 0 .../src/utils/checkStaticObjectOrArray.ts | 0 .../src/utils/convertAtomicsToCss.ts | 0 .../src/utils/cssFnValueToVariable.ts | 0 .../src/utils/cssFunctionTransformerPlugin.ts | 0 .../src/utils/emotion.ts | 0 .../src/utils/extendTheme.ts | 0 .../src/utils/generateCss.ts | 0 .../src/utils/index.ts | 0 .../src/utils/isUnitLess.ts | 0 .../src/utils/pre-linaria-plugin.ts | 0 .../src/utils/preprocessor.ts | 0 .../src/utils/processCssObject.ts | 0 .../src/utils/sxObjectExtractor.ts | 2 +- .../src/utils/sxPropConverter.ts | 0 .../src/utils/valueToLiteral.ts | 2 +- .../styles.css | 0 packages/pigment-css-react/tests/Box.spec.tsx | 22 + .../tests/README.md | 0 .../tests/css/css.test.ts | 0 .../tests/css/fixtures/css.input.js | 0 .../tests/css/fixtures/css.output.css | 2 +- .../tests/css/fixtures/css.output.js | 1 + .../fixtures/keyframes-theme.input.js | 29 + .../fixtures/keyframes-theme.output.css | 22 + .../fixtures/keyframes-theme.output.js | 2 + .../keyframes/fixtures/keyframes.input.js | 10 + .../keyframes/fixtures/keyframes.output.css | 16 + .../keyframes/fixtures/keyframes.output.js | 2 + .../tests/keyframes/keyframes.test.ts | 36 + .../styled/fixtures/styled-theme.input.js | 36 + .../styled/fixtures/styled-theme.output.css} | 17 +- .../styled/fixtures/styled-theme.output.js | 10 + .../tests/styled/fixtures/styled.input.js | 7 +- .../tests/styled/fixtures/styled.output.css | 26 + .../tests/styled/fixtures/styled.output.js | 6 +- .../tests/styled/styled.test.ts | 47 + .../tests/sx/fixtures/sxProps.input.js | 0 .../tests/sx/fixtures/sxProps.output.css | 18 +- .../tests/sx/fixtures/sxProps.output.js | 16 +- .../tests/sx/fixtures/sxProps2.input.js | 0 .../tests/sx/fixtures/sxProps2.output.css | 8 +- .../tests/sx/fixtures/sxProps2.output.js | 4 +- .../tests/sx/sx.test.ts | 0 .../tests/testUtils.ts | 0 .../theme/index.d.ts | 0 .../theme/index.js | 0 .../theme/index.mjs | 0 .../tsconfig.build.json | 0 .../tsconfig.json | 0 .../tsup.config.ts | 6 +- .../.gitignore | 0 .../package.json | 6 +- .../src/index.ts | 60 +- .../tsconfig.build.json | 0 .../tsconfig.json | 4 +- .../tsup.config.ts | 2 +- .../.gitignore | 0 .../package.json | 8 +- .../src/index.ts | 0 .../src/vite-plugin.ts | 0 .../tsconfig.build.json | 0 .../tsconfig.json | 4 +- .../tsup.config.ts | 2 +- packages/pigment-nextjs-plugin/LICENSE | 21 + packages/pigment-react/LICENSE | 21 + packages/pigment-react/README.md | 718 +------ .../processors/base-processor-Bkjcr8yc.d.mts | 10 + .../processors/base-processor-Bkjcr8yc.d.ts | 10 + .../processors/chunk-3NOOOXUY.js | 25 + .../processors/chunk-3NOOOXUY.js.map | 1 + .../processors/chunk-7L3GVF7S.js | 73 + .../processors/chunk-7L3GVF7S.js.map | 1 + .../processors/chunk-DTNT7PFI.mjs | 111 ++ .../processors/chunk-DTNT7PFI.mjs.map | 1 + .../processors/chunk-HIMMIWAJ.mjs | 69 + .../processors/chunk-HIMMIWAJ.mjs.map | 1 + .../processors/chunk-IUHN7KUC.mjs | 18 + .../processors/chunk-IUHN7KUC.mjs.map | 1 + .../processors/chunk-MRLAB7WR.js | 113 ++ .../processors/chunk-MRLAB7WR.js.map | 1 + .../processors/chunk-QVEBVMS6.mjs | 251 +++ .../processors/chunk-QVEBVMS6.mjs.map | 1 + .../processors/chunk-SBKEDKMF.js | 278 +++ .../processors/chunk-SBKEDKMF.js.map | 1 + .../processors/createUseThemeProps.d.mts | 15 + .../processors/createUseThemeProps.d.ts | 15 + .../processors/createUseThemeProps.js | 61 + .../processors/createUseThemeProps.js.map | 1 + .../processors/createUseThemeProps.mjs | 59 + .../processors/createUseThemeProps.mjs.map | 1 + packages/pigment-react/processors/css.d.mts | 35 + packages/pigment-react/processors/css.d.ts | 35 + packages/pigment-react/processors/css.js | 153 ++ packages/pigment-react/processors/css.js.map | 1 + packages/pigment-react/processors/css.mjs | 147 ++ packages/pigment-react/processors/css.mjs.map | 1 + .../processors/generateAtomics.d.mts | 31 + .../processors/generateAtomics.d.ts | 31 + .../processors/generateAtomics.js | 160 ++ .../processors/generateAtomics.js.map | 1 + .../processors/generateAtomics.mjs | 154 ++ .../processors/generateAtomics.mjs.map | 1 + .../pigment-react/processors/keyframes.d.mts | 22 + .../pigment-react/processors/keyframes.d.ts | 22 + .../pigment-react/processors/keyframes.js | 145 ++ .../pigment-react/processors/keyframes.js.map | 1 + .../pigment-react/processors/keyframes.mjs | 143 ++ .../processors/keyframes.mjs.map | 1 + .../pigment-react/processors/styled.d.mts | 245 +++ packages/pigment-react/processors/styled.d.ts | 245 +++ packages/pigment-react/processors/styled.js | 399 ++++ .../pigment-react/processors/styled.js.map | 1 + packages/pigment-react/processors/styled.mjs | 397 ++++ .../pigment-react/processors/styled.mjs.map | 1 + packages/pigment-react/processors/sx.d.mts | 20 + packages/pigment-react/processors/sx.d.ts | 20 + packages/pigment-react/processors/sx.js | 153 ++ packages/pigment-react/processors/sx.js.map | 1 + packages/pigment-react/processors/sx.mjs | 151 ++ packages/pigment-react/processors/sx.mjs.map | 1 + .../tests/css/fixtures/css.output.js | 1 - .../keyframes/fixtures/keyframes.output.css | 8 - .../keyframes/fixtures/keyframes.output.js | 1 - .../tests/keyframes/keyframes.test.ts | 13 - .../pigment-react/tests/styled/styled.test.ts | 40 - .../pigment-react/utils/chunk-RY3PR5BX.js | 49 + .../pigment-react/utils/chunk-RY3PR5BX.js.map | 1 + .../pigment-react/utils/chunk-XZYQOGJP.mjs | 44 + .../utils/chunk-XZYQOGJP.mjs.map | 1 + packages/pigment-react/utils/index.d.mts | 121 ++ packages/pigment-react/utils/index.d.ts | 121 ++ packages/pigment-react/utils/index.js | 148 ++ packages/pigment-react/utils/index.js.map | 1 + packages/pigment-react/utils/index.mjs | 139 ++ packages/pigment-react/utils/index.mjs.map | 1 + .../utils/pre-linaria-plugin.d.mts | 8 + .../utils/pre-linaria-plugin.d.ts | 8 + .../pigment-react/utils/pre-linaria-plugin.js | 260 +++ .../utils/pre-linaria-plugin.js.map | 1 + .../utils/pre-linaria-plugin.mjs | 258 +++ .../utils/pre-linaria-plugin.mjs.map | 1 + packages/pigment-unplugin/LICENSE | 21 + packages/pigment-vite-plugin/LICENSE | 21 + packages/rsc-builder/buildRsc.ts | 4 - packages/rsc-builder/package.json | 2 +- packages/test-utils/src/createMount.tsx | 2 +- packages/test-utils/src/createRenderer.tsx | 2 +- packages/test-utils/src/initMatchers.ts | 2 +- pnpm-lock.yaml | 1175 ++++++------ scripts/README.md | 4 +- scripts/copyFiles.mjs | 7 +- scripts/copyFilesUtils.mjs | 12 +- scripts/coreTypeScriptProjects.js | 4 - scripts/generateProptypes.ts | 5 +- scripts/pigmentcss-render-mui-demos.mjs | 8 +- scripts/releaseChangelog.mjs | 2 +- scripts/sizeSnapshot/webpack.config.js | 18 - test/README.md | 14 +- test/bundling/README.md | 4 +- test/e2e/README.md | 2 +- .../mui-system/theme-scoping.test.tsx | 43 - test/karma.conf.js | 1 - test/karma.tests.js | 7 - test/package.json | 1 - test/regressions/README.md | 4 +- .../ButtonNext/IconLabelButtonsNext.js | 26 - .../ButtonNext/MultilineButtonNext.js | 14 - tsconfig.json | 16 +- tsup.config.ts | 3 + webpackBaseConfig.js | 5 +- 981 files changed, 11141 insertions(+), 45415 deletions(-) rename apps/{pigment-next-app => pigment-css-next-app}/.eslintrc (100%) rename apps/{pigment-next-app => pigment-css-next-app}/.gitignore (100%) rename apps/{pigment-next-app => pigment-css-next-app}/README.md (100%) rename apps/{pigment-next-app => pigment-css-next-app}/next.config.js (100%) rename apps/{pigment-next-app => pigment-css-next-app}/package.json (80%) rename apps/{pigment-next-app => pigment-css-next-app}/public/next.svg (100%) rename apps/{pigment-next-app => pigment-css-next-app}/public/vercel.svg (100%) rename apps/{pigment-next-app => pigment-css-next-app}/src/app/box/page.tsx (100%) rename apps/{pigment-next-app => pigment-css-next-app}/src/app/favicon.ico (100%) rename apps/{pigment-next-app => pigment-css-next-app}/src/app/globals.css (100%) rename apps/{pigment-next-app => pigment-css-next-app}/src/app/layout.tsx (100%) rename apps/{pigment-next-app => pigment-css-next-app}/src/app/material-ui/layout.tsx (100%) rename apps/{pigment-next-app => pigment-css-next-app}/src/app/material-ui/page.tsx (100%) create mode 100644 apps/pigment-css-next-app/src/app/material-ui/react-accordion/page.tsx rename apps/{pigment-next-app => pigment-css-next-app}/src/app/material-ui/react-alert/page.tsx (100%) create mode 100644 apps/pigment-css-next-app/src/app/material-ui/react-autocomplete/page.tsx rename apps/{pigment-next-app => pigment-css-next-app}/src/app/material-ui/react-avatar/page.tsx (100%) rename apps/{pigment-next-app => pigment-css-next-app}/src/app/material-ui/react-badge/page.tsx (100%) create mode 100644 apps/pigment-css-next-app/src/app/material-ui/react-breadcrumbs/page.tsx create mode 100644 apps/pigment-css-next-app/src/app/material-ui/react-slider/page.tsx rename apps/{pigment-next-app => pigment-css-next-app}/src/app/material-ui/react-switch/page.tsx (100%) rename apps/{pigment-next-app => pigment-css-next-app}/src/app/page.module.css (100%) rename apps/{pigment-next-app => pigment-css-next-app}/src/app/page.tsx (100%) rename apps/{pigment-next-app => pigment-css-next-app}/src/app/theme.ts (100%) rename apps/{pigment-next-app => pigment-css-next-app}/src/augment.d.ts (100%) rename apps/{pigment-next-app => pigment-css-next-app}/src/components/Box.jsx (100%) rename apps/{pigment-next-app => pigment-css-next-app}/tsconfig.json (93%) rename apps/{pigment-vite-app => pigment-css-vite-app}/.eslintrc (100%) rename apps/{pigment-vite-app => pigment-css-vite-app}/.gitignore (100%) rename apps/{pigment-vite-app => pigment-css-vite-app}/README.md (100%) rename apps/{pigment-vite-app => pigment-css-vite-app}/index.html (100%) rename apps/{pigment-vite-app => pigment-css-vite-app}/package.json (88%) rename apps/{pigment-vite-app => pigment-css-vite-app}/postcss.config.cjs (100%) rename apps/{pigment-vite-app => pigment-css-vite-app}/src/App.tsx (100%) rename apps/{pigment-vite-app => pigment-css-vite-app}/src/Box.jsx (100%) rename apps/{pigment-vite-app => pigment-css-vite-app}/src/Layout.tsx (100%) rename apps/{pigment-vite-app => pigment-css-vite-app}/src/Slider/ZeroSlider.test.jsx (100%) rename apps/{pigment-vite-app => pigment-css-vite-app}/src/Slider/ZeroSlider.tsx (100%) rename apps/{pigment-vite-app => pigment-css-vite-app}/src/augment.ts (100%) rename apps/{pigment-vite-app => pigment-css-vite-app}/src/component.tsx (100%) rename apps/{pigment-vite-app => pigment-css-vite-app}/src/extend-zero.ts (100%) rename apps/{pigment-vite-app => pigment-css-vite-app}/src/main.tsx (100%) rename apps/{pigment-vite-app => pigment-css-vite-app}/src/pages/index.tsx (100%) create mode 100644 apps/pigment-css-vite-app/src/pages/material-ui/react-accordion.tsx rename apps/{pigment-vite-app => pigment-css-vite-app}/src/pages/material-ui/react-alert.tsx (100%) create mode 100644 apps/pigment-css-vite-app/src/pages/material-ui/react-autocomplete.tsx rename apps/{pigment-vite-app => pigment-css-vite-app}/src/pages/material-ui/react-avatar.tsx (100%) rename apps/{pigment-vite-app => pigment-css-vite-app}/src/pages/material-ui/react-badge.tsx (100%) create mode 100644 apps/pigment-css-vite-app/src/pages/material-ui/react-breadcrumbs.tsx create mode 100644 apps/pigment-css-vite-app/src/pages/material-ui/react-slider.tsx rename apps/{pigment-vite-app => pigment-css-vite-app}/src/pages/material-ui/react-switch.tsx (100%) rename apps/{pigment-vite-app => pigment-css-vite-app}/tsconfig.json (86%) rename apps/{pigment-vite-app => pigment-css-vite-app}/vite-env.d.ts (100%) rename apps/{pigment-vite-app => pigment-css-vite-app}/vite.config.ts (100%) create mode 100644 apps/pigment-next-app/next-env.d.ts create mode 100644 apps/pigment-next-app/src/app/material-ui/react-modal/page.tsx delete mode 100644 apps/pigment-next-app/src/app/slider/page.tsx delete mode 100644 apps/pigment-next-app/src/components/Slider/ZeroSlider.tsx create mode 100644 apps/pigment-vite-app/src/pages/material-ui/react-modal.tsx delete mode 100644 docs/data/material/components/badges/BadgeMaterialYouPlayground.js delete mode 100644 docs/data/material/components/button-group/ButtonGroupMaterialYouPlayground.js delete mode 100644 docs/data/material/components/buttons/ButtonMaterialYouPlayground.js delete mode 100644 docs/data/material/components/chips/ChipMaterialYouPlayground.js delete mode 100644 docs/data/material/components/dividers/DividerMaterialYouPlayground.js delete mode 100644 docs/data/material/components/progress/ProgressMaterialYouPlayground.js delete mode 100644 docs/data/material/components/slider/SliderMaterialYouPlayground.js delete mode 100644 docs/data/material/getting-started/templates/checkout/InfoMobile.tsx.preview delete mode 100644 docs/data/material/guides/material-3-components/MD3AndV5Usage.js delete mode 100644 docs/data/material/guides/material-3-components/MD3AndV5Usage.tsx delete mode 100644 docs/data/material/guides/material-3-components/MD3AndV5Usage.tsx.preview delete mode 100644 docs/data/material/guides/material-3-components/MD3ButtonUsage.js delete mode 100644 docs/data/material/guides/material-3-components/MD3ButtonUsage.tsx delete mode 100644 docs/data/material/guides/material-3-components/MD3ButtonUsage.tsx.preview delete mode 100644 docs/data/material/guides/material-3-components/MD3Theming.js delete mode 100644 docs/data/material/guides/material-3-components/MD3Theming.tsx delete mode 100644 docs/data/material/guides/material-3-components/MD3Theming.tsx.preview delete mode 100644 docs/data/material/guides/material-3-components/material-3-components.md create mode 100644 docs/mui-vale.zip create mode 100644 docs/mui-vale/.vale.ini create mode 100644 docs/mui-vale/styles/MUI/CorrectReferenceAllCases.yml create mode 100644 docs/mui-vale/styles/MUI/CorrectRerenceCased.yml create mode 100644 docs/mui-vale/styles/MUI/GoogleLatin.yml rename docs/{writing-rules/BrandName.yml => mui-vale/styles/MUI/MuiBrandName.yml} (80%) create mode 100644 docs/mui-vale/styles/MUI/NoBritish.yml rename docs/{writing-rules => mui-vale/styles/MUI}/NoCompanyName.yml (67%) create mode 100644 docs/pages/blog/bringing-consistency-to-material-ui-customization-apis.js create mode 100644 docs/pages/blog/bringing-consistency-to-material-ui-customization-apis.md rename docs/pages/{material-ui/guides/material-3-components.js => experiments/docs/custom-components.js} (60%) create mode 100644 docs/pages/experiments/docs/custom-components.md delete mode 100644 docs/pages/experiments/material-next/menu.tsx delete mode 100644 docs/pages/experiments/md3/buttons.tsx delete mode 100644 docs/pages/experiments/md3/index.tsx delete mode 100644 docs/pages/experiments/md3/inputs.tsx create mode 100644 docs/public/static/blog/bringing-consistency-to-material-ui-customization-apis/card.png delete mode 100644 docs/src/modules/components/MaterialYouUsageDemo.tsx delete mode 100644 docs/writing-rules.zip delete mode 100644 docs/writing-rules/ComponentNameConventions.yml delete mode 100644 docs/writing-rules/ComposedWords.yml delete mode 100644 docs/writing-rules/NamingConventions.yml delete mode 100644 docs/writing-rules/Typos.yml rename netlify/functions/{feedback-management.js => feedback-management.mts} (82%) rename {packages => packages-internal}/docs-utils/.npmignore (100%) rename {packages => packages-internal}/docs-utils/CHANGELOG.md (64%) rename {packages => packages-internal}/docs-utils/README.md (89%) rename {packages => packages-internal}/docs-utils/package.json (87%) rename {packages => packages-internal}/docs-utils/src/ComponentClassDefinition.ts (100%) rename {packages => packages-internal}/docs-utils/src/createTypeScriptProject.ts (100%) rename {packages => packages-internal}/docs-utils/src/getPropsFromComponentNode.ts (100%) rename {packages => packages-internal}/docs-utils/src/index.ts (100%) rename {packages => packages-internal}/docs-utils/tsconfig.build.json (100%) rename {packages => packages-internal}/docs-utils/tsconfig.json (100%) delete mode 100644 packages/mui-material-next/CONTRIBUTING.md delete mode 100644 packages/mui-material-next/README.md delete mode 100644 packages/mui-material-next/migration.md delete mode 100644 packages/mui-material-next/package.json delete mode 100644 packages/mui-material-next/src/Badge/Badge.spec.tsx delete mode 100644 packages/mui-material-next/src/Badge/Badge.test.tsx delete mode 100644 packages/mui-material-next/src/Badge/Badge.tsx delete mode 100644 packages/mui-material-next/src/Badge/Badge.types.ts delete mode 100644 packages/mui-material-next/src/Badge/badgeClasses.ts delete mode 100644 packages/mui-material-next/src/Badge/index.ts delete mode 100644 packages/mui-material-next/src/Button/Button.spec.tsx delete mode 100644 packages/mui-material-next/src/Button/Button.test.js delete mode 100644 packages/mui-material-next/src/Button/Button.tsx delete mode 100644 packages/mui-material-next/src/Button/Button.types.ts delete mode 100644 packages/mui-material-next/src/Button/buttonClasses.ts delete mode 100644 packages/mui-material-next/src/Button/index.ts delete mode 100644 packages/mui-material-next/src/ButtonBase/ButtonBase.test.tsx delete mode 100644 packages/mui-material-next/src/ButtonBase/ButtonBase.tsx delete mode 100644 packages/mui-material-next/src/ButtonBase/ButtonBase.types.ts delete mode 100644 packages/mui-material-next/src/ButtonBase/Ripple.test.js delete mode 100644 packages/mui-material-next/src/ButtonBase/Ripple.tsx delete mode 100644 packages/mui-material-next/src/ButtonBase/TouchRipple.test.js delete mode 100644 packages/mui-material-next/src/ButtonBase/TouchRipple.tsx delete mode 100644 packages/mui-material-next/src/ButtonBase/TouchRipple.types.ts delete mode 100644 packages/mui-material-next/src/ButtonBase/buttonBaseClasses.ts delete mode 100644 packages/mui-material-next/src/ButtonBase/index.ts delete mode 100644 packages/mui-material-next/src/ButtonBase/touchRippleClasses.ts delete mode 100644 packages/mui-material-next/src/ButtonBase/useTouchRipple.ts delete mode 100644 packages/mui-material-next/src/ButtonGroup/ButtonGroup.test.tsx delete mode 100644 packages/mui-material-next/src/ButtonGroup/ButtonGroup.tsx delete mode 100644 packages/mui-material-next/src/ButtonGroup/ButtonGroup.types.ts delete mode 100644 packages/mui-material-next/src/ButtonGroup/ButtonGroupButtonContext.ts delete mode 100644 packages/mui-material-next/src/ButtonGroup/ButtonGroupContext.ts delete mode 100644 packages/mui-material-next/src/ButtonGroup/buttonGroupClasses.ts delete mode 100644 packages/mui-material-next/src/ButtonGroup/index.ts delete mode 100644 packages/mui-material-next/src/Chip/Chip.test.tsx delete mode 100644 packages/mui-material-next/src/Chip/Chip.tsx delete mode 100644 packages/mui-material-next/src/Chip/Chip.types.ts delete mode 100644 packages/mui-material-next/src/Chip/chipClasses.ts delete mode 100644 packages/mui-material-next/src/Chip/index.ts delete mode 100644 packages/mui-material-next/src/CircularProgress/CircularProgress.test.tsx delete mode 100644 packages/mui-material-next/src/CircularProgress/CircularProgress.tsx delete mode 100644 packages/mui-material-next/src/CircularProgress/CircularProgress.types.ts delete mode 100644 packages/mui-material-next/src/CircularProgress/circularProgressClasses.ts delete mode 100644 packages/mui-material-next/src/CircularProgress/index.ts delete mode 100644 packages/mui-material-next/src/Divider/Divider.test.tsx delete mode 100644 packages/mui-material-next/src/Divider/Divider.tsx delete mode 100644 packages/mui-material-next/src/Divider/Divider.types.ts delete mode 100644 packages/mui-material-next/src/Divider/dividerClasses.ts delete mode 100644 packages/mui-material-next/src/Divider/index.ts delete mode 100644 packages/mui-material-next/src/FilledInput/FilledInput.spec.tsx delete mode 100644 packages/mui-material-next/src/FilledInput/FilledInput.test.tsx delete mode 100644 packages/mui-material-next/src/FilledInput/FilledInput.tsx delete mode 100644 packages/mui-material-next/src/FilledInput/FilledInput.types.ts delete mode 100644 packages/mui-material-next/src/FilledInput/filledInputClasses.ts delete mode 100644 packages/mui-material-next/src/FilledInput/index.ts delete mode 100644 packages/mui-material-next/src/FormControl/FormControl.test.tsx delete mode 100644 packages/mui-material-next/src/FormControl/FormControl.tsx delete mode 100644 packages/mui-material-next/src/FormControl/FormControl.types.ts delete mode 100644 packages/mui-material-next/src/FormControl/FormControlContext.ts delete mode 100644 packages/mui-material-next/src/FormControl/formControlClasses.ts delete mode 100644 packages/mui-material-next/src/FormControl/formControlState.js delete mode 100644 packages/mui-material-next/src/FormControl/index.ts delete mode 100644 packages/mui-material-next/src/FormControl/useFormControl.ts delete mode 100644 packages/mui-material-next/src/FormHelperText/FormHelperText.spec.tsx delete mode 100644 packages/mui-material-next/src/FormHelperText/FormHelperText.test.tsx delete mode 100644 packages/mui-material-next/src/FormHelperText/FormHelperText.tsx delete mode 100644 packages/mui-material-next/src/FormHelperText/FormHelperText.types.ts delete mode 100644 packages/mui-material-next/src/FormHelperText/formHelperTextClasses.ts delete mode 100644 packages/mui-material-next/src/FormHelperText/index.ts delete mode 100644 packages/mui-material-next/src/FormLabel/FormLabel.test.tsx delete mode 100644 packages/mui-material-next/src/FormLabel/FormLabel.tsx delete mode 100644 packages/mui-material-next/src/FormLabel/FormLabel.types.ts delete mode 100644 packages/mui-material-next/src/FormLabel/formLabelClasses.ts delete mode 100644 packages/mui-material-next/src/FormLabel/index.ts delete mode 100644 packages/mui-material-next/src/IconButton/IconButton.d.ts delete mode 100644 packages/mui-material-next/src/IconButton/IconButton.js delete mode 100644 packages/mui-material-next/src/IconButton/IconButton.test.js delete mode 100644 packages/mui-material-next/src/IconButton/iconButtonClasses.ts delete mode 100644 packages/mui-material-next/src/IconButton/index.d.ts delete mode 100644 packages/mui-material-next/src/IconButton/index.js delete mode 100644 packages/mui-material-next/src/InputAdornment/InputAdornment.d.ts delete mode 100644 packages/mui-material-next/src/InputAdornment/InputAdornment.js delete mode 100644 packages/mui-material-next/src/InputAdornment/InputAdornment.test.js delete mode 100644 packages/mui-material-next/src/InputAdornment/index.d.ts delete mode 100644 packages/mui-material-next/src/InputAdornment/index.js delete mode 100644 packages/mui-material-next/src/InputAdornment/inputAdornmentClasses.ts delete mode 100644 packages/mui-material-next/src/InputBase/InputBase.spec.tsx delete mode 100644 packages/mui-material-next/src/InputBase/InputBase.test.tsx delete mode 100644 packages/mui-material-next/src/InputBase/InputBase.tsx delete mode 100644 packages/mui-material-next/src/InputBase/InputBase.types.ts delete mode 100644 packages/mui-material-next/src/InputBase/index.d.ts delete mode 100644 packages/mui-material-next/src/InputBase/index.js delete mode 100644 packages/mui-material-next/src/InputBase/inputBaseClasses.ts delete mode 100644 packages/mui-material-next/src/InputBase/utils.test.js delete mode 100644 packages/mui-material-next/src/InputBase/utils.ts delete mode 100644 packages/mui-material-next/src/InputLabel/InputLabel.spec.tsx delete mode 100644 packages/mui-material-next/src/InputLabel/InputLabel.test.tsx delete mode 100644 packages/mui-material-next/src/InputLabel/InputLabel.tsx delete mode 100644 packages/mui-material-next/src/InputLabel/InputLabel.types.ts delete mode 100644 packages/mui-material-next/src/InputLabel/index.ts delete mode 100644 packages/mui-material-next/src/InputLabel/inputLabelClasses.ts delete mode 100644 packages/mui-material-next/src/LinearProgress/LinearProgress.test.tsx delete mode 100644 packages/mui-material-next/src/LinearProgress/LinearProgress.tsx delete mode 100644 packages/mui-material-next/src/LinearProgress/LinearProgress.types.ts delete mode 100644 packages/mui-material-next/src/LinearProgress/index.ts delete mode 100644 packages/mui-material-next/src/LinearProgress/linearProgressClasses.ts delete mode 100644 packages/mui-material-next/src/List/List.d.ts delete mode 100644 packages/mui-material-next/src/List/List.js delete mode 100644 packages/mui-material-next/src/List/List.spec.tsx delete mode 100644 packages/mui-material-next/src/List/List.test.js delete mode 100644 packages/mui-material-next/src/List/ListContext.d.ts delete mode 100644 packages/mui-material-next/src/List/ListContext.js delete mode 100644 packages/mui-material-next/src/List/index.d.ts delete mode 100644 packages/mui-material-next/src/List/index.js delete mode 100644 packages/mui-material-next/src/List/listClasses.ts delete mode 100644 packages/mui-material-next/src/ListItem/ListItem.d.ts delete mode 100644 packages/mui-material-next/src/ListItem/ListItem.js delete mode 100644 packages/mui-material-next/src/ListItem/ListItem.spec.tsx delete mode 100644 packages/mui-material-next/src/ListItem/ListItem.test.js delete mode 100644 packages/mui-material-next/src/ListItem/index.d.ts delete mode 100644 packages/mui-material-next/src/ListItem/index.js delete mode 100644 packages/mui-material-next/src/ListItem/listItemClasses.ts delete mode 100644 packages/mui-material-next/src/ListItemAvatar/ListItemAvatar.d.ts delete mode 100644 packages/mui-material-next/src/ListItemAvatar/ListItemAvatar.js delete mode 100644 packages/mui-material-next/src/ListItemAvatar/ListItemAvatar.test.js delete mode 100644 packages/mui-material-next/src/ListItemAvatar/index.d.ts delete mode 100644 packages/mui-material-next/src/ListItemAvatar/index.js delete mode 100644 packages/mui-material-next/src/ListItemAvatar/listItemAvatarClasses.ts delete mode 100644 packages/mui-material-next/src/ListItemButton/ListItemButton.d.ts delete mode 100644 packages/mui-material-next/src/ListItemButton/ListItemButton.js delete mode 100644 packages/mui-material-next/src/ListItemButton/ListItemButton.test.js delete mode 100644 packages/mui-material-next/src/ListItemButton/index.d.ts delete mode 100644 packages/mui-material-next/src/ListItemButton/index.js delete mode 100644 packages/mui-material-next/src/ListItemButton/listItemButtonClasses.ts delete mode 100644 packages/mui-material-next/src/ListItemIcon/ListItemIcon.d.ts delete mode 100644 packages/mui-material-next/src/ListItemIcon/ListItemIcon.js delete mode 100644 packages/mui-material-next/src/ListItemIcon/ListItemIcon.test.js delete mode 100644 packages/mui-material-next/src/ListItemIcon/index.d.ts delete mode 100644 packages/mui-material-next/src/ListItemIcon/index.js delete mode 100644 packages/mui-material-next/src/ListItemIcon/listItemIconClasses.ts delete mode 100644 packages/mui-material-next/src/ListItemSecondaryAction/ListItemSecondaryAction.d.ts delete mode 100644 packages/mui-material-next/src/ListItemSecondaryAction/ListItemSecondaryAction.js delete mode 100644 packages/mui-material-next/src/ListItemSecondaryAction/ListItemSecondaryAction.test.js delete mode 100644 packages/mui-material-next/src/ListItemSecondaryAction/index.d.ts delete mode 100644 packages/mui-material-next/src/ListItemSecondaryAction/index.js delete mode 100644 packages/mui-material-next/src/ListItemSecondaryAction/listItemSecondaryActionClasses.ts delete mode 100644 packages/mui-material-next/src/ListItemText/ListItemText.d.ts delete mode 100644 packages/mui-material-next/src/ListItemText/ListItemText.js delete mode 100644 packages/mui-material-next/src/ListItemText/ListItemText.spec.tsx delete mode 100644 packages/mui-material-next/src/ListItemText/ListItemText.test.js delete mode 100644 packages/mui-material-next/src/ListItemText/index.d.ts delete mode 100644 packages/mui-material-next/src/ListItemText/index.js delete mode 100644 packages/mui-material-next/src/ListItemText/listItemTextClasses.ts delete mode 100644 packages/mui-material-next/src/ListSubheader/ListSubheader.d.ts delete mode 100644 packages/mui-material-next/src/ListSubheader/ListSubheader.js delete mode 100644 packages/mui-material-next/src/ListSubheader/ListSubheader.test.js delete mode 100644 packages/mui-material-next/src/ListSubheader/index.d.ts delete mode 100644 packages/mui-material-next/src/ListSubheader/index.js delete mode 100644 packages/mui-material-next/src/ListSubheader/listSubheaderClasses.ts delete mode 100644 packages/mui-material-next/src/Menu/Menu.test.tsx delete mode 100644 packages/mui-material-next/src/Menu/Menu.tsx delete mode 100644 packages/mui-material-next/src/Menu/Menu.types.ts delete mode 100644 packages/mui-material-next/src/Menu/index.ts delete mode 100644 packages/mui-material-next/src/Menu/menuClasses.ts delete mode 100644 packages/mui-material-next/src/MenuItem/MenuItem.spec.tsx delete mode 100644 packages/mui-material-next/src/MenuItem/MenuItem.test.tsx delete mode 100644 packages/mui-material-next/src/MenuItem/MenuItem.tsx delete mode 100644 packages/mui-material-next/src/MenuItem/MenuItem.types.ts delete mode 100644 packages/mui-material-next/src/MenuItem/index.ts delete mode 100644 packages/mui-material-next/src/MenuItem/menuItemClasses.ts delete mode 100644 packages/mui-material-next/src/Option/Option.spec.tsx delete mode 100644 packages/mui-material-next/src/Option/Option.test.tsx delete mode 100644 packages/mui-material-next/src/Option/Option.tsx delete mode 100644 packages/mui-material-next/src/Option/Option.types.ts delete mode 100644 packages/mui-material-next/src/Option/index.ts delete mode 100644 packages/mui-material-next/src/Option/optionClasses.ts delete mode 100644 packages/mui-material-next/src/OutlinedInput/NotchedOutline.d.ts delete mode 100644 packages/mui-material-next/src/OutlinedInput/NotchedOutline.js delete mode 100644 packages/mui-material-next/src/OutlinedInput/NotchedOutline.test.js delete mode 100644 packages/mui-material-next/src/OutlinedInput/OutlinedInput.d.ts delete mode 100644 packages/mui-material-next/src/OutlinedInput/OutlinedInput.js delete mode 100644 packages/mui-material-next/src/OutlinedInput/OutlinedInput.test.js delete mode 100644 packages/mui-material-next/src/OutlinedInput/index.d.ts delete mode 100644 packages/mui-material-next/src/OutlinedInput/index.js delete mode 100644 packages/mui-material-next/src/OutlinedInput/outlinedInputClasses.ts delete mode 100644 packages/mui-material-next/src/Select/Select.d.ts delete mode 100644 packages/mui-material-next/src/Select/Select.js delete mode 100644 packages/mui-material-next/src/Select/Select.spec.tsx delete mode 100644 packages/mui-material-next/src/Select/Select.test.js delete mode 100644 packages/mui-material-next/src/Select/SelectInput.d.ts delete mode 100644 packages/mui-material-next/src/Select/SelectInput.js delete mode 100644 packages/mui-material-next/src/Select/index.d.ts delete mode 100644 packages/mui-material-next/src/Select/index.js delete mode 100644 packages/mui-material-next/src/Select/selectClasses.ts delete mode 100644 packages/mui-material-next/src/Slider/Slider.spec.tsx delete mode 100644 packages/mui-material-next/src/Slider/Slider.test.js delete mode 100644 packages/mui-material-next/src/Slider/Slider.tsx delete mode 100644 packages/mui-material-next/src/Slider/Slider.types.ts delete mode 100644 packages/mui-material-next/src/Slider/index.ts delete mode 100644 packages/mui-material-next/src/Slider/sliderClasses.ts delete mode 100644 packages/mui-material-next/src/Slider/useSliderElementsOverlap.ts delete mode 100644 packages/mui-material-next/src/Snackbar/Snackbar.d.ts delete mode 100644 packages/mui-material-next/src/Snackbar/Snackbar.js delete mode 100644 packages/mui-material-next/src/Snackbar/Snackbar.test.js delete mode 100644 packages/mui-material-next/src/Snackbar/index.d.ts delete mode 100644 packages/mui-material-next/src/Snackbar/index.js delete mode 100644 packages/mui-material-next/src/Snackbar/snackbarClasses.ts delete mode 100644 packages/mui-material-next/src/SnackbarContent/SnackbarContent.d.ts delete mode 100644 packages/mui-material-next/src/SnackbarContent/SnackbarContent.js delete mode 100644 packages/mui-material-next/src/SnackbarContent/SnackbarContent.test.js delete mode 100644 packages/mui-material-next/src/SnackbarContent/index.d.ts delete mode 100644 packages/mui-material-next/src/SnackbarContent/index.js delete mode 100644 packages/mui-material-next/src/SnackbarContent/snackbarContentClasses.ts delete mode 100644 packages/mui-material-next/src/Switch/Switch.test.tsx delete mode 100644 packages/mui-material-next/src/Switch/Switch.tsx delete mode 100644 packages/mui-material-next/src/Switch/Switch.types.ts delete mode 100644 packages/mui-material-next/src/Switch/index.ts delete mode 100644 packages/mui-material-next/src/Switch/switchClasses.ts delete mode 100644 packages/mui-material-next/src/Tab/Tab.d.ts delete mode 100644 packages/mui-material-next/src/Tab/Tab.js delete mode 100644 packages/mui-material-next/src/Tab/Tab.test.js delete mode 100644 packages/mui-material-next/src/Tab/index.d.ts delete mode 100644 packages/mui-material-next/src/Tab/index.js delete mode 100644 packages/mui-material-next/src/Tab/tabClasses.ts delete mode 100644 packages/mui-material-next/src/TabScrollButton/TabScrollButton.d.ts delete mode 100644 packages/mui-material-next/src/TabScrollButton/TabScrollButton.js delete mode 100644 packages/mui-material-next/src/TabScrollButton/TabScrollButton.test.js delete mode 100644 packages/mui-material-next/src/TabScrollButton/index.d.ts delete mode 100644 packages/mui-material-next/src/TabScrollButton/index.js delete mode 100644 packages/mui-material-next/src/TabScrollButton/tabScrollButtonClasses.ts delete mode 100644 packages/mui-material-next/src/TablePagination/TablePagination.d.ts delete mode 100644 packages/mui-material-next/src/TablePagination/TablePagination.js delete mode 100644 packages/mui-material-next/src/TablePagination/TablePagination.spec.tsx delete mode 100644 packages/mui-material-next/src/TablePagination/TablePagination.test.js delete mode 100644 packages/mui-material-next/src/TablePagination/index.d.ts delete mode 100644 packages/mui-material-next/src/TablePagination/index.js delete mode 100644 packages/mui-material-next/src/TablePagination/tablePaginationClasses.ts delete mode 100644 packages/mui-material-next/src/Tabs/ScrollbarSize.js delete mode 100644 packages/mui-material-next/src/Tabs/ScrollbarSize.test.js delete mode 100644 packages/mui-material-next/src/Tabs/Tabs.d.ts delete mode 100644 packages/mui-material-next/src/Tabs/Tabs.js delete mode 100644 packages/mui-material-next/src/Tabs/Tabs.spec.tsx delete mode 100644 packages/mui-material-next/src/Tabs/Tabs.test.js delete mode 100644 packages/mui-material-next/src/Tabs/TabsList.js delete mode 100644 packages/mui-material-next/src/Tabs/TabsListContext.js delete mode 100644 packages/mui-material-next/src/Tabs/index.d.ts delete mode 100644 packages/mui-material-next/src/Tabs/index.js delete mode 100644 packages/mui-material-next/src/Tabs/tabsClasses.ts delete mode 100644 packages/mui-material-next/src/index.test.js delete mode 100644 packages/mui-material-next/src/index.ts delete mode 100644 packages/mui-material-next/src/internal/svg-icons/ArrowDropDown.tsx delete mode 100644 packages/mui-material-next/src/internal/svg-icons/CheckBox.tsx delete mode 100644 packages/mui-material-next/src/internal/svg-icons/Clear.tsx delete mode 100644 packages/mui-material-next/src/styles/CssVarsProvider.tsx delete mode 100644 packages/mui-material-next/src/styles/Theme.types.ts delete mode 100644 packages/mui-material-next/src/styles/createDarkColorScheme.ts delete mode 100644 packages/mui-material-next/src/styles/createLightColorScheme.ts delete mode 100644 packages/mui-material-next/src/styles/defaultTheme.ts delete mode 100644 packages/mui-material-next/src/styles/elevation.ts delete mode 100644 packages/mui-material-next/src/styles/extendTheme.test.ts delete mode 100644 packages/mui-material-next/src/styles/extendTheme.ts delete mode 100644 packages/mui-material-next/src/styles/identifier.ts delete mode 100644 packages/mui-material-next/src/styles/index.ts delete mode 100644 packages/mui-material-next/src/styles/motion.test.js delete mode 100644 packages/mui-material-next/src/styles/motion.ts delete mode 100644 packages/mui-material-next/src/styles/palette.ts delete mode 100644 packages/mui-material-next/src/styles/shape.ts delete mode 100644 packages/mui-material-next/src/styles/shouldSkipGeneratingVar.ts delete mode 100644 packages/mui-material-next/src/styles/state.ts delete mode 100644 packages/mui-material-next/src/styles/styled.test.js delete mode 100644 packages/mui-material-next/src/styles/styled.ts delete mode 100644 packages/mui-material-next/src/styles/sxConfig.ts delete mode 100644 packages/mui-material-next/src/styles/typeface.ts delete mode 100644 packages/mui-material-next/src/styles/typescale.ts delete mode 100644 packages/mui-material-next/src/styles/useTheme.ts delete mode 100644 packages/mui-material-next/src/styles/useThemeProps.ts delete mode 100644 packages/mui-material-next/src/utils/createSvgIcon.tsx delete mode 100644 packages/mui-material-next/src/utils/shouldSpreadAdditionalProps.ts delete mode 100644 packages/mui-material-next/test/describeConformance.ts delete mode 100644 packages/mui-material-next/tsconfig.build.json delete mode 100644 packages/mui-material-next/tsconfig.json create mode 100644 packages/mui-material/src/styles/rootShouldForwardProp.ts create mode 100644 packages/mui-material/src/styles/slotShouldForwardProp.ts create mode 100644 packages/mui-utils/src/useLocalStorageState/index.ts create mode 100644 packages/mui-utils/src/useLocalStorageState/useLocalStorageState.ts rename packages/{pigment-nextjs-plugin => pigment-css-nextjs-plugin}/.eslintrc (100%) rename packages/{pigment-nextjs-plugin => pigment-css-nextjs-plugin}/.gitignore (100%) rename packages/{pigment-nextjs-plugin => pigment-css-nextjs-plugin}/loader.js (100%) rename packages/{pigment-nextjs-plugin => pigment-css-nextjs-plugin}/next-font.js (100%) rename packages/{pigment-nextjs-plugin => pigment-css-nextjs-plugin}/next-image.js (100%) rename packages/{pigment-nextjs-plugin => pigment-css-nextjs-plugin}/package.json (92%) rename packages/{pigment-nextjs-plugin => pigment-css-nextjs-plugin}/src/index.ts (64%) rename packages/{pigment-nextjs-plugin => pigment-css-nextjs-plugin}/src/virtual-css-loader.js (100%) rename packages/{pigment-nextjs-plugin => pigment-css-nextjs-plugin}/tsconfig.build.json (100%) rename packages/{pigment-nextjs-plugin => pigment-css-nextjs-plugin}/tsconfig.json (100%) rename packages/{pigment-nextjs-plugin => pigment-css-nextjs-plugin}/tsup.config.ts (100%) rename packages/{pigment-nextjs-plugin => pigment-css-nextjs-plugin}/zero-virtual.css (100%) rename packages/{pigment-react => pigment-css-react}/.eslintignore (100%) rename packages/{pigment-react => pigment-css-react}/.eslintrc (100%) rename packages/{pigment-react => pigment-css-react}/.gitignore (100%) create mode 100644 packages/pigment-css-react/README.md rename packages/{pigment-react => pigment-css-react}/exports/createUseThemeProps.js (100%) rename packages/{pigment-react => pigment-css-react}/exports/css.js (100%) rename packages/{pigment-react => pigment-css-react}/exports/generateAtomics.js (100%) rename packages/{pigment-react => pigment-css-react}/exports/keyframes.js (100%) rename packages/{pigment-react => pigment-css-react}/exports/styled.js (100%) rename packages/{pigment-react => pigment-css-react}/exports/sx-plugin.js (100%) rename packages/{pigment-react => pigment-css-react}/exports/sx.js (100%) rename packages/{pigment-react => pigment-css-react}/package.json (85%) create mode 100644 packages/pigment-css-react/src/Box.d.ts create mode 100644 packages/pigment-css-react/src/Box.jsx rename packages/{pigment-react => pigment-css-react}/src/base.d.ts (51%) rename packages/{pigment-react => pigment-css-react}/src/createUseThemeProps.d.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/createUseThemeProps.js (100%) rename packages/{pigment-react => pigment-css-react}/src/css.d.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/css.js (100%) rename packages/{pigment-react => pigment-css-react}/src/generateAtomics.d.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/generateAtomics.js (100%) rename packages/{pigment-react => pigment-css-react}/src/index.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/keyframes.d.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/keyframes.js (100%) rename packages/{pigment-react => pigment-css-react}/src/processors/base-processor.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/processors/createUseThemeProps.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/processors/css.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/processors/generateAtomics.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/processors/keyframes.ts (97%) rename packages/{pigment-react => pigment-css-react}/src/processors/styled.ts (99%) rename packages/{pigment-react => pigment-css-react}/src/processors/sx.ts (96%) rename packages/{pigment-react => pigment-css-react}/src/styled.d.ts (70%) rename packages/{pigment-react => pigment-css-react}/src/styled.jsx (100%) rename packages/{pigment-react => pigment-css-react}/src/sx.d.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/sx.js (100%) rename packages/{pigment-react => pigment-css-react}/src/theme.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/utils/checkStaticObjectOrArray.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/utils/convertAtomicsToCss.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/utils/cssFnValueToVariable.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/utils/cssFunctionTransformerPlugin.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/utils/emotion.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/utils/extendTheme.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/utils/generateCss.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/utils/index.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/utils/isUnitLess.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/utils/pre-linaria-plugin.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/utils/preprocessor.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/utils/processCssObject.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/utils/sxObjectExtractor.ts (97%) rename packages/{pigment-react => pigment-css-react}/src/utils/sxPropConverter.ts (100%) rename packages/{pigment-react => pigment-css-react}/src/utils/valueToLiteral.ts (97%) rename packages/{pigment-react => pigment-css-react}/styles.css (100%) create mode 100644 packages/pigment-css-react/tests/Box.spec.tsx rename packages/{pigment-react => pigment-css-react}/tests/README.md (100%) rename packages/{pigment-react => pigment-css-react}/tests/css/css.test.ts (100%) rename packages/{pigment-react => pigment-css-react}/tests/css/fixtures/css.input.js (100%) rename packages/{pigment-react => pigment-css-react}/tests/css/fixtures/css.output.css (74%) create mode 100644 packages/pigment-css-react/tests/css/fixtures/css.output.js create mode 100644 packages/pigment-css-react/tests/keyframes/fixtures/keyframes-theme.input.js create mode 100644 packages/pigment-css-react/tests/keyframes/fixtures/keyframes-theme.output.css create mode 100644 packages/pigment-css-react/tests/keyframes/fixtures/keyframes-theme.output.js rename packages/{pigment-react => pigment-css-react}/tests/keyframes/fixtures/keyframes.input.js (58%) create mode 100644 packages/pigment-css-react/tests/keyframes/fixtures/keyframes.output.css create mode 100644 packages/pigment-css-react/tests/keyframes/fixtures/keyframes.output.js create mode 100644 packages/pigment-css-react/tests/keyframes/keyframes.test.ts create mode 100644 packages/pigment-css-react/tests/styled/fixtures/styled-theme.input.js rename packages/{pigment-react/tests/styled/fixtures/styled.output.css => pigment-css-react/tests/styled/fixtures/styled-theme.output.css} (62%) create mode 100644 packages/pigment-css-react/tests/styled/fixtures/styled-theme.output.js rename packages/{pigment-react => pigment-css-react}/tests/styled/fixtures/styled.input.js (75%) create mode 100644 packages/pigment-css-react/tests/styled/fixtures/styled.output.css rename packages/{pigment-react => pigment-css-react}/tests/styled/fixtures/styled.output.js (83%) create mode 100644 packages/pigment-css-react/tests/styled/styled.test.ts rename packages/{pigment-react => pigment-css-react}/tests/sx/fixtures/sxProps.input.js (100%) rename packages/{pigment-react => pigment-css-react}/tests/sx/fixtures/sxProps.output.css (60%) rename packages/{pigment-react => pigment-css-react}/tests/sx/fixtures/sxProps.output.js (61%) rename packages/{pigment-react => pigment-css-react}/tests/sx/fixtures/sxProps2.input.js (100%) rename packages/{pigment-react => pigment-css-react}/tests/sx/fixtures/sxProps2.output.css (79%) rename packages/{pigment-react => pigment-css-react}/tests/sx/fixtures/sxProps2.output.js (69%) rename packages/{pigment-react => pigment-css-react}/tests/sx/sx.test.ts (100%) rename packages/{pigment-react => pigment-css-react}/tests/testUtils.ts (100%) rename packages/{pigment-react => pigment-css-react}/theme/index.d.ts (100%) rename packages/{pigment-react => pigment-css-react}/theme/index.js (100%) rename packages/{pigment-react => pigment-css-react}/theme/index.mjs (100%) rename packages/{pigment-react => pigment-css-react}/tsconfig.build.json (100%) rename packages/{pigment-react => pigment-css-react}/tsconfig.json (100%) rename packages/{pigment-react => pigment-css-react}/tsup.config.ts (82%) rename packages/{pigment-unplugin => pigment-css-unplugin}/.gitignore (100%) rename packages/{pigment-unplugin => pigment-css-unplugin}/package.json (92%) rename packages/{pigment-unplugin => pigment-css-unplugin}/src/index.ts (84%) rename packages/{pigment-unplugin => pigment-css-unplugin}/tsconfig.build.json (100%) rename packages/{pigment-unplugin => pigment-css-unplugin}/tsconfig.json (77%) rename packages/{pigment-unplugin => pigment-css-unplugin}/tsup.config.ts (81%) rename packages/{pigment-vite-plugin => pigment-css-vite-plugin}/.gitignore (100%) rename packages/{pigment-vite-plugin => pigment-css-vite-plugin}/package.json (89%) rename packages/{pigment-vite-plugin => pigment-css-vite-plugin}/src/index.ts (100%) rename packages/{pigment-vite-plugin => pigment-css-vite-plugin}/src/vite-plugin.ts (100%) rename packages/{pigment-vite-plugin => pigment-css-vite-plugin}/tsconfig.build.json (100%) rename packages/{pigment-vite-plugin => pigment-css-vite-plugin}/tsconfig.json (76%) rename packages/{pigment-vite-plugin => pigment-css-vite-plugin}/tsup.config.ts (86%) create mode 100644 packages/pigment-nextjs-plugin/LICENSE create mode 100644 packages/pigment-react/LICENSE create mode 100644 packages/pigment-react/processors/base-processor-Bkjcr8yc.d.mts create mode 100644 packages/pigment-react/processors/base-processor-Bkjcr8yc.d.ts create mode 100644 packages/pigment-react/processors/chunk-3NOOOXUY.js create mode 100644 packages/pigment-react/processors/chunk-3NOOOXUY.js.map create mode 100644 packages/pigment-react/processors/chunk-7L3GVF7S.js create mode 100644 packages/pigment-react/processors/chunk-7L3GVF7S.js.map create mode 100644 packages/pigment-react/processors/chunk-DTNT7PFI.mjs create mode 100644 packages/pigment-react/processors/chunk-DTNT7PFI.mjs.map create mode 100644 packages/pigment-react/processors/chunk-HIMMIWAJ.mjs create mode 100644 packages/pigment-react/processors/chunk-HIMMIWAJ.mjs.map create mode 100644 packages/pigment-react/processors/chunk-IUHN7KUC.mjs create mode 100644 packages/pigment-react/processors/chunk-IUHN7KUC.mjs.map create mode 100644 packages/pigment-react/processors/chunk-MRLAB7WR.js create mode 100644 packages/pigment-react/processors/chunk-MRLAB7WR.js.map create mode 100644 packages/pigment-react/processors/chunk-QVEBVMS6.mjs create mode 100644 packages/pigment-react/processors/chunk-QVEBVMS6.mjs.map create mode 100644 packages/pigment-react/processors/chunk-SBKEDKMF.js create mode 100644 packages/pigment-react/processors/chunk-SBKEDKMF.js.map create mode 100644 packages/pigment-react/processors/createUseThemeProps.d.mts create mode 100644 packages/pigment-react/processors/createUseThemeProps.d.ts create mode 100644 packages/pigment-react/processors/createUseThemeProps.js create mode 100644 packages/pigment-react/processors/createUseThemeProps.js.map create mode 100644 packages/pigment-react/processors/createUseThemeProps.mjs create mode 100644 packages/pigment-react/processors/createUseThemeProps.mjs.map create mode 100644 packages/pigment-react/processors/css.d.mts create mode 100644 packages/pigment-react/processors/css.d.ts create mode 100644 packages/pigment-react/processors/css.js create mode 100644 packages/pigment-react/processors/css.js.map create mode 100644 packages/pigment-react/processors/css.mjs create mode 100644 packages/pigment-react/processors/css.mjs.map create mode 100644 packages/pigment-react/processors/generateAtomics.d.mts create mode 100644 packages/pigment-react/processors/generateAtomics.d.ts create mode 100644 packages/pigment-react/processors/generateAtomics.js create mode 100644 packages/pigment-react/processors/generateAtomics.js.map create mode 100644 packages/pigment-react/processors/generateAtomics.mjs create mode 100644 packages/pigment-react/processors/generateAtomics.mjs.map create mode 100644 packages/pigment-react/processors/keyframes.d.mts create mode 100644 packages/pigment-react/processors/keyframes.d.ts create mode 100644 packages/pigment-react/processors/keyframes.js create mode 100644 packages/pigment-react/processors/keyframes.js.map create mode 100644 packages/pigment-react/processors/keyframes.mjs create mode 100644 packages/pigment-react/processors/keyframes.mjs.map create mode 100644 packages/pigment-react/processors/styled.d.mts create mode 100644 packages/pigment-react/processors/styled.d.ts create mode 100644 packages/pigment-react/processors/styled.js create mode 100644 packages/pigment-react/processors/styled.js.map create mode 100644 packages/pigment-react/processors/styled.mjs create mode 100644 packages/pigment-react/processors/styled.mjs.map create mode 100644 packages/pigment-react/processors/sx.d.mts create mode 100644 packages/pigment-react/processors/sx.d.ts create mode 100644 packages/pigment-react/processors/sx.js create mode 100644 packages/pigment-react/processors/sx.js.map create mode 100644 packages/pigment-react/processors/sx.mjs create mode 100644 packages/pigment-react/processors/sx.mjs.map delete mode 100644 packages/pigment-react/tests/css/fixtures/css.output.js delete mode 100644 packages/pigment-react/tests/keyframes/fixtures/keyframes.output.css delete mode 100644 packages/pigment-react/tests/keyframes/fixtures/keyframes.output.js delete mode 100644 packages/pigment-react/tests/keyframes/keyframes.test.ts delete mode 100644 packages/pigment-react/tests/styled/styled.test.ts create mode 100644 packages/pigment-react/utils/chunk-RY3PR5BX.js create mode 100644 packages/pigment-react/utils/chunk-RY3PR5BX.js.map create mode 100644 packages/pigment-react/utils/chunk-XZYQOGJP.mjs create mode 100644 packages/pigment-react/utils/chunk-XZYQOGJP.mjs.map create mode 100644 packages/pigment-react/utils/index.d.mts create mode 100644 packages/pigment-react/utils/index.d.ts create mode 100644 packages/pigment-react/utils/index.js create mode 100644 packages/pigment-react/utils/index.js.map create mode 100644 packages/pigment-react/utils/index.mjs create mode 100644 packages/pigment-react/utils/index.mjs.map create mode 100644 packages/pigment-react/utils/pre-linaria-plugin.d.mts create mode 100644 packages/pigment-react/utils/pre-linaria-plugin.d.ts create mode 100644 packages/pigment-react/utils/pre-linaria-plugin.js create mode 100644 packages/pigment-react/utils/pre-linaria-plugin.js.map create mode 100644 packages/pigment-react/utils/pre-linaria-plugin.mjs create mode 100644 packages/pigment-react/utils/pre-linaria-plugin.mjs.map create mode 100644 packages/pigment-unplugin/LICENSE create mode 100644 packages/pigment-vite-plugin/LICENSE delete mode 100644 test/regressions/fixtures/ButtonNext/IconLabelButtonsNext.js delete mode 100644 test/regressions/fixtures/ButtonNext/MultilineButtonNext.js diff --git a/.circleci/config.yml b/.circleci/config.yml index c10fe610fb550f..e662bbf871ae94 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,7 +21,7 @@ parameters: type: string default: '' -defaults: &defaults +default-job: &default-job parameters: react-version: description: The version of react to be used @@ -47,6 +47,10 @@ defaults: &defaults docker: - image: cimg/node:18.19 +default-context: &default-context + context: + - org-global + # CircleCI has disabled the cache across forks for security reasons. # Following their official statement, it was a quick solution, they # are working on providing this feature back with appropriate security measures. @@ -62,7 +66,7 @@ commands: browsers: type: boolean default: false - description: 'Set to true if you intend to any browser (e.g. with playwright).' + description: 'Set to true if you intend to any browser (for example with playwright).' steps: - run: @@ -113,7 +117,7 @@ commands: jobs: checkout: - <<: *defaults + <<: *default-job steps: - checkout - install_js @@ -135,7 +139,7 @@ jobs: pnpm dedupe --check fi test_unit: - <<: *defaults + <<: *default-job steps: - checkout - install_js @@ -187,7 +191,7 @@ jobs: chmod +x codecov ./codecov -t ${CODECOV_TOKEN} -Z -F "$REACT_VERSION-jsdom" test_lint: - <<: *defaults + <<: *default-job steps: - checkout - install_js @@ -203,8 +207,45 @@ jobs: - run: name: Lint Markdown command: pnpm markdownlint + - run: + # See https://circleci.com/developer/orbs/orb/circleci/vale as reference + name: Install Vale + command: | + #!/bin/bash + VALE_STR_CLI_VERSION=3.3.0 + + mkdir /tmp/vale-extract + cd /tmp/vale-extract + GZIPPED_OUTPUT="vale.tar.gz" + BINARY_URL=https://github.com/errata-ai/vale/releases/download/v${VALE_STR_CLI_VERSION}/vale_${VALE_STR_CLI_VERSION}_Linux_64-bit.tar.gz + curl -sSL "$BINARY_URL" -o "${GZIPPED_OUTPUT}" + + if [ ! -s "${GZIPPED_OUTPUT}" ]; then + echo "Downloaded file is empty" + rm "${GZIPPED_OUTPUT}" + exit 1 + fi + + tar -xzf "${GZIPPED_OUTPUT}" + $SUDO mv vale /usr/local/bin + rm "${GZIPPED_OUTPUT}" + + # validate installation + if [[ -z "$(command -v vale)" ]]; then + echo "vale installation failed" + exit 1 + else + echo "vale installation successful" + vale --version + exit 0 + fi + - run: + name: Lint writing style + command: | + vale sync + pnpm run valelint test_static: - <<: *defaults + <<: *default-job steps: - checkout - install_js @@ -246,7 +287,7 @@ jobs: pnpm docs:link-check git add -A && git diff --exit-code --staged test_types: - <<: *defaults + <<: *default-job resource_class: 'medium+' steps: - checkout @@ -288,7 +329,7 @@ jobs: - packages/mui-lab/build - packages/mui-utils/build test_types_next: - <<: *defaults + <<: *default-job resource_class: 'medium+' steps: - checkout @@ -335,7 +376,7 @@ jobs: node scripts/testBuiltTypes.mjs exit 0 test_browser: - <<: *defaults + <<: *default-job resource_class: 'medium+' docker: - image: mcr.microsoft.com/playwright:v1.42.1-focal @@ -366,7 +407,7 @@ jobs: path: /tmp/_karma_webpack_ destination: artifact-file test_e2e: - <<: *defaults + <<: *default-job docker: - image: mcr.microsoft.com/playwright:v1.42.1-focal environment: @@ -386,7 +427,7 @@ jobs: command: pnpm test:umd test_e2e_website: # NOTE: This workflow runs after successful docs deploy. See /test/e2e-website/README.md#ci - <<: *defaults + <<: *default-job docker: - image: mcr.microsoft.com/playwright:v1.42.1-focal environment: @@ -401,7 +442,7 @@ jobs: environment: PLAYWRIGHT_TEST_BASE_URL: << parameters.e2e-base-url >> test_profile: - <<: *defaults + <<: *default-job docker: - image: mcr.microsoft.com/playwright:v1.42.1-focal environment: @@ -428,7 +469,7 @@ jobs: path: tmp/react-profiler-report/karma destination: react-profiler-report/karma test_regressions: - <<: *defaults + <<: *default-job docker: - image: mcr.microsoft.com/playwright:v1.42.1-focal environment: @@ -444,7 +485,7 @@ jobs: name: Upload screenshots to Argos CI command: pnpm test:argos test_bundling_prepare: - <<: *defaults + <<: *default-job steps: - checkout - install_js @@ -457,7 +498,7 @@ jobs: - '*/build' test_bundling_node-esm: - <<: *defaults + <<: *default-job working_directory: /tmp/material-ui/test/bundling/fixtures/node-esm/ steps: - checkout: @@ -481,7 +522,7 @@ jobs: pnpm start exit 0 test_bundling_next-webpack4: - <<: *defaults + <<: *default-job working_directory: /tmp/material-ui/test/bundling/fixtures/next-webpack4/ docker: - image: mcr.microsoft.com/playwright:v1.42.1-focal @@ -505,7 +546,7 @@ jobs: name: Test fixture command: pnpm start test_bundling_next-webpack5: - <<: *defaults + <<: *default-job working_directory: /tmp/material-ui/test/bundling/fixtures/next-webpack5/ docker: - image: mcr.microsoft.com/playwright:v1.42.1-focal @@ -529,7 +570,7 @@ jobs: name: Test fixture command: pnpm start test_bundling_create-react-app: - <<: *defaults + <<: *default-job working_directory: /tmp/material-ui/test/bundling/fixtures/create-react-app/ docker: - image: mcr.microsoft.com/playwright:v1.42.1-focal @@ -553,7 +594,7 @@ jobs: name: Test fixture command: pnpm start test_bundling_snowpack: - <<: *defaults + <<: *default-job working_directory: /tmp/material-ui/test/bundling/fixtures/snowpack/ docker: - image: mcr.microsoft.com/playwright:v1.42.1-focal @@ -577,7 +618,7 @@ jobs: name: Test fixture command: pnpm start test_bundling_vite: - <<: *defaults + <<: *default-job working_directory: /tmp/material-ui/test/bundling/fixtures/vite/ docker: - image: mcr.microsoft.com/playwright:v1.42.1-focal @@ -601,7 +642,7 @@ jobs: name: Test fixture command: pnpm start test_bundling_esbuild: - <<: *defaults + <<: *default-job working_directory: /tmp/material-ui/test/bundling/fixtures/esbuild/ docker: - image: mcr.microsoft.com/playwright:v1.42.1-focal @@ -629,7 +670,7 @@ jobs: pnpm start exit 0 test_bundling_gatsby: - <<: *defaults + <<: *default-job working_directory: /tmp/material-ui/test/bundling/fixtures/gatsby/ docker: - image: mcr.microsoft.com/playwright:v1.42.1-focal @@ -652,7 +693,7 @@ jobs: name: Test fixture command: pnpm start test_bundle_size_monitor: - <<: *defaults + <<: *default-job steps: - checkout - install_js @@ -731,7 +772,7 @@ jobs: environment: DANGER_COMMAND: 'reportBundleSize' test_benchmark: - <<: *defaults + <<: *default-job docker: - image: mcr.microsoft.com/playwright:v1.42.1-focal environment: @@ -751,37 +792,48 @@ workflows: when: equal: [pipeline, << pipeline.parameters.workflow >>] jobs: - - checkout + - checkout: + <<: *default-context - test_unit: + <<: *default-context requires: - checkout - test_lint: + <<: *default-context requires: - checkout - test_static: + <<: *default-context requires: - checkout - test_types: + <<: *default-context requires: - checkout - test_browser: + <<: *default-context requires: - checkout - test_regressions: + <<: *default-context requires: - checkout - test_e2e: + <<: *default-context requires: - checkout - test_bundle_size_monitor: + <<: *default-context requires: - checkout e2e-website: when: equal: [e2e-website, << pipeline.parameters.workflow >>] jobs: - - checkout + - checkout: + <<: *default-context - test_e2e_website: + <<: *default-context requires: - checkout @@ -789,36 +841,46 @@ workflows: when: equal: [bundling, << pipeline.parameters.workflow >>] jobs: - - test_bundling_prepare + - test_bundling_prepare: + <<: *default-context - test_bundling_node-esm: + <<: *default-context requires: - test_bundling_prepare - test_bundling_next-webpack4: + <<: *default-context requires: - test_bundling_prepare - test_bundling_next-webpack5: + <<: *default-context requires: - test_bundling_prepare - test_bundling_create-react-app: + <<: *default-context requires: - test_bundling_prepare - test_bundling_snowpack: + <<: *default-context requires: - test_bundling_prepare - test_bundling_vite: + <<: *default-context requires: - test_bundling_prepare - test_bundling_esbuild: + <<: *default-context requires: - test_bundling_prepare - test_bundling_gatsby: + <<: *default-context requires: - test_bundling_prepare profile: when: equal: [profile, << pipeline.parameters.workflow >>] jobs: - - test_profile + - test_profile: + <<: *default-context react-17: triggers: - schedule: @@ -829,12 +891,16 @@ workflows: - master jobs: - test_unit: + <<: *default-context react-version: ^17.0.0 - test_browser: + <<: *default-context react-version: ^17.0.0 - test_regressions: + <<: *default-context react-version: ^17.0.0 - test_e2e: + <<: *default-context react-version: ^17.0.0 react-next: triggers: @@ -846,12 +912,16 @@ workflows: - master jobs: - test_unit: + <<: *default-context react-version: next - test_browser: + <<: *default-context react-version: next - test_regressions: + <<: *default-context react-version: next - test_e2e: + <<: *default-context react-version: next typescript-next: triggers: @@ -862,9 +932,11 @@ workflows: only: - master jobs: - - test_types_next + - test_types_next: + <<: *default-context benchmark: when: equal: [benchmark, << pipeline.parameters.workflow >>] jobs: - - test_benchmark + - test_benchmark: + <<: *default-context diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index 6b395d8ebe8487..57e18e57f08442 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -3,7 +3,6 @@ "installCommand": "install:codesandbox", "node": "18", "packages": [ - "packages/docs-utils", "packages/markdown", "packages/mui-babel-macros", "packages/mui-base", @@ -13,7 +12,6 @@ "packages/mui-icons-material", "packages/mui-joy", "packages/mui-lab", - "packages/mui-material-next", "packages/mui-material-nextjs", "packages/mui-material", "packages/mui-private-theming", @@ -23,6 +21,7 @@ "packages/mui-system", "packages/mui-types", "packages/mui-utils", + "packages-internal/docs-utils", "packages-internal/scripts" ], "publishDirectory": { @@ -32,11 +31,11 @@ "@mui/docs": "packages/mui-docs/build", "@mui/icons-material": "packages/mui-icons-material/build", "@mui/internal-babel-macros": "packages/mui-babel-macros", + "@mui/internal-docs-utils": "packages-internal/docs-utils", "@mui/internal-markdown": "packages/markdown", "@mui/internal-scripts": "packages-internal/scripts", "@mui/joy": "packages/mui-joy/build", "@mui/lab": "packages/mui-lab/build", - "@mui/material-next": "packages/mui-material-next/build", "@mui/material-nextjs": "packages/mui-material-nextjs/build", "@mui/material": "packages/mui-material/build", "@mui/private-theming": "packages/mui-private-theming/build", @@ -45,8 +44,7 @@ "@mui/styles": "packages/mui-styles/build", "@mui/system": "packages/mui-system/build", "@mui/types": "packages/mui-types/build", - "@mui/utils": "packages/mui-utils/build", - "@mui-internal/docs-utils": "packages/docs-utils" + "@mui/utils": "packages/mui-utils/build" }, "sandboxes": [ "/examples/material-ui-cra-ts", diff --git a/.eslintignore b/.eslintignore index 5043b4bb97f494..0c319dcfc059aa 100644 --- a/.eslintignore +++ b/.eslintignore @@ -19,12 +19,12 @@ /packages/mui-icons-material/src/*.js /packages/mui-icons-material/templateSvgIcon.js /packages/mui-utils/macros/__fixtures__/ -/packages/pigment-react/utils/ -/packages/pigment-react/processors/ -/packages/pigment-react/exports/ -/packages/pigment-react/theme/ -/packages/pigment-react/tests/**/fixtures -/packages/pigment-nextjs-plugin/loader.js +/packages/pigment-css-react/utils/ +/packages/pigment-css-react/processors/ +/packages/pigment-css-react/exports/ +/packages/pigment-css-react/theme/ +/packages/pigment-css-react/tests/**/fixtures +/packages/pigment-css-nextjs-plugin/loader.js # Ignore fixtures /packages-internal/scripts/typescript-to-proptypes/test/*/* /test/bundling/fixtures/**/*.fixture.js diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 6e8cfcc755e3d1..ee08e27e9519d9 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6 + uses: github/codeql-action/init@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7 with: languages: typescript config-file: ./.github/codeql/codeql-config.yml @@ -30,4 +30,4 @@ jobs: # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6 + uses: github/codeql-action/analyze@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7 diff --git a/.github/workflows/no-response.yml b/.github/workflows/no-response.yml index f268f0c23959de..b4a63e97879364 100644 --- a/.github/workflows/no-response.yml +++ b/.github/workflows/no-response.yml @@ -8,8 +8,10 @@ on: issue_comment: types: [created] schedule: - # Schedule for five minutes after the hour, every hour - - cron: '5 * * * *' + # These runs in our repos are spread evenly throughout the day to avoid hitting rate limits. + # If you change this schedule, consider changing the remaining repositories as well. + # Runs at 12 am, 12 pm + - cron: '0 0,12 * * *' permissions: {} diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index e00b33d3986606..444373cd60ed00 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -43,6 +43,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6 + uses: github/codeql-action/upload-sarif@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7 with: sarif_file: results.sarif diff --git a/.github/workflows/vale-action.yml b/.github/workflows/vale-action.yml index 2c13e1ddbc7805..fce1352d13c355 100644 --- a/.github/workflows/vale-action.yml +++ b/.github/workflows/vale-action.yml @@ -14,11 +14,8 @@ jobs: steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: errata-ai/vale-action@38bf078c328061f59879b347ca344a718a736018 # v2.1.0 - continue-on-error: true + continue-on-error: true # GitHub Action flag needed until https://github.com/errata-ai/vale-action/issues/89 is fixed with: - reporter: github-pr-review - files: docs/data - env: - # Required, set by GitHub actions automatically: - # https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + fail_on_error: true + reporter: github-pr-check + token: ${{secrets.GITHUB_TOKEN}} diff --git a/.vale.ini b/.vale.ini index d1720b276aac71..0270cf93a0bf49 100644 --- a/.vale.ini +++ b/.vale.ini @@ -1,28 +1,23 @@ -# Config vale. More information at https://docs.errata.ai/vale/config +# Vale config. More information at https://vale.sh/docs/topics/config/ StylesPath = .github/styles -MinAlertLevel = suggestion +MinAlertLevel = warning -# The docs/writing-rules.zip is generated by `pnpm docs:zipRules` -Packages = Google, docs/writing-rules.zip +# The docs/mui-vale.zip is generated by `pnpm docs:zipRules` +Packages = Google, docs/mui-vale.zip [*.md] -# Ignore code injection which start with {{... +# Ignore code injections that start with {{... BlockIgnores = {{.* -BasedOnStyles = writing-rules +BasedOnStyles = MUI -# Google: -Google.FirstPerson = YES # Avoid first-person pronouns such as I, me, ...'. -Google.GenderBias = YES # Avoid gendered profession -Google.OxfordComma = YES -Google.Quotes = YES # Commas and periods go inside quotation marks. -Google.Spelling = YES # In general, use American spelling (word ending with 'nised' or 'logue') -Google.We = YES # Try to avoid using first-person plural -Google.Latin = YES # Try to avoid latin expressions e.g. and i.e. +# Google errors: +Google.GenderBias = YES # No Gender bias +# Google warings: +Google.FirstPerson = YES # Avoid first-person +Google.We = YES # Avoid first-person plural +Google.Will = YES # Avoid future tense +Google.OxfordComma = YES # Prefer Oxford comma -# Those rules are not repected a lot -# Google.Passive = YES # In general, use active voice instead of passive voice. -Google.Will = YES # Avoid using will - -# False positives with "1st" nearly no use in our doc -# Google.Units = YES # Put a nonbreaking space between the number and the unit \ No newline at end of file +[*CHANGELOG*.md] +MUI.CorrectReferenceAllCases = NO diff --git a/CHANGELOG.md b/CHANGELOG.md index 162d194f580c03..3228a93542ad5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,153 @@ # [Versions](https://mui.com/versions/) +## v5.15.14 + + + +_Mar 18, 2024_ + +A big thanks to the 15 contributors who made this release possible. +This release was mostly about 🐛 bug fixes and 📚 documentation improvements. + +### `@mui/material@5.15.13` + +- [Accordion] Convert to support CSS extraction (#41221) @mnajdova +- ​[Autocomplete] Convert to support CSS extraction (#40330) @mnajdova +- ​[Slider] Convert to support CSS extraction (#41201) @mnajdova +- ​[Select] Fix variant type (#41405) @sai6855 +- ​[typescript] Use interface instead of type for props (#41500) @siriwatknp + +### `@pigment-css/react@0.0.3` + +- ​Add Box component (#41451) @brijeshb42 + +### `pigment-css/nextjs-plugin@0.0.3` + +- ​Fix alias resolver (#41494) @brijeshb42 +- ​Follow-up to #41494 (#41502) @brijeshb42 + +### Docs + +- ​[joy-ui] Add UI improvements to the side navigation demo (#41461) @cipherlogs +- ​[pigment-css] Add media query guide (#41473) @siriwatknp +- ​[pigment-css] Fixing location of the ExtendTheme type in the docs (#41499) @jherr +- ​[material-ui][Progress] Add Circular progress gradient demo from Github comment (#40559) @DiegoAndai + +### Core + +- ​[blog] Bringing consistency to Material UI customization APIs (#41040) @DiegoAndai +- ​[code-infra] Rename @mui-internal/docs-utils to @mui/internal-docs-utils (#41498) @michaldudak +- ​[code-infra] Copy translations.json to @mui/docs build folder (#41472) @Janpot +- ​[core] Use Circle CI context (#41532) @oliviertassinari +- ​[core] Fix CHANGELOG format and update date (#41481) @DiegoAndai +- ​[docs] Fix useStorageState regressions (#41223) @Janpot +- ​[docs] Fix some Vale errors (#41516) @oliviertassinari +- ​[material-ui][docs] Fix landing page template's h1 size (#41543) @zanivan +- ​[material-ui][docs] Apply new code header docs feature (#41508) @danilo-leal +- ​[material-next] Drop the package (#41544) @mnajdova +- ​[docs-infra] Fail CI on Vale error (#40944) @oliviertassinari +- ​[docs-infra] Improve Vale config @oliviertassinari +- ​[docs-infra] Add a feature list "component" (#41484) @danilo-leal +- ​[docs-infra] Add code block header classes (#41487) @danilo-leal +- ​[docs-infra] Make the Algolia search input label invisible (#41542) @danilo-leal +- ​[website] Improve navbar's items hover state (#41535) @EyaOuenniche +- ​[website] Split Toolpad documentation (#41316) @bharatkashyap +- ​[website] Use MUI X Data Grid v7-beta (#41276) @cherniavskii + +All contributors of this release in alphabetical order: @bharatkashyap, @brijeshb42, @cherniavskii, @cipherlogs, @danilo-leal, @DiegoAndai, @EyaOuenniche, @Janpot, @jherr, @michaldudak, @mnajdova, @oliviertassinari, @sai6855, @siriwatknp, @zanivan + +## v5.15.13 + + + +_Mar 13, 2024_ + +A big thanks to the 18 contributors who made this release possible. Here are some highights ✨ + +- The Material UI free Checkout template got a design uplift (#41447) @zanivan + +### `@mui/material@5.15.13` + +- [Alert] Add `slots` and `slotProps` type to theme (#41324) @sai6855 +- [Autocomplete] Fix the options list being added to the DOM in `freeSolo` mode even when there are no options, causing style problems (#41300) @rakeshmusturi +- Add `paperChannel` token (#41447) @siriwatknp +- [Switch] Convert to support CSS extraction (#41367) @alexfauquette +- [Tooltip] Support event handlers with extra parameters (#41320) @LukasTy + +### `@mui/system@5.15.13` + +- [RtlProvider] Add component & hook (#41241) @mnajdova + +### `@mui/utils@5.15.13` + +- [utils] Fix visually hidden styles' margin unit (#41477) @michaldudak + +### `@mui/codemod@5.15.13` + +- Fix merging of slotProps and componentProps (#41323) @sai6855 + +### `@mui/base@5.0.0-beta.39` + +- [material-ui][joy-ui][Autocomplete] Keep in sync highlighted index when the option still exists (#41306) @CGNonofr +- [FormControl] Export `FormControlOwnerState` type from index (#41287) @michaeldfoley +- [material-ui][TextareaAutosize] Fix inline style not getting applied (#41369) @ZeeshanTamboli + +### `@pigment-css/react@0.0.2` + +- Handle more scenarios while transforming sx prop (#41372) @brijeshb42 +- Improve testing of fixtures (#41389) @brijeshb42 +- Fix `keyframes` serialize styles error (#41395) @siriwatknp +- Use class selector instead of class value (#41442) @brijeshb42 +- [next] Warn about unsupported turbo mode in Next.js (#41445) @brijeshb42 + +### Docs + +- [material-ui] Refine checkout template (#40967) @zanivan +- [material-ui] Add docs for complementary List components (#41329) @anle9650 +- [material-ui] Add docs for complementary Dialog components (#41313) @jwithington +- [material-ui] Fix Templates live preview link (#41467) @danilo-leal +- [material-ui] Polish out the templates page (#41468) @zanivan +- [material-ui] Adjust the Templates card design (#41450) @danilo-leal +- [joy-ui] Remove unnecessary styles in color inversion footer demo (#41419) @cipherlogs +- [joy-ui] Update case studies chip background color (#41413) @cipherlogs +- [joy-ui] Remove wrong CSS prop from the Sign-in-side template (#41383) @cipherlogs +- [joy-ui] Fix broken link on the Color Inversion page (#41407) @cipherlogs +- [pigment] Add example and guide section (#41249) @siriwatknp +- [pigment-css] Brand name nonbreaking space (#41438) @oliviertassinari +- [pigment-css] Fix import on the README (#41411) @danilo-leal +- [pigment-css] Edit starter template links on README (#41409) @danilo-leal +- [pigment-css] Tweak the examples and edit READMEs (#41408) @danilo-leal +- [pigment-css] Adjust the bit about CSS vars on the README (#41463) @danilo-leal +- Finish brand name fixes #41438 @oliviertassinari +- Remove noreferrer @oliviertassinari +- Fix v4 docs `` appearing in notifications (#41390) @peterwangsc +- Update GitHub project links (#41370) @danilo-leal + +### Core + +- [pigment] Make all Pigment CSS packages public (#41404) @brijeshb42 +- [pigment] Rename directories to match package names (#41453) @brijeshb42 +- [pigment-css] Example fix leading spaces (#41439) @oliviertassinari +- [code-infra] Add short note about e2e-website workflow schedule (#41355) @Janpot +- [code-infra] Add alias for icon types (#41248) @Janpot +- [code-infra] Reduce concurrency of typescript:ci further (#41392) @Janpot +- [code-infra] Reduce concurrency for test_types ci job (#41385) @Janpot +- [code-infra] Adapt API code generator to Base UI repo needs (#41475) @michaldudak +- [docs-infra] Don't generate preview files for the templates (#41379) @mnajdova +- [docs-infra] Fix Pigment CSS apps path in the render mui demos script (#41476) @mnajdova +- [docs-infra] move feedback to ESM (#41381) @alexfauquette +- [docs-infra] Improve color contrast throughout (#41387) @danilo-leal +- [docs-infra] Simplify Algolia crawler config (#41312) @oliviertassinari +- [docs-infra] Adjust the tabs and layout selection design (#41084) @danilo-leal +- [blog] Update the Base UI post with links to dedicated repo (#41358) @danilo-leal +- [website] Update the Careers page role (#41384) @danilo-leal +- [website] Compress about images @oliviertassinari +- [website] Improve color contrast on the homepage (#41465) @danilo-leal +- [examples] Add pigment-css-vite-ts starter example (#41196) @siriwatknp +- [examples] Add pigment-css-nextjs-ts starter project (#41105) @siriwatknp + +All contributors of this release in alphabetical order: @alexfauquette, @anle9650, @brijeshb42, @CGNonofr, @cipherlogs, @danilo-leal, @jwithington, @Janpot, @michaeldfoley, @michaldudak, @mnajdova, @oliviertassinari, @peterwangsc, @rakeshmusturi, @sai6855, @siriwatknp, @zanivan, @ZeeshanTamboli + ## v5.15.12 @@ -33,7 +181,7 @@ This release was mostly about 🐛 bug fixes and 📚 documentation improvements - ​[pigment-css] Rename zero-runtime to pigmentcss (#41317) @brijeshb42 - ​Fix createSpacing return type (#41318) @matystroia - ​[zero] Add support for styled tagged-template literals (#41268) @brijeshb42 -- ​[zero] Set up Material UI migration demos (#41267) @siriwatknp +- ​[zero] Set up Material UI migration demos (#41267) @siriwatknp - ​[zero] Move extendTheme to already existing @mui/zero-runtime/utils (#41254) @brijeshb42 - ​[zero] Remove `object` intersection from CSS Fallback (#41271) @siriwatknp - ​[zero] Minor wording changes in README (#41253) @brijeshb42 @@ -70,7 +218,7 @@ This release was mostly about 🐛 bug fixes and 📚 documentation improvements - ​[docs-infra] Fix missing non breaking spaces @oliviertassinari - ​[docs-infra] Add design customizations to the disclosure element (#41285) @danilo-leal - ​[docs-infra] Adjust headings dark mode color (#41292) @danilo-leal -- ​[docs-infra] Fix Stack Overflow breaking space @oliviertassinari +- ​[docs-infra] Fix Stack Overflow breaking space @oliviertassinari - ​[docs-infra] Fix product selector popup not closing on route change (#41166) @divyammadhok - ​[docs-infra] Improve fix blank links ad @oliviertassinari - ​[docs-infra] Support interfaces for X docs (#41069) @alexfauquette @@ -177,7 +325,7 @@ This release was mostly about 🐛 bug fixes and 📚 documentation improvements - [docs-infra] Reduce scrollbar width on ROC (#41148) @oliviertassinari - [docs-infra] Add external link arrow (#41129) @siriwatknp - [docs-infra] Fix external link arrow (#41181) @alexfauquette -- [docs-infra] Flag NPM and Github as wrong spellings @oliviertassinari +- [docs-infra] Flag npm and GitHub as wrong spellings @oliviertassinari - [docs-infra] Fix display when the default props is undefined (#41114) @oliviertassinari - [docs-infra] Remove random layout assignment (#40862) @alexfauquette - [docs-infra] Add spacing and contrast improvements (#41191) @danilo-leal @@ -225,7 +373,7 @@ This release was mostly about 🐛 bug fixes and 📚 documentation improvements - Fix 301 redirection to StackBlitz @oliviertassinari - Fix h1 on Joy UI templates @oliviertassinari - Have MUI workspace own the CodeSandbox @oliviertassinari -- Add notification for mui x v7 beta (#41001) @joserodolfofreitas +- Add notification for MUI X v7 beta (#41001) @joserodolfofreitas - Fix 301 links @oliviertassinari - Fix Next.js v13.5.1 SEO regression (#40302) @oliviertassinari - Add a 404 page (#40884) @danilo-leal @@ -1502,7 +1650,7 @@ A big thanks to the 17 contributors who made this release possible. Here are som - [docs] Update Autocomplete demo for React 18 (#39162) @oliviertassinari - [docs-infra] Tweak feedback footer section design (#36556) @danilo-leal - [docs-infra] Improve code syntax highlight (#39181) @oliviertassinari -- [docs][base] Add Tailwind CSS + plain CSS demo on the TextArea page (#39046) @alisasanib +- [docs][base] Add Tailwind CSS + plain CSS demo on the TextArea page (#39046) @alisasanib - [docs][base-ui] Fix style for root div of multiline input (#39182) @ttlpta - [docs][base-ui] Improve Select's country select demo (#38983) @oliviertassinari - [docs][joy-ui] Add scrollable tabs example (#39260) @siriwatknp @@ -1572,7 +1720,7 @@ This release was mostly about 🐛 bug fixes and 📚 documentation improvements ### Docs - Improve focus trap demo (#38985) @oliviertassinari -- Add Tailwind CSS + plain CSS demo on the Tabs page (#39000) @alisasanib +- Add Tailwind CSS + plain CSS demo on the Tabs page (#39000) @alisasanib - Improve the default theme viewer design (#39049) @danilo-leal - Add live demo with CssVarsProvider (#38792) @oliviertassinari - Fix wrong hash on Card's page (#39151) @mnajdova @@ -1638,7 +1786,7 @@ A big thanks to the 16 contributors who made this release possible. This release ### Docs -- ​<!-- 21 -->[docs][base] Add Tailwind CSS + plain CSS demo on the NumberInput page (#38928) @alisasanib +- ​<!-- 21 -->[docs][base] Add Tailwind CSS + plain CSS demo on the NumberInput page (#38928) @alisasanib - ​<!-- 13 -->[docs][Dialog] Add non-modal dialog docs & demo (#38684) @mnajdova - ​<!-- 12 -->[docs] Fix number input wrong demo @oliviertassinari - ​<!-- 11 -->[docs] Exclude joy-ui LinearProgressCountup from visual regression (#38969) @siriwatknp @@ -1745,7 +1893,7 @@ A big thanks to the 18 contributors who made this release possible. Here are som ### Examples - ​<!-- 14 -->[examples] Add shortcut to open example in online IDE (#38572) @oliviertassinari -- ​<!-- 61 -->[examples][base-ui] Add Base UI + Vite + Tailwind CSS example in TypeScript (#37595) @dvkam +- ​<!-- 61 -->[examples][base-ui] Add Base UI + Vite + Tailwind CSS example in TypeScript (#37595) @dvkam ### Core @@ -1814,9 +1962,9 @@ A big thanks to the 25 contributors who made this release possible. - ​<!-- 52 -->Update changelog (#38704) @mj12albert - ​<!-- 49 -->[docs][Autocomplete] Require referentially stable value (#38734) @michaldudak - ​<!-- 48 -->[docs][base-ui] Add type parameter to the button in prepareForSlot demo (#38640) @michaldudak -- ​<!-- 47 -->[docs][base-ui] Fix the broken image in the Tailwind CSS guide (#38721) @michaldudak +- ​<!-- 47 -->[docs][base-ui] Fix the broken image in the Tailwind CSS guide (#38721) @michaldudak - ​<!-- 46 -->[docs][base-ui]: Working With Tailwind Guide - revises example code to avoid import errors (#38693) @christophermorin -- ​<!-- 45 -->[docs][base] Add Tailwind CSS + plain CSS demo on the Menu page (#38618) @alisasanib +- ​<!-- 45 -->[docs][base] Add Tailwind CSS + plain CSS demo on the Menu page (#38618) @alisasanib - ​<!-- 44 -->[blog] Clearer blog release title @oliviertassinari - ​<!-- 43 -->[blog] Add a post for the Tree View migration (#38407) @flaviendelangle - ​<!-- 34 -->[docs] Fix broken links to Next.js docs (#38764) @ruflair @@ -1951,10 +2099,10 @@ A big thanks to the 21 contributors who made this release possible. Here are som - [docs][base-ui] Fix Menu Hooks demo (#38479) @homerchen19 - [docs][base-ui] Correct the MUI System quickstart example (#38496) @michaldudak - [docs][base-ui] Add Tailwind & plain CSS demos for Autocomplete page (#38157) @mj12albert -- [docs][base-ui] Add Tailwind CSS + plain CSS demo on the Input page (#38302) @alisasanib -- [docs][base-ui] Add Tailwind CSS + plain CSS demo on the Snackbar, Badge, Switch pages (#38425) @alisasanib -- [docs][base-ui] Add Tailwind CSS + plain CSS demo on the Slider page (#38413) @alisasanib -- [docs][base-ui] Add Tailwind CSS + plain CSS demo on the Select page (#38367) @alisasanib +- [docs][base-ui] Add Tailwind CSS + plain CSS demo on the Input page (#38302) @alisasanib +- [docs][base-ui] Add Tailwind CSS + plain CSS demo on the Snackbar, Badge, Switch pages (#38425) @alisasanib +- [docs][base-ui] Add Tailwind CSS + plain CSS demo on the Slider page (#38413) @alisasanib +- [docs][base-ui] Add Tailwind CSS + plain CSS demo on the Select page (#38367) @alisasanib - [docs][joy-ui] Fix typo: Classname -> Class name for consistency (#38510) @alexfauquette - [docs][joy-ui] Revise the theme color page (#38402) @danilo-leal - [docs][joy-ui] Sort templates by popularity (#38490) @oliviertassinari @@ -1979,7 +2127,7 @@ A big thanks to the 21 contributors who made this release possible. Here are som - [docs] Fix typo in Base UI @oliviertassinari - [docs] Update the backers page (#38505) @danilo-leal - [docs] Add stray design adjustments to the docs (#38501) @danilo-leal -- [docs] Use IBM Plex Sans in Tailwind CSS demos (#38464) @mnajdova +- [docs] Use IBM Plex Sans in Tailwind CSS demos (#38464) @mnajdova - [docs] Fix SEO issues reported by ahrefs (#38423) @oliviertassinari ### Examples @@ -2066,7 +2214,7 @@ A big thanks to the 17 contributors who made this release possible. Here are som - ​<!-- 14 -->[docs][material-ui] Remove incorrect `aria-label`s in extended variant examples of Floating Action Button (#37170) @ashleykolodziej - ​<!-- 13 -->[docs][material-ui] Adjust slightly the installation page content (#38380) @danilo-leal - ​<!-- 12 -->[docs][Switch] Fix the readOnly class name in docs (#38277) @michaldudak -- ​<!-- 11 -->[docs][TablePagination] Add Tailwind CSS & plain CSS introduction demo (#38286) @mnajdova +- ​<!-- 11 -->[docs][TablePagination] Add Tailwind CSS & plain CSS introduction demo (#38286) @mnajdova ### Examples @@ -2152,7 +2300,7 @@ A big thanks to the 18 contributors who made this release possible. Here are som ### Docs - ​<!-- 33 -->[docs][AppBar] Fix `ResponsiveAppBar` demo logo href (#38346) @iownthegame -- ​<!-- 30 -->[docs][base] Add Tailwind CSS + plain CSS demo on the Button page (#38240) @alisasanib +- ​<!-- 30 -->[docs][base] Add Tailwind CSS + plain CSS demo on the Button page (#38240) @alisasanib - ​<!-- 29 -->[docs][Menu][base] Remove `Unstyled` prefix from demos' function names (#38270) @sai6855 - ​<!-- 22 -->[docs] Add themeable component guide (#37908) @siriwatknp - ​<!-- 21 -->[docs] Fix Joy UI demo background color (#38307) @oliviertassinari @@ -2247,7 +2395,7 @@ A big thanks to the 17 contributors who made this release possible. Here are som - ​<!-- 39 -->[docs][material] Revise and update Examples doc (#38205) @samuelsycamore - ​<!-- 38 -->[docs] Fix typo in notifications.json @mbrookes - ​<!-- 37 -->[docs-infra] Remove leftover standardNavIcon (#38252) @DiegoAndai -- ​<!-- 34 -->[docs][base] Add Tailwind CSS & plain CSS demos on the Popper page (#37953) @zanivan +- ​<!-- 34 -->[docs][base] Add Tailwind CSS & plain CSS demos on the Popper page (#37953) @zanivan - ​<!-- 31 -->[docs][Button][joy] Improve `loading` prop documentation (#38156) @sai6855 - ​<!-- 25 -->[docs] Prepare docs infra for Tree View migration to X (#38202) @flaviendelangle - ​<!-- 24 -->[docs] Fix SEO issues reported by ahrefs @oliviertassinari @@ -2302,14 +2450,14 @@ A big thanks to the 23 contributors who made this release possible. ### Docs - ​<!-- 37 -->[docs] Add listbox placement demo for Select (#38130) @sai6855 -- ​<!-- 36 -->[docs][base] Add Tailwind CSS & plain CSS demo on the Tabs page (#37910) @mnajdova -- ​<!-- 35 -->[docs][base] Add Tailwind CSS & plain CSS demos on the Textarea page (#37943) @zanivan +- ​<!-- 36 -->[docs][base] Add Tailwind CSS & plain CSS demo on the Tabs page (#37910) @mnajdova +- ​<!-- 35 -->[docs][base] Add Tailwind CSS & plain CSS demos on the Textarea page (#37943) @zanivan - ​<!-- 29 -->[docs] Fix Joy UI menu example (#38140) @harikrishnanp - ​<!-- 28 -->[docs] Remove translations section from contributing guide (#38125) @nikohoffren -- ​<!-- 27 -->[docs] Fix Base UI Button Tailwind CSS padding @oliviertassinari +- ​<!-- 27 -->[docs] Fix Base UI Button Tailwind CSS padding @oliviertassinari - ​<!-- 26 -->[docs] Mention in hompage hero that Core is free (#38075) @mbrookes - ​<!-- 25 -->[docs] Fix a typo in notifications.json (#38078) @mbrookes -- ​<!-- 24 -->[docs] Add Tailwind CSS & plain CSS demo on the table pagination page (#37937) @mnajdova +- ​<!-- 24 -->[docs] Add Tailwind CSS & plain CSS demo on the table pagination page (#37937) @mnajdova - ​<!-- 23 -->[docs] Implement the new API display design (#37405) @alexfauquette - ​<!-- 22 -->[docs] Update migration installation code blocks (#38028) @danilo-leal - ​<!-- 21 -->[docs][joy] Revise the Joy UI Link page (#37829) @danilo-leal @@ -2381,7 +2529,7 @@ A big thanks to the 24 contributors who made this release possible. Here are som ### Docs -- ​<!-- 52 -->[docs][base] Add Tailwind CSS & plain CSS demo on the form control page (#37914) @mnajdova +- ​<!-- 52 -->[docs][base] Add Tailwind CSS & plain CSS demo on the form control page (#37914) @mnajdova - ​<!-- 51 -->[docs][base] Make Base UI Select demos denser (#37836) @zanivan - ​<!-- 38 -->[docs] Link Material UI from the landing page (#37971) @oliviertassinari - ​<!-- 37 -->[docs] Fix the empty /components page (#38010) @brijeshb42 @@ -2393,7 +2541,7 @@ A big thanks to the 24 contributors who made this release possible. Here are som - ​<!-- 31 -->[docs] Link charts in the roadmap (#37944) @oliviertassinari - ​<!-- 30 -->[docs] Improve changelog @oliviertassinari - ​<!-- 29 -->[docs] Improve the Select docs (#37279) @michaldudak -- ​<!-- 16 -->[docs][menu] Add Tailwind CSS & plain CSS demo on the Menu page (#37856) @mnajdova +- ​<!-- 16 -->[docs][menu] Add Tailwind CSS & plain CSS demo on the Menu page (#37856) @mnajdova - ​<!-- 15 -->[example] Update EmotionCacheProvider to work with GlobalStyles (#37962) @siriwatknp ### Core @@ -2467,15 +2615,15 @@ A big thanks to the 15 contributors who made this release possible. Here are som - [docs] Polish Ukraine banner (#37905) @oliviertassinari - [docs] Reduce Ukraine banner size (#34795) @oliviertassinari - [docs] Add callouts about controlled vs uncontrolled components in Core docs (#37849) @samuelsycamore -- [docs] Add missing Portal elements to Tailwind CSS interoperability guide (#37807) @enrique-ramirez +- [docs] Add missing Portal elements to Tailwind CSS interoperability guide (#37807) @enrique-ramirez - [docs] Small pickers migration improvement (#37815) @alexfauquette - [docs] Fix pickers product name (#37825) @LukasTy - [docs][Joy][Link] Set `variant` and `color` defaults for the playground (#37817) @Studio384 - [docs][Joy][Table] Add `undefined` as an option to `stripe` (#37816) @Studio384 -- [docs][base] Add Tailwind CSS & plain CSS demo on the Snackbar page (#37812) @mnajdova -- [docs][base] Add Tailwind CSS & plain CSS demo on Badge page (#37768) @mnajdova +- [docs][base] Add Tailwind CSS & plain CSS demo on the Snackbar page (#37812) @mnajdova +- [docs][base] Add Tailwind CSS & plain CSS demo on Badge page (#37768) @mnajdova - [docs][base] Fix Nested modal demo positioning (#37506) @gitstart -- [docs][base] Add Tailwind CSS & plain CSS demo on the Switch page (#37728) @mnajdova +- [docs][base] Add Tailwind CSS & plain CSS demo on the Switch page (#37728) @mnajdova - [docs-infra] Remove code tags in ToC (#37834) @cherniavskii - [docs-infra] Fixes in API pages generation (#37813) @mnajdova - [docs-infra] Add test case when using sh (#37818) @oliviertassinari @@ -2536,10 +2684,10 @@ This release focuses primarily on 🐛 bug fixes, 📚 documentation, and ⚙️ ### Docs - [docs][base] Add demo for using the Button as a link (#37317) @AdamSundberg -- [docs][base] Add Tailwind CSS + plain CSS demo on the Select page (#37725) @mnajdova +- [docs][base] Add Tailwind CSS + plain CSS demo on the Select page (#37725) @mnajdova - [docs][base] Make Base UI input demos denser (#37750) @zanivan - [docs][base] Make Base UI button demos denser (#37689) @zanivan -- [docs][base] Add Tailwind CSS & plain CSS demos on the Input page (#37685) @mnajdova +- [docs][base] Add Tailwind CSS & plain CSS demos on the Input page (#37685) @mnajdova - [docs][base] Fix horizontal scrolling on the mobile input page (#37688) @zanivan - [docs] Improve Base UI index page (#37761) @oliviertassinari - [docs] Fix incorrect package URL in README of example material-vite (#37755) @Dlouxgit @@ -2548,7 +2696,7 @@ This release focuses primarily on 🐛 bug fixes, 📚 documentation, and ⚙️ - [docs][material] Rename custom tab panel in Tabs demo to prevent confusion with @mui/lab (#37638) @MUK-Dev - [docs][tabs] Document how to use routing with Tabs in Base UI (#37369) @michaldudak - [docs] Rename product to productId (#37801) @siriwatknp -- [docs][base] Add Tailwind CSS & plain CSS demo on the Slider page (#37736) @mnajdova +- [docs][base] Add Tailwind CSS & plain CSS demo on the Slider page (#37736) @mnajdova ### Core @@ -2631,7 +2779,7 @@ A big thanks to the 25 contributors who made this release possible. Here are som - ​<!-- 40 -->[docs][base] Add page for all Base UI components (#37536) @danilo-leal - ​<!-- 33 -->[docs] Fix scrollbar on snackbar page (#37657) @oliviertassinari - ​<!-- 32 -->[docs] Switch order of snackbar buttons in demos (#37389) @Primajin -- ​<!-- 31 -->[docs] Add support for Tailwind CSS and plain CSS demos (#37319) @mnajdova +- ​<!-- 31 -->[docs] Add support for Tailwind CSS and plain CSS demos (#37319) @mnajdova - ​<!-- 30 -->[docs] Tree view color fix for dark mode in Gmail example (#37051) @PunitSoniME - ​<!-- 29 -->[docs] Inline the Base UI demo (#37603) @oliviertassinari - ​<!-- 28 -->[docs] Fix typo in themed components page (#37598) @vinayr @@ -3719,7 +3867,7 @@ A big thanks to the 15 contributors who made this release possible. Here are som - ​<!-- 27 -->[docs][base] List slots in API documentation (#36104) @hbjORbj - ​<!-- 26 -->[docs] Add missing sandbox adapter deps resolving (#36291) @LukasTy - ​<!-- 25 -->[docs] Allow to pass navigation bar banner from outside (#36299) @MBilalShafi -- ​<!-- 24 -->[docs] Fix code on the Working with Tailwind CSS guide (#36090) @mnajdova +- ​<!-- 24 -->[docs] Fix code on the Working with Tailwind CSS guide (#36090) @mnajdova - ​<!-- 23 -->[docs] Remove See Slots Section text from Material UI slots description (#36284) @hbjORbj - ​<!-- 22 -->[docs] Fix emotion warning `:first-child` (#36263) @siriwatknp - ​<!-- 21 -->[docs][joy] Improve the descriptions of props in API docs (#36307) @hbjORbj @@ -4830,7 +4978,7 @@ A big thanks to the 10 contributors who made this release possible. Here are som - [base] Make CSS class prefixes consistent (#33411) @michaldudak **This is a breaking change for anyone who depends on the class names applied to Base components.** - If you use the `<component>UnstyledClasses` objects, you won't notice a difference. Only if you depend on the resulting class names (e.g. in CSS stylesheets), you'll have to adjust your code. + If you use the `<component>UnstyledClasses` objects, you won't notice a difference. Only if you depend on the resulting class names (for example in CSS stylesheets), you'll have to adjust your code. ```diff -.ButtonUnstyled-root { ... }; @@ -5535,7 +5683,7 @@ A big thanks to the 16 contributors who made this release possible. Here are som - [docs] Fix typo in the ClickAwayListerner name (#33813) @pawelsmigielski - [docs] Fix link to `Basics` section in `Trap Focus` docs (#33772) @ZeeshanTamboli - [docs] z-index added in popper when used by split button (#33763) @PunitSoniME -- [docs] Improve the guide for using @mui/base with Tailwind CSS (#33670) @mnajdova +- [docs] Improve the guide for using @mui/base with Tailwind CSS (#33670) @mnajdova - [docs] Fix warnings related to Next.js' links (#33693) @mnajdova - [docs] Add notification to aggregation blogpost (#33745) @joserodolfofreitas - [docs] Add Grid version 2 docs (#33554) @siriwatknp @@ -5805,7 +5953,7 @@ A big thanks to the 19 contributors who made this release possible. Here are som - [docs] Add "refine" demo to showcase (#33240) @omeraplak - [docs] Add Webpack alias for legacy utils package (#33376) @jgbae - [docs] Improve external link icons synonyms (#33257) @davidgarciab -- [examples] Update Base UI with Tailwind CSS to use the latest versions of the dependencies (#33401) @mnajdova +- [examples] Update Base UI with Tailwind CSS to use the latest versions of the dependencies (#33401) @mnajdova - [examples] Add Base UI example (#33154) @siriwatknp ### Core @@ -5864,7 +6012,7 @@ A big thanks to the 13 contributors who made this release possible. Here are som - [docs] Add caveat about class components with Tooltip (#33325) @joshkel - [docs] Fix SEO issues (#33288) @oliviertassinari - [docs] Fix Slider's "player" demo (#33267) @xlianghang -- [website] Link MUI Toolpad in mui.com (#33287) @oliviertassinari +- [website] Link Toolpad in mui.com (#33287) @oliviertassinari All contributors of this release in alphabetical order: @aaarichter, @aaronlademann-wf, @danilo-leal, @henriqueholtz, @jake-collibra, @joshkel, @MattiasMartens, @Methuselah96, @michaldudak, @oliviertassinari, @siriwatknp, @TimoWilhelm, @xlianghang @@ -5979,7 +6127,7 @@ A big thanks to the 14 contributors who made this release possible. Here are som - ​<!-- 28 -->[blog] Update Blogpost to clear confusion on "no impact" disclaimer. (#33131) @joserodolfofreitas - ​<!-- 27 -->[blog] Add post about v5 Migration guide update (#33063) @samuelsycamore - ​<!-- 26 -->[blog] Fix display on Safari (#33102) @oliviertassinari -- ​<!-- 18 -->[docs] Add guide on how to use Base UI with Tailwind CSS (#33100) @mnajdova +- ​<!-- 18 -->[docs] Add guide on how to use Base UI with Tailwind CSS (#33100) @mnajdova - ​<!-- 17 -->[docs] Improve Joy template UX (#33159) @siriwatknp - ​<!-- 16 -->[docs] Update Shadow DOM guide (#33160) @cherniavskii - ​<!-- 15 -->[docs] Fix SEO regressions (#33106) @oliviertassinari @@ -6257,7 +6405,7 @@ A big thanks to the 21 contributors who made this release possible. Here are som - ​<!-- 31 -->[docs] Simplify header DOM structure (#32844) @oliviertassinari - ​<!-- 30 -->[docs] Fix CodeSandbox & StackBlitz generation (#32726) @siriwatknp - ​<!-- 29 -->[docs] Fix urls to columns pages in pricing table (#32842) @alexfauquette -- ​<!-- 28 -->[docs] Fix Tailwind CSS integration docs (#32512) @robertwt7 +- ​<!-- 28 -->[docs] Fix Tailwind CSS integration docs (#32512) @robertwt7 - ​<!-- 27 -->[docs] Fixed wrong command for the `link-underline-hover` codemod (#32793) @veronikaslc - ​<!-- 26 -->[docs] Fixed broken link on the icons page (#32780) @SamuelMaddox - ​<!-- 25 -->[docs] Add "back to top" button (#30441) @VibhorJaiswal @@ -6828,7 +6976,7 @@ A big thanks to the 17 contributors who made this release possible. Here are som - ​<!-- 21 -->[docs] Fix in-house ad for the design kits (#31965) @oliviertassinari - ​<!-- 20 -->[docs] Fix the documentation for filterOptions in Autocomplete API page (#31416) @santhoshbala0178 - ​<!-- 19 -->[docs] Update href for 'TypeScript guide on theme customization' (#31880) @NickFoden -- ​<!-- 18 -->[docs] Fix the CSS modules example in the Interoperability page (#31935) @WilsonNet +- ​<!-- 18 -->[docs] Fix the CSS Modules example in the Interoperability page (#31935) @WilsonNet - ​<!-- 17 -->[docs] Fix small typo in the `styled()` utility page (#31967) @jason1985 - ​<!-- 16 -->[docs] Update mui-x on material-ui navigation (#31810) @siriwatknp - ​<!-- 15 -->[docs] Copy ClickAwayListener docs to Base (#31878) @michaldudak @@ -7197,7 +7345,7 @@ A big thanks to the 16 contributors who made this release possible. Here are som - [docs] Use full product names (Material UI, MUI System) (#30960) @oliviertassinari - [docs] Prefer useEnhancedEffect to avoid server side warnings (#30977) @mnajdova - [docs] Fix force redirection to a different locale (#30967) @oliviertassinari -- [docs] Add live Tailwind CSS demo (#30966) @oliviertassinari +- [docs] Add live Tailwind CSS demo (#30966) @oliviertassinari - [website] Add banner for promoting priority open roles (#31076) @danilo-leal - [website] Open Full-stack Engineer role for studio (#31038) @prakhargupta1 - [website] Minor security improvements (#31062) @oliviertassinari @@ -7380,7 +7528,7 @@ _Jan 24, 2022_ A big thanks to the 12 contributors who made this release possible. Here are some highlights ✨: -- 🛠 @mnajdova added interoperability guide for using Tailwind CSS (#30700) +- 🛠 @mnajdova added interoperability guide for using Tailwind CSS (#30700) - A meaningful number of 🐛 bug fixes and 📚 documentation improvements. ### `@mui/icons-material@5.3.1` @@ -7406,7 +7554,7 @@ A big thanks to the 12 contributors who made this release possible. Here are som - ​<!-- 21 -->[docs] Fix SEO crawl errors (#30733) @oliviertassinari - ​<!-- 20 -->[docs] Update migration-v4.md (#30721) @ddecrulle - ​<!-- 19 -->[docs] Fix migration issues detected by `ahrefs` (#30751) @siriwatknp -- ​<!-- 18 -->[docs] Add interoprability guide for using Tailwind CSS (#30700) @mnajdova +- ​<!-- 18 -->[docs] Add interoprability guide for using Tailwind CSS (#30700) @mnajdova - ​<!-- 17 -->[docs] Fix typo in containedSizeMedium class (#30723) @aaneitchik - ​<!-- 16 -->[docs] Hotfix the wrong URL in X marketing page (#30729) @siriwatknp - ​<!-- 15 -->[docs] Post migration preparation fix (#30716) @siriwatknp @@ -7620,7 +7768,7 @@ A big thanks to the 14 contributors who made this release possible. Here are som - ​<!-- 08 -->[docs] Fix incorrect number of breakpoint helpers (#30353) @chwallen - ​<!-- 07 -->[docs] Update outdated links (#30260) @oliviertassinari - ​<!-- 06 -->[docs] Support redirects from old urls to /material/\* (#30286) @siriwatknp -- ​<!-- 05 -->[examples] Fix CSS modules integration (#30381) @oliviertassinari +- ​<!-- 05 -->[examples] Fix CSS Modules integration (#30381) @oliviertassinari - ​<!-- 02 -->[website] Fix SEO issues (#30372) @oliviertassinari - ​<!-- 01 -->[website] Sync sponsors (#30259) @oliviertassinari @@ -9990,7 +10138,7 @@ A big thanks to the 14 contributors who made this release possible. Here are som - [ButtonBase] Fix role="button" attribute (#26271) @Gautam-Arora24 - [Dialog] Fix support for custom breakpoints (#26331) @jeferson-sb - [Select] Open popup below button (#26200) @oliviertassinari -- [TextField] Add variants support, e.g. custom sizes (#26468) @siriwatknp +- [TextField] Add variants support, for example custom sizes (#26468) @siriwatknp - [Tooltip] Improve handling of small vs. touch screens (#26097) @oliviertassinari ### `@material-ui/codemod@5.0.0-alpha.35` @@ -11515,7 +11663,7 @@ A big thanks to the 30 contributors who made this release possible. Here are som - ​<!-- 78 -->[system] Use spacing unit in `gap`, `rowGap`, and `columnGap` (#24794) @ruppysuppy - If you were using a number previously, you need to provide the value in `px` to bypass the new transformation with `theme.spacing`. The change was done for consistency with the Grid spacing prop and the other system spacing properties, e.g. `<Box padding={2}>`. + If you were using a number previously, you need to provide the value in `px` to bypass the new transformation with `theme.spacing`. The change was done for consistency with the Grid spacing prop and the other system spacing properties, for example `<Box padding={2}>`. ```diff <Box @@ -11899,7 +12047,7 @@ A big thanks to the 14 contributors who made this release possible. Here are som - 👩‍🎤 Migrate the Avatar to emotion (#24114) @oliviertassinari - 👩‍🎤 Migrate the Button to emotion (#24107, #24100) @mnajdova - ♿️ Improve TrapFocus behavior, ignore the container as a tabbable element (#23364) @gregnb - In rare cases, an element might not longer be tabbable when looping, e.g. overflow container in Firefox. + In rare cases, an element might not longer be tabbable when looping, for example overflow container in Firefox. You can work around the problem by adding a `tabIndex={0}` or customizing the `getTabbable` prop. - And many more 🐛 bug fixes and 📚 improvements. @@ -12439,7 +12587,7 @@ A big thanks to the 34 contributors who made this release possible. Here are som While the source code is currently hosted in the [main repository](https://github.com/mui/material-ui), we might move it to the [x repository](https://github.com/mui/mui-x) in the future, depending on what is easier for the commercial date range picker. The date picker will stay open source no matter what. -- 📚 Revamp the documentation for [MUI System](https://mui.com/system/getting-started/). The System contains CSS utilities. The documentation now promotes the use of the `sx` prop. It's ideal for adding one-off styles, e.g. padding, but when pushed to its limits, it can be used to implement quickly a complete page. +- 📚 Revamp the documentation for [MUI System](https://mui.com/system/getting-started/). The System contains CSS utilities. The documentation now promotes the use of the `sx` prop. It's ideal for adding one-off styles, for example padding, but when pushed to its limits, it can be used to implement quickly a complete page. - 👩‍🎨 Upgrade emotion to v11 (#23007) @mnajdova. - And many more 🐛 bug fixes and 📚 improvements. @@ -12551,7 +12699,7 @@ A big thanks to the 20 contributors who made this release possible. Here are som Another reason for introducing this package is to prepare the groundwork for a [second theme](https://github.com/mui/material-ui/issues/22485) (not Material Design based). - A note on the terminology: "unstyled" means that the components have the same API as the "styled" components but come without CSS. Material UI also contains "headless" components that exposes a hook API, e.g. [useAutocomplete](https://mui.com/components/autocomplete/#useautocomplete) or [usePagination](https://mui.com/components/pagination/#usepagination). + A note on the terminology: "unstyled" means that the components have the same API as the "styled" components but come without CSS. Material UI also contains "headless" components that exposes a hook API, for example [useAutocomplete](https://mui.com/components/autocomplete/#useautocomplete) or [usePagination](https://mui.com/components/pagination/#usepagination). This change is part of our strategy to iterate on the v5 architecture with the `Slider` first. In the next alpha release, we plan to replace the v4 slider with the v5 slider. Once the new approach is stress-tested and validated, we will roll it out to all the components. @@ -12578,7 +12726,7 @@ A big thanks to the 20 contributors who made this release possible. Here are som - [Avatar] Fix usage of srcset property (#23286) @matheuspiment - [ClickAwayListener] Fix mounting behavior in Portals in React 17 (#23315) @eps1lon - [core] Allow React 17 (#23311) @eps1lon -- [Icon] Fix translation, e.g. Google Translate (#23237) @cbeltrangomez84 +- [Icon] Fix translation, for example Google Translate (#23237) @cbeltrangomez84 - [LinearProgress] Fix Safari's bug during composition of different paint (#23293) @montogeek - [Radio] Fix dot misalignment in Safari (#23239) @anasufana - [styled-engine] Fix tagged template syntax with multiple expressions (#23269) @eps1lon @@ -14260,7 +14408,7 @@ A big thanks to the 11 contributors who made this release possible. - [core] Drop support for non-ref-forwarding class components (#21811) @eps1lon Support for non-ref-forwarding class components in the `component` prop or as an immediate `children` has been dropped. If you were using `unstable_createStrictModeTheme` or didn't see any warnings related to `findDOMNode` in `React.StrictMode` then you don't need to do anything. Otherwise check out the ["Caveat with refs" section in our composition guide](/guides/composition/#caveat-with-refs) to find out how to migrate. - This change affects almost all components where you're using the `component` prop or passing `children` to components that require `children` to be elements (e.g. `<MenuList><CustomMenuItem /></MenuList>`) + This change affects almost all components where you're using the `component` prop or passing `children` to components that require `children` to be elements (for example `<MenuList><CustomMenuItem /></MenuList>`) - [Stepper] Use context API (#21613) @baterson Rely on the context over the `React.cloneElement()` API. This change makes composition easier. diff --git a/CHANGELOG.old.md b/CHANGELOG.old.md index bae916e5ed7ffa..decee52c129471 100644 --- a/CHANGELOG.old.md +++ b/CHANGELOG.old.md @@ -101,7 +101,7 @@ A big thanks to the 12 contributors who made this release possible. It includes ### @material-ui/system@4.12.0 -- [Box] Deprecate css prop in favor of sx (#23480) @mnajdova +- [Box] Deprecate `css` prop in favor of `sx` prop (#23480) @mnajdova ### Docs @@ -180,7 +180,7 @@ You can expect similar releases like this one in the coming months. - [theme] Deprecate theme.mixins.gutters (#22245) @joshwooding - [Avatar] Add circular variant (#22090) @eps1lon - [Badge] Add overlap circular and rectangular (#22076) @eps1lon -- [Box] Deprecate css prop in favor of sx (#23480) @mnajdova +- [Box] Deprecate `css` prop in favor of `sx` prop (#23480) @mnajdova - [CircularProgress] Backport simplified determinate style & deprecate static (#22094) @mbrookes - [Dialog] Deprecate the transition onX props (#22114) @mbrookes - [GridList] Rename to ImageList & add deprecation warnings (#22363) @mbrookes @@ -654,7 +654,7 @@ Here are some highlights ✨: - [l10n] Add Hindi (hi-IN) locale (#20916) @chandan-singh - [Popper] Fix keepMounted visibility (#20937) @weslenng -- [Select] Focus labelled element on click (#20833) @qkdreyer +- [Select] Focus labeled element on click (#20833) @qkdreyer - [Slider] Fix center label in IE11 (#20942) @Uneetpatel7 - [Tabs] Add `selectionFollowsFocus` (#20936) @eps1lon - [Tabs] Forward aria-label\* attributes to tablist (#20986) @eps1lon @@ -1064,7 +1064,7 @@ Here are some highlights ✨: - ⚛️ Improve the DX, migrate a couple of props' descriptions to TypeScript (#20298, #20171, #20264) @eps1lon. - ![typescript](https://user-images.githubusercontent.com/3165635/77828342-1f376080-711b-11ea-8c9d-c1c245fb17b0.png) + ![TypeScript](https://user-images.githubusercontent.com/3165635/77828342-1f376080-711b-11ea-8c9d-c1c245fb17b0.png) The coverage has increase from 17 to 50 components. We are working on migrating the 94 missing components. @@ -1135,7 +1135,7 @@ Here are some highlights ✨: - [core] Batch small changes (#20255) @oliviertassinari - [core] Fix docs:start which should start next.js server (#20202) @ro7584 - [core] Fix maintenance workflow failing on fork PRs (#20195) @eps1lon -- [core] Format all ts files (#20233) @eps1lon +- [core] Format all .ts files (#20233) @eps1lon ## 4.9.7 @@ -1367,7 +1367,7 @@ A big thanks to the 18 contributors who made this release possible. ### `@material-ui/core@v4.9.3` - [l10n] Add Estonian (et-EE) locale (#19707) @villuv -- [ScopedCssBaseline] Allow css to be only applied on children (#19669) @TomPradat +- [ScopedCssBaseline] Allow CSS to be only applied on children (#19669) @TomPradat ### `@material-ui/system@v4.9.3` @@ -1723,7 +1723,7 @@ A big thanks to the 22 contributors who made this release possible. ### Docs -- [docs] Add TS demo for MenuPopupState (#18998) @eps1lon +- [docs] Add TypeScript demo for MenuPopupState (#18998) @eps1lon - [docs] Add yarn install instructions in CONTRIBUTING.md (#18970) @hiteshkundal - [docs] Clarify not all components have 'component' prop (#19015) @JamieS1211 - [docs] Fix syntax error in palette customization example (#19008) @mumairofficial @@ -1978,7 +1978,7 @@ Here are some highlights ✨: - [Collapse] Add support for unitless collapsedHeight (#18461) @weslenng - [Grid] Infer `displayName` (#18481) @NMinhNguyen - [HiddenCss] Fix warning when using custom breakpoints (#18382) @eps1lon -- [Modal] Prefer to lock scroll on body than html element (#18445) @andreasheim +- [Modal] Prefer to lock scroll on body than HTML element (#18445) @andreasheim - [Popper] Use context for RTL support (#18381) @MisterQH - [Slider] Increase interaction area (#18429) @oliviertassinari - [Slider] Make the slider work as intended when max%step !== 0 (#18438) @macfire10 @@ -2220,18 +2220,18 @@ Here are some highlights ✨: ### `@material-ui/core@v4.5.2` - [Avatar] Revert #17694, correct the API docs, add tests (#18026) @mbrookes -- [Checkbox] Add TS demo for FormControlLabelPosition (#17964) @burtyish +- [Checkbox] Add TypeScript demo for FormControlLabelPosition (#17964) @burtyish - [Dialog] Fix labelledby and describedby placement (#18032) @eps1lon - [Dialog] Reduce margins (#17867) @rahulkotha18 - [ExpansionPanelSummary] Test in StrictMode (#17873) @eps1lon -- [FormControlLabel] Add missing CSS class keys to TS (#17963) @itayyehezkel +- [FormControlLabel] Add missing CSS class keys to TypeScript (#17963) @itayyehezkel - [Link] Warn when using plain function component in `component` (#17825) @Nikhil-Pavan-Sai - [ListSubheader] Reduce specificity of TypeScript type (#17715) @sakulstra - [Menu] Add new context menu demo (#17839) @SarthakC - [Modal] Fix tabIndex customization (#17939) @Cyrus-d - [Modal] Improve Gatsby support (#17972) @sreetej1998 - [Popper] Revert position fix (#17914) @rahulkotha18 -- [Select] Add labelId to implement proper labelling (#17892) @eps1lon +- [Select] Add labelId to implement proper labeling (#17892) @eps1lon - [Select] Better support Preact (#18027) @glromeo - [Select] Document how values are compared (#17912) @DustinRobison - [Slider] Apply the disabled pseudo class on the thumb too (#18011) @hoop71 @@ -2250,7 +2250,7 @@ Here are some highlights ✨: ### `@material-ui/styles@v4.5.2` -- [styles] Allow ref on withTheme components in TS (#17695) @ianschmitz +- [styles] Allow ref on withTheme components in TypeScript (#17695) @ianschmitz ### `@material-ui/system@v4.5.2` @@ -2262,12 +2262,12 @@ Here are some highlights ✨: ### Docs -- [docs] Add TS demo for DynamicCSS (#17994) @netochaves -- [docs] Add TS demo for DynamicCSSVariables (#17983) @netochaves -- [docs] Add TS demo for MaterialTable (#17938) @schapka -- [docs] Add TS demo for WithWidth (#17930) @burtyish -- [docs] Add TS demos for SimpleNoSsr and FrameDeferring (#17913) @ganes1410 -- [docs] Add TS demos for SplitButton in components/buttons (#17862) @rahmatrhd +- [docs] Add TypeScript demo for DynamicCSS (#17994) @netochaves +- [docs] Add TypeScript demo for DynamicCSSVariables (#17983) @netochaves +- [docs] Add TypeScript demo for MaterialTable (#17938) @schapka +- [docs] Add TypeScript demo for WithWidth (#17930) @burtyish +- [docs] Add TypeScript demos for SimpleNoSsr and FrameDeferring (#17913) @ganes1410 +- [docs] Add TypeScript demos for SplitButton in components/buttons (#17862) @rahmatrhd - [docs] Add demo for actions in ExpansionPanelSummary (#17969) @ayliao - [docs] Add demo for prominent app bar (#17894) @burtyish - [docs] Add notification about the date picker survey @oliviertassinari @@ -2370,14 +2370,14 @@ Here are some highlights ✨: ### Docs -- [docs] Add Customization/Components TS demo (#17788) @limatgans -- [docs] Add Media Query TS demo (#17766) @lksilva -- [docs] Add TS demos for guides/interoperability (#17804) @limatgans -- [docs] Add classNames TS demo (#17771) @lksilva -- [docs] Add component demos in ts (#17790) @lksilva -- [docs] Add dynamic class name TS demo (#17793) @lksilva -- [docs] Add useWidth TS demo (#17770) @lksilva -- [docs] Added TS Demos for component/toggle-button (#17822) @limatgans +- [docs] Add Customization/Components TypeScript demo (#17788) @limatgans +- [docs] Add Media Query TypeScript demo (#17766) @lksilva +- [docs] Add TypeScript demos for guides/interoperability (#17804) @limatgans +- [docs] Add classNames TypeScript demo (#17771) @lksilva +- [docs] Add component demos in TypeScript (#17790) @lksilva +- [docs] Add dynamic class name TypeScript demo (#17793) @lksilva +- [docs] Add useWidth TypeScript demo (#17770) @lksilva +- [docs] Added TypeScript Demos for component/toggle-button (#17822) @limatgans - [docs] Better strict mode switch (#17684) @eps1lon - [docs] Change imports from @material-ui/styles to @material-ui/core/styles (#17447) @mnemanja - [docs] Extend size-snapshot (#17633) @eps1lon @@ -2835,7 +2835,7 @@ Here are some highlights ✨: - [benchmark] Fix not running (#16900) @ypresto - [ci] Ignore dependabot branches (#16893) @eps1lon - [core] Generate PropTypes from type definitions (#16642) @merceyz -- [core] Optimise destructuring for useState, useReducer (#16842) @NMinhNguyen +- [core] Optimize destructuring for useState, useReducer (#16842) @NMinhNguyen - yarn docs:api @oliviertassinari ## 4.3.1 @@ -3377,7 +3377,7 @@ Here are some highlights ✨: ### `@material-ui/icons@v4.1.0` -- [icons] Simplify generated index.d.ts to reduce TS compile time (#16083) @phryneas +- [icons] Simplify generated index.d.ts to reduce TypeScript compile time (#16083) @phryneas ### Docs @@ -3528,7 +3528,7 @@ Here are some highlights ✨: ### `@material-ui/codemod@v4.0.1` -- [codemod] Create spacing api codemod (#15782) @joshwooding +- [codemod] Create spacing API codemod (#15782) @joshwooding ### `@material-ui/styles@v4.0.1` @@ -3572,7 +3572,7 @@ Here are some highlights ✨: - [core] Add cross-env to docs:size-why (#15816) @merceyz - [core] Change the top package name so we get the number of dependents packages @oliviertassinari -- [core] Fix not appearing in github used/dependents (#15859) @eps1lon +- [core] Fix not appearing in GitHub used/dependents (#15859) @eps1lon - [core] Prepare focus visible polyfill in ref phase (#15851) @eps1lon - [core] Remove babel-node for server/shared modules (#15764) @cvanem - [core] Remove dependency on workspace (#15849) @eps1lon @@ -3719,7 +3719,7 @@ This is a stability release preparing v4. ### `@material-ui/system@v4.0.0-beta.2` -- [system] Fix css function rejecting certain prop types (#15611) @eps1lon +- [system] Fix CSS function rejecting certain prop types (#15611) @eps1lon ### `@material-ui/lab@v4.0.0-alpha.11` @@ -3737,7 +3737,7 @@ This is a stability release preparing v4. - [docs] Fix the adapting makeStyles based on props example syntax (#15621) @devarsh - [docs] Improve installation instructions for running the docs locally (#15608) @andreawaxman - [docs] Improve v3 migration guide (#15615) @eps1lon -- [docs] Link edit page button to github editor (#15659) @mbrookes +- [docs] Link edit page button to GitHub editor (#15659) @mbrookes - [docs] Miscellaneous polish (#15665) @eps1lon - [docs] Reorganize the structure (#15603) @mbrookes - [docs] Update the translations (#15653) @mbrookes @@ -3957,7 +3957,7 @@ You will learn more about v4 in the final release blog post and our plans for th ### Core - [core] Fix the CI fail (#15428) @oliviertassinari -- [ci] Fail when demos are only available in TS (#15460) @eps1lon +- [ci] Fail when demos are only available in TypeScript (#15460) @eps1lon - [core] Fix useLayoutEffect warnings on the server (#15463) @eps1lon - [core] Minor nitpicks (#15432) @joshwooding - [core] Use terser for minification in umd bundle (#15491) @eps1lon @@ -4228,7 +4228,7 @@ Here are some highlights ✨: ### Core - [test] Forward ref behavior (#15131) @eps1lon -- [core] Use explicit html entity (#15132) @eps1lon +- [core] Use explicit HTML entity (#15132) @eps1lon - [test] Decouple root class from root component (#15168) @eps1lon - [core] Polish `type` type of button related components (#15158) @eps1lon - [DialogContentText] Test conformance (#15206) @eps1lon @@ -4606,7 +4606,7 @@ Here are some highlights ✨: - [docs] Fix Drawer demos accessibility (#14728) @tiagodreis - [docs] Add "Portals" to the styled components documentation (#14720) @C-Rodg - [docs] Specify PaletteIntention syntax (#14727) @ozydingo -- [docs] Add button demos in ts (#14739) @eps1lon +- [docs] Add button demos in TypeScript (#14739) @eps1lon - [docs] Document the migration from v3 to v4 (#14741) @oliviertassinari - [docs] before() is Mocha; beforeEach() is Jest (#14743) @masaok - [docs] Fix IE11 build (#14781) @oliviertassinari @@ -4784,7 +4784,7 @@ Here are some highlights ✨: - [InputLabel] Remove FormLabelClasses in favor of asterisk class (#14504) @umairfarooq44 -You should be able to override all the styles of the FormLabel component using the css API of the InputLabel component. We do no longer need the `FormLabelClasses` property. +You should be able to override all the styles of the FormLabel component using the CSS API of the InputLabel component. We do no longer need the `FormLabelClasses` property. ```diff <InputLabel @@ -4837,13 +4837,13 @@ Remove the first option argument of `withTheme()`. The first argument was a plac - [docs] Update CHANGELOG.md (#14516) @saculbr - [examples] Add lib to tsconfig (#14507) @eps1lon - [docs] Enable es, fr, pt & ru (#14537) @oliviertassinari -- [docs] Add ts demos for menus, fixes ClickAwayListener onClickAway type (#14535) @eps1lon +- [docs] Add TypeScript demos for menus, fixes ClickAwayListener onClickAway type (#14535) @eps1lon - [docs] Update the styling of the TOC (#14520) @mbrookes - [docs] Update breakpoints.md for clarity (#14527) @matthewjwhitney - [docs] Fix Horizontal Non-linear Stepper demo (#14551) @SVTerziev - [docs] Update the branch for Crowdin (#14550) @mbrookes - [docs] Fix hooks codesandbox broken (#14553) @Abbo44 -- [docs] Fix css anchor link (#14554) @umairfarooq44 +- [docs] Fix CSS anchor link (#14554) @umairfarooq44 - [examples] Improve the Gastby integration (#14552) @oliviertassinari - [docs] Add examples of global class names (#14563) @n-batalha - [docs] Change Gitter to Spectrum (#14558) @mbrookes @@ -5033,7 +5033,7 @@ _Tip: you can provide more than one argument: `theme.spacing(1, 2) // = '8px 16p - [docs] Reword certain phrases to improve i10n (#14457) @eps1lon - [docs] Fix IE11 crash on demo pages (#14466) @eps1lon - [docs] Add french translation (#14467) @zek0faws -- [docs] Standardise compose util usage (#14472) @mbrookes +- [docs] Standardize compose util usage (#14472) @mbrookes - [docs] Additional tweaks to English l10n strings (#14471) @mbrookes - [examples] Improve the v3/v4 distinction (#14476) @oliviertassinari - [docs] Change interpolation library (#14481) @mbrookes @@ -5161,7 +5161,7 @@ Here are some highlights ✨: - [LinearProgress] Remove dead bar2Determinate CSS class (#14298) @lmcarreiro - [Select] Help automated UI testing (#14289) @oumaima1234 - [MobileStepper] Fix typo CSS API (#14300) @DenrizSusam -- [Link] Add ts test and distinguish from react-router link test (#14304) @rosskevin +- [Link] Add TypeScript test and distinguish from react-router link test (#14304) @rosskevin ### `@material-ui/styles@v3.0.0-alpha.9` @@ -5516,7 +5516,7 @@ you to add them up quickly in your head without having to worry about decimals. #### Changes -- [Card][list] Change sub-components to have fixed gutters (#13788) @joshwooding +- [Card][list] Change subcomponents to have fixed gutters (#13788) @joshwooding - [Button] Fix padding for Text Button variant to adhere to spec (#13827) @bdeloeste - [ButtonBase] Add stop ripple on context menu event (#13740) @joshwooding - [Menu] Add reason value and update documentation for on close reason (#13877) @rfbotto @@ -5540,7 +5540,7 @@ you to add them up quickly in your head without having to worry about decimals. - [docs] Fix search suggestions on dark mode (#13874) @rfbotto - [docs] Add accessibility section to selection-controls with demo (#13896) @wyseguyonline -- [docs] Add support for multiple demo variants e.g. JS or Hooks (#13873) @adeelibr +- [docs] Add support for multiple demo variants - for example JS or Hooks (#13873) @adeelibr - [docs] Remove the withRoot HOC (#13909) @oliviertassinari - [docs] Add material-ui-pickers in pickers page (#13697) @dmtrKovalenko - [docs] Continue #13806 and port back some fix from @system (#13917) @oliviertassinari @@ -5901,7 +5901,7 @@ Here are some highlights ✨: - [Drawer] Add a root style rule for consistency (#13418) @KirankumarAmbati - [Menu] Fix position regression (#13419) @oliviertassinari - [Menu] Add a visual regression test (#13420) @oliviertassinari -- [Select] Fix focused text colour (#13423) @joshwooding +- [Select] Fix focused text color (#13423) @joshwooding - [Tabs] Fix misaligned tab (#13421) @Ang-YC - [OutlinedInput] Improve customization (#13428) @oliviertassinari - [CircularProgress] Introduce disableShrink property (#13430) @joshwooding @@ -6295,7 +6295,7 @@ It contains many bug fixes 🐛 and documentation improvements 📝. - [InputLabel] Fix Chrome's autofill (#12926) @PutziSan - [Tooltip] Fix unwanted tooltip opening (#12929) @ayubov - [TextField] Fix RTL support of outlined (#12939) @RobertPurcea -- [Button] Make the outlined button border grey when disabled (#12933) @dispix +- [Button] Make the outlined button border gray when disabled (#12933) @dispix - [RootRef] Keep track of the DOM node changes (#12953) @oliviertassinari - [Grid] Fix rounding errors (#12952) @RobertPurcea - [Tooltip] Back to 100% test coverage (#12954) @oliviertassinari @@ -6311,7 +6311,7 @@ It contains many bug fixes 🐛 and documentation improvements 📝. ### Docs -- [docs] Make jss insertion point reference the same as html comment (#12896) @emattias +- [docs] Make JSS insertion point reference the same as HTML comment (#12896) @emattias - [docs] Small fixes (#12904) @oliviertassinari - [docs] Add reference to material-ui-theme-editor (#12888) @jdrouet - [docs] Add another case to check when SSR fails (#12908) @oliviertassinari @@ -6717,7 +6717,7 @@ N/A - [docs] Add some Render Props demos (#12366) @jedwards1211 - [docs] Add example layouts (#12410) @mbrookes - [core] Fix some errors when porting demos to TypeScript (#12417) @PavelPZ -- [docs] Standardise the wording between icon docs and readme (#12425) @mbrookes +- [docs] Standardize the wording between icon docs and readme (#12425) @mbrookes - [docs] Improve the withTheme example (#12428) @oliviertassinari - [docs] Rename layouts to page-layout-examples + minor fixes (#12430) @mbrookes - [docs] Ensure `inputRef` is wired up to react-number-format's input (#12444) @NMinhNguyen @@ -6737,7 +6737,7 @@ N/A - [core] Add hoverOpacity to TypeAction interface (#12455) @hassan-zaheer - [core] Save some bytes in the super() logic (#12476) @oliviertassinari - [core] Upgrade the dependencies (#12477) @oliviertassinari -- [typescript] improve autocomplete for css properties in createStyles (#12456) @eps1lon +- [typescript] improve autocomplete for CSS properties in createStyles (#12456) @eps1lon #### Lab @@ -7132,7 +7132,7 @@ N/A - [docs] Document jss-nested rule reference feature (#11901) @i8ramin - [docs] Correct markdown example from svg icon (#11922) @GabrielDuarteM -- [docs] TS decorating reword (#11923) @swpease +- [docs] TypeScript decorating reword (#11923) @swpease ### Core @@ -7813,7 +7813,7 @@ As long as you are providing a valid URL to `<CardMedia image />`, it should be - [docs] Fix the flow example (#11118) @prastut - [docs] Add showcase for Local Insights (#11131) @hrdymchl - [docs] Add iOS momentum scrolling (#11140) @cherniavskii -- [docs] Add a CSS modules example (#11171) @oliviertassinari +- [docs] Add a CSS Modules example (#11171) @oliviertassinari - [docs] Fix typo in themes.md (#11149) @zhuangya - [docs] Make sure next@6 is working (#11168) @oliviertassinari - [docs] Correct spelling error in FormDialog.js example (#11176) @weldon0405 @@ -8199,7 +8199,7 @@ N/A - [docs] Add project to showcase (#10614) @jdupont - [docs] Fix typo (#10621) @prastut -- [docs] Updating the TS example to use CssBaseline (#10633) @yuchen-w +- [docs] Updating the TypeScript example to use CssBaseline (#10633) @yuchen-w - [docs] Better support of multiline for downshift (#10641) @oliviertassinari - [docs] Simplify LongMenu demo (#10646) @RichardLindhout - [docs] Improve the onboarding (#10639) @oliviertassinari @@ -9547,7 +9547,7 @@ Here are some highlights ✨: - [docs] Better definition of what withStyles is (#9235) @ajay2507 - [docs] Save 11% on the images (#9400) @oliviertassinari - [docs] Add a downshift example (#9401) @oliviertassinari -- [docs] Fix Tabs examples typography & standardise code (#9366) @mbrookes +- [docs] Fix Tabs examples typography and standardize code (#9366) @mbrookes - [docs] Add a Plugins paragraph (#9399) @oliviertassinari - [docs] Fix code formatting (#9414) @oliviertassinari - [docs] Add codesandbox edit button (#9416) @oliviertassinari @@ -9693,7 +9693,7 @@ In the following diff `SwitchBase` can be a `Checkbox` a `Radio` or a `Switch`. - [Popover] Implement ability to pass coordinates as anchor (#9004) @jackyho112 - [TextField] Fix undefined blur event (#9042) @nareshbhatia - [Slide] Support dynamic anchor (#9055) @oliviertassinari -- [Input] Remove grey highlight on iOS (#9057) @oliviertassinari +- [Input] Remove gray highlight on iOS (#9057) @oliviertassinari - [Grid] Add missing wrap-reverse classname (#9076) @dehli - [breakpoint] Fix xs value (#9078) @oliviertassinari - [TablePagination] Fix IE11 colSpan issue (#9086) @sakulstra @@ -10061,7 +10061,7 @@ Here are some highlights ✨: </TableFooter> ``` -- [typescript] Fix withStyles typing for class components; remove usage as TS decorator (#8561) @pelotom +- [typescript] Fix withStyles typing for class components; remove usage as TypeScript decorator (#8561) @pelotom We drop the TypeScript decorator support. ### Component Fixes / Enhancements @@ -10446,7 +10446,7 @@ A big thanks to the 13 contributors who made this release possible. - [ButtonBase] Make the `button` and `a` behavior the same (#8130) @oliviertassinari - [withStyle] Memoize the classes object between renders (#8142) @oliviertassinari - [typescript] Fix for Popover -> PaperProps typing (#8129) @xaviergonz -- [typescript] Fix for createPalette TS types (#8123) @xaviergonz +- [typescript] Fix for createPalette TypeScript types (#8123) @xaviergonz - [LinearProgress] Fix loop (#8146) @oliviertassinari - [Card] Add `backgroundPosition: 'center'` to CardMedia (#8148) @kripod - [ImgBot] Optimize images (#8154) @dabutvin @@ -10819,7 +10819,7 @@ A big thanks to the 7 contributors who made this release possible. ### Core - [flow] Export type Props for composability (#7609) @rosskevin -- [typescript] Add TS typings (#7553) @sebald +- [typescript] Add TypeScript typings (#7553) @sebald - [typescript] Improve the coverage (#7606) @sebald - [core] Add isMuiComponent helper (#7635) @katzoo @@ -10882,7 +10882,7 @@ We have been fixing many bugs and implemented new features. The styling solution has also been greatly improved: - Better performance -- Shorter class names in production, e.g. `c1y` +- Shorter class names in production, for example `c1y` - Better readable class names in development - No longer required `MuiThemeProvider` - Simpler `createStyleSheet` API with an optional name @@ -13834,7 +13834,7 @@ _Jan. 26, 2015_ - Radio Button styling now matches material design specs - This component has been revamped and can now be controlled or uncontrolled. - Slider - - Fixed a css bug with slider handles (#225) + - Fixed a CSS bug with slider handles (#225) - Added onDragStart and onDragStop props (#217) - Snackbar - Fixed Ghost hidden snackbar (#235) @@ -14100,7 +14100,7 @@ _Nov. 7, 2014_ - All icon names had to change because of this. Sorry. :( - PaperButton - Added href prop - - Css fixes + - CSS fixes - Dialog - Added onShow event - Children contents of the dialog is only rendered if the dialog is opened @@ -14114,8 +14114,8 @@ _Nov. 7, 2014_ _Nov. 5, 2014_ -- css fix on paper component +- CSS fix on paper component - hover transition fix on buttons - removed selected state on drop down icon component -- css fix on left nav component +- CSS fix on left nav component - added prop on left nav component to allow left nav to be docked and hidden diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ee48c4390a2faa..7db6aa91515e6a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -72,11 +72,11 @@ git remote add upstream https://github.com/mui/material-ui.git <!-- #default-branch-switch --> -3. Synchronize your local `master` branch with the upstream one: +3. Synchronize your local `next` branch with the upstream one: ```bash -git checkout master -git pull upstream master +git checkout next +git pull upstream next ``` 4. Install the dependencies with pnpm (yarn or npm aren't supported): @@ -146,7 +146,7 @@ Make sure the following is true: <!-- #default-branch-switch --> -- The branch is targeted at `master` for ongoing development. All tests are passing. Code that lands in `master` must be compatible with the latest stable release. It may contain additional features but no breaking changes. We should be able to release a new minor version from the tip of `master` at any time. +- The branch is targeted at `next` for ongoing development. All tests are passing. Code that lands in `next` must be compatible with the latest alpha/beta release. It may contain additional features but no breaking changes. We should be able to release a new minor version from the tip of `next` at any time. - If a feature is being added: - If the result was already achievable with the core library, you've explained why this feature needs to be added to the core. - If this is a common use case, you've added an example to the documentation. @@ -253,7 +253,7 @@ Click on **Details** to find out more about them. ### Updating the component API documentation The component API in the component `propTypes` and under `docs/pages/api-docs` is auto-generated from the [JSDoc](https://jsdoc.app/about-getting-started.html) in the TypeScript declarations. -Be sure to update the documentation in the corresponding `.d.ts` files (e.g. `packages/mui-material/src/Button/Button.d.ts` for `<Button>`) and then run: +Be sure to update the documentation in the corresponding `.d.ts` files (for example `packages/mui-material/src/Button/Button.d.ts` for `<Button>`) and then run: ```bash $ pnpm proptypes @@ -300,11 +300,11 @@ docs/src/pages/components/buttons/ #### 2. Write the demo code We uses TypeScript to document our components. -We prefer demos written in TS (using the `.tsx` file format). +We prefer demos written in TypeScript (using the `.tsx` file format). After creating a TypeScript demo, run `pnpm docs:typescript:formatted` to automatically create the JavaScript version, which is also required. -If you're not familiar with TypeScript, you can write the demo in JavaScript, and a core contributor may help you migrate it to TS. +If you're not familiar with TypeScript, you can write the demo in JavaScript, and a core contributor may help you migrate it to TypeScript. #### 3. Edit the page's Markdown file diff --git a/README.md b/README.md index 1c97359217a66e..7a05c9ef9c8b79 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ You can find complete templates and themes in the [MUI Store](https://mui.com/s Read the [contributing guide](/CONTRIBUTING.md) to learn about our development process, how to propose bug fixes and improvements, and how to build and test your changes. Contributing is about more than just issues and pull requests! -There are many other ways to [support MUI](https://mui.com/material-ui/getting-started/faq/#mui-is-awesome-how-can-i-support-the-project) beyond contributing to the code base. +There are many other ways to [support Material UI](https://mui.com/material-ui/getting-started/faq/#mui-is-awesome-how-can-i-support-the-project) beyond contributing to the code base. ## Changelog @@ -125,7 +125,7 @@ The [changelog](https://github.com/mui/material-ui/releases) is regularly update ## Roadmap -Future plans and high-priority features and enhancements can be found in our [roadmap](https://mui.com/material-ui/discover-more/roadmap/). +Future plans and high-priority features and enhancements can be found in the [roadmap](https://mui.com/material-ui/discover-more/roadmap/). ## License diff --git a/TYPESCRIPT_CONVENTION.md b/TYPESCRIPT_CONVENTION.md index 27a472c2125ab7..6ba33bb990f0f1 100644 --- a/TYPESCRIPT_CONVENTION.md +++ b/TYPESCRIPT_CONVENTION.md @@ -8,7 +8,7 @@ ### `Props Interface` -- export interface `{ComponentName}classes` from `{component}Classes.ts` and add comment for generating api docs (for internal components, may or may not expose classes but don't need comment) +- export interface `{ComponentName}classes` from `{component}Classes.ts` and add comment for generating API docs (for internal components, may or may not expose classes but don't need comment) - export interface `{ComponentName}Props` - always export props interface (use `interface` over `type`) from the component file diff --git a/apps/local-ui-lib/package.json b/apps/local-ui-lib/package.json index 8e7e2264364d9c..9cf9342c098b12 100644 --- a/apps/local-ui-lib/package.json +++ b/apps/local-ui-lib/package.json @@ -3,6 +3,6 @@ "version": "0.0.1", "private": true, "dependencies": { - "@pigment-css/react": "file:../../packages/pigment-react" + "@pigment-css/react": "file:../../packages/pigment-css-react" } } diff --git a/apps/local-ui-lib/tsconfig.json b/apps/local-ui-lib/tsconfig.json index c876374fe985c9..cad7b2a86bf955 100644 --- a/apps/local-ui-lib/tsconfig.json +++ b/apps/local-ui-lib/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.json", "references": [ { - "path": "../../packages/pigment-react" + "path": "../../packages/pigment-css-react" } ] } diff --git a/apps/pigment-next-app/.eslintrc b/apps/pigment-css-next-app/.eslintrc similarity index 100% rename from apps/pigment-next-app/.eslintrc rename to apps/pigment-css-next-app/.eslintrc diff --git a/apps/pigment-next-app/.gitignore b/apps/pigment-css-next-app/.gitignore similarity index 100% rename from apps/pigment-next-app/.gitignore rename to apps/pigment-css-next-app/.gitignore diff --git a/apps/pigment-next-app/README.md b/apps/pigment-css-next-app/README.md similarity index 100% rename from apps/pigment-next-app/README.md rename to apps/pigment-css-next-app/README.md diff --git a/apps/pigment-next-app/next.config.js b/apps/pigment-css-next-app/next.config.js similarity index 100% rename from apps/pigment-next-app/next.config.js rename to apps/pigment-css-next-app/next.config.js diff --git a/apps/pigment-next-app/package.json b/apps/pigment-css-next-app/package.json similarity index 80% rename from apps/pigment-next-app/package.json rename to apps/pigment-css-next-app/package.json index 99c9a8de5bb66c..eeff6a21ce1156 100644 --- a/apps/pigment-next-app/package.json +++ b/apps/pigment-css-next-app/package.json @@ -9,7 +9,7 @@ "clean": "rimraf .next" }, "dependencies": { - "@pigment-css/react": "file:../../packages/pigment-react", + "@pigment-css/react": "file:../../packages/pigment-css-react", "@mui/utils": "file:../../packages/mui-utils/build", "@mui/base": "file:../../packages/mui-base/build", "@mui/material": "file:../../packages/mui-material/build", @@ -23,8 +23,8 @@ "next": "latest" }, "devDependencies": { - "@pigment-css/unplugin": "file:../../packages/pigment-unplugin", - "@pigment-css/nextjs-plugin": "file:../../packages/pigment-nextjs-plugin", + "@pigment-css/unplugin": "file:../../packages/pigment-css-unplugin", + "@pigment-css/nextjs-plugin": "file:../../packages/pigment-css-nextjs-plugin", "@types/node": "^20.5.7", "@types/react": "^18.2.55", "@types/react-dom": "^18.2.19", diff --git a/apps/pigment-next-app/public/next.svg b/apps/pigment-css-next-app/public/next.svg similarity index 100% rename from apps/pigment-next-app/public/next.svg rename to apps/pigment-css-next-app/public/next.svg diff --git a/apps/pigment-next-app/public/vercel.svg b/apps/pigment-css-next-app/public/vercel.svg similarity index 100% rename from apps/pigment-next-app/public/vercel.svg rename to apps/pigment-css-next-app/public/vercel.svg diff --git a/apps/pigment-next-app/src/app/box/page.tsx b/apps/pigment-css-next-app/src/app/box/page.tsx similarity index 100% rename from apps/pigment-next-app/src/app/box/page.tsx rename to apps/pigment-css-next-app/src/app/box/page.tsx diff --git a/apps/pigment-next-app/src/app/favicon.ico b/apps/pigment-css-next-app/src/app/favicon.ico similarity index 100% rename from apps/pigment-next-app/src/app/favicon.ico rename to apps/pigment-css-next-app/src/app/favicon.ico diff --git a/apps/pigment-next-app/src/app/globals.css b/apps/pigment-css-next-app/src/app/globals.css similarity index 100% rename from apps/pigment-next-app/src/app/globals.css rename to apps/pigment-css-next-app/src/app/globals.css diff --git a/apps/pigment-next-app/src/app/layout.tsx b/apps/pigment-css-next-app/src/app/layout.tsx similarity index 100% rename from apps/pigment-next-app/src/app/layout.tsx rename to apps/pigment-css-next-app/src/app/layout.tsx diff --git a/apps/pigment-next-app/src/app/material-ui/layout.tsx b/apps/pigment-css-next-app/src/app/material-ui/layout.tsx similarity index 100% rename from apps/pigment-next-app/src/app/material-ui/layout.tsx rename to apps/pigment-css-next-app/src/app/material-ui/layout.tsx diff --git a/apps/pigment-next-app/src/app/material-ui/page.tsx b/apps/pigment-css-next-app/src/app/material-ui/page.tsx similarity index 100% rename from apps/pigment-next-app/src/app/material-ui/page.tsx rename to apps/pigment-css-next-app/src/app/material-ui/page.tsx diff --git a/apps/pigment-css-next-app/src/app/material-ui/react-accordion/page.tsx b/apps/pigment-css-next-app/src/app/material-ui/react-accordion/page.tsx new file mode 100644 index 00000000000000..7d47e9c5a83194 --- /dev/null +++ b/apps/pigment-css-next-app/src/app/material-ui/react-accordion/page.tsx @@ -0,0 +1,58 @@ +'use client'; +import * as React from 'react'; +import AccordionExpandDefault from '../../../../../../docs/data/material/components/accordion/AccordionExpandDefault'; +import AccordionExpandIcon from '../../../../../../docs/data/material/components/accordion/AccordionExpandIcon'; +import AccordionTransition from '../../../../../../docs/data/material/components/accordion/AccordionTransition'; +import AccordionUsage from '../../../../../../docs/data/material/components/accordion/AccordionUsage'; +import ControlledAccordions from '../../../../../../docs/data/material/components/accordion/ControlledAccordions'; +import CustomizedAccordions from '../../../../../../docs/data/material/components/accordion/CustomizedAccordions'; +import DisabledAccordion from '../../../../../../docs/data/material/components/accordion/DisabledAccordion'; + +export default function Accordion() { + return ( + <React.Fragment> + <section> + <h2> Accordion Expand Default</h2> + <div className="demo-container"> + <AccordionExpandDefault /> + </div> + </section> + <section> + <h2> Accordion Expand Icon</h2> + <div className="demo-container"> + <AccordionExpandIcon /> + </div> + </section> + <section> + <h2> Accordion Transition</h2> + <div className="demo-container"> + <AccordionTransition /> + </div> + </section> + <section> + <h2> Accordion Usage</h2> + <div className="demo-container"> + <AccordionUsage /> + </div> + </section> + <section> + <h2> Controlled Accordions</h2> + <div className="demo-container"> + <ControlledAccordions /> + </div> + </section> + <section> + <h2> Customized Accordions</h2> + <div className="demo-container"> + <CustomizedAccordions /> + </div> + </section> + <section> + <h2> Disabled Accordion</h2> + <div className="demo-container"> + <DisabledAccordion /> + </div> + </section> + </React.Fragment> + ); +} diff --git a/apps/pigment-next-app/src/app/material-ui/react-alert/page.tsx b/apps/pigment-css-next-app/src/app/material-ui/react-alert/page.tsx similarity index 100% rename from apps/pigment-next-app/src/app/material-ui/react-alert/page.tsx rename to apps/pigment-css-next-app/src/app/material-ui/react-alert/page.tsx diff --git a/apps/pigment-css-next-app/src/app/material-ui/react-autocomplete/page.tsx b/apps/pigment-css-next-app/src/app/material-ui/react-autocomplete/page.tsx new file mode 100644 index 00000000000000..76d790513f7567 --- /dev/null +++ b/apps/pigment-css-next-app/src/app/material-ui/react-autocomplete/page.tsx @@ -0,0 +1,191 @@ +'use client'; +import * as React from 'react'; +import Asynchronous from '../../../../../../docs/data/material/components/autocomplete/Asynchronous'; +import AutocompleteHint from '../../../../../../docs/data/material/components/autocomplete/AutocompleteHint'; +import CheckboxesTags from '../../../../../../docs/data/material/components/autocomplete/CheckboxesTags'; +import ComboBox from '../../../../../../docs/data/material/components/autocomplete/ComboBox'; +import ControllableStates from '../../../../../../docs/data/material/components/autocomplete/ControllableStates'; +import CountrySelect from '../../../../../../docs/data/material/components/autocomplete/CountrySelect'; +import CustomInputAutocomplete from '../../../../../../docs/data/material/components/autocomplete/CustomInputAutocomplete'; +import CustomizedHook from '../../../../../../docs/data/material/components/autocomplete/CustomizedHook'; +import DisabledOptions from '../../../../../../docs/data/material/components/autocomplete/DisabledOptions'; +import Filter from '../../../../../../docs/data/material/components/autocomplete/Filter'; +import FixedTags from '../../../../../../docs/data/material/components/autocomplete/FixedTags'; +import FreeSolo from '../../../../../../docs/data/material/components/autocomplete/FreeSolo'; +import FreeSoloCreateOption from '../../../../../../docs/data/material/components/autocomplete/FreeSoloCreateOption'; +import FreeSoloCreateOptionDialog from '../../../../../../docs/data/material/components/autocomplete/FreeSoloCreateOptionDialog'; +import GitHubLabel from '../../../../../../docs/data/material/components/autocomplete/GitHubLabel'; +import GloballyCustomizedOptions from '../../../../../../docs/data/material/components/autocomplete/GloballyCustomizedOptions'; +import GoogleMaps from '../../../../../../docs/data/material/components/autocomplete/GoogleMaps'; +import Grouped from '../../../../../../docs/data/material/components/autocomplete/Grouped'; +import Highlights from '../../../../../../docs/data/material/components/autocomplete/Highlights'; +import LimitTags from '../../../../../../docs/data/material/components/autocomplete/LimitTags'; +import Playground from '../../../../../../docs/data/material/components/autocomplete/Playground'; +import RenderGroup from '../../../../../../docs/data/material/components/autocomplete/RenderGroup'; +import Sizes from '../../../../../../docs/data/material/components/autocomplete/Sizes'; +import Tags from '../../../../../../docs/data/material/components/autocomplete/Tags'; +import UseAutocomplete from '../../../../../../docs/data/material/components/autocomplete/UseAutocomplete'; +import Virtualize from '../../../../../../docs/data/material/components/autocomplete/Virtualize'; + +export default function Autocomplete() { + return ( + <React.Fragment> + <section> + <h2> Asynchronous</h2> + <div className="demo-container"> + <Asynchronous /> + </div> + </section> + <section> + <h2> Autocomplete Hint</h2> + <div className="demo-container"> + <AutocompleteHint /> + </div> + </section> + <section> + <h2> Checkboxes Tags</h2> + <div className="demo-container"> + <CheckboxesTags /> + </div> + </section> + <section> + <h2> Combo Box</h2> + <div className="demo-container"> + <ComboBox /> + </div> + </section> + <section> + <h2> Controllable States</h2> + <div className="demo-container"> + <ControllableStates /> + </div> + </section> + <section> + <h2> Country Select</h2> + <div className="demo-container"> + <CountrySelect /> + </div> + </section> + <section> + <h2> Custom Input Autocomplete</h2> + <div className="demo-container"> + <CustomInputAutocomplete /> + </div> + </section> + <section> + <h2> Customized Hook</h2> + <div className="demo-container"> + <CustomizedHook /> + </div> + </section> + <section> + <h2> Disabled Options</h2> + <div className="demo-container"> + <DisabledOptions /> + </div> + </section> + <section> + <h2> Filter</h2> + <div className="demo-container"> + <Filter /> + </div> + </section> + <section> + <h2> Fixed Tags</h2> + <div className="demo-container"> + <FixedTags /> + </div> + </section> + <section> + <h2> Free Solo</h2> + <div className="demo-container"> + <FreeSolo /> + </div> + </section> + <section> + <h2> Free Solo Create Option</h2> + <div className="demo-container"> + <FreeSoloCreateOption /> + </div> + </section> + <section> + <h2> Free Solo Create Option Dialog</h2> + <div className="demo-container"> + <FreeSoloCreateOptionDialog /> + </div> + </section> + <section> + <h2> Git Hub Label</h2> + <div className="demo-container"> + <GitHubLabel /> + </div> + </section> + <section> + <h2> Globally Customized Options</h2> + <div className="demo-container"> + <GloballyCustomizedOptions /> + </div> + </section> + <section> + <h2> Google Maps</h2> + <div className="demo-container"> + <GoogleMaps /> + </div> + </section> + <section> + <h2> Grouped</h2> + <div className="demo-container"> + <Grouped /> + </div> + </section> + <section> + <h2> Highlights</h2> + <div className="demo-container"> + <Highlights /> + </div> + </section> + <section> + <h2> Limit Tags</h2> + <div className="demo-container"> + <LimitTags /> + </div> + </section> + <section> + <h2> Playground</h2> + <div className="demo-container"> + <Playground /> + </div> + </section> + <section> + <h2> Render Group</h2> + <div className="demo-container"> + <RenderGroup /> + </div> + </section> + <section> + <h2> Sizes</h2> + <div className="demo-container"> + <Sizes /> + </div> + </section> + <section> + <h2> Tags</h2> + <div className="demo-container"> + <Tags /> + </div> + </section> + <section> + <h2> Use Autocomplete</h2> + <div className="demo-container"> + <UseAutocomplete /> + </div> + </section> + <section> + <h2> Virtualize</h2> + <div className="demo-container"> + <Virtualize /> + </div> + </section> + </React.Fragment> + ); +} diff --git a/apps/pigment-next-app/src/app/material-ui/react-avatar/page.tsx b/apps/pigment-css-next-app/src/app/material-ui/react-avatar/page.tsx similarity index 100% rename from apps/pigment-next-app/src/app/material-ui/react-avatar/page.tsx rename to apps/pigment-css-next-app/src/app/material-ui/react-avatar/page.tsx diff --git a/apps/pigment-next-app/src/app/material-ui/react-badge/page.tsx b/apps/pigment-css-next-app/src/app/material-ui/react-badge/page.tsx similarity index 100% rename from apps/pigment-next-app/src/app/material-ui/react-badge/page.tsx rename to apps/pigment-css-next-app/src/app/material-ui/react-badge/page.tsx diff --git a/apps/pigment-css-next-app/src/app/material-ui/react-breadcrumbs/page.tsx b/apps/pigment-css-next-app/src/app/material-ui/react-breadcrumbs/page.tsx new file mode 100644 index 00000000000000..2bc3ae9757a7a7 --- /dev/null +++ b/apps/pigment-css-next-app/src/app/material-ui/react-breadcrumbs/page.tsx @@ -0,0 +1,58 @@ +'use client'; +import * as React from 'react'; +import ActiveLastBreadcrumb from '../../../../../../docs/data/material/components/breadcrumbs/ActiveLastBreadcrumb'; +import BasicBreadcrumbs from '../../../../../../docs/data/material/components/breadcrumbs/BasicBreadcrumbs'; +import CollapsedBreadcrumbs from '../../../../../../docs/data/material/components/breadcrumbs/CollapsedBreadcrumbs'; +import CustomSeparator from '../../../../../../docs/data/material/components/breadcrumbs/CustomSeparator'; +import CustomizedBreadcrumbs from '../../../../../../docs/data/material/components/breadcrumbs/CustomizedBreadcrumbs'; +import IconBreadcrumbs from '../../../../../../docs/data/material/components/breadcrumbs/IconBreadcrumbs'; +import RouterBreadcrumbs from '../../../../../../docs/data/material/components/breadcrumbs/RouterBreadcrumbs'; + +export default function Breadcrumbs() { + return ( + <React.Fragment> + <section> + <h2> Active Last Breadcrumb</h2> + <div className="demo-container"> + <ActiveLastBreadcrumb /> + </div> + </section> + <section> + <h2> Basic Breadcrumbs</h2> + <div className="demo-container"> + <BasicBreadcrumbs /> + </div> + </section> + <section> + <h2> Collapsed Breadcrumbs</h2> + <div className="demo-container"> + <CollapsedBreadcrumbs /> + </div> + </section> + <section> + <h2> Custom Separator</h2> + <div className="demo-container"> + <CustomSeparator /> + </div> + </section> + <section> + <h2> Customized Breadcrumbs</h2> + <div className="demo-container"> + <CustomizedBreadcrumbs /> + </div> + </section> + <section> + <h2> Icon Breadcrumbs</h2> + <div className="demo-container"> + <IconBreadcrumbs /> + </div> + </section> + <section> + <h2> Router Breadcrumbs</h2> + <div className="demo-container"> + <RouterBreadcrumbs /> + </div> + </section> + </React.Fragment> + ); +} diff --git a/apps/pigment-css-next-app/src/app/material-ui/react-slider/page.tsx b/apps/pigment-css-next-app/src/app/material-ui/react-slider/page.tsx new file mode 100644 index 00000000000000..97fb8108c1cb09 --- /dev/null +++ b/apps/pigment-css-next-app/src/app/material-ui/react-slider/page.tsx @@ -0,0 +1,142 @@ +'use client'; +import * as React from 'react'; +import ColorSlider from '../../../../../../docs/data/material/components/slider/ColorSlider'; +import ContinuousSlider from '../../../../../../docs/data/material/components/slider/ContinuousSlider'; +import CustomMarks from '../../../../../../docs/data/material/components/slider/CustomMarks'; +import CustomizedSlider from '../../../../../../docs/data/material/components/slider/CustomizedSlider'; +import DiscreteSlider from '../../../../../../docs/data/material/components/slider/DiscreteSlider'; +import DiscreteSliderLabel from '../../../../../../docs/data/material/components/slider/DiscreteSliderLabel'; +import DiscreteSliderMarks from '../../../../../../docs/data/material/components/slider/DiscreteSliderMarks'; +import DiscreteSliderSteps from '../../../../../../docs/data/material/components/slider/DiscreteSliderSteps'; +import DiscreteSliderValues from '../../../../../../docs/data/material/components/slider/DiscreteSliderValues'; +import InputSlider from '../../../../../../docs/data/material/components/slider/InputSlider'; +import MinimumDistanceSlider from '../../../../../../docs/data/material/components/slider/MinimumDistanceSlider'; +import MusicPlayerSlider from '../../../../../../docs/data/material/components/slider/MusicPlayerSlider'; +import NonLinearSlider from '../../../../../../docs/data/material/components/slider/NonLinearSlider'; +import RangeSlider from '../../../../../../docs/data/material/components/slider/RangeSlider'; +import SliderSizes from '../../../../../../docs/data/material/components/slider/SliderSizes'; +import TrackFalseSlider from '../../../../../../docs/data/material/components/slider/TrackFalseSlider'; +import TrackInvertedSlider from '../../../../../../docs/data/material/components/slider/TrackInvertedSlider'; +import VerticalAccessibleSlider from '../../../../../../docs/data/material/components/slider/VerticalAccessibleSlider'; +import VerticalSlider from '../../../../../../docs/data/material/components/slider/VerticalSlider'; + +export default function Slider() { + return ( + <React.Fragment> + <section> + <h2> Color Slider</h2> + <div className="demo-container"> + <ColorSlider /> + </div> + </section> + <section> + <h2> Continuous Slider</h2> + <div className="demo-container"> + <ContinuousSlider /> + </div> + </section> + <section> + <h2> Custom Marks</h2> + <div className="demo-container"> + <CustomMarks /> + </div> + </section> + <section> + <h2> Customized Slider</h2> + <div className="demo-container"> + <CustomizedSlider /> + </div> + </section> + <section> + <h2> Discrete Slider</h2> + <div className="demo-container"> + <DiscreteSlider /> + </div> + </section> + <section> + <h2> Discrete Slider Label</h2> + <div className="demo-container"> + <DiscreteSliderLabel /> + </div> + </section> + <section> + <h2> Discrete Slider Marks</h2> + <div className="demo-container"> + <DiscreteSliderMarks /> + </div> + </section> + <section> + <h2> Discrete Slider Steps</h2> + <div className="demo-container"> + <DiscreteSliderSteps /> + </div> + </section> + <section> + <h2> Discrete Slider Values</h2> + <div className="demo-container"> + <DiscreteSliderValues /> + </div> + </section> + <section> + <h2> Input Slider</h2> + <div className="demo-container"> + <InputSlider /> + </div> + </section> + <section> + <h2> Minimum Distance Slider</h2> + <div className="demo-container"> + <MinimumDistanceSlider /> + </div> + </section> + <section> + <h2> Music Player Slider</h2> + <div className="demo-container"> + <MusicPlayerSlider /> + </div> + </section> + <section> + <h2> Non Linear Slider</h2> + <div className="demo-container"> + <NonLinearSlider /> + </div> + </section> + <section> + <h2> Range Slider</h2> + <div className="demo-container"> + <RangeSlider /> + </div> + </section> + <section> + <h2> Slider Sizes</h2> + <div className="demo-container"> + <SliderSizes /> + </div> + </section> + <section> + <h2> Track False Slider</h2> + <div className="demo-container"> + <TrackFalseSlider /> + </div> + </section> + <section> + <h2> Track Inverted Slider</h2> + <div className="demo-container"> + <TrackInvertedSlider /> + </div> + </section> + <section> + <h2> Vertical Accessible Slider</h2> + <div className="demo-container"> + <VerticalAccessibleSlider /> + </div> + </section> + <section> + <h2> Vertical Slider</h2> + <div className="demo-container"> + <VerticalSlider /> + </div> + </section> + </React.Fragment> + ); +} diff --git a/apps/pigment-next-app/src/app/material-ui/react-switch/page.tsx b/apps/pigment-css-next-app/src/app/material-ui/react-switch/page.tsx similarity index 100% rename from apps/pigment-next-app/src/app/material-ui/react-switch/page.tsx rename to apps/pigment-css-next-app/src/app/material-ui/react-switch/page.tsx diff --git a/apps/pigment-next-app/src/app/page.module.css b/apps/pigment-css-next-app/src/app/page.module.css similarity index 100% rename from apps/pigment-next-app/src/app/page.module.css rename to apps/pigment-css-next-app/src/app/page.module.css diff --git a/apps/pigment-next-app/src/app/page.tsx b/apps/pigment-css-next-app/src/app/page.tsx similarity index 100% rename from apps/pigment-next-app/src/app/page.tsx rename to apps/pigment-css-next-app/src/app/page.tsx diff --git a/apps/pigment-next-app/src/app/theme.ts b/apps/pigment-css-next-app/src/app/theme.ts similarity index 100% rename from apps/pigment-next-app/src/app/theme.ts rename to apps/pigment-css-next-app/src/app/theme.ts diff --git a/apps/pigment-next-app/src/augment.d.ts b/apps/pigment-css-next-app/src/augment.d.ts similarity index 100% rename from apps/pigment-next-app/src/augment.d.ts rename to apps/pigment-css-next-app/src/augment.d.ts diff --git a/apps/pigment-next-app/src/components/Box.jsx b/apps/pigment-css-next-app/src/components/Box.jsx similarity index 100% rename from apps/pigment-next-app/src/components/Box.jsx rename to apps/pigment-css-next-app/src/components/Box.jsx diff --git a/apps/pigment-next-app/tsconfig.json b/apps/pigment-css-next-app/tsconfig.json similarity index 93% rename from apps/pigment-next-app/tsconfig.json rename to apps/pigment-css-next-app/tsconfig.json index 7f52bcf9932c64..49a985026c3a0d 100644 --- a/apps/pigment-next-app/tsconfig.json +++ b/apps/pigment-css-next-app/tsconfig.json @@ -35,7 +35,7 @@ "path": "../../packages/mui-material/tsconfig.build.json" }, { - "path": "../../packages/pigment-react/tsconfig.json" + "path": "../../packages/pigment-css-react/tsconfig.json" } ] } diff --git a/apps/pigment-vite-app/.eslintrc b/apps/pigment-css-vite-app/.eslintrc similarity index 100% rename from apps/pigment-vite-app/.eslintrc rename to apps/pigment-css-vite-app/.eslintrc diff --git a/apps/pigment-vite-app/.gitignore b/apps/pigment-css-vite-app/.gitignore similarity index 100% rename from apps/pigment-vite-app/.gitignore rename to apps/pigment-css-vite-app/.gitignore diff --git a/apps/pigment-vite-app/README.md b/apps/pigment-css-vite-app/README.md similarity index 100% rename from apps/pigment-vite-app/README.md rename to apps/pigment-css-vite-app/README.md diff --git a/apps/pigment-vite-app/index.html b/apps/pigment-css-vite-app/index.html similarity index 100% rename from apps/pigment-vite-app/index.html rename to apps/pigment-css-vite-app/index.html diff --git a/apps/pigment-vite-app/package.json b/apps/pigment-css-vite-app/package.json similarity index 88% rename from apps/pigment-vite-app/package.json rename to apps/pigment-css-vite-app/package.json index 4c4c70d581786e..3822681587fc07 100644 --- a/apps/pigment-vite-app/package.json +++ b/apps/pigment-css-vite-app/package.json @@ -9,7 +9,7 @@ "build": "vite build" }, "dependencies": { - "@pigment-css/react": "file:../../packages/pigment-react", + "@pigment-css/react": "file:../../packages/pigment-css-react", "@mui/utils": "file:../../packages/mui-utils/build", "@mui/base": "file:../../packages/mui-base/build", "@mui/material": "file:../../packages/mui-material/build", @@ -26,7 +26,7 @@ "@babel/preset-env": "^7.23.9", "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", - "@pigment-css/vite-plugin": "file:../../packages/pigment-vite-plugin", + "@pigment-css/vite-plugin": "file:../../packages/pigment-css-vite-plugin", "@types/react": "^18.2.55", "@types/react-dom": "^18.2.19", "@vitejs/plugin-react": "^4.2.1", diff --git a/apps/pigment-vite-app/postcss.config.cjs b/apps/pigment-css-vite-app/postcss.config.cjs similarity index 100% rename from apps/pigment-vite-app/postcss.config.cjs rename to apps/pigment-css-vite-app/postcss.config.cjs diff --git a/apps/pigment-vite-app/src/App.tsx b/apps/pigment-css-vite-app/src/App.tsx similarity index 100% rename from apps/pigment-vite-app/src/App.tsx rename to apps/pigment-css-vite-app/src/App.tsx diff --git a/apps/pigment-vite-app/src/Box.jsx b/apps/pigment-css-vite-app/src/Box.jsx similarity index 100% rename from apps/pigment-vite-app/src/Box.jsx rename to apps/pigment-css-vite-app/src/Box.jsx diff --git a/apps/pigment-vite-app/src/Layout.tsx b/apps/pigment-css-vite-app/src/Layout.tsx similarity index 100% rename from apps/pigment-vite-app/src/Layout.tsx rename to apps/pigment-css-vite-app/src/Layout.tsx diff --git a/apps/pigment-vite-app/src/Slider/ZeroSlider.test.jsx b/apps/pigment-css-vite-app/src/Slider/ZeroSlider.test.jsx similarity index 100% rename from apps/pigment-vite-app/src/Slider/ZeroSlider.test.jsx rename to apps/pigment-css-vite-app/src/Slider/ZeroSlider.test.jsx diff --git a/apps/pigment-vite-app/src/Slider/ZeroSlider.tsx b/apps/pigment-css-vite-app/src/Slider/ZeroSlider.tsx similarity index 100% rename from apps/pigment-vite-app/src/Slider/ZeroSlider.tsx rename to apps/pigment-css-vite-app/src/Slider/ZeroSlider.tsx diff --git a/apps/pigment-vite-app/src/augment.ts b/apps/pigment-css-vite-app/src/augment.ts similarity index 100% rename from apps/pigment-vite-app/src/augment.ts rename to apps/pigment-css-vite-app/src/augment.ts diff --git a/apps/pigment-vite-app/src/component.tsx b/apps/pigment-css-vite-app/src/component.tsx similarity index 100% rename from apps/pigment-vite-app/src/component.tsx rename to apps/pigment-css-vite-app/src/component.tsx diff --git a/apps/pigment-vite-app/src/extend-zero.ts b/apps/pigment-css-vite-app/src/extend-zero.ts similarity index 100% rename from apps/pigment-vite-app/src/extend-zero.ts rename to apps/pigment-css-vite-app/src/extend-zero.ts diff --git a/apps/pigment-vite-app/src/main.tsx b/apps/pigment-css-vite-app/src/main.tsx similarity index 100% rename from apps/pigment-vite-app/src/main.tsx rename to apps/pigment-css-vite-app/src/main.tsx diff --git a/apps/pigment-vite-app/src/pages/index.tsx b/apps/pigment-css-vite-app/src/pages/index.tsx similarity index 100% rename from apps/pigment-vite-app/src/pages/index.tsx rename to apps/pigment-css-vite-app/src/pages/index.tsx diff --git a/apps/pigment-css-vite-app/src/pages/material-ui/react-accordion.tsx b/apps/pigment-css-vite-app/src/pages/material-ui/react-accordion.tsx new file mode 100644 index 00000000000000..3920a1e6f9b6f9 --- /dev/null +++ b/apps/pigment-css-vite-app/src/pages/material-ui/react-accordion.tsx @@ -0,0 +1,59 @@ +import * as React from 'react'; +import MaterialUILayout from '../../Layout'; +import AccordionExpandDefault from '../../../../../docs/data/material/components/accordion/AccordionExpandDefault.tsx'; +import AccordionExpandIcon from '../../../../../docs/data/material/components/accordion/AccordionExpandIcon.tsx'; +import AccordionTransition from '../../../../../docs/data/material/components/accordion/AccordionTransition.tsx'; +import AccordionUsage from '../../../../../docs/data/material/components/accordion/AccordionUsage.tsx'; +import ControlledAccordions from '../../../../../docs/data/material/components/accordion/ControlledAccordions.tsx'; +import CustomizedAccordions from '../../../../../docs/data/material/components/accordion/CustomizedAccordions.tsx'; +import DisabledAccordion from '../../../../../docs/data/material/components/accordion/DisabledAccordion.tsx'; + +export default function Accordion() { + return ( + <MaterialUILayout> + <h1>Accordion</h1> + <section> + <h2> Accordion Expand Default</h2> + <div className="demo-container"> + <AccordionExpandDefault /> + </div> + </section> + <section> + <h2> Accordion Expand Icon</h2> + <div className="demo-container"> + <AccordionExpandIcon /> + </div> + </section> + <section> + <h2> Accordion Transition</h2> + <div className="demo-container"> + <AccordionTransition /> + </div> + </section> + <section> + <h2> Accordion Usage</h2> + <div className="demo-container"> + <AccordionUsage /> + </div> + </section> + <section> + <h2> Controlled Accordions</h2> + <div className="demo-container"> + <ControlledAccordions /> + </div> + </section> + <section> + <h2> Customized Accordions</h2> + <div className="demo-container"> + <CustomizedAccordions /> + </div> + </section> + <section> + <h2> Disabled Accordion</h2> + <div className="demo-container"> + <DisabledAccordion /> + </div> + </section> + </MaterialUILayout> + ); +} diff --git a/apps/pigment-vite-app/src/pages/material-ui/react-alert.tsx b/apps/pigment-css-vite-app/src/pages/material-ui/react-alert.tsx similarity index 100% rename from apps/pigment-vite-app/src/pages/material-ui/react-alert.tsx rename to apps/pigment-css-vite-app/src/pages/material-ui/react-alert.tsx diff --git a/apps/pigment-css-vite-app/src/pages/material-ui/react-autocomplete.tsx b/apps/pigment-css-vite-app/src/pages/material-ui/react-autocomplete.tsx new file mode 100644 index 00000000000000..677a0062ea8633 --- /dev/null +++ b/apps/pigment-css-vite-app/src/pages/material-ui/react-autocomplete.tsx @@ -0,0 +1,192 @@ +import * as React from 'react'; +import MaterialUILayout from '../../Layout'; +import Asynchronous from '../../../../../docs/data/material/components/autocomplete/Asynchronous.tsx'; +import AutocompleteHint from '../../../../../docs/data/material/components/autocomplete/AutocompleteHint.tsx'; +import CheckboxesTags from '../../../../../docs/data/material/components/autocomplete/CheckboxesTags.tsx'; +import ComboBox from '../../../../../docs/data/material/components/autocomplete/ComboBox.tsx'; +import ControllableStates from '../../../../../docs/data/material/components/autocomplete/ControllableStates.tsx'; +import CountrySelect from '../../../../../docs/data/material/components/autocomplete/CountrySelect.tsx'; +import CustomInputAutocomplete from '../../../../../docs/data/material/components/autocomplete/CustomInputAutocomplete.tsx'; +import CustomizedHook from '../../../../../docs/data/material/components/autocomplete/CustomizedHook.tsx'; +import DisabledOptions from '../../../../../docs/data/material/components/autocomplete/DisabledOptions.tsx'; +import Filter from '../../../../../docs/data/material/components/autocomplete/Filter.tsx'; +import FixedTags from '../../../../../docs/data/material/components/autocomplete/FixedTags.tsx'; +import FreeSolo from '../../../../../docs/data/material/components/autocomplete/FreeSolo.tsx'; +import FreeSoloCreateOption from '../../../../../docs/data/material/components/autocomplete/FreeSoloCreateOption.tsx'; +import FreeSoloCreateOptionDialog from '../../../../../docs/data/material/components/autocomplete/FreeSoloCreateOptionDialog.tsx'; +import GitHubLabel from '../../../../../docs/data/material/components/autocomplete/GitHubLabel.tsx'; +import GloballyCustomizedOptions from '../../../../../docs/data/material/components/autocomplete/GloballyCustomizedOptions.tsx'; +import GoogleMaps from '../../../../../docs/data/material/components/autocomplete/GoogleMaps.tsx'; +import Grouped from '../../../../../docs/data/material/components/autocomplete/Grouped.tsx'; +import Highlights from '../../../../../docs/data/material/components/autocomplete/Highlights.tsx'; +import LimitTags from '../../../../../docs/data/material/components/autocomplete/LimitTags.tsx'; +import Playground from '../../../../../docs/data/material/components/autocomplete/Playground.tsx'; +import RenderGroup from '../../../../../docs/data/material/components/autocomplete/RenderGroup.tsx'; +import Sizes from '../../../../../docs/data/material/components/autocomplete/Sizes.tsx'; +import Tags from '../../../../../docs/data/material/components/autocomplete/Tags.tsx'; +import UseAutocomplete from '../../../../../docs/data/material/components/autocomplete/UseAutocomplete.tsx'; +import Virtualize from '../../../../../docs/data/material/components/autocomplete/Virtualize.tsx'; + +export default function Autocomplete() { + return ( + <MaterialUILayout> + <h1>Autocomplete</h1> + <section> + <h2> Asynchronous</h2> + <div className="demo-container"> + <Asynchronous /> + </div> + </section> + <section> + <h2> Autocomplete Hint</h2> + <div className="demo-container"> + <AutocompleteHint /> + </div> + </section> + <section> + <h2> Checkboxes Tags</h2> + <div className="demo-container"> + <CheckboxesTags /> + </div> + </section> + <section> + <h2> Combo Box</h2> + <div className="demo-container"> + <ComboBox /> + </div> + </section> + <section> + <h2> Controllable States</h2> + <div className="demo-container"> + <ControllableStates /> + </div> + </section> + <section> + <h2> Country Select</h2> + <div className="demo-container"> + <CountrySelect /> + </div> + </section> + <section> + <h2> Custom Input Autocomplete</h2> + <div className="demo-container"> + <CustomInputAutocomplete /> + </div> + </section> + <section> + <h2> Customized Hook</h2> + <div className="demo-container"> + <CustomizedHook /> + </div> + </section> + <section> + <h2> Disabled Options</h2> + <div className="demo-container"> + <DisabledOptions /> + </div> + </section> + <section> + <h2> Filter</h2> + <div className="demo-container"> + <Filter /> + </div> + </section> + <section> + <h2> Fixed Tags</h2> + <div className="demo-container"> + <FixedTags /> + </div> + </section> + <section> + <h2> Free Solo</h2> + <div className="demo-container"> + <FreeSolo /> + </div> + </section> + <section> + <h2> Free Solo Create Option</h2> + <div className="demo-container"> + <FreeSoloCreateOption /> + </div> + </section> + <section> + <h2> Free Solo Create Option Dialog</h2> + <div className="demo-container"> + <FreeSoloCreateOptionDialog /> + </div> + </section> + <section> + <h2> Git Hub Label</h2> + <div className="demo-container"> + <GitHubLabel /> + </div> + </section> + <section> + <h2> Globally Customized Options</h2> + <div className="demo-container"> + <GloballyCustomizedOptions /> + </div> + </section> + <section> + <h2> Google Maps</h2> + <div className="demo-container"> + <GoogleMaps /> + </div> + </section> + <section> + <h2> Grouped</h2> + <div className="demo-container"> + <Grouped /> + </div> + </section> + <section> + <h2> Highlights</h2> + <div className="demo-container"> + <Highlights /> + </div> + </section> + <section> + <h2> Limit Tags</h2> + <div className="demo-container"> + <LimitTags /> + </div> + </section> + <section> + <h2> Playground</h2> + <div className="demo-container"> + <Playground /> + </div> + </section> + <section> + <h2> Render Group</h2> + <div className="demo-container"> + <RenderGroup /> + </div> + </section> + <section> + <h2> Sizes</h2> + <div className="demo-container"> + <Sizes /> + </div> + </section> + <section> + <h2> Tags</h2> + <div className="demo-container"> + <Tags /> + </div> + </section> + <section> + <h2> Use Autocomplete</h2> + <div className="demo-container"> + <UseAutocomplete /> + </div> + </section> + <section> + <h2> Virtualize</h2> + <div className="demo-container"> + <Virtualize /> + </div> + </section> + </MaterialUILayout> + ); +} diff --git a/apps/pigment-vite-app/src/pages/material-ui/react-avatar.tsx b/apps/pigment-css-vite-app/src/pages/material-ui/react-avatar.tsx similarity index 100% rename from apps/pigment-vite-app/src/pages/material-ui/react-avatar.tsx rename to apps/pigment-css-vite-app/src/pages/material-ui/react-avatar.tsx diff --git a/apps/pigment-vite-app/src/pages/material-ui/react-badge.tsx b/apps/pigment-css-vite-app/src/pages/material-ui/react-badge.tsx similarity index 100% rename from apps/pigment-vite-app/src/pages/material-ui/react-badge.tsx rename to apps/pigment-css-vite-app/src/pages/material-ui/react-badge.tsx diff --git a/apps/pigment-css-vite-app/src/pages/material-ui/react-breadcrumbs.tsx b/apps/pigment-css-vite-app/src/pages/material-ui/react-breadcrumbs.tsx new file mode 100644 index 00000000000000..45845fd495b0ba --- /dev/null +++ b/apps/pigment-css-vite-app/src/pages/material-ui/react-breadcrumbs.tsx @@ -0,0 +1,59 @@ +import * as React from 'react'; +import MaterialUILayout from '../../Layout'; +import ActiveLastBreadcrumb from '../../../../../docs/data/material/components/breadcrumbs/ActiveLastBreadcrumb.tsx'; +import BasicBreadcrumbs from '../../../../../docs/data/material/components/breadcrumbs/BasicBreadcrumbs.tsx'; +import CollapsedBreadcrumbs from '../../../../../docs/data/material/components/breadcrumbs/CollapsedBreadcrumbs.tsx'; +import CustomSeparator from '../../../../../docs/data/material/components/breadcrumbs/CustomSeparator.tsx'; +import CustomizedBreadcrumbs from '../../../../../docs/data/material/components/breadcrumbs/CustomizedBreadcrumbs.tsx'; +import IconBreadcrumbs from '../../../../../docs/data/material/components/breadcrumbs/IconBreadcrumbs.tsx'; +import RouterBreadcrumbs from '../../../../../docs/data/material/components/breadcrumbs/RouterBreadcrumbs.tsx'; + +export default function Breadcrumbs() { + return ( + <MaterialUILayout> + <h1>Breadcrumbs</h1> + <section> + <h2> Active Last Breadcrumb</h2> + <div className="demo-container"> + <ActiveLastBreadcrumb /> + </div> + </section> + <section> + <h2> Basic Breadcrumbs</h2> + <div className="demo-container"> + <BasicBreadcrumbs /> + </div> + </section> + <section> + <h2> Collapsed Breadcrumbs</h2> + <div className="demo-container"> + <CollapsedBreadcrumbs /> + </div> + </section> + <section> + <h2> Custom Separator</h2> + <div className="demo-container"> + <CustomSeparator /> + </div> + </section> + <section> + <h2> Customized Breadcrumbs</h2> + <div className="demo-container"> + <CustomizedBreadcrumbs /> + </div> + </section> + <section> + <h2> Icon Breadcrumbs</h2> + <div className="demo-container"> + <IconBreadcrumbs /> + </div> + </section> + <section> + <h2> Router Breadcrumbs</h2> + <div className="demo-container"> + <RouterBreadcrumbs /> + </div> + </section> + </MaterialUILayout> + ); +} diff --git a/apps/pigment-css-vite-app/src/pages/material-ui/react-slider.tsx b/apps/pigment-css-vite-app/src/pages/material-ui/react-slider.tsx new file mode 100644 index 00000000000000..963612339e2faf --- /dev/null +++ b/apps/pigment-css-vite-app/src/pages/material-ui/react-slider.tsx @@ -0,0 +1,143 @@ +import * as React from 'react'; +import MaterialUILayout from '../../Layout'; +import ColorSlider from '../../../../../docs/data/material/components/slider/ColorSlider.tsx'; +import ContinuousSlider from '../../../../../docs/data/material/components/slider/ContinuousSlider.tsx'; +import CustomMarks from '../../../../../docs/data/material/components/slider/CustomMarks.tsx'; +import CustomizedSlider from '../../../../../docs/data/material/components/slider/CustomizedSlider.tsx'; +import DiscreteSlider from '../../../../../docs/data/material/components/slider/DiscreteSlider.tsx'; +import DiscreteSliderLabel from '../../../../../docs/data/material/components/slider/DiscreteSliderLabel.tsx'; +import DiscreteSliderMarks from '../../../../../docs/data/material/components/slider/DiscreteSliderMarks.tsx'; +import DiscreteSliderSteps from '../../../../../docs/data/material/components/slider/DiscreteSliderSteps.tsx'; +import DiscreteSliderValues from '../../../../../docs/data/material/components/slider/DiscreteSliderValues.tsx'; +import InputSlider from '../../../../../docs/data/material/components/slider/InputSlider.tsx'; +import MinimumDistanceSlider from '../../../../../docs/data/material/components/slider/MinimumDistanceSlider.tsx'; +import MusicPlayerSlider from '../../../../../docs/data/material/components/slider/MusicPlayerSlider.tsx'; +import NonLinearSlider from '../../../../../docs/data/material/components/slider/NonLinearSlider.tsx'; +import RangeSlider from '../../../../../docs/data/material/components/slider/RangeSlider.tsx'; +import SliderSizes from '../../../../../docs/data/material/components/slider/SliderSizes.tsx'; +import TrackFalseSlider from '../../../../../docs/data/material/components/slider/TrackFalseSlider.tsx'; +import TrackInvertedSlider from '../../../../../docs/data/material/components/slider/TrackInvertedSlider.tsx'; +import VerticalAccessibleSlider from '../../../../../docs/data/material/components/slider/VerticalAccessibleSlider.tsx'; +import VerticalSlider from '../../../../../docs/data/material/components/slider/VerticalSlider.tsx'; + +export default function Slider() { + return ( + <MaterialUILayout> + <h1>Slider</h1> + <section> + <h2> Color Slider</h2> + <div className="demo-container"> + <ColorSlider /> + </div> + </section> + <section> + <h2> Continuous Slider</h2> + <div className="demo-container"> + <ContinuousSlider /> + </div> + </section> + <section> + <h2> Custom Marks</h2> + <div className="demo-container"> + <CustomMarks /> + </div> + </section> + <section> + <h2> Customized Slider</h2> + <div className="demo-container"> + <CustomizedSlider /> + </div> + </section> + <section> + <h2> Discrete Slider</h2> + <div className="demo-container"> + <DiscreteSlider /> + </div> + </section> + <section> + <h2> Discrete Slider Label</h2> + <div className="demo-container"> + <DiscreteSliderLabel /> + </div> + </section> + <section> + <h2> Discrete Slider Marks</h2> + <div className="demo-container"> + <DiscreteSliderMarks /> + </div> + </section> + <section> + <h2> Discrete Slider Steps</h2> + <div className="demo-container"> + <DiscreteSliderSteps /> + </div> + </section> + <section> + <h2> Discrete Slider Values</h2> + <div className="demo-container"> + <DiscreteSliderValues /> + </div> + </section> + <section> + <h2> Input Slider</h2> + <div className="demo-container"> + <InputSlider /> + </div> + </section> + <section> + <h2> Minimum Distance Slider</h2> + <div className="demo-container"> + <MinimumDistanceSlider /> + </div> + </section> + <section> + <h2> Music Player Slider</h2> + <div className="demo-container"> + <MusicPlayerSlider /> + </div> + </section> + <section> + <h2> Non Linear Slider</h2> + <div className="demo-container"> + <NonLinearSlider /> + </div> + </section> + <section> + <h2> Range Slider</h2> + <div className="demo-container"> + <RangeSlider /> + </div> + </section> + <section> + <h2> Slider Sizes</h2> + <div className="demo-container"> + <SliderSizes /> + </div> + </section> + <section> + <h2> Track False Slider</h2> + <div className="demo-container"> + <TrackFalseSlider /> + </div> + </section> + <section> + <h2> Track Inverted Slider</h2> + <div className="demo-container"> + <TrackInvertedSlider /> + </div> + </section> + <section> + <h2> Vertical Accessible Slider</h2> + <div className="demo-container"> + <VerticalAccessibleSlider /> + </div> + </section> + <section> + <h2> Vertical Slider</h2> + <div className="demo-container"> + <VerticalSlider /> + </div> + </section> + </MaterialUILayout> + ); +} diff --git a/apps/pigment-vite-app/src/pages/material-ui/react-switch.tsx b/apps/pigment-css-vite-app/src/pages/material-ui/react-switch.tsx similarity index 100% rename from apps/pigment-vite-app/src/pages/material-ui/react-switch.tsx rename to apps/pigment-css-vite-app/src/pages/material-ui/react-switch.tsx diff --git a/apps/pigment-vite-app/tsconfig.json b/apps/pigment-css-vite-app/tsconfig.json similarity index 86% rename from apps/pigment-vite-app/tsconfig.json rename to apps/pigment-css-vite-app/tsconfig.json index 3ac8cebf99a277..918f90a862a498 100644 --- a/apps/pigment-vite-app/tsconfig.json +++ b/apps/pigment-css-vite-app/tsconfig.json @@ -15,7 +15,7 @@ "path": "../../packages/mui-material/tsconfig.build.json" }, { - "path": "../../packages/pigment-react/tsconfig.json" + "path": "../../packages/pigment-css-react/tsconfig.json" } ] } diff --git a/apps/pigment-vite-app/vite-env.d.ts b/apps/pigment-css-vite-app/vite-env.d.ts similarity index 100% rename from apps/pigment-vite-app/vite-env.d.ts rename to apps/pigment-css-vite-app/vite-env.d.ts diff --git a/apps/pigment-vite-app/vite.config.ts b/apps/pigment-css-vite-app/vite.config.ts similarity index 100% rename from apps/pigment-vite-app/vite.config.ts rename to apps/pigment-css-vite-app/vite.config.ts diff --git a/apps/pigment-next-app/next-env.d.ts b/apps/pigment-next-app/next-env.d.ts new file mode 100644 index 00000000000000..4f11a03dc6cc37 --- /dev/null +++ b/apps/pigment-next-app/next-env.d.ts @@ -0,0 +1,5 @@ +/// <reference types="next" /> +/// <reference types="next/image-types/global" /> + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/apps/pigment-next-app/src/app/material-ui/react-modal/page.tsx b/apps/pigment-next-app/src/app/material-ui/react-modal/page.tsx new file mode 100644 index 00000000000000..763472163e7732 --- /dev/null +++ b/apps/pigment-next-app/src/app/material-ui/react-modal/page.tsx @@ -0,0 +1,51 @@ +'use client'; +import * as React from 'react'; +import BasicModal from '../../../../../../docs/data/material/components/modal/BasicModal'; +import KeepMountedModal from '../../../../../../docs/data/material/components/modal/KeepMountedModal'; +import NestedModal from '../../../../../../docs/data/material/components/modal/NestedModal'; +import ServerModal from '../../../../../../docs/data/material/components/modal/ServerModal'; +import SpringModal from '../../../../../../docs/data/material/components/modal/SpringModal'; +import TransitionsModal from '../../../../../../docs/data/material/components/modal/TransitionsModal'; + +export default function Modal() { + return ( + <React.Fragment> + <section> + <h2> Basic Modal</h2> + <div className="demo-container"> + <BasicModal /> + </div> + </section> + <section> + <h2> Keep Mounted Modal</h2> + <div className="demo-container"> + <KeepMountedModal /> + </div> + </section> + <section> + <h2> Nested Modal</h2> + <div className="demo-container"> + <NestedModal /> + </div> + </section> + <section> + <h2> Server Modal</h2> + <div className="demo-container"> + <ServerModal /> + </div> + </section> + <section> + <h2> Spring Modal</h2> + <div className="demo-container"> + <SpringModal /> + </div> + </section> + <section> + <h2> Transitions Modal</h2> + <div className="demo-container"> + <TransitionsModal /> + </div> + </section> + </React.Fragment> + ); +} diff --git a/apps/pigment-next-app/src/app/slider/page.tsx b/apps/pigment-next-app/src/app/slider/page.tsx deleted file mode 100644 index 07e5fca1c0f2f2..00000000000000 --- a/apps/pigment-next-app/src/app/slider/page.tsx +++ /dev/null @@ -1,100 +0,0 @@ -'use client'; -import * as React from 'react'; -import { styled } from '@pigment-css/react'; -import { Button } from 'local-ui-lib'; -import Slider from '@/components/Slider/ZeroSlider'; - -const HalfWidth = styled.div(({ theme }) => ({ - marginLeft: 20, - width: '50%', - maxHeight: 100, - padding: 20, - border: `1px solid ${theme.vars.palette.primary.main}`, -})); - -export default function SliderPage() { - const [value, setValue] = React.useState(50); - const [isColorPrimary, setIsColorPrimary] = React.useState(true); - const [size, setSize] = React.useState('medium'); - const [showMarks, setShowMarks] = React.useState(true); - const [isTrackInverted, setIsTrackInverted] = React.useState(false); - const [disabled, setDisabled] = React.useState(false); - const [isHorizontal, setIsHorizontal] = React.useState(true); - - return ( - <div style={{ margin: 10 }}> - <div> - <div> - <label> - <input - type="checkbox" - checked={isColorPrimary} - onChange={() => setIsColorPrimary(!isColorPrimary)} - /> - Toggle Color: {isColorPrimary ? 'Primary' : 'Secondary'} - </label> - </div> - <div> - <label> - <input - type="checkbox" - checked={isTrackInverted} - onChange={() => setIsTrackInverted(!isTrackInverted)} - /> - Track type: {isTrackInverted ? 'Inverted' : 'Normal'} - </label> - </div> - <div> - <label> - <input - type="checkbox" - checked={isHorizontal} - onChange={() => setIsHorizontal(!isHorizontal)} - /> - Orientation: {isHorizontal ? 'Horizontal' : 'Vertical'} - </label> - </div> - <div> - <label> - <input type="checkbox" checked={disabled} onChange={() => setDisabled(!disabled)} /> - Disabled: {disabled ? 'Yes' : 'No'} - </label> - </div> - <div> - <label> - <input type="checkbox" checked={showMarks} onChange={() => setShowMarks(!showMarks)} /> - Show marks: {showMarks ? 'Yes' : 'No'} - </label> - </div> - <div> - <label> - Change Size: - <select value={size} onChange={(ev) => setSize(ev.target.value)}> - <option value="small">Small</option> - <option value="medium">Medium</option> - </select> - </label> - </div> - <Button>Hello</Button> - </div> - <HalfWidth> - <Slider - aria-label="Small steps" - defaultValue={50} - step={2} - marks={showMarks} - min={0} - max={100} - valueLabelDisplay="auto" - orientation={isHorizontal ? 'horizontal' : 'vertical'} - size={size as 'small' | 'medium'} - color={isColorPrimary ? 'primary' : 'secondary'} - track={isTrackInverted ? 'inverted' : 'normal'} - disabled={disabled} - value={value} - onChange={(ev, val) => setValue(val as number)} - /> - </HalfWidth> - </div> - ); -} diff --git a/apps/pigment-next-app/src/components/Slider/ZeroSlider.tsx b/apps/pigment-next-app/src/components/Slider/ZeroSlider.tsx deleted file mode 100644 index cef2f605d9c5cf..00000000000000 --- a/apps/pigment-next-app/src/components/Slider/ZeroSlider.tsx +++ /dev/null @@ -1,945 +0,0 @@ -import * as React from 'react'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/utils'; -import { - type SliderOwnerState, - type SliderProps, - sliderClasses, - getSliderUtilityClass, -} from '@mui/material/Slider'; -import { isHostComponent, useSlotProps } from '@mui/base/utils'; -import { styled } from '@pigment-css/react'; -import { capitalize } from '@mui/material/utils'; -import SliderValueLabel from '@mui/material/Slider/SliderValueLabel'; -import { useSlider, valueToPercent } from '@mui/base/useSlider'; -import { alpha, lighten, darken } from '@mui/system/colorManipulator'; -import type { Theme } from '@mui/material/styles'; - -const shouldSpreadAdditionalProps = (Slot?: React.ElementType) => { - return !Slot || !isHostComponent(Slot); -}; - -function Identity<T>(x: T): T { - return x; -} - -type SliderNestedOwnerState = SliderOwnerState & { - ownerState: SliderOwnerState; -}; - -const SliderRoot = styled('span', { - name: 'MuiSlider', - slot: 'Root', - overridesResolver(props, styles) { - const { ownerState } = props; - return [ - styles.root, - styles[`color${capitalize(ownerState.color ?? '')}`], - ownerState.size !== 'medium' && styles[`size${capitalize(ownerState.size ?? '')}`], - ownerState.marked && styles.marked, - ownerState.orientation === 'vertical' && styles.vertical, - ownerState.track === 'inverted' && styles.trackInverted, - ownerState.track === false && styles.trackFalse, - ]; - }, -})<SliderNestedOwnerState>(({ theme }) => ({ - borderRadius: '12px', - boxSizing: 'content-box', - display: 'inline-block', - position: 'relative', - cursor: 'pointer', - touchAction: 'none', - WebkitTapHighlightColor: 'transparent', - '@media print': { - printColorAdjust: 'exact', - }, - [`&.${sliderClasses.disabled}`]: { - pointerEvents: 'none', - cursor: 'default', - color: (theme.vars || theme).palette.grey[400], - }, - [`&.${sliderClasses.dragging}`]: { - [`& .${sliderClasses.thumb}, & .${sliderClasses.track}`]: { - transition: 'none', - }, - }, - variants: [ - { - props({ color }) { - return color === 'primary'; - }, - style: { - color: (theme.vars || theme).palette.primary.main, - }, - }, - { - props: { - color: 'secondary', - }, - style: { - color: (theme.vars || theme).palette.secondary.main, - }, - }, - { - props: { - orientation: 'horizontal', - }, - style: { - height: 4, - width: '100%', - padding: '13px 0', - // The primary input mechanism of the device includes a pointing device of limited accuracy. - '@media (pointer: coarse)': { - // Reach 42px touch target, about ~8mm on screen. - padding: '20px 0', - }, - }, - }, - { - props: { - orientation: 'horizontal', - size: 'small', - }, - style: { - height: 2, - }, - }, - { - props: { - orientation: 'horizontal', - marked: true, - }, - style: { - marginBottom: 20, - }, - }, - { - props: { - orientation: 'vertical', - }, - style: { - height: '100%', - width: 4, - padding: '0 13px', - // The primary input mechanism of the device includes a pointing device of limited accuracy. - '@media (pointer: coarse)': { - // Reach 42px touch target, about ~8mm on screen. - padding: '0 20px', - }, - }, - }, - { - props: { - orientation: 'vertical', - size: 'small', - }, - style: { - width: 2, - }, - }, - { - props: { - orientation: 'vertical', - marked: true, - }, - style: { - marginRight: 44, - }, - }, - ], -})); - -export { SliderRoot }; - -const SliderRail = styled('span', { - name: 'MuiSlider', - slot: 'Rail', - overridesResolver: (props, styles) => styles.rail, -})<SliderNestedOwnerState>({ - display: 'block', - position: 'absolute', - borderRadius: 'inherit', - backgroundColor: 'currentColor', - opacity: 0.38, - variants: [ - { - props: { - orientation: 'horizontal', - }, - style: { - width: '100%', - height: 'inherit', - top: '50%', - transform: 'translateY(-50%)', - }, - }, - { - props: { - orientation: 'vertical', - }, - style: { - height: '100%', - width: 'inherit', - left: '50%', - transform: 'translateX(-50%)', - }, - }, - { - props: { - track: 'inverted', - }, - style: { - opacity: 1, - }, - }, - ], -}); - -export { SliderRail }; - -const SliderTrack = styled('span', { - name: 'MuiSlider', - slot: 'Track', - overridesResolver: (props, styles) => styles.track, -})<SliderNestedOwnerState>(({ theme }) => { - const lightPrimaryColor = theme.vars - ? theme.vars.palette.Slider.primaryTrack - : lighten(theme.palette.primary.main, 0.62); - const lightSecondaryColor = theme.vars - ? theme.vars.palette.Slider.secondaryTrack - : lighten(theme.palette.secondary.main, 0.62); - const darkPrimaryColor = theme.vars - ? theme.vars.palette.Slider.primaryTrack - : darken(theme.palette.primary.main, 0.5); - const darkSecondaryColor = theme.vars - ? theme.vars.palette.Slider.secondaryTrack - : darken(theme.palette.secondary.main, 0.5); - - return { - display: 'block', - position: 'absolute', - borderRadius: 'inherit', - border: '1px solid currentColor', - backgroundColor: 'currentColor', - transition: theme.transitions.create(['left', 'width', 'bottom', 'height'], { - duration: theme.transitions.duration.shortest, - }), - variants: [ - { - props: { - color: 'primary', - }, - style: { - '--slider-track-color': lightPrimaryColor, - ...theme.applyStyles('dark', { - '--slider-track-color': darkPrimaryColor, - }), - }, - }, - { - props: { - color: 'secondary', - }, - style: { - '--slider-track-color': lightSecondaryColor, - ...theme.applyStyles('dark', { - '--slider-track-color': darkSecondaryColor, - }), - }, - }, - { - props: { - size: 'small', - }, - style: { - border: 'none', - }, - }, - { - props: { - orientation: 'horizontal', - }, - style: { - height: 'inherit', - top: '50%', - transform: 'translateY(-50%)', - }, - }, - { - props: { - orientation: 'vertical', - }, - style: { - width: 'inherit', - left: '50%', - transform: 'translateX(-50%)', - }, - }, - { - props: { - track: false, - }, - style: { - display: 'none', - }, - }, - { - props: { - track: 'inverted', - color: 'primary', - }, - style: { - backgroundColor: (theme.vars ?? theme).palette.Slider?.primaryTrack, - borderColor: (theme.vars ?? theme).palette.Slider?.primaryTrack, - }, - }, - { - props: { - track: 'inverted', - color: 'secondary', - }, - style: { - backgroundColor: (theme.vars ?? theme).palette.Slider?.secondaryTrack, - borderColor: (theme.vars ?? theme).palette.Slider?.secondaryTrack, - }, - }, - ], - }; -}); - -export { SliderTrack }; - -const SliderThumb = styled('span', { - name: 'MuiSlider', - slot: 'Thumb', - overridesResolver: (props, styles) => { - const { ownerState } = props; - return [ - styles.thumb, - styles[`thumbColor${capitalize(ownerState.color ?? '')}`], - ownerState.size !== 'medium' && styles[`thumbSize${capitalize(ownerState.size ?? '')}`], - ]; - }, -})<SliderNestedOwnerState>(({ theme }) => ({ - position: 'absolute', - width: 20, - height: 20, - boxSizing: 'border-box', - borderRadius: '50%', - outline: 0, - backgroundColor: 'currentColor', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - transition: theme.transitions.create(['box-shadow', 'left', 'bottom'], { - duration: theme.transitions.duration.shortest, - }), - '&:before': { - position: 'absolute', - content: '""', - borderRadius: 'inherit', - width: '100%', - height: '100%', - boxShadow: (theme.vars || theme).shadows[2], - }, - '&::after': { - position: 'absolute', - content: '""', - borderRadius: '50%', - // 42px is the hit target - width: 42, - height: 42, - top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)', - }, - [`&:hover, &.${sliderClasses.focusVisible}`]: { - boxShadow: `0px 0px 0px 8px var(--slider-thumb-shadow-color)`, - '@media (hover: none)': { - boxShadow: 'none', - }, - }, - [`&.${sliderClasses.active}`]: { - boxShadow: `0px 0px 0px 14px var(--slider-thumb-shadow-color)`, - }, - [`&.${sliderClasses.disabled}`]: { - '&:hover': { - boxShadow: 'none', - }, - }, - variants: [ - { - props: { - color: 'primary', - }, - style: { - '--slider-thumb-shadow-color': theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / 0.16)` - : alpha((theme as Theme).palette.primary.main, 0.16), - }, - }, - { - props: { - color: 'secondary', - }, - style: { - '--slider-thumb-shadow-color': theme.vars - ? `rgba(${theme.vars.palette.secondary.mainChannel} / 0.16)` - : alpha((theme as Theme).palette.secondary.main, 0.16), - }, - }, - { - props: { - size: 'small', - }, - style: { - width: 12, - height: 12, - '&:before': { - boxShadow: 'none', - }, - }, - }, - { - props: { - orientation: 'horizontal', - }, - style: { - top: '50%', - transform: 'translate(-50%, -50%)', - }, - }, - { - props: { - orientation: 'vertical', - }, - style: { - left: '50%', - transform: 'translate(-50%, 50%)', - }, - }, - ], -})); - -export { SliderThumb }; - -const StyledSliderValueLabel = styled(SliderValueLabel, { - name: 'MuiSlider', - slot: 'ValueLabel', - overridesResolver: (props, styles) => styles.valueLabel, -})<SliderOwnerState>(({ theme }) => ({ - zIndex: 1, - whiteSpace: 'nowrap', - ...theme.typography.body2, - fontWeight: 500, - transition: theme.transitions.create(['transform'], { - duration: theme.transitions.duration.shortest, - }), - position: 'absolute', - backgroundColor: (theme.vars || theme).palette.grey[600], - borderRadius: '2px', - color: (theme.vars || theme).palette.common.white, - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - padding: '0.25rem 0.75rem', - variants: [ - { - props: { - size: 'small', - }, - style: { - fontSize: theme.typography.pxToRem(12), - padding: '0.25rem 0.5rem', - }, - }, - { - props: { - orientation: 'horizontal', - }, - style: { - top: '-10px', - transformOrigin: 'bottom center', - transform: 'translateY(-100%) scale(0)', - '&:before': { - position: 'absolute', - content: '""', - width: 8, - height: 8, - transform: 'translate(-50%, 50%) rotate(45deg)', - backgroundColor: 'inherit', - bottom: 0, - left: '50%', - }, - [`&.${sliderClasses.valueLabelOpen}`]: { - transform: 'translateY(-100%) scale(1)', - }, - }, - }, - { - props: { - orientation: 'vertical', - }, - style: { - top: '50%', - right: '30px', - transform: 'translateY(-50%) scale(0)', - transformOrigin: 'right center', - '&:before': { - position: 'absolute', - content: '""', - width: 8, - height: 8, - transform: 'translate(-50%, -50%) rotate(45deg)', - backgroundColor: 'inherit', - right: -8, - top: '50%', - }, - [`&.${sliderClasses.valueLabelOpen}`]: { - transform: 'translateY(-50%) scale(1)', - }, - }, - }, - { - props: { - orientation: 'vertical', - size: 'small', - }, - style: { - right: '20px', - }, - }, - ], -})); - -export { StyledSliderValueLabel as SliderValueLabel }; - -const SliderMark = styled('span', { - name: 'MuiSlider', - slot: 'Mark', - shouldForwardProp: (prop) => prop !== 'markActive', - // @TODO - Support this in `styled` implementation - // shouldForwardProp: (prop) => slotShouldForwardProp(prop) && prop !== 'markActive', - overridesResolver: (props, styles) => { - const { markActive } = props; - - return [styles.mark, markActive && styles.markActive]; - }, -})<SliderOwnerState & { markActive?: boolean }>(({ theme }) => ({ - position: 'absolute', - width: 2, - height: 2, - borderRadius: 1, - backgroundColor: 'currentColor', - variants: [ - { - props: { - orientation: 'horizontal', - }, - style: { - top: '50%', - transform: 'translate(-1px, -50%)', - }, - }, - { - props: { - orientation: 'vertical', - }, - style: { - left: '50%', - transform: 'translate(-50%, 1px)', - }, - }, - { - props: { - markActive: true, - }, - style: { - backgroundColor: (theme.vars || theme).palette.background.paper, - opacity: 0.8, - }, - }, - ], -})); - -export { SliderMark }; - -const SliderMarkLabel = styled('span', { - name: 'MuiSlider', - slot: 'MarkLabel', - // @TODO - // shouldForwardProp: (prop) => slotShouldForwardProp(prop) && prop !== 'markLabelActive', - overridesResolver: (props, styles) => styles.markLabel, -})<SliderOwnerState>(({ theme }) => ({ - ...theme.typography.body2, - color: (theme.vars || theme).palette.text.secondary, - position: 'absolute', - whiteSpace: 'nowrap', - variants: [ - { - props: { - orientation: 'horizontal', - }, - style: { - top: 30, - transform: 'translateX(-50%)', - '@media (pointer: coarse)': { - top: 40, - }, - }, - }, - { - props: { - orientation: 'vertical', - }, - style: { - left: 36, - transform: 'translateY(50%)', - '@media (pointer: coarse)': { - left: 44, - }, - }, - }, - { - props: { - markLabelActive: true, - }, - style: { - color: (theme.vars || theme).palette.text.primary, - }, - }, - ], -})); - -const useUtilityClasses = (ownerState: SliderOwnerState) => { - const { disabled, dragging, marked, orientation, track, classes, color, size } = ownerState; - - const slots = { - root: [ - 'root', - disabled && 'disabled', - dragging && 'dragging', - marked && 'marked', - orientation === 'vertical' && 'vertical', - track === 'inverted' && 'trackInverted', - track === false && 'trackFalse', - color && `color${capitalize(color)}`, - size && `size${capitalize(size)}`, - ], - rail: ['rail'], - track: ['track'], - mark: ['mark'], - markActive: ['markActive'], - markLabel: ['markLabel'], - markLabelActive: ['markLabelActive'], - valueLabel: ['valueLabel'], - thumb: [ - 'thumb', - disabled && 'disabled', - size && `thumbSize${capitalize(size)}`, - color && `thumbColor${capitalize(color)}`, - ], - active: ['active'], - disabled: ['disabled'], - focusVisible: ['focusVisible'], - }; - - return composeClasses(slots, getSliderUtilityClass, classes); -}; - -export { SliderMarkLabel }; - -const Forward = ({ children }: React.PropsWithChildren) => children; - -const Slider = React.forwardRef<HTMLSpanElement, SliderProps>(function Slider(props, ref) { - // @TODO - Figure out how to persist this information - const isRtl = false; // theme.direction === 'rtl'; - - const { - 'aria-label': ariaLabel, - 'aria-valuetext': ariaValuetext, - 'aria-labelledby': ariaLabelledby, - component = 'span', - components = {}, - componentsProps = {}, - color = 'primary', - classes: classesProp, - className, - disableSwap = false, - disabled = false, - getAriaLabel, - getAriaValueText, - marks: marksProp = false, - max = 100, - min = 0, - orientation = 'horizontal', - size = 'medium', - step = 1, - scale = Identity, - slotProps, - slots, - track = 'normal', - valueLabelDisplay = 'off', - valueLabelFormat = Identity, - ...other - } = props; - - const ownerState: SliderOwnerState = { - ...props, - isRtl, - max, - min, - classes: classesProp, - disabled, - disableSwap, - orientation, - marks: marksProp, - color, - size, - step, - scale, - track, - valueLabelDisplay, - valueLabelFormat, - }; - - const { - axisProps, - getRootProps, - getHiddenInputProps, - getThumbProps, - open, - active, - axis, - focusedThumbIndex, - range, - dragging, - marks, - values, - trackOffset, - trackLeap, - getThumbStyle, - } = useSlider({ - ...ownerState, - rootRef: ref, - }); - - ownerState.marked = marks.length > 0 && marks.some((mark) => mark.label); - ownerState.dragging = dragging; - ownerState.focusedThumbIndex = focusedThumbIndex; - - const classes = useUtilityClasses(ownerState); - - const RootSlot = slots?.root ?? components.Root ?? SliderRoot; - const RailSlot = slots?.rail ?? components.Rail ?? SliderRail; - const TrackSlot = slots?.track ?? components.Track ?? SliderTrack; - const ThumbSlot = slots?.thumb ?? components.Thumb ?? SliderThumb; - const ValueLabelSlot = slots?.valueLabel ?? components.ValueLabel ?? StyledSliderValueLabel; - const MarkSlot = slots?.mark ?? components.Mark ?? SliderMark; - const MarkLabelSlot = slots?.markLabel ?? components.MarkLabel ?? SliderMarkLabel; - const InputSlot = slots?.input ?? components.Input ?? 'input'; - - const rootSlotProps = slotProps?.root ?? componentsProps.root; - const railSlotProps = slotProps?.rail ?? componentsProps.rail; - const trackSlotProps = slotProps?.track ?? componentsProps.track; - const thumbSlotProps = slotProps?.thumb ?? componentsProps.thumb; - const valueLabelSlotProps = slotProps?.valueLabel ?? componentsProps.valueLabel; - const markSlotProps = slotProps?.mark ?? componentsProps.mark; - const markLabelSlotProps = slotProps?.markLabel ?? componentsProps.markLabel; - const inputSlotProps = slotProps?.input ?? componentsProps.input; - - const rootProps = useSlotProps({ - elementType: RootSlot, - getSlotProps: getRootProps, - externalSlotProps: rootSlotProps, - externalForwardedProps: other, - additionalProps: { - ...(shouldSpreadAdditionalProps(RootSlot) && { - as: component, - }), - }, - ownerState: { - ...ownerState, - ...rootSlotProps?.ownerState, - }, - className: [classes.root, className], - }); - - const railProps = useSlotProps({ - elementType: RailSlot, - externalSlotProps: railSlotProps, - ownerState, - className: classes.rail, - }); - - const trackProps = useSlotProps({ - elementType: TrackSlot, - externalSlotProps: trackSlotProps, - additionalProps: { - style: { - ...axisProps[axis].offset(trackOffset), - ...axisProps[axis].leap(trackLeap), - }, - }, - ownerState: { - ...ownerState, - ...trackSlotProps?.ownerState, - }, - className: classes.track, - }); - - const thumbProps = useSlotProps({ - elementType: ThumbSlot, - getSlotProps: getThumbProps, - externalSlotProps: thumbSlotProps, - ownerState: { - ...ownerState, - ...thumbSlotProps?.ownerState, - }, - className: classes.thumb, - }); - - const valueLabelProps = useSlotProps({ - elementType: ValueLabelSlot, - externalSlotProps: valueLabelSlotProps, - ownerState: { - ...ownerState, - ...valueLabelSlotProps?.ownerState, - }, - className: classes.valueLabel, - }); - - const markProps = useSlotProps({ - elementType: MarkSlot, - externalSlotProps: markSlotProps, - ownerState, - className: classes.mark, - }); - - const markLabelProps = useSlotProps({ - elementType: MarkLabelSlot, - externalSlotProps: markLabelSlotProps, - ownerState, - className: classes.markLabel, - }); - - const inputSliderProps = useSlotProps({ - elementType: InputSlot, - getSlotProps: getHiddenInputProps, - externalSlotProps: inputSlotProps, - ownerState, - }); - - return ( - <RootSlot {...rootProps}> - <RailSlot {...railProps} /> - <TrackSlot {...trackProps} /> - {marks - .filter((mark) => mark.value >= min && mark.value <= max) - .map((mark, index) => { - const percent = valueToPercent(mark.value, min, max); - const style = axisProps[axis].offset(percent); - - let markActive; - if (track === false) { - markActive = values.indexOf(mark.value) !== -1; - } else { - markActive = - (track === 'normal' && - (range - ? mark.value >= values[0] && mark.value <= values[values.length - 1] - : mark.value <= values[0])) || - (track === 'inverted' && - (range - ? mark.value <= values[0] || mark.value >= values[values.length - 1] - : mark.value >= values[0])); - } - - return ( - <React.Fragment key={index}> - <MarkSlot - data-index={index} - {...markProps} - {...(!isHostComponent(MarkSlot) && { - markActive, - })} - style={{ ...style, ...markProps.style }} - className={clsx(markProps.className, { - [classes.markActive]: markActive, - })} - /> - {mark.label != null ? ( - <MarkLabelSlot - aria-hidden - data-index={index} - {...markLabelProps} - {...(!isHostComponent(MarkLabelSlot) && { - markLabelActive: markActive, - })} - style={{ ...style, ...markLabelProps.style }} - className={clsx(classes.markLabel, markLabelProps.className, { - [classes.markLabelActive]: markActive, - })} - > - {mark.label} - </MarkLabelSlot> - ) : null} - </React.Fragment> - ); - })} - {values.map((value, index) => { - const percent = valueToPercent(value, min, max); - const style = axisProps[axis].offset(percent); - - const ValueLabelComponent = valueLabelDisplay === 'off' ? Forward : ValueLabelSlot; - - return ( - /* TODO v6: Change component structure. It will help in avoiding the complicated React.cloneElement API added in SliderValueLabel component. Should be: Thumb -> Input, ValueLabel. Follow Joy UI's Slider structure. */ - <ValueLabelComponent - key={index} - {...(!isHostComponent(ValueLabelComponent) && { - valueLabelFormat, - valueLabelDisplay, - value: - typeof valueLabelFormat === 'function' - ? valueLabelFormat(scale(value), index) - : valueLabelFormat, - index, - open: open === index || active === index || valueLabelDisplay === 'on', - disabled, - })} - {...valueLabelProps} - > - <ThumbSlot - data-index={index} - {...thumbProps} - className={clsx(classes.thumb, thumbProps.className, { - [classes.active]: active === index, - [classes.focusVisible]: focusedThumbIndex === index, - })} - style={{ - ...style, - ...getThumbStyle(index), - ...thumbProps.style, - }} - > - <InputSlot - data-index={index} - aria-label={getAriaLabel ? getAriaLabel(index) : ariaLabel} - aria-valuenow={scale(value)} - aria-labelledby={ariaLabelledby} - aria-valuetext={ - getAriaValueText ? getAriaValueText(scale(value), index) : ariaValuetext - } - value={values[index]} - {...inputSliderProps} - /> - </ThumbSlot> - </ValueLabelComponent> - ); - })} - </RootSlot> - ); -}); - -export default Slider; diff --git a/apps/pigment-vite-app/src/pages/material-ui/react-modal.tsx b/apps/pigment-vite-app/src/pages/material-ui/react-modal.tsx new file mode 100644 index 00000000000000..8e5341af93c0b3 --- /dev/null +++ b/apps/pigment-vite-app/src/pages/material-ui/react-modal.tsx @@ -0,0 +1,52 @@ +import * as React from 'react'; +import MaterialUILayout from '../../Layout'; +import BasicModal from '../../../../../docs/data/material/components/modal/BasicModal.tsx'; +import KeepMountedModal from '../../../../../docs/data/material/components/modal/KeepMountedModal.tsx'; +import NestedModal from '../../../../../docs/data/material/components/modal/NestedModal.tsx'; +import ServerModal from '../../../../../docs/data/material/components/modal/ServerModal.tsx'; +import SpringModal from '../../../../../docs/data/material/components/modal/SpringModal.tsx'; +import TransitionsModal from '../../../../../docs/data/material/components/modal/TransitionsModal.tsx'; + +export default function Modal() { + return ( + <MaterialUILayout> + <h1>Modal</h1> + <section> + <h2> Basic Modal</h2> + <div className="demo-container"> + <BasicModal /> + </div> + </section> + <section> + <h2> Keep Mounted Modal</h2> + <div className="demo-container"> + <KeepMountedModal /> + </div> + </section> + <section> + <h2> Nested Modal</h2> + <div className="demo-container"> + <NestedModal /> + </div> + </section> + <section> + <h2> Server Modal</h2> + <div className="demo-container"> + <ServerModal /> + </div> + </section> + <section> + <h2> Spring Modal</h2> + <div className="demo-container"> + <SpringModal /> + </div> + </section> + <section> + <h2> Transitions Modal</h2> + <div className="demo-container"> + <TransitionsModal /> + </div> + </section> + </MaterialUILayout> + ); +} diff --git a/apps/pnpm-lock.yaml b/apps/pnpm-lock.yaml index e0c6444c44b2ad..0df1319ac41672 100644 --- a/apps/pnpm-lock.yaml +++ b/apps/pnpm-lock.yaml @@ -55,8 +55,8 @@ importers: specifier: ^4.14.202 version: 4.14.202 '@types/node': - specifier: ^18.19.19 - version: 18.19.20 + specifier: ^18.19.22 + version: 18.19.22 '@types/prettier': specifier: ^2.7.3 version: 2.7.3 @@ -116,8 +116,8 @@ importers: specifier: ^10.0.6 version: 10.0.6 '@types/node': - specifier: ^18.19.19 - version: 18.19.20 + specifier: ^18.19.22 + version: 18.19.22 babel-plugin-tester: specifier: ^11.0.4 version: 11.0.4(@babel/core@7.23.9) @@ -262,8 +262,8 @@ importers: version: 15.8.1 devDependencies: '@types/node': - specifier: ^18.19.19 - version: 18.19.20 + specifier: ^18.19.22 + version: 18.19.22 '@types/prop-types': specifier: ^15.7.11 version: 15.7.11 @@ -1059,8 +1059,8 @@ importers: specifier: ^10.0.6 version: 10.0.6 '@types/node': - specifier: ^18.19.19 - version: 18.19.20 + specifier: ^18.19.22 + version: 18.19.22 '@types/react': specifier: ^18.2.55 version: 18.2.55 @@ -1087,17 +1087,17 @@ importers: version: 15.2.0 publishDirectory: build - ../packages/pigment-nextjs-plugin: + ../packages/pigment-css-nextjs-plugin: dependencies: '@pigment-css/unplugin': specifier: workspace:^ - version: link:../pigment-unplugin + version: link:../pigment-css-unplugin devDependencies: next: specifier: ^13.5.1 version: 13.5.1(react-dom@18.2.0)(react@18.2.0) - ../packages/pigment-react: + ../packages/pigment-css-react: dependencies: '@babel/core': specifier: ^7.23.9 @@ -1154,6 +1154,9 @@ importers: specifier: ^4.3.1 version: 4.3.1 devDependencies: + '@babel/plugin-syntax-jsx': + specifier: ^7.23.3 + version: 7.23.3(@babel/core@7.23.9) '@types/babel__core': specifier: ^7.20.5 version: 7.20.5 @@ -1163,15 +1166,21 @@ importers: '@types/babel__helper-plugin-utils': specifier: ^7.10.3 version: 7.10.3 + '@types/chai': + specifier: ^4.3.12 + version: 4.3.12 '@types/cssesc': specifier: ^3.0.2 version: 3.0.2 '@types/lodash': specifier: ^4.14.202 version: 4.14.202 + '@types/mocha': + specifier: ^10.0.6 + version: 10.0.6 '@types/node': - specifier: ^18.19.19 - version: 18.19.20 + specifier: ^18.19.22 + version: 18.19.22 '@types/react': specifier: ^18.2.55 version: 18.2.55 @@ -1181,18 +1190,21 @@ importers: chai: specifier: ^4.4.1 version: 4.4.1 + prettier: + specifier: ^3.2.5 + version: 3.2.5 react: specifier: ^18.2.0 version: 18.2.0 - ../packages/pigment-unplugin: + ../packages/pigment-css-unplugin: dependencies: '@babel/core': specifier: ^7.23.9 version: 7.23.9 '@pigment-css/react': specifier: workspace:^ - version: link:../pigment-react + version: link:../pigment-css-react '@wyw-in-js/shared': specifier: ^0.5.0 version: 0.5.0 @@ -1210,7 +1222,7 @@ importers: specifier: ^7.20.5 version: 7.20.5 - ../packages/pigment-vite-plugin: + ../packages/pigment-css-vite-plugin: dependencies: '@babel/core': specifier: ^7.23.9 @@ -1220,7 +1232,7 @@ importers: version: 7.23.3(@babel/core@7.23.9) '@pigment-css/react': specifier: workspace:^ - version: link:../pigment-react + version: link:../pigment-css-react '@wyw-in-js/shared': specifier: ^0.5.0 version: 0.5.0 @@ -1349,10 +1361,10 @@ importers: local-ui-lib: dependencies: '@pigment-css/react': - specifier: file:../../packages/pigment-react - version: file:../packages/pigment-react(@types/react@18.2.55)(react@18.2.0)(typescript@5.3.3) + specifier: file:../../packages/pigment-css-react + version: file:../packages/pigment-css-react(@types/react@18.2.55)(react@18.2.0)(typescript@5.3.3) - pigment-next-app: + pigment-css-next-app: dependencies: '@emotion/cache': specifier: latest @@ -1368,7 +1380,7 @@ importers: version: file:../packages/mui-material/build(@emotion/react@11.11.4)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) '@mui/material-nextjs': specifier: file:../../packages/mui-material-nextjs/build - version: file:../packages/mui-material-nextjs/build(@emotion/cache@11.11.0)(@mui/material@5.15.12)(@types/react@18.2.55)(next@14.1.0)(react@18.2.0) + version: file:../packages/mui-material-nextjs/build(@emotion/cache@11.11.0)(@mui/material@5.15.12)(@types/react@18.2.55)(next@14.1.3)(react@18.2.0) '@mui/system': specifier: file:../../packages/mui-system/build version: file:../packages/mui-system/build(@emotion/react@11.11.4)(@types/react@18.2.55)(react@18.2.0) @@ -1376,14 +1388,14 @@ importers: specifier: file:../../packages/mui-utils/build version: file:../packages/mui-utils/build(@types/react@18.2.55)(react@18.2.0) '@pigment-css/react': - specifier: file:../../packages/pigment-react - version: file:../packages/pigment-react(@types/react@18.2.55)(react@18.2.0)(typescript@5.3.3) + specifier: file:../../packages/pigment-css-react + version: file:../packages/pigment-css-react(@types/react@18.2.55)(react@18.2.0)(typescript@5.3.3) local-ui-lib: specifier: workspace:^ version: link:../local-ui-lib next: specifier: latest - version: 14.1.0(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0) + version: 14.1.3(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0) react: specifier: ^18.2.0 version: 18.2.0 @@ -1392,11 +1404,11 @@ importers: version: 18.2.0(react@18.2.0) devDependencies: '@pigment-css/nextjs-plugin': - specifier: file:../../packages/pigment-nextjs-plugin - version: file:../packages/pigment-nextjs-plugin(next@14.1.0) + specifier: file:../../packages/pigment-css-nextjs-plugin + version: file:../packages/pigment-css-nextjs-plugin(next@14.1.3) '@pigment-css/unplugin': - specifier: file:../../packages/pigment-unplugin - version: file:../packages/pigment-unplugin(typescript@5.3.3) + specifier: file:../../packages/pigment-css-unplugin + version: file:../packages/pigment-css-unplugin(typescript@5.3.3) '@types/node': specifier: ^20.5.7 version: 20.11.22 @@ -1413,7 +1425,7 @@ importers: specifier: ^5.3.3 version: 5.3.3 - pigment-vite-app: + pigment-css-vite-app: dependencies: '@mui/base': specifier: file:../../packages/mui-base/build @@ -1431,8 +1443,8 @@ importers: specifier: file:../../packages/mui-utils/build version: file:../packages/mui-utils/build(@types/react@18.2.55)(react@18.2.0) '@pigment-css/react': - specifier: file:../../packages/pigment-react - version: file:../packages/pigment-react(@types/react@18.2.55)(react@18.2.0)(typescript@5.3.3) + specifier: file:../../packages/pigment-css-react + version: file:../packages/pigment-css-react(@types/react@18.2.55)(react@18.2.0)(typescript@5.3.3) clsx: specifier: ^2.1.0 version: 2.1.0 @@ -1462,8 +1474,8 @@ importers: specifier: ^7.23.3 version: 7.23.3(@babel/core@7.23.9) '@pigment-css/vite-plugin': - specifier: file:../../packages/pigment-vite-plugin - version: file:../packages/pigment-vite-plugin(vite@5.0.12) + specifier: file:../../packages/pigment-css-vite-plugin + version: file:../packages/pigment-css-vite-plugin(vite@5.0.12) '@types/react': specifier: ^18.2.55 version: 18.2.55 @@ -3298,8 +3310,8 @@ packages: resolution: {integrity: sha512-CIMWiOTyflFn/GFx33iYXkgLSQsMQZV4jB91qaj/TfxGaGOXxn8C1j72TaUSPIyN7ziS/AYG46kGmnvuk1oOpg==} dev: true - /@next/env@14.1.0: - resolution: {integrity: sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==} + /@next/env@14.1.3: + resolution: {integrity: sha512-VhgXTvrgeBRxNPjyfBsDIMvgsKDxjlpw4IAUsHCX8Gjl1vtHUYRT3+xfQ/wwvLPDd/6kqfLqk9Pt4+7gysuCKQ==} /@next/swc-darwin-arm64@13.5.1: resolution: {integrity: sha512-Bcd0VFrLHZnMmJy6LqV1CydZ7lYaBao8YBEdQUVzV8Ypn/l5s//j5ffjfvMzpEQ4mzlAj3fIY+Bmd9NxpWhACw==} @@ -3310,8 +3322,8 @@ packages: dev: true optional: true - /@next/swc-darwin-arm64@14.1.0: - resolution: {integrity: sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==} + /@next/swc-darwin-arm64@14.1.3: + resolution: {integrity: sha512-LALu0yIBPRiG9ANrD5ncB3pjpO0Gli9ZLhxdOu6ZUNf3x1r3ea1rd9Q+4xxUkGrUXLqKVK9/lDkpYIJaCJ6AHQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -3327,8 +3339,8 @@ packages: dev: true optional: true - /@next/swc-darwin-x64@14.1.0: - resolution: {integrity: sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==} + /@next/swc-darwin-x64@14.1.3: + resolution: {integrity: sha512-E/9WQeXxkqw2dfcn5UcjApFgUq73jqNKaE5bysDm58hEUdUGedVrnRhblhJM7HbCZNhtVl0j+6TXsK0PuzXTCg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -3344,8 +3356,8 @@ packages: dev: true optional: true - /@next/swc-linux-arm64-gnu@14.1.0: - resolution: {integrity: sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==} + /@next/swc-linux-arm64-gnu@14.1.3: + resolution: {integrity: sha512-USArX9B+3rZSXYLFvgy0NVWQgqh6LHWDmMt38O4lmiJNQcwazeI6xRvSsliDLKt+78KChVacNiwvOMbl6g6BBw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -3361,8 +3373,8 @@ packages: dev: true optional: true - /@next/swc-linux-arm64-musl@14.1.0: - resolution: {integrity: sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==} + /@next/swc-linux-arm64-musl@14.1.3: + resolution: {integrity: sha512-esk1RkRBLSIEp1qaQXv1+s6ZdYzuVCnDAZySpa62iFTMGTisCyNQmqyCTL9P+cLJ4N9FKCI3ojtSfsyPHJDQNw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -3378,8 +3390,8 @@ packages: dev: true optional: true - /@next/swc-linux-x64-gnu@14.1.0: - resolution: {integrity: sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==} + /@next/swc-linux-x64-gnu@14.1.3: + resolution: {integrity: sha512-8uOgRlYEYiKo0L8YGeS+3TudHVDWDjPVDUcST+z+dUzgBbTEwSSIaSgF/vkcC1T/iwl4QX9iuUyUdQEl0Kxalg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -3395,8 +3407,8 @@ packages: dev: true optional: true - /@next/swc-linux-x64-musl@14.1.0: - resolution: {integrity: sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==} + /@next/swc-linux-x64-musl@14.1.3: + resolution: {integrity: sha512-DX2zqz05ziElLoxskgHasaJBREC5Y9TJcbR2LYqu4r7naff25B4iXkfXWfcp69uD75/0URmmoSgT8JclJtrBoQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -3412,8 +3424,8 @@ packages: dev: true optional: true - /@next/swc-win32-arm64-msvc@14.1.0: - resolution: {integrity: sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==} + /@next/swc-win32-arm64-msvc@14.1.3: + resolution: {integrity: sha512-HjssFsCdsD4GHstXSQxsi2l70F/5FsRTRQp8xNgmQs15SxUfUJRvSI9qKny/jLkY3gLgiCR3+6A7wzzK0DBlfA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] @@ -3429,8 +3441,8 @@ packages: dev: true optional: true - /@next/swc-win32-ia32-msvc@14.1.0: - resolution: {integrity: sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==} + /@next/swc-win32-ia32-msvc@14.1.3: + resolution: {integrity: sha512-DRuxD5axfDM1/Ue4VahwSxl1O5rn61hX8/sF0HY8y0iCbpqdxw3rB3QasdHn/LJ6Wb2y5DoWzXcz3L1Cr+Thrw==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] @@ -3446,8 +3458,8 @@ packages: dev: true optional: true - /@next/swc-win32-x64-msvc@14.1.0: - resolution: {integrity: sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==} + /@next/swc-win32-x64-msvc@14.1.3: + resolution: {integrity: sha512-uC2DaDoWH7h1P/aJ4Fok3Xiw6P0Lo4ez7NbowW2VGNXw/Xv6tOuLUcxhBYZxsSUJtpeknCi8/fvnSpyCFp4Rcg==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -3815,8 +3827,8 @@ packages: resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} dev: true - /@types/node@18.19.20: - resolution: {integrity: sha512-SKXZvI375jkpvAj8o+5U2518XQv76mAsixqfXiVyWyXZbVWQK25RurFovYpVIxVzul0rZoH58V/3SkEnm7s3qA==} + /@types/node@18.19.22: + resolution: {integrity: sha512-p3pDIfuMg/aXBmhkyanPshdfJuX5c5+bQjYLIikPLXAUycEogij/c50n/C+8XOA5L93cU4ZRXtn+dNQGi0IZqQ==} dependencies: undici-types: 5.26.5 dev: true @@ -6895,8 +6907,8 @@ packages: - babel-plugin-macros dev: true - /next@14.1.0(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==} + /next@14.1.3(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-oexgMV2MapI0UIWiXKkixF8J8ORxpy64OuJ/J9oVUmIthXOUCcuVEZX+dtpgq7wIfIqtBwQsKEDXejcjTsan9g==} engines: {node: '>=18.17.0'} hasBin: true peerDependencies: @@ -6910,7 +6922,7 @@ packages: sass: optional: true dependencies: - '@next/env': 14.1.0 + '@next/env': 14.1.3 '@swc/helpers': 0.5.2 busboy: 1.6.0 caniuse-lite: 1.0.30001591 @@ -6920,15 +6932,15 @@ packages: react-dom: 18.2.0(react@18.2.0) styled-jsx: 5.1.1(@babel/core@7.23.9)(react@18.2.0) optionalDependencies: - '@next/swc-darwin-arm64': 14.1.0 - '@next/swc-darwin-x64': 14.1.0 - '@next/swc-linux-arm64-gnu': 14.1.0 - '@next/swc-linux-arm64-musl': 14.1.0 - '@next/swc-linux-x64-gnu': 14.1.0 - '@next/swc-linux-x64-musl': 14.1.0 - '@next/swc-win32-arm64-msvc': 14.1.0 - '@next/swc-win32-ia32-msvc': 14.1.0 - '@next/swc-win32-x64-msvc': 14.1.0 + '@next/swc-darwin-arm64': 14.1.3 + '@next/swc-darwin-x64': 14.1.3 + '@next/swc-linux-arm64-gnu': 14.1.3 + '@next/swc-linux-arm64-musl': 14.1.3 + '@next/swc-linux-x64-gnu': 14.1.3 + '@next/swc-linux-x64-musl': 14.1.3 + '@next/swc-win32-arm64-msvc': 14.1.3 + '@next/swc-win32-ia32-msvc': 14.1.3 + '@next/swc-win32-x64-msvc': 14.1.3 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -8998,7 +9010,7 @@ packages: react: 18.2.0 dev: false - file:../packages/mui-material-nextjs/build(@emotion/cache@11.11.0)(@mui/material@5.15.12)(@types/react@18.2.55)(next@14.1.0)(react@18.2.0): + file:../packages/mui-material-nextjs/build(@emotion/cache@11.11.0)(@mui/material@5.15.12)(@types/react@18.2.55)(next@14.1.3)(react@18.2.0): resolution: {directory: ../packages/mui-material-nextjs/build, type: directory} id: file:../packages/mui-material-nextjs/build name: '@mui/material-nextjs' @@ -9022,7 +9034,7 @@ packages: '@emotion/cache': 11.11.0 '@mui/material': file:../packages/mui-material/build(@emotion/react@11.11.4)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.2.55 - next: 14.1.0(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0) + next: 14.1.3(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 dev: false @@ -9114,20 +9126,20 @@ packages: react-is: 18.2.0 dev: false - file:../packages/pigment-nextjs-plugin(next@14.1.0): - resolution: {directory: ../packages/pigment-nextjs-plugin, type: directory} - id: file:../packages/pigment-nextjs-plugin + file:../packages/pigment-css-nextjs-plugin(next@14.1.3): + resolution: {directory: ../packages/pigment-css-nextjs-plugin, type: directory} + id: file:../packages/pigment-css-nextjs-plugin name: '@pigment-css/nextjs-plugin' peerDependencies: next: ^12.0.0 || ^13.0.0 || ^14.0.0 dependencies: - '@pigment-css/unplugin': link:../packages/pigment-unplugin - next: 14.1.0(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0) + '@pigment-css/unplugin': link:../packages/pigment-css-unplugin + next: 14.1.3(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0) dev: true - file:../packages/pigment-react(@types/react@18.2.55)(react@18.2.0)(typescript@5.3.3): - resolution: {directory: ../packages/pigment-react, type: directory} - id: file:../packages/pigment-react + file:../packages/pigment-css-react(@types/react@18.2.55)(react@18.2.0)(typescript@5.3.3): + resolution: {directory: ../packages/pigment-css-react, type: directory} + id: file:../packages/pigment-css-react name: '@pigment-css/react' peerDependencies: react: ^17.0.0 || ^18.0.0 @@ -9157,13 +9169,13 @@ packages: - typescript dev: false - file:../packages/pigment-unplugin(typescript@5.3.3): - resolution: {directory: ../packages/pigment-unplugin, type: directory} - id: file:../packages/pigment-unplugin + file:../packages/pigment-css-unplugin(typescript@5.3.3): + resolution: {directory: ../packages/pigment-css-unplugin, type: directory} + id: file:../packages/pigment-css-unplugin name: '@pigment-css/unplugin' dependencies: '@babel/core': 7.23.9 - '@pigment-css/react': link:../packages/pigment-react + '@pigment-css/react': link:../packages/pigment-css-react '@wyw-in-js/shared': 0.5.0 '@wyw-in-js/transform': 0.5.0(typescript@5.3.3) babel-plugin-transform-react-remove-prop-types: 0.4.24 @@ -9173,16 +9185,16 @@ packages: - typescript dev: true - file:../packages/pigment-vite-plugin(vite@5.0.12): - resolution: {directory: ../packages/pigment-vite-plugin, type: directory} - id: file:../packages/pigment-vite-plugin + file:../packages/pigment-css-vite-plugin(vite@5.0.12): + resolution: {directory: ../packages/pigment-css-vite-plugin, type: directory} + id: file:../packages/pigment-css-vite-plugin name: '@pigment-css/vite-plugin' peerDependencies: vite: ^4.0.0 || ^5.0.0 dependencies: '@babel/core': 7.23.9 '@babel/preset-typescript': 7.23.3(@babel/core@7.23.9) - '@pigment-css/react': link:../packages/pigment-react + '@pigment-css/react': link:../packages/pigment-css-react '@wyw-in-js/shared': 0.5.0 '@wyw-in-js/transform': 0.5.0(typescript@5.3.3) babel-plugin-transform-react-remove-prop-types: 0.4.24 diff --git a/apps/pnpm-workspace.yaml b/apps/pnpm-workspace.yaml index d770d322ad42ee..da23d95e53e26f 100644 --- a/apps/pnpm-workspace.yaml +++ b/apps/pnpm-workspace.yaml @@ -2,7 +2,7 @@ packages: - 'local-ui-lib' - 'pigment-*' - '../packages/waterfall' - - '../packages/docs-utils' + - '../packages-internal/docs-utils' - '../packages/test-utils' - '../packages-internal/*' - '../packages/mui-*' diff --git a/babel.config.js b/babel.config.js index 7e6e17d626dd81..48ca2abea4ce50 100644 --- a/babel.config.js +++ b/babel.config.js @@ -30,10 +30,9 @@ module.exports = function getBabelConfig(api) { '@mui/private-theming': resolveAliasPath('./packages/mui-private-theming/src'), '@mui/base': resolveAliasPath('./packages/mui-base/src'), '@mui/utils': resolveAliasPath('./packages/mui-utils/src'), - '@mui/material-next': resolveAliasPath('./packages/mui-material-next/src'), '@mui/joy': resolveAliasPath('./packages/mui-joy/src'), - '@pigment-css/react': resolveAliasPath('./packages/pigment-react/src'), - '@mui-internal/docs-utils': resolveAliasPath('./packages/docs-utils/src'), + '@pigment-css/react': resolveAliasPath('./packages/pigment-css-react/src'), + '@mui/internal-docs-utils': resolveAliasPath('./packages-internal/docs-utils/src'), docs: resolveAliasPath('./docs'), test: resolveAliasPath('./test'), }; diff --git a/dangerfile.ts b/dangerfile.ts index 72d77fe2615db4..190f1941279805 100644 --- a/dangerfile.ts +++ b/dangerfile.ts @@ -194,7 +194,7 @@ function addDeployPreviewUrls() { const fragments = url.split('/').reverse(); if (fragments[0] === fragments[1]) { // check if the end of pathname is the same as the one before - // e.g. `/data/material/getting-started/overview/overview.md + // for example `/data/material/getting-started/overview/overview.md url = fragments.slice(1).reverse().join('/'); } diff --git a/docs/data/base/components/badge/badge.md b/docs/data/base/components/badge/badge.md index 71f557d58fd79c..e5c11fd7021bfd 100644 --- a/docs/data/base/components/badge/badge.md +++ b/docs/data/base/components/badge/badge.md @@ -99,7 +99,7 @@ You may not need to use hooks unless you find that you're limited by the customi :::info The following features can be used with both components and hooks. -For the sake of simplicity, demos and code snippets primarily feature components. +For the sake of simplicity, demos, and code snippets primarily feature components. ::: ### Badge content diff --git a/docs/data/base/components/button/button.md b/docs/data/base/components/button/button.md index a3302c53b20fe4..33482d138fa452 100644 --- a/docs/data/base/components/button/button.md +++ b/docs/data/base/components/button/button.md @@ -115,7 +115,7 @@ Do not add the `ref` parameter to the button element manually, as the correct re :::info The following features can be used with both components and hooks. -For the sake of simplicity, demos and code snippets primarily feature components. +For the sake of simplicity, demos, and code snippets primarily feature components. ::: ### Custom elements diff --git a/docs/data/base/components/focus-trap/focus-trap.md b/docs/data/base/components/focus-trap/focus-trap.md index 76c850021919f3..e04aa60f994042 100644 --- a/docs/data/base/components/focus-trap/focus-trap.md +++ b/docs/data/base/components/focus-trap/focus-trap.md @@ -78,7 +78,7 @@ The following demo uses the [Portal](/base-ui/react-portal/) component to render ### Using a toggle inside the trap -The most common use-case for the Focus Trap component is to maintain focus within a [Modal](/base-ui/react-modal/) component that is entirely separate from the element that opens the modal. +The most common use case for the Focus Trap component is to maintain focus within a [Modal](/base-ui/react-modal/) component that is entirely separate from the element that opens the modal. But you can also create a toggle button for the `open` prop of the Focus Trap component that is stored inside of the component itself, as shown in the following demo: {{"demo": "ContainedToggleTrappedFocus.js"}} diff --git a/docs/data/base/components/form-control/form-control.md b/docs/data/base/components/form-control/form-control.md index cd7ee94950fd2c..e60a78eeae01bd 100644 --- a/docs/data/base/components/form-control/form-control.md +++ b/docs/data/base/components/form-control/form-control.md @@ -82,7 +82,7 @@ The demo below shows how to integrate this hook with its component counterpart: {{"demo": "UseFormControl.js", "defaultCodeOpen": false}} -Note that even though Form Control supports both controlled and uncontrolled-style APIs (i.e. it accepts `value` and `defaultValue` props), `useFormControlContext` returns only the controlled `value`. +Note that even though Form Control supports both controlled and uncontrolled-style APIs (that is it accepts `value` and `defaultValue` props), `useFormControlContext` returns only the controlled `value`. This way, you don't have to implement both in your custom input—Form Control does this for you. :::info @@ -97,7 +97,7 @@ Learn more about controlled and uncontrolled components in the [React documentat :::info The following features can be used with both components and hooks. -For the sake of simplicity, demos and code snippets primarily feature components. +For the sake of simplicity, demos, and code snippets primarily feature components. ::: ### Accessing the form control state diff --git a/docs/data/base/components/input/input.md b/docs/data/base/components/input/input.md index 5134081bc1f995..829f2b3e7487d5 100644 --- a/docs/data/base/components/input/input.md +++ b/docs/data/base/components/input/input.md @@ -105,7 +105,7 @@ The demo below shows how to use the `useInput` hook to create a custom input com :::info The following features can be used with both components and hooks. -For the sake of simplicity, demos and code snippets primarily feature components. +For the sake of simplicity, demos, and code snippets primarily feature components. ::: ### Adornments diff --git a/docs/data/base/components/menu/menu.md b/docs/data/base/components/menu/menu.md index a90790dad30f05..dfc881cd2684dd 100644 --- a/docs/data/base/components/menu/menu.md +++ b/docs/data/base/components/menu/menu.md @@ -166,7 +166,7 @@ The `useMenuItemContextStabilizer` hook ensures that the context value changes o :::info The following features can be used with both components and hooks. -For the sake of simplicity, demos and code snippets primarily feature components. +For the sake of simplicity, demos, and code snippets primarily feature components. ::: ### Wrapping Menu Items diff --git a/docs/data/base/components/no-ssr/no-ssr.md b/docs/data/base/components/no-ssr/no-ssr.md index a844312b759c87..5bdc6fbf9fce0a 100644 --- a/docs/data/base/components/no-ssr/no-ssr.md +++ b/docs/data/base/components/no-ssr/no-ssr.md @@ -38,7 +38,7 @@ At its core, the No-SSR component's purpose is to defer rendering from the serve ### Delay client-side rendering -You can also use No-SSR to delay the rendering of specific components on the client side—for example, to let the rest of the application load before an especially complex or data-heavy component. +You can also use No-SSR to delay the rendering of specific components on the client-side—for example, to let the rest of the application load before an especially complex or data-heavy component. The following demo shows how to use the `defer` prop to prioritize rendering the rest of the app outside of what is nested within No-SSR: diff --git a/docs/data/base/components/select/select.md b/docs/data/base/components/select/select.md index ddd8a143a5e449..7b66ecc94cc074 100644 --- a/docs/data/base/components/select/select.md +++ b/docs/data/base/components/select/select.md @@ -242,7 +242,7 @@ The `useOptionContextStabilizer` hook ensures that the context value changes onl :::info The following features can be used with both components and hooks. -For the sake of simplicity, demos and code snippets primarily feature components. +For the sake of simplicity, demos, and code snippets primarily feature components. ::: ### Selected value appearance diff --git a/docs/data/base/components/snackbar/snackbar.md b/docs/data/base/components/snackbar/snackbar.md index 340b477c9d0b2f..cc2648ec57394a 100644 --- a/docs/data/base/components/snackbar/snackbar.md +++ b/docs/data/base/components/snackbar/snackbar.md @@ -109,7 +109,7 @@ You may not need to use hooks unless you find that you're limited by the customi :::info The following features can be used with both components and hooks. -For the sake of simplicity, demos and code snippets primarily feature components. +For the sake of simplicity, demos, and code snippets primarily feature components. ::: ### Transitions diff --git a/docs/data/base/components/tabs/tabs.md b/docs/data/base/components/tabs/tabs.md index c7c4a607acb732..aaf0a1a86923b6 100644 --- a/docs/data/base/components/tabs/tabs.md +++ b/docs/data/base/components/tabs/tabs.md @@ -35,7 +35,7 @@ import { TabPanel } from '@mui/base/TabPanel'; import { Tabs } from '@mui/base/Tabs'; ``` -By default, Tab components and their corresponding panels are **zero-indexed** (i.e. the first tab has a `value` of `0`, then `1`, `2`, etc.). +By default, Tab components and their corresponding panels are **zero-indexed** (that is the first tab has a `value` of `0`, then `1`, `2`, etc.). Clicking on a given Tab opens the panel with the same `value`, which corresponds to the order in which each component is nested within its container. Though not required, you can add the `value` prop to the Tab and Tab Panel to take control over how these components are associated with one another. diff --git a/docs/data/base/getting-started/customization/customization.md b/docs/data/base/getting-started/customization/customization.md index b9b21a6884a145..59fc91b4c797ec 100644 --- a/docs/data/base/getting-started/customization/customization.md +++ b/docs/data/base/getting-started/customization/customization.md @@ -29,9 +29,9 @@ The answer depends on the styling solution used in the project: You can either [style the components using the built-in classes](#applying-custom-css-rules) or [specify your own classes](#customizing-slot-props) and reference them in your stylesheets. -#### CSS Modules +#### CSS Modules -When working with [CSS Modules](https://github.com/css-modules/css-modules), the simplest approach is to [specify custom classes using `slotProps`](#customizing-slot-props), as shown below: +When working with [CSS Modules](https://github.com/css-modules/css-modules), the simplest approach is to [specify custom classes using `slotProps`](#customizing-slot-props), as shown below: ```tsx import clsx from 'clsx'; @@ -57,9 +57,9 @@ export default function Switch(props) { In this example we're using the [clsx](https://www.npmjs.com/package/clsx) utility to reduce the effort needed to apply class names conditionally. -#### Tailwind CSS +#### Tailwind CSS -Use [`slotProps`](#customizing-slot-props) to apply custom styles using [Tailwind CSS](https://tailwindcss.com/), as shown below: +Use [`slotProps`](#customizing-slot-props) to apply custom styles using [Tailwind CSS](https://tailwindcss.com/), as shown below: ```tsx import { Switch as BaseSwitch, SwitchOwnerState } from '@mui/base/Switch'; @@ -83,7 +83,7 @@ export default function Switch(props) { } ``` -See our [Working with Tailwind CSS guide](/base-ui/guides/working-with-tailwind-css/) for more information about integrating Base UI and Tailwind CSS. +See our [Working with Tailwind CSS guide](/base-ui/guides/working-with-tailwind-css/) for more information about integrating Base UI and Tailwind CSS. #### Styled components @@ -195,7 +195,7 @@ If you need complete control over a component's rendered HTML structure, you can Hooks give you access to the _logic_ that a component uses, but without any default structure. See ["Components vs. hooks" on the Base Usage page](/base-ui/getting-started/usage/#components-vs-hooks) for more details. -Hooks return the current state of the component (e.g. `checked`, `disabled`, `open`, etc.) and provide functions that return props you can apply to your fully custom components. +Hooks return the current state of the component (for example `checked`, `disabled`, `open`, etc.) and provide functions that return props you can apply to your fully custom components. In the case of [Switch](/base-ui/react-switch/), the component is accompanied by the `useSwitch` hook which gives you all of the functionality without any structure. diff --git a/docs/data/base/getting-started/overview/overview.md b/docs/data/base/getting-started/overview/overview.md index 5d1327c7f1276d..4770357341cc6e 100644 --- a/docs/data/base/getting-started/overview/overview.md +++ b/docs/data/base/getting-started/overview/overview.md @@ -17,8 +17,8 @@ Base UI includes prebuilt components with production-ready functionality, along With Base UI, you can rapidly build on top of our foundational components using any styling solution you choose—no need to override any default style engine or theme. :::warning -Base UI's API is currently being revised; there will be no new features or components added to the current implementation. -Learn more about plans for Base UI in [this blog post](/blog/base-ui-2024-plans/). +Base UI's API is currently being revised; there will be no new features or components added to the current implementation. +Learn more about plans for Base UI in [this blog post](/blog/base-ui-2024-plans/). ::: ## Advantages of Base UI diff --git a/docs/data/base/getting-started/quickstart/quickstart.md b/docs/data/base/getting-started/quickstart/quickstart.md index 46be4af8e57ca5..0bcf49c0652a79 100644 --- a/docs/data/base/getting-started/quickstart/quickstart.md +++ b/docs/data/base/getting-started/quickstart/quickstart.md @@ -106,7 +106,7 @@ Pass a `className` prop and use it as a styling hook: <Button className="btn">Create Repository</Button> ``` -Base UI components like the Button come with a classes object (e.g. `buttonClasses`) that provides class hooks for styling a particular state. +Base UI components like the Button come with a classes object (for example `buttonClasses`) that provides class hooks for styling a particular state. ```css /* To style the disabled state: */ @@ -120,15 +120,15 @@ The demo below shows how to create the Primer button using plain CSS with Base  {{"demo": "BaseButtonPlainCss.js", "defaultCodeOpen": false}} -### Styling with Tailwind CSS +### Styling with Tailwind CSS -After installing Tailwind CSS, pass its utility classes to `className`, as shown below: +After installing Tailwind CSS, pass its utility classes to `className`, as shown below: ```tsx <Button className="bg-green-600 rounded-md py-1 px-4...">Create Repository</Button> ``` -The demo below shows how to build the Primer button using Tailwind CSS: +The demo below shows how to build the Primer button using Tailwind CSS: {{"demo": "BaseButtonTailwind.js", "hideToolbar": true, "bg": "inline"}} diff --git a/docs/data/base/getting-started/roadmap/roadmap.md b/docs/data/base/getting-started/roadmap/roadmap.md index 26e20e250cf474..0b138cb782ef44 100644 --- a/docs/data/base/getting-started/roadmap/roadmap.md +++ b/docs/data/base/getting-started/roadmap/roadmap.md @@ -1,22 +1,22 @@ # Roadmap -<p class="description">Keep up with ongoing projects and help shape the future of Base UI.</p> +<p class="description">Keep up with ongoing projects and help shape the future of Base UI.</p> ## How we prioritize -Base UI is a community-driven project, meaning we usually pick the issues and suggestions that resonate the most with the community. +Base UI is a community-driven project, meaning we usually pick the issues and suggestions that resonate the most with the community. Therefore, make sure to leave an upvote 👍 on [the GitHub issues](https://github.com/mui/base-ui/issues) you are most interested in. -Additionally, we conduct annual [developer surveys](/blog/?tags=Developer+survey/) which also serve as key inputs for Base UI's roadmap. +Additionally, we conduct annual [developer surveys](/blog/?tags=Developer+survey/) which also serve as key inputs for Base UI's roadmap. Your participation is invaluable—keep an eye on MUI's social media to catch the next survey and help shape the future of the library! ## Keeping track of the roadmap ### GitHub project -The Base UI GitHub project is where you can see the ongoing priorities for the library. +The Base UI GitHub project is where you can see the ongoing priorities for the library. We typically add umbrella issues to the project board after discussing them internally. -**[Visit the Base UI project board 👉](https://github.com/orgs/mui/projects/1/views/13)** +**[Visit the Base UI project board 👉](https://github.com/orgs/mui/projects/1/views/13)** -<img src="/static/base-ui/roadmap/github-roadmap.png" style="width: 814px;" alt="A screenshot of the public Base UI GitHub project." width="1628" height="400" /> +<img src="/static/base-ui/roadmap/github-roadmap.png" style="width: 814px;" alt="A screenshot of the public Base UI GitHub project." width="1628" height="400" /> diff --git a/docs/data/base/guides/next-js-app-router/next-js-app-router.md b/docs/data/base/guides/next-js-app-router/next-js-app-router.md index ece4eb58d44be6..11db3639e39933 100644 --- a/docs/data/base/guides/next-js-app-router/next-js-app-router.md +++ b/docs/data/base/guides/next-js-app-router/next-js-app-router.md @@ -6,7 +6,7 @@ Starting fresh on a new App Router-based project? -Jump right into the code with [this example: Base UI - Next.js App Router with Tailwind CSS in TypeScript](https://github.com/mui/material-ui/tree/master/examples/base-ui-nextjs-tailwind-ts). +Jump right into the code with [this example: Base UI - Next.js App Router with Tailwind CSS in TypeScript](https://github.com/mui/material-ui/tree/master/examples/base-ui-nextjs-tailwind-ts). ## Next.js and React Server Components @@ -24,11 +24,11 @@ For more details, see [this explanation](https://github.com/reactwg/server-compo ## Setting up Base UI with the App Router Base UI gives you the freedom to choose your own styling solution, so setting up a Next.js App Router project largely depends on what you choose. -This guide covers Tailwind CSS, Emotion, and other CSS-in-JS solutions like styled-components. +This guide covers Tailwind CSS, Emotion, and other CSS-in-JS solutions like styled-components. -### Tailwind CSS +### Tailwind CSS -Follow the [Tailwind CSS guide on working with Next.js](https://tailwindcss.com/docs/guides/nextjs), and be sure to add the `app` directory and other directories to `tailwind.config.js`, as shown below: +Follow the [Tailwind CSS guide on working with Next.js](https://tailwindcss.com/docs/guides/nextjs), and be sure to add the `app` directory and other directories to `tailwind.config.js`, as shown below: ```js /** @type {import('tailwindcss').Config} */ @@ -46,7 +46,7 @@ module.exports = { }; ``` -Refer to this [example repo](https://github.com/mui/material-ui/tree/master/examples/base-ui-nextjs-tailwind-ts) for a full working demo of a Next.js 13 app using Base UI and Tailwind CSS. +Refer to this [example repo](https://github.com/mui/material-ui/tree/master/examples/base-ui-nextjs-tailwind-ts) for a full working demo of a Next.js 13 app using Base UI and Tailwind CSS. ### Emotion @@ -124,7 +124,7 @@ export default function RootLayout(props) { } ``` -If you need to further override theme styles (e.g. using CSS modules), Emotion provides the `prepend: true` option for `createCache` to reverse the injection order, so custom styles can override the theme without using `!important`. +If you need to further override theme styles (for example using CSS Modules), Emotion provides the `prepend: true` option for `createCache` to reverse the injection order, so custom styles can override the theme without using `!important`. Currently, `prepend` does not work reliably with the App Router, but you can work around it by wrapping Emotion styles in a CSS `@layer` with a modification to the snippet above: diff --git a/docs/data/base/guides/working-with-tailwind-css/working-with-tailwind-css.md b/docs/data/base/guides/working-with-tailwind-css/working-with-tailwind-css.md index fef60e311bbe5f..92cf9f71fa4ad9 100644 --- a/docs/data/base/guides/working-with-tailwind-css/working-with-tailwind-css.md +++ b/docs/data/base/guides/working-with-tailwind-css/working-with-tailwind-css.md @@ -1,16 +1,16 @@ -# Working with Tailwind CSS +# Working with Tailwind CSS -<p class="description">Learn how to style Base UI components with Tailwind CSS.</p> +<p class="description">Learn how to style Base UI components with Tailwind CSS.</p> ## Getting started -The goal of this guide is to teach you how to style Base UI components using Tailwind CSS while building an interactive and accessible app. +The goal of this guide is to teach you how to style Base UI components using Tailwind CSS while building an interactive and accessible app. ### Prerequisites This guide assumes that you have a basic working knowledge of the following: -- Tailwind CSS +- Tailwind CSS - TypeScript in React - building React UI components @@ -22,13 +22,13 @@ Here's what it will look like in the end: {{"demo": "PlayerFinal.js", "hideToolbar": true, "bg": true}} :::info -All credits go to the Tailwind Labs team for designing this component, found on the [Tailwind CSS website](https://tailwindcss.com/). +All credits go to the Tailwind Labs team for designing this component, found on the [Tailwind CSS website](https://tailwindcss.com/). ::: ## Setting up the project We'll use [`create-react-app` with TypeScript](https://create-react-app.dev/docs/adding-typescript/#installation) for this guide. -After you have created the project, follow the instructions given on the [Tailwind CSS installation page](https://tailwindcss.com/docs/guides/create-react-app) in order to configure `tailwind`. +After you have created the project, follow the instructions given on the [Tailwind CSS installation page](https://tailwindcss.com/docs/guides/create-react-app) in order to configure `tailwind`. Next, install `@mui/base` in the project: ```bash @@ -37,7 +37,7 @@ npm install @mui/base ## Adding the player markup -Now, create a file called `Player.tsx` and add the markup below, which includes Tailwind CSS classes: +Now, create a file called `Player.tsx` and add the markup below, which includes Tailwind CSS classes: **Player.tsx** @@ -286,7 +286,7 @@ const Slider = React.forwardRef(function Slider( export default Slider; ``` -To assign specific Tailwind CSS utility classes for each part of the component, we're using `slotProps`. +To assign specific Tailwind CSS utility classes for each part of the component, we're using `slotProps`. Most of them were copied from the original markup with small adjustments now that it is interactive. ### Add the slider to the player @@ -513,9 +513,9 @@ Some classes were slightly changed on some buttons so we have a consistent focus These are the things we covered in this guide: -✅ How to use Tailwind CSS utility classes to style Base UI components, using the `slotProps` prop for targeting specific slots within the component.\ +✅ How to use Tailwind CSS utility classes to style Base UI components, using the `slotProps` prop for targeting specific slots within the component.\ ✅ How to create custom components for specific slots in more complex customization scenarios. We used the `component` prop to pass them into the parent component.\ ✅ How to apply conditional styling based on the owner component's state using a callback as value for the `slotProps` prop. -Get all the code used in this guide in the [Base UI with Tailwind CSS](https://codesandbox.io/p/sandbox/working-with-tailwind-css-dhmf8w) example project. +Get all the code used in this guide in the [Base UI with Tailwind CSS](https://codesandbox.io/p/sandbox/working-with-tailwind-css-dhmf8w) example project. diff --git a/docs/data/docs-infra/pages.ts b/docs/data/docs-infra/pages.ts index d8dbb02f3d954f..0cd74435bba91f 100644 --- a/docs/data/docs-infra/pages.ts +++ b/docs/data/docs-infra/pages.ts @@ -14,6 +14,7 @@ const pages: readonly MuiPage[] = [ children: [ { pathname: '/experiments/docs/callouts' }, { pathname: '/experiments/docs/codeblock' }, + { pathname: '/experiments/docs/custom-components' }, { pathname: '/experiments/docs/demos' }, { pathname: '/experiments/docs/data-grid-premium', title: 'API DataGridPremium' }, ], diff --git a/docs/data/joy/components/autocomplete/FreeSoloCreateOptionDialog.js b/docs/data/joy/components/autocomplete/FreeSoloCreateOptionDialog.js index 9035d29c01b11d..731724086cd6a6 100644 --- a/docs/data/joy/components/autocomplete/FreeSoloCreateOptionDialog.js +++ b/docs/data/joy/components/autocomplete/FreeSoloCreateOptionDialog.js @@ -80,7 +80,7 @@ export default function FreeSoloCreateOptionDialog() { }} options={top100Films} getOptionLabel={(option) => { - // e.g. value selected with enter, right from the input + // for example value selected with enter, right from the input if (typeof option === 'string') { return option; } diff --git a/docs/data/joy/components/autocomplete/FreeSoloCreateOptionDialog.tsx b/docs/data/joy/components/autocomplete/FreeSoloCreateOptionDialog.tsx index f0931094d3d28c..66be05b2f75806 100644 --- a/docs/data/joy/components/autocomplete/FreeSoloCreateOptionDialog.tsx +++ b/docs/data/joy/components/autocomplete/FreeSoloCreateOptionDialog.tsx @@ -80,7 +80,7 @@ export default function FreeSoloCreateOptionDialog() { }} options={top100Films} getOptionLabel={(option) => { - // e.g. value selected with enter, right from the input + // for example value selected with enter, right from the input if (typeof option === 'string') { return option; } diff --git a/docs/data/joy/components/autocomplete/autocomplete.md b/docs/data/joy/components/autocomplete/autocomplete.md index 3947809b70c2bb..a9e17593ccc14e 100644 --- a/docs/data/joy/components/autocomplete/autocomplete.md +++ b/docs/data/joy/components/autocomplete/autocomplete.md @@ -147,7 +147,7 @@ It displays a progress state as long as the network request is pending. ### Search input -Use `freeSolo` to create a **search input** with suggestions experience, e.g. Google search or [react-autowhatever](https://github.com/moroshko/react-autowhatever). +Use `freeSolo` to create a **search input** with suggestions experience, for example Google search or [react-autowhatever](https://github.com/moroshko/react-autowhatever). {{"demo": "FreeSolo.js"}} @@ -324,7 +324,7 @@ A possible workaround is to remove the `id` to have the component generate a ran In addition to remembering past entered values, the browser might also propose **autofill** suggestions (saved login, address, or payment details). In the event you want the avoid autofill, you can try the following: -- Name the input without leaking any information the browser can use. e.g. `id="field1"` instead of `id="country"`. If you leave the id empty, the component uses a random id. +- Name the input without leaking any information the browser can use. For example `id="field1"` instead of `id="country"`. If you leave the id empty, the component uses a random id. - Set `autoComplete="new-password"` (some browsers will suggest a strong password for inputs with this attribute setting): ```jsx diff --git a/docs/data/joy/components/button-group/button-group.md b/docs/data/joy/components/button-group/button-group.md index d77cffd4e83c97..68e5e8ba128eab 100644 --- a/docs/data/joy/components/button-group/button-group.md +++ b/docs/data/joy/components/button-group/button-group.md @@ -71,7 +71,7 @@ Use `spacing` prop to control the gap between buttons. If the `spacing` is set t :::success The type of value can be: -- `string`: any valid CSS length unit, e.g. `px`, `rem`, `em`, etc. +- `string`: any valid CSS length unit, for example `px`, `rem`, `em`, etc. - `number`: will be calculated by `theme.spacing` function. - `array`: the responsive values based on the breakpoints defined in the theme. - `object`: the key must be one of the breakpoints defined in the theme (the defaults are `"xs" | "sm" | "md" | "lg" | "xl")`, and the value is the spacing of type `string` or `number`. diff --git a/docs/data/joy/components/grid/grid.md b/docs/data/joy/components/grid/grid.md index ff29fb560ae99f..1d0e14ac6cd926 100644 --- a/docs/data/joy/components/grid/grid.md +++ b/docs/data/joy/components/grid/grid.md @@ -14,7 +14,7 @@ githubLabel: 'component: Grid' The Grid component, based on a 12-column grid layout, creates visual consistency between layouts while allowing flexibility across a wide variety of designs. :::warning -The `Grid` component shouldn't be confused with a data grid; it is closer to a layout grid. For a data grid head to the [MUI X Data Grid`](/x/react-data-grid/) component. +The `Grid` component shouldn't be confused with a data grid; it is closer to a layout grid. For a data grid head to the [MUI X Data Grid`](/x/react-data-grid/) component. ::: ## Basics diff --git a/docs/data/joy/components/list/list.md b/docs/data/joy/components/list/list.md index 1f581b0a8e4225..0904eb53699c19 100644 --- a/docs/data/joy/components/list/list.md +++ b/docs/data/joy/components/list/list.md @@ -252,7 +252,7 @@ It also supports keyboard navigation, inspired by the [Roving UX](https://github To ensure that your List is accessible, here are some factors you should consider: -- Use the appropriate HTML semantic element for the list (eg. `ol` or `ul`), to ensure that assistive technologies can correctly interpret the list structure. +- Use the appropriate HTML semantic element for the list (for example `ol` or `ul`), to ensure that assistive technologies can correctly interpret the list structure. - Make sure to use a meaningful name that describes the content of the list in the `aria-labelledby` prop. - Use `role` attributes to provide additional information about the purpose of the list and its items. For example, use `role="list"` for the list and `role="listitem"` for each list item. diff --git a/docs/data/joy/components/modal/modal.md b/docs/data/joy/components/modal/modal.md index 2256c4b431a4f1..9fe6a5c5621f37 100644 --- a/docs/data/joy/components/modal/modal.md +++ b/docs/data/joy/components/modal/modal.md @@ -53,7 +53,7 @@ export default function MyApp() { ### Basic usage The Modal accepts only a single React element as a child. -That can be either a Joy UI component, e.g. [`Sheet`](/joy-ui/react-sheet/), or any other custom element. +That can be either a Joy UI component, for example [`Sheet`](/joy-ui/react-sheet/), or any other custom element. Use the Modal Close component to render a close button that inherits the modal's `onClose` function. diff --git a/docs/data/joy/components/radio-button/radio-button.md b/docs/data/joy/components/radio-button/radio-button.md index e033a85ee31b84..8cce03965dc20a 100644 --- a/docs/data/joy/components/radio-button/radio-button.md +++ b/docs/data/joy/components/radio-button/radio-button.md @@ -14,7 +14,7 @@ waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/radio/ ## Introduction -Radio buttons let users make a mutually exclusive choice (e.g., this or that). +Radio buttons let users make a mutually exclusive choice (for example: "this" or "that"). Only one selection is allowed from the available set of options. Radio buttons should have the most commonly used option selected by default. diff --git a/docs/data/joy/components/switch/switch.md b/docs/data/joy/components/switch/switch.md index 9e8d20ec74234f..b67463b390bd2f 100644 --- a/docs/data/joy/components/switch/switch.md +++ b/docs/data/joy/components/switch/switch.md @@ -52,7 +52,7 @@ By default, the color of the switch changes from `neutral` to `primary` when it ### Label -When a `Switch` is used together with `FormControl` and `FormLabel`, the switch is labelled automatically. You can also use `FormHelperText` to include a description to the switch as well. +When a `Switch` is used together with `FormControl` and `FormLabel`, the switch is labeled automatically. You can also use `FormHelperText` to include a description to the switch as well. {{"demo": "SwitchControl.js"}} @@ -139,7 +139,7 @@ Here are a few tips to make sure you have an accessible switch component: - Every form control component should have proper labels. This includes radio buttons, checkboxes, and switches. In most cases, this is done using the `<label>` element. - - If a label can't be applied, make sure to add an attribute (e.g. `aria-label`, `aria-labelledby`, `title`) to the input slot inside the `slotProps` prop. + - If a label can't be applied, make sure to add an attribute (for example `aria-label`, `aria-labelledby`, `title`) to the input slot inside the `slotProps` prop. ```jsx <Switch value="checkedA" slotProps={{ 'aria-label': 'Switch A' }} /> ``` diff --git a/docs/data/joy/components/textarea/textarea.md b/docs/data/joy/components/textarea/textarea.md index 85f1b5206df449..c1a29d13c9a6c1 100644 --- a/docs/data/joy/components/textarea/textarea.md +++ b/docs/data/joy/components/textarea/textarea.md @@ -50,7 +50,7 @@ Toggle the palette that's being used to color the by text field by using the `co ### Form props -Standard form attributes are supported e.g. `required`, `disabled`, etc. +Standard form attributes are supported for example `required`, `disabled`, etc. {{"demo": "TextareaFormProps.js"}} diff --git a/docs/data/joy/components/typography/TypographyTitleBody.js b/docs/data/joy/components/typography/TypographyTitleBody.js index 3a4317ef645b65..b4e54adcf3ef8c 100644 --- a/docs/data/joy/components/typography/TypographyTitleBody.js +++ b/docs/data/joy/components/typography/TypographyTitleBody.js @@ -61,7 +61,7 @@ export default function TypographyTitleBody() { </Typography> </Typography> <Typography level="body-sm"> - Metadata, e.g. a date.{' '} + Metadata, for example a date.{' '} <Typography level="body-sm" textColor="var(--joy-palette-success-plainColor)" @@ -97,7 +97,7 @@ export default function TypographyTitleBody() { </Typography> </Typography> <Typography level="body-xs"> - Metadata, e.g. a date.{' '} + Metadata, for example a date.{' '} <Typography level="body-xs" textColor="var(--joy-palette-success-plainColor)" diff --git a/docs/data/joy/components/typography/TypographyTitleBody.tsx b/docs/data/joy/components/typography/TypographyTitleBody.tsx index 3a4317ef645b65..b4e54adcf3ef8c 100644 --- a/docs/data/joy/components/typography/TypographyTitleBody.tsx +++ b/docs/data/joy/components/typography/TypographyTitleBody.tsx @@ -61,7 +61,7 @@ export default function TypographyTitleBody() { </Typography> </Typography> <Typography level="body-sm"> - Metadata, e.g. a date.{' '} + Metadata, for example a date.{' '} <Typography level="body-sm" textColor="var(--joy-palette-success-plainColor)" @@ -97,7 +97,7 @@ export default function TypographyTitleBody() { </Typography> </Typography> <Typography level="body-xs"> - Metadata, e.g. a date.{' '} + Metadata, for example a date.{' '} <Typography level="body-xs" textColor="var(--joy-palette-success-plainColor)" diff --git a/docs/data/joy/components/typography/typography.md b/docs/data/joy/components/typography/typography.md index cd4b749f0b933a..52ccec7e071ba3 100644 --- a/docs/data/joy/components/typography/typography.md +++ b/docs/data/joy/components/typography/typography.md @@ -78,7 +78,7 @@ These values include various heading levels (h1, h2, h3, etc.) as well as body t Additionally, you can also use the level prop to control the font size, weight, line height, and other typographic properties. :::warning -Keep in mind that each level renders a specific HTML tag (e.g. "h1" renders as an `<h1>` element, "body-md" renders as a `<p>`, etc.) +Keep in mind that each level renders a specific HTML tag (for example "h1" renders as an `<h1>` element, "body-md" renders as a `<p>`, etc.) ::: {{"demo": "TypographyScales.js"}} diff --git a/docs/data/joy/customization/theme-colors/theme-colors.md b/docs/data/joy/customization/theme-colors/theme-colors.md index d934885c269287..452fd42d569fcb 100644 --- a/docs/data/joy/customization/theme-colors/theme-colors.md +++ b/docs/data/joy/customization/theme-colors/theme-colors.md @@ -81,7 +81,7 @@ For example, here's how you'd make the Joy UI Button match the colors of anothe {{"demo": "BootstrapVariantTokens.js"}} - Bootstrap's default buttons are comparable to Joy UI's `solid` variant. -- Bootstrap's `secondary` variant uses a grey color, similar to Joy UI's `neutral`. +- Bootstrap's `secondary` variant uses a gray color, similar to Joy UI's `neutral`. - Bootstrap's `btn-light` is similar to Joy UI's button using the `soft` variant and `neutral` color palette. - Joy UI's defaults don't include anything similar to Bootstrap's `btn-dark`. - We can recreate it using one of the three main customization approaches. diff --git a/docs/data/joy/customization/theme-shadow/theme-shadow.md b/docs/data/joy/customization/theme-shadow/theme-shadow.md index 4c8224f0eefc6f..6fa937b4b44aa3 100644 --- a/docs/data/joy/customization/theme-shadow/theme-shadow.md +++ b/docs/data/joy/customization/theme-shadow/theme-shadow.md @@ -116,7 +116,7 @@ const theme = extendTheme({ ``` :::warning -The `shadowChannel` value must be rgb channels, e.g. `187 187 187`. +The `shadowChannel` value must be rgb channels, for example `187 187 187`. ::: ## Customizing shadows on an element diff --git a/docs/data/joy/customization/themed-components/themed-components.md b/docs/data/joy/customization/themed-components/themed-components.md index 249980b4b0f75b..a69f127f583b99 100644 --- a/docs/data/joy/customization/themed-components/themed-components.md +++ b/docs/data/joy/customization/themed-components/themed-components.md @@ -190,7 +190,7 @@ declare module '@mui/joy/Button' { ### Extend sizes The following code snippet illustrates how to provide additional sizes to a component beyond `sm`, `md`, and `lg`. -We recommend following the established "t-shirt size" naming convention (e.g. `xs`, `xl`, `xxl`, etc.) to maintain consistency with all the other props. +We recommend following the established "t-shirt size" naming convention (for example `xs`, `xl`, `xxl`, etc.) to maintain consistency with all the other props. The example below extends the Button sizes to include `xs` and `xl` values: diff --git a/docs/data/joy/customization/using-css-variables/using-css-variables.md b/docs/data/joy/customization/using-css-variables/using-css-variables.md index 94ee3976e2af41..0f8d08de2373f4 100644 --- a/docs/data/joy/customization/using-css-variables/using-css-variables.md +++ b/docs/data/joy/customization/using-css-variables/using-css-variables.md @@ -122,7 +122,7 @@ const Div = styled('div')(({ theme }) => ({ ``` :::warning -The format of the channel tokens uses a space as a separator (e.g., `61 131 246`), which means you have to use `/` to combine the channel token with an opacity value. +The format of the channel tokens uses a space as a separator (for example `61 131 246`), which means you have to use `/` to combine the channel token with an opacity value. ```js `rgba(${theme.vars.palette.primary.mainChannel} / 0.2)`, // ✅ correct diff --git a/docs/data/joy/main-features/color-inversion/ColorInversionNavigation.js b/docs/data/joy/main-features/color-inversion/ColorInversionNavigation.js index 30ab1e7a924819..313c1e7ae20881 100644 --- a/docs/data/joy/main-features/color-inversion/ColorInversionNavigation.js +++ b/docs/data/joy/main-features/color-inversion/ColorInversionNavigation.js @@ -1,7 +1,7 @@ import * as React from 'react'; import Avatar from '@mui/joy/Avatar'; -import Badge, { badgeClasses } from '@mui/joy/Badge'; +import Badge from '@mui/joy/Badge'; import Box from '@mui/joy/Box'; import Card from '@mui/joy/Card'; import CardContent from '@mui/joy/CardContent'; @@ -15,6 +15,7 @@ import ListItemButton from '@mui/joy/ListItemButton'; import ListItemDecorator from '@mui/joy/ListItemDecorator'; import Typography from '@mui/joy/Typography'; import Select from '@mui/joy/Select'; +import Tooltip from '@mui/joy/Tooltip'; import Option from '@mui/joy/Option'; import Sheet from '@mui/joy/Sheet'; import PieChart from '@mui/icons-material/PieChart'; @@ -30,25 +31,19 @@ export default function ColorInversionNavigation() { <Box sx={{ display: 'flex', borderRadius: 'sm', overflow: 'auto' }}> <Sheet variant="solid" - color="neutral" invertedColors sx={{ p: 2, ...(color !== 'neutral' && { - bgcolor: `${color}.800`, + bgcolor: `${color}.700`, }), }} > <Select - variant="outlined" + variant="soft" defaultValue="1" size="sm" - placeholder={ - <div> - <Typography level="inherit">Saleshouse</Typography> - <Typography level="body-md">general team</Typography> - </div> - } + color={color} startDecorator={ <Sheet variant="solid" @@ -59,10 +54,15 @@ export default function ColorInversionNavigation() { alignSelf: 'center', }} > - <BubbleChartIcon sx={{ m: 0 }} /> + <BubbleChartIcon fontSize="small" sx={{ m: 0 }} /> </Sheet> } - sx={{ py: 1 }} + sx={{ + py: 1, + bgcolor: 'transparent', + border: '1px solid', + borderColor: 'divider', + }} > <Option value="1">General team</Option> <Option value="2">Engineering team</Option> @@ -89,8 +89,8 @@ export default function ColorInversionNavigation() { <Chip data-skip-inverted-colors size="sm" - color="warning" variant="soft" + color={color} sx={{ ml: 'auto' }} > 5 @@ -131,27 +131,25 @@ export default function ColorInversionNavigation() { </Card> </Sheet> <Sheet - variant="soft" - color="neutral" + variant="solid" invertedColors - sx={(theme) => ({ + sx={{ p: 2, display: 'flex', flexDirection: 'column', + alignItems: 'center', gap: 2, - ...(color !== 'neutral' && { - bgcolor: `${color}.900`, - }), + bgcolor: `${color}.800`, + '& .MuiBadge-root': { '--Badge-ringColor': '#FFF' }, + '& .MuiBadge-colorSuccess': { bgcolor: 'success.400' }, '& button': { borderRadius: '50%', padding: 0, - '&:hover': { - boxShadow: theme.shadow.md, - }, + '--IconButton-size': '3rem', }, - })} + }} > - <Badge badgeContent="7" size="sm"> + <Badge badgeContent="7" badgeInset="10%" size="sm"> <IconButton> <Avatar src="/static/images/avatar/3.jpg" /> </IconButton> @@ -161,28 +159,28 @@ export default function ColorInversionNavigation() { vertical: 'bottom', horizontal: 'right', }} - badgeInset="14%" - sx={{ [`& .${badgeClasses.badge}`]: { bgcolor: 'success.300' } }} + badgeInset="15%" + color="success" > <IconButton> <Avatar src="/static/images/avatar/4.jpg" /> </IconButton> </Badge> - <IconButton variant="soft" aria-label="Add another chat"> - <AddIcon /> - </IconButton> + <Tooltip title="Add another chat" variant="soft"> + <IconButton sx={{ color: 'text.tertiary' }}> + <AddIcon /> + </IconButton> + </Tooltip> <IconButton - variant="plain" - size="sm" onClick={() => { const colors = ['primary', 'neutral', 'danger', 'success', 'warning']; const nextColorIndex = colors.indexOf(color) + 1; setColor(colors[nextColorIndex] ?? colors[0]); }} - sx={{ mt: 'auto', height: '40px' }} + sx={{ mt: 'auto', color: 'text.tertiary' }} > - <ColorLensRoundedIcon fontSize="small" /> + <ColorLensRoundedIcon /> </IconButton> </Sheet> </Box> diff --git a/docs/data/joy/main-features/color-inversion/ColorInversionNavigation.tsx b/docs/data/joy/main-features/color-inversion/ColorInversionNavigation.tsx index c41c4aed4e09cb..1ead86f5885d62 100644 --- a/docs/data/joy/main-features/color-inversion/ColorInversionNavigation.tsx +++ b/docs/data/joy/main-features/color-inversion/ColorInversionNavigation.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { ColorPaletteProp } from '@mui/joy/styles'; import Avatar from '@mui/joy/Avatar'; -import Badge, { badgeClasses } from '@mui/joy/Badge'; +import Badge from '@mui/joy/Badge'; import Box from '@mui/joy/Box'; import Card from '@mui/joy/Card'; import CardContent from '@mui/joy/CardContent'; @@ -15,6 +15,7 @@ import ListItemButton from '@mui/joy/ListItemButton'; import ListItemDecorator from '@mui/joy/ListItemDecorator'; import Typography from '@mui/joy/Typography'; import Select from '@mui/joy/Select'; +import Tooltip from '@mui/joy/Tooltip'; import Option from '@mui/joy/Option'; import Sheet from '@mui/joy/Sheet'; import PieChart from '@mui/icons-material/PieChart'; @@ -30,25 +31,19 @@ export default function ColorInversionNavigation() { <Box sx={{ display: 'flex', borderRadius: 'sm', overflow: 'auto' }}> <Sheet variant="solid" - color="neutral" invertedColors sx={{ p: 2, ...(color !== 'neutral' && { - bgcolor: `${color}.800`, + bgcolor: `${color}.700`, }), }} > <Select - variant="outlined" + variant="soft" defaultValue="1" size="sm" - placeholder={ - <div> - <Typography level="inherit">Saleshouse</Typography> - <Typography level="body-md">general team</Typography> - </div> - } + color={color} startDecorator={ <Sheet variant="solid" @@ -59,10 +54,15 @@ export default function ColorInversionNavigation() { alignSelf: 'center', }} > - <BubbleChartIcon sx={{ m: 0 }} /> + <BubbleChartIcon fontSize="small" sx={{ m: 0 }} /> </Sheet> } - sx={{ py: 1 }} + sx={{ + py: 1, + bgcolor: 'transparent', + border: '1px solid', + borderColor: 'divider', + }} > <Option value="1">General team</Option> <Option value="2">Engineering team</Option> @@ -89,8 +89,8 @@ export default function ColorInversionNavigation() { <Chip data-skip-inverted-colors size="sm" - color="warning" variant="soft" + color={color} sx={{ ml: 'auto' }} > 5 @@ -131,27 +131,25 @@ export default function ColorInversionNavigation() { </Card> </Sheet> <Sheet - variant="soft" - color="neutral" + variant="solid" invertedColors - sx={(theme) => ({ + sx={{ p: 2, display: 'flex', flexDirection: 'column', + alignItems: 'center', gap: 2, - ...(color !== 'neutral' && { - bgcolor: `${color}.900`, - }), + bgcolor: `${color}.800`, + '& .MuiBadge-root': { '--Badge-ringColor': '#FFF' }, + '& .MuiBadge-colorSuccess': { bgcolor: 'success.400' }, '& button': { borderRadius: '50%', padding: 0, - '&:hover': { - boxShadow: theme.shadow.md, - }, + '--IconButton-size': '3rem', }, - })} + }} > - <Badge badgeContent="7" size="sm"> + <Badge badgeContent="7" badgeInset="10%" size="sm"> <IconButton> <Avatar src="/static/images/avatar/3.jpg" /> </IconButton> @@ -161,19 +159,19 @@ export default function ColorInversionNavigation() { vertical: 'bottom', horizontal: 'right', }} - badgeInset="14%" - sx={{ [`& .${badgeClasses.badge}`]: { bgcolor: 'success.300' } }} + badgeInset="15%" + color="success" > <IconButton> <Avatar src="/static/images/avatar/4.jpg" /> </IconButton> </Badge> - <IconButton variant="soft" aria-label="Add another chat"> - <AddIcon /> - </IconButton> + <Tooltip title="Add another chat" variant="soft"> + <IconButton sx={{ color: 'text.tertiary' }}> + <AddIcon /> + </IconButton> + </Tooltip> <IconButton - variant="plain" - size="sm" onClick={() => { const colors: ColorPaletteProp[] = [ 'primary', @@ -185,9 +183,9 @@ export default function ColorInversionNavigation() { const nextColorIndex = colors.indexOf(color) + 1; setColor(colors[nextColorIndex] ?? colors[0]); }} - sx={{ mt: 'auto', height: '40px' }} + sx={{ mt: 'auto', color: 'text.tertiary' }} > - <ColorLensRoundedIcon fontSize="small" /> + <ColorLensRoundedIcon /> </IconButton> </Sheet> </Box> diff --git a/docs/data/joy/migration/TitleBodyIconExample.js b/docs/data/joy/migration/TitleBodyIconExample.js index 0a790f3ad80bdd..e14d7acf9d48a8 100644 --- a/docs/data/joy/migration/TitleBodyIconExample.js +++ b/docs/data/joy/migration/TitleBodyIconExample.js @@ -60,7 +60,7 @@ export default function TitleBodyIconExample() { some information of it. </Typography> <Typography level="body-sm"> - <i>body-sm</i>: Metadata, e.g. a date. + <i>body-sm</i>: Metadata, for example a date. </Typography> </div> </Stack> @@ -90,7 +90,7 @@ export default function TitleBodyIconExample() { some information of it. </Typography> <Typography level="body-xs"> - <i>body-xs</i>: Metadata, e.g. a date. + <i>body-xs</i>: Metadata, for example a date. </Typography> </div> </Stack> diff --git a/docs/data/joy/migration/TitleBodyIconExample.tsx b/docs/data/joy/migration/TitleBodyIconExample.tsx index 0a790f3ad80bdd..e14d7acf9d48a8 100644 --- a/docs/data/joy/migration/TitleBodyIconExample.tsx +++ b/docs/data/joy/migration/TitleBodyIconExample.tsx @@ -60,7 +60,7 @@ export default function TitleBodyIconExample() { some information of it. </Typography> <Typography level="body-sm"> - <i>body-sm</i>: Metadata, e.g. a date. + <i>body-sm</i>: Metadata, for example a date. </Typography> </div> </Stack> @@ -90,7 +90,7 @@ export default function TitleBodyIconExample() { some information of it. </Typography> <Typography level="body-xs"> - <i>body-xs</i>: Metadata, e.g. a date. + <i>body-xs</i>: Metadata, for example a date. </Typography> </div> </Stack> diff --git a/docs/data/material/components/autocomplete/FreeSoloCreateOptionDialog.js b/docs/data/material/components/autocomplete/FreeSoloCreateOptionDialog.js index 47e51826760a40..22516f183d3bb1 100644 --- a/docs/data/material/components/autocomplete/FreeSoloCreateOptionDialog.js +++ b/docs/data/material/components/autocomplete/FreeSoloCreateOptionDialog.js @@ -75,7 +75,7 @@ export default function FreeSoloCreateOptionDialog() { id="free-solo-dialog-demo" options={top100Films} getOptionLabel={(option) => { - // e.g. value selected with enter, right from the input + // for example value selected with enter, right from the input if (typeof option === 'string') { return option; } diff --git a/docs/data/material/components/autocomplete/FreeSoloCreateOptionDialog.tsx b/docs/data/material/components/autocomplete/FreeSoloCreateOptionDialog.tsx index 429d3dedc6bde3..779b26e26d6d17 100644 --- a/docs/data/material/components/autocomplete/FreeSoloCreateOptionDialog.tsx +++ b/docs/data/material/components/autocomplete/FreeSoloCreateOptionDialog.tsx @@ -75,7 +75,7 @@ export default function FreeSoloCreateOptionDialog() { id="free-solo-dialog-demo" options={top100Films} getOptionLabel={(option) => { - // e.g. value selected with enter, right from the input + // for example value selected with enter, right from the input if (typeof option === 'string') { return option; } diff --git a/docs/data/material/components/autocomplete/autocomplete.md b/docs/data/material/components/autocomplete/autocomplete.md index 9dab4d8409dcc8..3fe82cf70232db 100644 --- a/docs/data/material/components/autocomplete/autocomplete.md +++ b/docs/data/material/components/autocomplete/autocomplete.md @@ -12,8 +12,8 @@ waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/combobox/ The widget is useful for setting the value of a single-line textbox in one of two types of scenarios: -1. The value for the textbox must be chosen from a predefined set of allowed values, e.g., a location field must contain a valid location name: [combo box](#combo-box). -2. The textbox may contain any arbitrary value, but it is advantageous to suggest possible values to the user, e.g., a search field may suggest similar or previous searches to save the user time: [free solo](#free-solo). +1. The value for the textbox must be chosen from a predefined set of allowed values, for example a location field must contain a valid location name: [combo box](#combo-box). +2. The textbox may contain any arbitrary value, but it is advantageous to suggest possible values to the user, for example a search field may suggest similar or previous searches to save the user time: [free solo](#free-solo). It's meant to be an improved version of the "react-select" and "downshift" packages. @@ -108,7 +108,7 @@ Set `freeSolo` to true so the textbox can contain any arbitrary value. ### Search input -The prop is designed to cover the primary use case of a **search input** with suggestions, e.g. Google search or react-autowhatever. +The prop is designed to cover the primary use case of a **search input** with suggestions, for example Google search or react-autowhatever. {{"demo": "FreeSolo.js"}} @@ -375,7 +375,7 @@ A possible workaround is to remove the `id` to have the component generate a ran In addition to remembering past entered values, the browser might also propose **autofill** suggestions (saved login, address, or payment details). In the event you want the avoid autofill, you can try the following: -- Name the input without leaking any information the browser can use. e.g. `id="field1"` instead of `id="country"`. If you leave the id empty, the component uses a random id. +- Name the input without leaking any information the browser can use. For example `id="field1"` instead of `id="country"`. If you leave the id empty, the component uses a random id. - Set `autoComplete="new-password"` (some browsers will suggest a strong password for inputs with this attribute setting): ```jsx diff --git a/docs/data/material/components/badges/BadgeMaterialYouPlayground.js b/docs/data/material/components/badges/BadgeMaterialYouPlayground.js deleted file mode 100644 index 08edf42bd77ff3..00000000000000 --- a/docs/data/material/components/badges/BadgeMaterialYouPlayground.js +++ /dev/null @@ -1,48 +0,0 @@ -import * as React from 'react'; -import Badge from '@mui/material-next/Badge'; -import MailIcon from '@mui/icons-material/Mail'; -import MaterialYouUsageDemo from 'docs/src/modules/components/MaterialYouUsageDemo'; - -export default function BadgeMaterialYouPlayground() { - return ( - <MaterialYouUsageDemo - componentName="Badge" - data={[ - { - propName: 'badgeContent', - defaultValue: 4, - }, - { - propName: 'color', - knob: 'select', - options: [ - 'error', - 'info', - 'warning', - 'success', - 'primary', - 'secondary', - 'tertiary', - ], - defaultValue: 'error', - }, - { - propName: 'size', - knob: 'select', - options: ['small', 'large'], - defaultValue: 'large', - }, - { - propName: 'invisible', - knob: 'switch', - defaultValue: false, - }, - ]} - renderDemo={({ vertical, ...rest }) => ( - <Badge {...rest}> - <MailIcon color="action" /> - </Badge> - )} - /> - ); -} diff --git a/docs/data/material/components/badges/badges.md b/docs/data/material/components/badges/badges.md index c92c9b90b995b4..143dd849cd3bea 100644 --- a/docs/data/material/components/badges/badges.md +++ b/docs/data/material/components/badges/badges.md @@ -71,18 +71,3 @@ You can't rely on the content of the badge to be announced correctly. You should provide a full description, for instance, with `aria-label`: {{"demo": "AccessibleBadges.js"}} - -## Experimental APIs - -### Material Design 3 - -The default Material UI Badge component follows the Material Design 2 specs. -To use the [M3](https://m3.material.io/) version, install the experimental `@mui/material-next` package. - -```js -import Badge from '@mui/material-next/Badge'; -``` - -{{"demo": "BadgeMaterialYouPlayground.js", "hideToolbar": true, "bg": "playground"}} - -To learn more about Material UI's M3 implementation, visit the [M3 Components documentation](/material-ui/guides/material-3-components/). diff --git a/docs/data/material/components/button-group/ButtonGroupMaterialYouPlayground.js b/docs/data/material/components/button-group/ButtonGroupMaterialYouPlayground.js deleted file mode 100644 index 46b92778394528..00000000000000 --- a/docs/data/material/components/button-group/ButtonGroupMaterialYouPlayground.js +++ /dev/null @@ -1,51 +0,0 @@ -import * as React from 'react'; -import Button from '@mui/material-next/Button'; -import ButtonGroup from '@mui/material-next/ButtonGroup'; -import FavoriteBorder from '@mui/icons-material/FavoriteBorder'; -import MaterialYouUsageDemo from 'docs/src/modules/components/MaterialYouUsageDemo'; - -export default function ButtonGroupMaterialYouPlayground() { - return ( - <MaterialYouUsageDemo - componentName="ButtonGroup" - data={[ - { - propName: 'color', - knob: 'select', - defaultValue: 'primary', - options: ['primary', 'secondary', 'tertiary'], - }, - { - propName: 'variant', - knob: 'select', - defaultValue: 'text', - options: ['text', 'outlined', 'filled', 'filledTonal', 'elevated'], - }, - { - propName: 'size', - knob: 'select', - options: ['small', 'medium', 'large'], - defaultValue: 'medium', - }, - { - propName: 'orientation', - knob: 'select', - options: ['horizontal', 'vertical'], - defaultValue: 'horizontal', - }, - { - propName: 'disabled', - knob: 'switch', - defaultValue: false, - }, - ]} - renderDemo={(props) => ( - <ButtonGroup {...props} aria-label="Basic button group"> - <Button>One</Button> - <Button>Two</Button> - <Button startIcon={<FavoriteBorder />}>Three</Button> - </ButtonGroup> - )} - /> - ); -} diff --git a/docs/data/material/components/button-group/button-group.md b/docs/data/material/components/button-group/button-group.md index 73f026b74dd4dd..a0645298d04bb6 100644 --- a/docs/data/material/components/button-group/button-group.md +++ b/docs/data/material/components/button-group/button-group.md @@ -55,16 +55,3 @@ You can remove the elevation with the `disableElevation` prop. You can use the [`<LoadingButton />`](/material-ui/react-button/#loading-button) from [`@mui/lab`](/material-ui/about-the-lab/) in the button group. {{"demo": "LoadingButtonGroup.js"}} - -### Material Design 3 - -The default Material UI Button Group component follows the Material Design 2 specs. -To use the [M3](https://m3.material.io/) version, install the experimental `@mui/material-next` package. - -```js -import ButtonGroup from '@mui/material-next/ButtonGroup'; -``` - -{{"demo": "ButtonGroupMaterialYouPlayground.js", "hideToolbar": true, "bg": "playground"}} - -To learn more about Material UI's M3 implementation, visit the [M3 Components documentation](/material-ui/guides/material-3-components/). diff --git a/docs/data/material/components/buttons/ButtonMaterialYouPlayground.js b/docs/data/material/components/buttons/ButtonMaterialYouPlayground.js deleted file mode 100644 index c3de24f10e9a5e..00000000000000 --- a/docs/data/material/components/buttons/ButtonMaterialYouPlayground.js +++ /dev/null @@ -1,52 +0,0 @@ -import * as React from 'react'; -import Box from '@mui/material/Box'; -import Button from '@mui/material-next/Button'; -import FavoriteBorder from '@mui/icons-material/FavoriteBorder'; -import MaterialYouUsageDemo from 'docs/src/modules/components/MaterialYouUsageDemo'; - -export default function ButtonMaterialYouPlayground() { - const [variant, setVariant] = React.useState('text'); - return ( - <MaterialYouUsageDemo - componentName="Button" - data={[ - { - propName: 'variant', - knob: 'select', - defaultValue: 'text', - options: ['text', 'outlined', 'filled', 'filledTonal', 'elevated'], - onChange: (e) => setVariant(e.target.value), - }, - ...(variant === 'filled' || variant === 'text' || variant === 'outlined' - ? [ - { - propName: 'color', - knob: 'select', - defaultValue: 'primary', - options: ['primary', 'secondary', 'tertiary'], - }, - ] - : []), - { - propName: 'size', - knob: 'select', - options: ['small', 'medium', 'large'], - defaultValue: 'medium', - }, - { - propName: 'disabled', - knob: 'switch', - defaultValue: false, - }, - ]} - renderDemo={(props) => ( - <Box sx={{ display: 'flex', gap: 2 }}> - <Button {...props}>Hello world</Button> - <Button {...props} startIcon={<FavoriteBorder />}> - Hello world - </Button> - </Box> - )} - /> - ); -} diff --git a/docs/data/material/components/buttons/buttons.md b/docs/data/material/components/buttons/buttons.md index 35089bd278cab9..247609d8aa46e9 100644 --- a/docs/data/material/components/buttons/buttons.md +++ b/docs/data/material/components/buttons/buttons.md @@ -196,18 +196,3 @@ To prevent this, ensure that the contents of the Loading Button are nested insid <span>Submit</span> </LoadingButton> ``` - -::: - -### Material Design 3 - -The default Material UI Button component follows the Material Design 2 specs. -To use the [M3](https://m3.material.io/) version, install the experimental `@mui/material-next` package. - -```js -import Button from '@mui/material-next/Button'; -``` - -{{"demo": "ButtonMaterialYouPlayground.js", "hideToolbar": true, "bg": "playground"}} - -To learn more about Material UI's M3 implementation, visit the [M3 Components documentation](/material-ui/guides/material-3-components/). diff --git a/docs/data/material/components/checkboxes/checkboxes.md b/docs/data/material/components/checkboxes/checkboxes.md index 1621413386f73f..c2ef0d2ec1984f 100644 --- a/docs/data/material/components/checkboxes/checkboxes.md +++ b/docs/data/material/components/checkboxes/checkboxes.md @@ -94,7 +94,7 @@ You can learn more about this in the [overrides documentation page](/material-ui - All form controls should have labels, and this includes radio buttons, checkboxes, and switches. In most cases, this is done by using the `<label>` element ([FormControlLabel](/material-ui/api/form-control-label/)). - When a label can't be used, it's necessary to add an attribute directly to the input component. - In this case, you can apply the additional attribute (e.g. `aria-label`, `aria-labelledby`, `title`) via the `inputProps` prop. + In this case, you can apply the additional attribute (for example `aria-label`, `aria-labelledby`, `title`) via the `inputProps` prop. ```jsx <Checkbox diff --git a/docs/data/material/components/chips/ChipMaterialYouPlayground.js b/docs/data/material/components/chips/ChipMaterialYouPlayground.js deleted file mode 100644 index 6da30fac769676..00000000000000 --- a/docs/data/material/components/chips/ChipMaterialYouPlayground.js +++ /dev/null @@ -1,59 +0,0 @@ -import * as React from 'react'; -import Box from '@mui/material/Box'; -import Chip from '@mui/material-next/Chip'; -import MaterialYouUsageDemo from 'docs/src/modules/components/MaterialYouUsageDemo'; - -export default function ChipMaterialYouPlayground() { - return ( - <MaterialYouUsageDemo - componentName="Chip" - data={[ - { - propName: 'variant', - knob: 'select', - defaultValue: 'filled', - options: ['filled', 'outlined', 'elevated'], - }, - { - propName: 'color', - knob: 'select', - options: [ - 'error', - 'info', - 'warning', - 'success', - 'primary', - 'secondary', - 'tertiary', - ], - }, - { - propName: 'size', - knob: 'select', - options: ['small', 'medium'], - defaultValue: 'medium', - }, - { - propName: 'disabled', - knob: 'switch', - defaultValue: false, - }, - ]} - renderDemo={(props) => ( - <Box sx={{ display: 'flex', gap: 2 }}> - <Chip {...props} label="Basic chip" /> - <Chip - {...props} - label="Clickable chip" - onClick={() => alert('Clicked M3 Chip')} - /> - <Chip - {...props} - label="Deletable chip" - onDelete={() => alert('Deleted M3 Chip')} - /> - </Box> - )} - /> - ); -} diff --git a/docs/data/material/components/chips/chips.md b/docs/data/material/components/chips/chips.md index 848d4381db46f2..65063426ca27a8 100644 --- a/docs/data/material/components/chips/chips.md +++ b/docs/data/material/components/chips/chips.md @@ -97,21 +97,6 @@ gain depth while clicked or touched. {{"demo": "ChipsPlayground.js", "hideToolbar": true}} -## Experimental API - -### Material Design 3 - -The default Material UI Chip component follows the Material Design 2 specs. -To use the [M3](https://m3.material.io/) version, install the experimental `@mui/material-next` package. - -```js -import Chip from '@mui/material-next/Chip'; -``` - -{{"demo": "ChipMaterialYouPlayground.js", "hideToolbar": true, "bg": "playground"}} - -To learn more about Material UI's M3 implementation, visit the [M3 Components documentation](/material-ui/guides/material-3-components/). - ## Accessibility -If the Chip is deletable or clickable then it is a button in tab order. When the Chip is focused (e.g. when tabbing) releasing (`keyup` event) `Backspace` or `Delete` will call the `onDelete` handler while releasing `Escape` will blur the Chip. +If the Chip is deletable or clickable then it is a button in tab order. When the Chip is focused (for example when tabbing) releasing (`keyup` event) `Backspace` or `Delete` will call the `onDelete` handler while releasing `Escape` will blur the Chip. diff --git a/docs/data/material/components/dialogs/dialogs.md b/docs/data/material/components/dialogs/dialogs.md index cbf62b794778ed..87a89ff8ed435a 100644 --- a/docs/data/material/components/dialogs/dialogs.md +++ b/docs/data/material/components/dialogs/dialogs.md @@ -17,18 +17,26 @@ Dialogs are purposefully interruptive, so they should be used sparingly. {{"component": "modules/components/ComponentLinkHeader.js"}} -## Basic dialog +## Introduction -Simple dialogs can provide additional details or actions about a list item. -For example, they can display avatars, icons, clarifying subtext, or orthogonal actions (such as adding an account). +Dialogs are implemented using a collection of related components: -Touch mechanics: - -- Choosing an option immediately commits the option and closes the menu -- Touching outside of the dialog, or pressing Back, cancels the action and closes the dialog +- Dialog: the parent component that renders the modal. +- Dialog Title: a wrapper used for the title of a Dialog. +- Dialog Actions: an optional container for a Dialog's Buttons. +- Dialog Content: an optional container for displaying the Dialog's content. +- Dialog Content Text: a wrapper for text inside of `<DialogContent />`. +- Slide: optional [Transition](https://mui.com/material-ui/transitions/#slide) used to slide the Dialog in from the edge of the screen. {{"demo": "SimpleDialogDemo.js"}} +## Basics + +```jsx +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +``` + ## Alerts Alerts are urgent interruptions, requiring acknowledgement, that inform the user about a situation. @@ -36,7 +44,7 @@ Alerts are urgent interruptions, requiring acknowledgement, that inform the user Most alerts don't need titles. They summarize a decision in a sentence or two by either: -- Asking a question (e.g. "Delete this conversation?") +- Asking a question (for example "Delete this conversation?") - Making a statement related to the action buttons Use title bar alerts only for high-risk situations, such as the potential loss of connectivity. diff --git a/docs/data/material/components/dividers/DividerMaterialYouPlayground.js b/docs/data/material/components/dividers/DividerMaterialYouPlayground.js deleted file mode 100644 index 06d2148815bb43..00000000000000 --- a/docs/data/material/components/dividers/DividerMaterialYouPlayground.js +++ /dev/null @@ -1,80 +0,0 @@ -import * as React from 'react'; -import Divider from '@mui/material-next/Divider'; -import MaterialYouUsageDemo from 'docs/src/modules/components/MaterialYouUsageDemo'; -import { - Avatar, - Box, - List, - ListItem, - ListItemAvatar, - ListItemText, -} from '@mui/material'; -import ImageIcon from '@mui/icons-material/Image'; -import WorkIcon from '@mui/icons-material/Work'; - -const listStyle = { - p: 0, - width: 300, - borderRadius: 2, - border: '1px solid', - borderColor: 'divider', - backgroundColor: 'background.paper', -}; - -export default function DividerMaterialYouPlayground() { - return ( - <MaterialYouUsageDemo - componentName="Divider" - data={[ - { - propName: 'variant', - knob: 'select', - options: ['fullWidth', 'inset', 'middle'], - defaultValue: 'fullWidth', - }, - { - propName: 'orientation', - knob: 'select', - options: ['horizontal', 'vertical'], - defaultValue: 'horizontal', - }, - ]} - renderDemo={(props) => { - const content = ( - <p> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus id - dignissim justo. Nulla ut facilisis ligula. - </p> - ); - - return props.orientation === 'horizontal' ? ( - <List sx={listStyle}> - <ListItem> - <ListItemAvatar> - <Avatar> - <ImageIcon /> - </Avatar> - </ListItemAvatar> - <ListItemText primary="Photos" secondary="Jan 9, 2014" /> - </ListItem> - <Divider {...props} /> - <ListItem> - <ListItemAvatar> - <Avatar> - <WorkIcon /> - </Avatar> - </ListItemAvatar> - <ListItemText primary="Work" secondary="Jan 7, 2014" /> - </ListItem> - </List> - ) : ( - <Box sx={{ display: 'flex', gap: 4 }}> - {content} - <Divider flexItem {...props} /> - {content} - </Box> - ); - }} - /> - ); -} diff --git a/docs/data/material/components/dividers/dividers.md b/docs/data/material/components/dividers/dividers.md index ec36d7a1c12296..f04e1470606b84 100644 --- a/docs/data/material/components/dividers/dividers.md +++ b/docs/data/material/components/dividers/dividers.md @@ -90,18 +90,3 @@ The Divider component is composed of a root `<hr>`. <!-- Divider children goes here --> </hr> ``` - -## Experimental APIs - -### Material Design 3 - -The default Material UI Divider component follows the Material Design 2 specs. -To use the [M3](https://m3.material.io/) version, install the experimental `@mui/material-next` package. - -```js -import Divider from '@mui/material-next/Divider'; -``` - -{{"demo": "DividerMaterialYouPlayground.js", "hideToolbar": true, "bg": "playground"}} - -To learn more about Material UI's M3 implementation, visit the [M3 Components documentation](/material-ui/guides/material-3-components/). diff --git a/docs/data/material/components/lists/lists.md b/docs/data/material/components/lists/lists.md index 8267257ea2e234..9a9e213a003db5 100644 --- a/docs/data/material/components/lists/lists.md +++ b/docs/data/material/components/lists/lists.md @@ -14,7 +14,20 @@ Lists are a continuous group of text or images. They are composed of items conta {{"component": "modules/components/ComponentLinkHeader.js"}} -## Basic List +## Introduction + +Lists present information in a concise, easy-to-follow format through a continuous, vertical index of text or images. + +Material UI Lists are implemented using a collection of related components: + +- List: a wrapper for list items. Renders as a `<ul>` by default. +- List Item: a common list item. Renders as an `<li>` by default. +- List Item Button: an action element to be used inside a list item. +- List Item Icon: an icon to be used inside of a list item. +- List Item Avatar: an avatar to be used inside of a list item. +- List Item Text: a container inside a list item, used to display text content. +- List Divider: a separator between list items. +- List Subheader: a label for a nested list. {{"demo": "BasicList.js", "bg": true}} @@ -28,6 +41,13 @@ The last item of the previous demo shows how you can render a link: You can find a [demo with React Router following this section](/material-ui/integrations/routing/#list) of the documentation. +## Basics + +```jsx +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +``` + ## Nested List {{"demo": "NestedList.js", "bg": true}} diff --git a/docs/data/material/components/modal/modal.md b/docs/data/material/components/modal/modal.md index ad799ada426ae2..d50cc10cb69af4 100644 --- a/docs/data/material/components/modal/modal.md +++ b/docs/data/material/components/modal/modal.md @@ -97,7 +97,7 @@ In order to display the modal, you need to disable the portal feature with the ` The modal moves the focus back to the body of the component if the focus tries to escape it. This is done for accessibility purposes. However, it might create issues. -In the event the users need to interact with another part of the page, e.g. with a chatbot window, you can disable the behavior: +In the event the users need to interact with another part of the page, for example with a chatbot window, you can disable the behavior: ```jsx <Modal disableEnforceFocus /> diff --git a/docs/data/material/components/progress/CustomizedProgressBars.js b/docs/data/material/components/progress/CustomizedProgressBars.js index 0eb012a3ce01c7..df1c522d6bc9b6 100644 --- a/docs/data/material/components/progress/CustomizedProgressBars.js +++ b/docs/data/material/components/progress/CustomizedProgressBars.js @@ -1,6 +1,7 @@ import * as React from 'react'; import { styled } from '@mui/material/styles'; import Box from '@mui/material/Box'; +import Stack from '@mui/material/Stack'; import CircularProgress, { circularProgressClasses, } from '@mui/material/CircularProgress'; @@ -53,12 +54,31 @@ function FacebookCircularProgress(props) { ); } +// From https://github.com/mui/material-ui/issues/9496#issuecomment-959408221 + +function GradientCircularProgress() { + return ( + <React.Fragment> + <svg width={0} height={0}> + <defs> + <linearGradient id="my_gradient" x1="0%" y1="0%" x2="0%" y2="100%"> + <stop offset="0%" stopColor="#e01cd5" /> + <stop offset="100%" stopColor="#1CB5E0" /> + </linearGradient> + </defs> + </svg> + <CircularProgress sx={{ 'svg circle': { stroke: 'url(#my_gradient)' } }} /> + </React.Fragment> + ); +} + export default function CustomizedProgressBars() { return ( - <Box sx={{ flexGrow: 1 }}> + <Stack spacing={2} sx={{ flexGrow: 1 }}> <FacebookCircularProgress /> + <GradientCircularProgress /> <br /> <BorderLinearProgress variant="determinate" value={50} /> - </Box> + </Stack> ); } diff --git a/docs/data/material/components/progress/CustomizedProgressBars.tsx b/docs/data/material/components/progress/CustomizedProgressBars.tsx index a3951b14b7bc23..7a6d658104e463 100644 --- a/docs/data/material/components/progress/CustomizedProgressBars.tsx +++ b/docs/data/material/components/progress/CustomizedProgressBars.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { styled } from '@mui/material/styles'; import Box from '@mui/material/Box'; +import Stack from '@mui/material/Stack'; import CircularProgress, { circularProgressClasses, CircularProgressProps, @@ -54,12 +55,30 @@ function FacebookCircularProgress(props: CircularProgressProps) { ); } +// From https://github.com/mui/material-ui/issues/9496#issuecomment-959408221 +function GradientCircularProgress() { + return ( + <React.Fragment> + <svg width={0} height={0}> + <defs> + <linearGradient id="my_gradient" x1="0%" y1="0%" x2="0%" y2="100%"> + <stop offset="0%" stopColor="#e01cd5" /> + <stop offset="100%" stopColor="#1CB5E0" /> + </linearGradient> + </defs> + </svg> + <CircularProgress sx={{ 'svg circle': { stroke: 'url(#my_gradient)' } }} /> + </React.Fragment> + ); +} + export default function CustomizedProgressBars() { return ( - <Box sx={{ flexGrow: 1 }}> + <Stack spacing={2} sx={{ flexGrow: 1 }}> <FacebookCircularProgress /> + <GradientCircularProgress /> <br /> <BorderLinearProgress variant="determinate" value={50} /> - </Box> + </Stack> ); } diff --git a/docs/data/material/components/progress/CustomizedProgressBars.tsx.preview b/docs/data/material/components/progress/CustomizedProgressBars.tsx.preview index fc729dfa08cbc3..af47cf1601ebf2 100644 --- a/docs/data/material/components/progress/CustomizedProgressBars.tsx.preview +++ b/docs/data/material/components/progress/CustomizedProgressBars.tsx.preview @@ -1,3 +1,4 @@ <FacebookCircularProgress /> +<GradientCircularProgress /> <br /> <BorderLinearProgress variant="determinate" value={50} /> \ No newline at end of file diff --git a/docs/data/material/components/progress/ProgressMaterialYouPlayground.js b/docs/data/material/components/progress/ProgressMaterialYouPlayground.js deleted file mode 100644 index d29e2e3ee0f6b3..00000000000000 --- a/docs/data/material/components/progress/ProgressMaterialYouPlayground.js +++ /dev/null @@ -1,74 +0,0 @@ -import * as React from 'react'; -import CircularProgress from '@mui/material-next/CircularProgress'; -import LinearProgress from '@mui/material-next/LinearProgress'; -import Box from '@mui/material/Box'; -import MaterialYouUsageDemo from 'docs/src/modules/components/MaterialYouUsageDemo'; - -export default function ProgressMaterialYouPlayground() { - const [value, setValue] = React.useState(0); - const [valueBuffer, setValueBuffer] = React.useState(0); - const [type, setType] = React.useState('CircularProgress'); - React.useEffect(() => { - const interval = setInterval(() => { - setValue((val) => (val >= 100 ? 0 : val + 10)); - setValueBuffer(Math.floor(Math.random() * 10)); - }, 1000); - return () => { - clearInterval(interval); - }; - }, []); - return ( - <MaterialYouUsageDemo - componentName={type} - data={[ - { - propName: 'type', - knob: 'select', - options: ['CircularProgress', 'LinearProgress'], - defaultValue: 'CircularProgress', - codeBlockDisplay: false, - onChange: (e) => setType(e.target.value), - }, - { - propName: 'variant', - knob: 'select', - options: [ - 'indeterminate', - 'determinate', - ...(type === 'LinearProgress' ? ['buffer', 'query'] : []), - ], - defaultValue: 'indeterminate', - }, - { - propName: 'color', - knob: 'select', - options: ['primary', 'secondary', 'tertiary'], - defaultValue: 'primary', - }, - { propName: 'fourColor', knob: 'switch', defaultValue: false }, - ]} - renderDemo={({ type: progressType, ...props }) => ( - <Box - sx={{ - width: 300, - display: 'flex', - flexDirection: 'column', - gap: 1, - alignItems: 'center', - }} - > - {type === 'CircularProgress' ? ( - <CircularProgress {...props} value={value} /> - ) : ( - <LinearProgress - {...props} - value={value} - valueBuffer={valueBuffer + value} - sx={{ width: 300 }} - /> - )} - </Box> - )} - /> - ); -} diff --git a/docs/data/material/components/progress/progress.md b/docs/data/material/components/progress/progress.md index ce23277fddb346..45deb2642dd285 100644 --- a/docs/data/material/components/progress/progress.md +++ b/docs/data/material/components/progress/progress.md @@ -150,19 +150,3 @@ You can solve the latter with: } } ``` - -## Experimental APIs - -### Material Design 3 - -The default Material UI Progress components follow the Material Design 2 specs. -To use the [M3](https://m3.material.io/) version, install the experimental `@mui/material-next` package. - -```js -import CircularProgress from '@mui/material-next/CircularProgress'; -import LinearProgress from '@mui/material-next/LinearProgress'; -``` - -{{"demo": "ProgressMaterialYouPlayground.js", "hideToolbar": true, "bg": "playground"}} - -To learn more about Material UI's M3 implementation, visit the [M3 Components documentation](/material-ui/guides/material-3-components/). diff --git a/docs/data/material/components/radio-buttons/radio-buttons.md b/docs/data/material/components/radio-buttons/radio-buttons.md index 8a39c6ed2acc9a..b73b440e9f5b36 100644 --- a/docs/data/material/components/radio-buttons/radio-buttons.md +++ b/docs/data/material/components/radio-buttons/radio-buttons.md @@ -106,7 +106,7 @@ import { useRadioGroup } from '@mui/material/RadioGroup'; - All form controls should have labels, and this includes radio buttons, checkboxes, and switches. In most cases, this is done by using the `<label>` element ([FormControlLabel](/material-ui/api/form-control-label/)). - When a label can't be used, it's necessary to add an attribute directly to the input component. - In this case, you can apply the additional attribute (e.g. `aria-label`, `aria-labelledby`, `title`) via the `inputProps` property. + In this case, you can apply the additional attribute (for example `aria-label`, `aria-labelledby`, `title`) via the `inputProps` property. ```jsx <Radio diff --git a/docs/data/material/components/slider/SliderMaterialYouPlayground.js b/docs/data/material/components/slider/SliderMaterialYouPlayground.js deleted file mode 100644 index 7755d9f82a193d..00000000000000 --- a/docs/data/material/components/slider/SliderMaterialYouPlayground.js +++ /dev/null @@ -1,49 +0,0 @@ -import * as React from 'react'; -import Slider from '@mui/material-next/Slider'; -import Box from '@mui/material/Box'; -import MaterialYouUsageDemo from 'docs/src/modules/components/MaterialYouUsageDemo'; - -export default function SliderMaterialYouPlayground() { - return ( - <MaterialYouUsageDemo - componentName="Slider" - data={[ - { - propName: 'min', - defaultValue: 0, - }, - { - propName: 'max', - defaultValue: 10, - }, - { - propName: 'valueLabelDisplay', - knob: 'select', - options: ['auto', 'on', 'off'], - defaultValue: 'off', - }, - { - propName: 'size', - knob: 'select', - options: ['small', 'medium'], - defaultValue: 'medium', - }, - { - propName: 'marks', - knob: 'switch', - defaultValue: false, - }, - { - propName: 'disabled', - knob: 'switch', - defaultValue: false, - }, - ]} - renderDemo={(props) => ( - <Box sx={{ width: 300 }}> - <Slider {...props}>Hello world</Slider> - </Box> - )} - /> - ); -} diff --git a/docs/data/material/components/slider/slider.md b/docs/data/material/components/slider/slider.md index e2ef612eeb4765..2420303b5989cb 100644 --- a/docs/data/material/components/slider/slider.md +++ b/docs/data/material/components/slider/slider.md @@ -99,7 +99,7 @@ You can learn more about this in the [overrides documentation page](/material-ui {{"demo": "VerticalSlider.js"}} -**WARNING**: Chrome, Safari and newer Edge versions i.e. any browser based on WebKit exposes `<Slider orientation="vertical" />` as horizontal ([chromium issue #1158217](https://bugs.chromium.org/p/chromium/issues/detail?id=1158217)). +**WARNING**: Chrome, Safari and newer Edge versions that is any browser based on WebKit exposes `<Slider orientation="vertical" />` as horizontal ([chromium issue #1158217](https://bugs.chromium.org/p/chromium/issues/detail?id=1158217)). By applying `-webkit-appearance: slider-vertical;` the slider is exposed as vertical. However, by applying `-webkit-appearance: slider-vertical;` keyboard navigation for horizontal keys (<kbd class="key">Arrow Left</kbd>, <kbd class="key">Arrow Right</kbd>) is reversed ([chromium issue #1162640](https://bugs.chromium.org/p/chromium/issues/detail?id=1162640)). @@ -165,18 +165,3 @@ You can solve the issue with: left: calc(-50% - 4px); } ``` - -## Experimental APIs - -### Material Design 3 - -The default Material UI Slider component follows the Material Design 2 specs. -To use the [M3](https://m3.material.io/) version, install the experimental `@mui/material-next` package. - -```js -import Slider from '@mui/material-next/Slider'; -``` - -{{"demo": "SliderMaterialYouPlayground.js", "hideToolbar": true, "bg": "playground"}} - -To learn more about Material UI's M3 implementation, visit the [M3 Components documentation](/material-ui/guides/material-3-components/). diff --git a/docs/data/material/components/switches/switches.md b/docs/data/material/components/switches/switches.md index 9562bbdf81dd38..81ba2f452d1077 100644 --- a/docs/data/material/components/switches/switches.md +++ b/docs/data/material/components/switches/switches.md @@ -77,7 +77,7 @@ You can change the placement of the label: `<Switch inputProps={{ role: 'switch' }}>` - All form controls should have labels, and this includes radio buttons, checkboxes, and switches. In most cases, this is done by using the `<label>` element ([FormControlLabel](/material-ui/api/form-control-label/)). - When a label can't be used, it's necessary to add an attribute directly to the input component. - In this case, you can apply the additional attribute (e.g. `aria-label`, `aria-labelledby`, `title`) via the `inputProps` prop. + In this case, you can apply the additional attribute (for example `aria-label`, `aria-labelledby`, `title`) via the `inputProps` prop. ```jsx <Switch value="checkedA" inputProps={{ 'aria-label': 'Switch A' }} /> diff --git a/docs/data/material/components/table/DataTable.js b/docs/data/material/components/table/DataTable.js index 140a43ac724245..19223749a35a33 100644 --- a/docs/data/material/components/table/DataTable.js +++ b/docs/data/material/components/table/DataTable.js @@ -17,8 +17,7 @@ const columns = [ description: 'This column has a value getter and is not sortable.', sortable: false, width: 160, - valueGetter: (params) => - `${params.row.firstName || ''} ${params.row.lastName || ''}`, + valueGetter: (value, row) => `${row.firstName || ''} ${row.lastName || ''}`, }, ]; diff --git a/docs/data/material/components/table/DataTable.tsx b/docs/data/material/components/table/DataTable.tsx index ded4d20968901b..386a2adc0afdd6 100644 --- a/docs/data/material/components/table/DataTable.tsx +++ b/docs/data/material/components/table/DataTable.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { DataGrid, GridColDef, GridValueGetterParams } from '@mui/x-data-grid'; +import { DataGrid, GridColDef } from '@mui/x-data-grid'; const columns: GridColDef[] = [ { field: 'id', headerName: 'ID', width: 70 }, @@ -17,8 +17,7 @@ const columns: GridColDef[] = [ description: 'This column has a value getter and is not sortable.', sortable: false, width: 160, - valueGetter: (params: GridValueGetterParams) => - `${params.row.firstName || ''} ${params.row.lastName || ''}`, + valueGetter: (value, row) => `${row.firstName || ''} ${row.lastName || ''}`, }, ]; diff --git a/docs/data/material/components/table/table.md b/docs/data/material/components/table/table.md index a597243c90fa8c..51a333e5c0d763 100644 --- a/docs/data/material/components/table/table.md +++ b/docs/data/material/components/table/table.md @@ -33,6 +33,11 @@ This constraint makes building rich data tables challenging. The [`DataGrid` component](/x/react-data-grid/) is designed for use-cases that are focused on handling large amounts of tabular data. While it comes with a more rigid structure, in exchange, you gain more powerful features. +:::info +The demo below uses the MUI X Data Grid v7, which is currently in beta. +Visit [the documentation](https://next.mui.com/x/react-data-grid/) to learn more about it. +::: + {{"demo": "DataTable.js", "bg": "inline"}} ## Dense table @@ -83,7 +88,7 @@ The `ActionsComponent` prop of the `TablePagination` component allows the implem Here is an example of a table with scrollable rows and fixed column headers. It leverages the `stickyHeader` prop. -(⚠️ no IE 11 support) +(⚠️ no IE 11 support) {{"demo": "StickyHeadTable.js", "bg": true}} diff --git a/docs/data/material/components/tabs/tabs.md b/docs/data/material/components/tabs/tabs.md index 0887d549e1edb0..f75936d72dee57 100644 --- a/docs/data/material/components/tabs/tabs.md +++ b/docs/data/material/components/tabs/tabs.md @@ -104,7 +104,7 @@ If you want to make sure the buttons are always visible, you should customize th ### Prevent scroll buttons Left and right scroll buttons are never be presented with `scrollButtons={false}`. -All scrolling must be initiated through user agent scrolling mechanisms (e.g. left/right swipe, shift mouse wheel, etc.) +All scrolling must be initiated through user agent scrolling mechanisms (for example left/right swipe, shift mouse wheel, etc.) {{"demo": "ScrollableTabsButtonPrevent.js", "bg": true}} @@ -173,7 +173,7 @@ The WAI-ARIA authoring practices have a detailed guide on [how to decide when to #### Demo The following two demos only differ in their keyboard navigation behavior. -Focus a tab and navigate with arrow keys to notice the difference, e.g. <kbd class="key">Arrow Left</kbd>. +Focus a tab and navigate with arrow keys to notice the difference, for example <kbd class="key">Arrow Left</kbd>. ```jsx /* Tabs where selection follows focus */ diff --git a/docs/data/material/components/text-fields/text-fields.md b/docs/data/material/components/text-fields/text-fields.md index f1ccc478de10dc..f9a954f8b6b225 100644 --- a/docs/data/material/components/text-fields/text-fields.md +++ b/docs/data/material/components/text-fields/text-fields.md @@ -30,7 +30,7 @@ but Material UI will continue to support it. ## Form props -Standard form attributes are supported e.g. `required`, `disabled`, `type`, etc. as well as a `helperText` which is used to give context about a field's input, such as how the input will be used. +Standard form attributes are supported, for example `required`, `disabled`, `type`, etc. as well as a `helperText` which is used to give context about a field's input, such as how the input will be used. {{"demo": "FormPropsTextFields.js"}} @@ -286,7 +286,7 @@ This can be fixed by passing a space character to the `helperText` prop: You can use third-party libraries to format an input. You have to provide a custom implementation of the `<input>` element with the `inputComponent` property. -The following demo uses the [react-imask](https://github.com/uNmAnNeR/imaskjs) and [react-number-format](https://github.com/s-yadav/react-number-format) libraries. The same concept could be applied to [e.g. react-stripe-element](https://github.com/mui/material-ui/issues/16037). +The following demo uses the [react-imask](https://github.com/uNmAnNeR/imaskjs) and [react-number-format](https://github.com/s-yadav/react-number-format) libraries. The same concept could be applied to, for example [react-stripe-element](https://github.com/mui/material-ui/issues/16037). {{"demo": "FormattedInputs.js"}} @@ -338,7 +338,7 @@ In order for the text field to be accessible, **the input should be linked to th </div> ``` -- If you are using the `TextField` component, you just have to provide a unique `id` unless you're using the `TextField` only client side. +- If you are using the `TextField` component, you just have to provide a unique `id` unless you're using the `TextField` only client-side. Until the UI is hydrated `TextField` without an explicit `id` will not have associated labels. - If you are composing the component: diff --git a/docs/data/material/components/use-media-query/use-media-query.md b/docs/data/material/components/use-media-query/use-media-query.md index 67bd79f443e2d9..caeb6337bfbec5 100644 --- a/docs/data/material/components/use-media-query/use-media-query.md +++ b/docs/data/material/components/use-media-query/use-media-query.md @@ -19,11 +19,11 @@ Some of the key features: ## Basic media query You should provide a media query to the first argument of the hook. -The media query string can be any valid CSS media query, e.g. [`'(prefers-color-scheme: dark)'`](/material-ui/customization/dark-mode/#system-preference). +The media query string can be any valid CSS media query, for example [`'(prefers-color-scheme: dark)'`](/material-ui/customization/dark-mode/#system-preference). {{"demo": "SimpleMediaQuery.js", "defaultCodeOpen": true}} -⚠️ You can't use `'print'` per browsers limitation, e.g. [Firefox](https://bugzilla.mozilla.org/show_bug.cgi?id=774398). +⚠️ You can't use `'print'` per browsers limitation, for example [Firefox](https://bugzilla.mozilla.org/show_bug.cgi?id=774398). ## Using Material UI's breakpoint helpers @@ -116,7 +116,7 @@ const theme = createTheme({ ``` :::info -Note that `noSsr` has no effects when using the `createRoot()` API (the client side only API introduced in React 18). +Note that `noSsr` has no effects when using the `createRoot()` API (the client-side only API introduced in React 18). ::: ## Server-side rendering diff --git a/docs/data/material/customization/color/color.md b/docs/data/material/customization/color/color.md index 43733e40fbac28..e6b17e100ed112 100644 --- a/docs/data/material/customization/color/color.md +++ b/docs/data/material/customization/color/color.md @@ -80,7 +80,7 @@ These color palettes, originally created by Material Design in 2014, are compris ### Important Terms -- **Palette**: A palette is a collection of colors, i.e. hues and their shades. Material UI provides all colors from the Material Design guidelines. +- **Palette**: A palette is a collection of colors, that is hues and their shades. Material UI provides all colors from the Material Design guidelines. [This color palette](#color-palette) has been designed with colors that work harmoniously with each other. - **Hue & Shade**: A single color within the palette is made up of a hue such as "red", and shade, such as "500". "red 50" is the lightest shade of red (_pink!_), while "red 900" is the darkest. diff --git a/docs/data/material/customization/typography/typography.md b/docs/data/material/customization/typography/typography.md index c540a3c09c33d5..65d0718aacae3b 100644 --- a/docs/data/material/customization/typography/typography.md +++ b/docs/data/material/customization/typography/typography.md @@ -31,9 +31,10 @@ const theme = createTheme({ To self-host fonts, download the font files in `ttf`, `woff`, and/or `woff2` formats and import them into your code. -⚠️ This requires that you have a plugin or loader in your build process that can handle loading `ttf`, `woff`, and -`woff2` files. Fonts will _not_ be embedded within your bundle. They will be loaded from your webserver instead of a -CDN. +:::warning +This requires that you have a plugin or loader in your build process that can handle loading `ttf`, `woff`, and +`woff2` files. Fonts will _not_ be embedded within your bundle. They will be loaded from your webserver instead of a CDN. +::: ```js import RalewayWoff2 from './fonts/Raleway-Regular.woff2'; @@ -181,7 +182,7 @@ html { } ``` -_You need to apply the above CSS on the html element of this page to see the below demo rendered correctly_ +You need to apply the above CSS on the HTML element of this page to see the below demo rendered correctly. {{"demo": "FontSizeTheme.js"}} diff --git a/docs/data/material/experimental-api/classname-generator/classname-generator.md b/docs/data/material/experimental-api/classname-generator/classname-generator.md index b51352928e8e2e..a2b5f0471883e6 100644 --- a/docs/data/material/experimental-api/classname-generator/classname-generator.md +++ b/docs/data/material/experimental-api/classname-generator/classname-generator.md @@ -10,7 +10,7 @@ This API is at an unstable stage and is subject to change in the future. ## Setup -By default, Material UI generates a global class name for each component slot. For example, `<Button>Text</Button>` generates html as: +By default, Material UI generates a global class name for each component slot. For example, `<Button>Text</Button>` generates HTML as: ```html <button diff --git a/docs/data/material/experimental-api/css-theme-variables/customization.md b/docs/data/material/experimental-api/css-theme-variables/customization.md index 910d01bd9e98d0..bc5362b5beca89 100644 --- a/docs/data/material/experimental-api/css-theme-variables/customization.md +++ b/docs/data/material/experimental-api/css-theme-variables/customization.md @@ -85,7 +85,7 @@ const theme = extendTheme({ ### Channel tokens -A channel token is a variable that consists of [color space channels](https://www.w3.org/TR/css-color-4/#color-syntax) but without the alpha component. The value of a channel token is separated by a space, e.g. `12 223 31`, which can be combined with the [color functions](https://www.w3.org/TR/css-color-4/#color-functions) to create a translucent color. +A channel token is a variable that consists of [color space channels](https://www.w3.org/TR/css-color-4/#color-syntax) but without the alpha component. The value of a channel token is separated by a space, for example `12 223 31`, which can be combined with the [color functions](https://www.w3.org/TR/css-color-4/#color-functions) to create a translucent color. The `extendTheme()` automatically generates channel tokens that are likely to be used frequently from the theme palette. Those colors are suffixed with `Channel`, for example: diff --git a/docs/data/material/experimental-api/css-theme-variables/migration.md b/docs/data/material/experimental-api/css-theme-variables/migration.md index 20e1d0cfb18670..02cf6c5a4bddc2 100644 --- a/docs/data/material/experimental-api/css-theme-variables/migration.md +++ b/docs/data/material/experimental-api/css-theme-variables/migration.md @@ -39,7 +39,7 @@ Other properties can be copied and pasted. - }, - ... - }, -- // ...other properties, e.g. breakpoints, spacing, shape, typography, components +- // ...other properties, for example breakpoints, spacing, shape, typography, components -}); -const darkTheme = createTheme({ diff --git a/docs/data/material/experimental-api/css-theme-variables/usage/usage.md b/docs/data/material/experimental-api/css-theme-variables/usage/usage.md index a9b5f33f0ef56c..08089eb2def2a3 100644 --- a/docs/data/material/experimental-api/css-theme-variables/usage/usage.md +++ b/docs/data/material/experimental-api/css-theme-variables/usage/usage.md @@ -7,7 +7,7 @@ The CSS variables API relies on a new experimental provider for the theme called `Experimental_CssVarsProvider` to inject styles into Material UI components. In addition to providing the theme in the inner React context, this new provider also generates CSS variables out of all tokens in the theme that are not functions, and makes them available in the context as well. -Once the `App` renders on the screen, you will see the CSS theme variables in the html `:root` stylesheet. +Once the `App` renders on the screen, you will see the CSS theme variables in the HTML `:root` stylesheet. The variables are flattened and prefixed with `--mui` by default: ```css @@ -101,7 +101,7 @@ The structure of this object is nearly identical to the theme structure, the onl Make sure that the components accessing `theme.vars.*` are rendered under the new provider, otherwise you will get a `TypeError`. ::: -- **Native CSS**: if you can't access the theme object, e.g. in a pure CSS file, you can use [`var()`](https://developer.mozilla.org/en-US/docs/Web/CSS/var) directly: +- **Native CSS**: if you can't access the theme object, for example in a pure CSS file, you can use [`var()`](https://developer.mozilla.org/en-US/docs/Web/CSS/var) directly: ```css /* external-scope.css */ diff --git a/docs/data/material/getting-started/overview/overview.md b/docs/data/material/getting-started/overview/overview.md index 810cb82a8e5484..d337d170b89db9 100644 --- a/docs/data/material/getting-started/overview/overview.md +++ b/docs/data/material/getting-started/overview/overview.md @@ -29,7 +29,7 @@ You can follow [this GitHub issue](https://github.com/mui/material-ui/issues/293 The [design kits](https://mui.com/design-kits/) streamline your workflow and boost consistency between designers and developers. - **Trusted by thousands of organizations:** Material UI has the largest UI community in the React ecosystem. It's almost as old as React itself—its history stretches back to 2014—and we're in this for the long haul. - You can count on the community's support for years to come (e.g. [Stack Overflow](https://insights.stackoverflow.com/trends?tags=material-ui)). + You can count on the community's support for years to come (for example [Stack Overflow](https://insights.stackoverflow.com/trends?tags=material-ui)). ### Material UI vs. Base UI diff --git a/docs/data/material/getting-started/templates/blog/README.md b/docs/data/material/getting-started/templates/blog/README.md index c582b088659a33..f2305a49255eec 100644 --- a/docs/data/material/getting-started/templates/blog/README.md +++ b/docs/data/material/getting-started/templates/blog/README.md @@ -4,7 +4,7 @@ <!-- #default-branch-switch --> -1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/master/examples). +1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/next/examples). 2. Make sure your project has the required dependencies: @mui/material, @mui/icons-material, @emotion/styled, @emotion/react, markdown-to-jsx. 3. Import and use the `Blog` component. @@ -12,4 +12,4 @@ <!-- #default-branch-switch --> -View the demo at https://mui.com/material-ui/getting-started/templates/blog/. +View the demo at https://next.mui.com/material-ui/getting-started/templates/blog/. diff --git a/docs/data/material/getting-started/templates/checkout/InfoMobile.tsx.preview b/docs/data/material/getting-started/templates/checkout/InfoMobile.tsx.preview deleted file mode 100644 index 792471f0f401c7..00000000000000 --- a/docs/data/material/getting-started/templates/checkout/InfoMobile.tsx.preview +++ /dev/null @@ -1,10 +0,0 @@ -<Button - variant="text" - endIcon={<ExpandMoreRoundedIcon />} - onClick={toggleDrawer(true)} -> - View details -</Button> -<Drawer open={open} anchor="top" onClose={toggleDrawer(false)}> - {DrawerList} -</Drawer> \ No newline at end of file diff --git a/docs/data/material/getting-started/templates/checkout/README.md b/docs/data/material/getting-started/templates/checkout/README.md index 9b6fb03b6cce10..1e23cde07e4e70 100644 --- a/docs/data/material/getting-started/templates/checkout/README.md +++ b/docs/data/material/getting-started/templates/checkout/README.md @@ -4,7 +4,7 @@ <!-- #default-branch-switch --> -1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/master/examples). +1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/next/examples). 2. Make sure your project has the required dependencies: @mui/material, @emotion/styled, @emotion/react. 3. Import and use the `Checkout` component. @@ -12,4 +12,4 @@ <!-- #default-branch-switch --> -View the demo at https://mui.com/material-ui/getting-started/templates/checkout/. +View the demo at https://next.mui.com/material-ui/getting-started/templates/checkout/. diff --git a/docs/data/material/getting-started/templates/dashboard/README.md b/docs/data/material/getting-started/templates/dashboard/README.md index 3415051ae20be5..56b0c8c1bbdbf7 100644 --- a/docs/data/material/getting-started/templates/dashboard/README.md +++ b/docs/data/material/getting-started/templates/dashboard/README.md @@ -4,7 +4,7 @@ <!-- #default-branch-switch --> -1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/master/examples). +1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/next/examples). 2. Make sure your project has the required dependencies: @mui/material, @mui/icons-material, @emotion/styled, @emotion/react, @mui/x-charts. 3. Import and use the `Dashboard` component. @@ -12,4 +12,4 @@ <!-- #default-branch-switch --> -View the demo at https://mui.com/material-ui/getting-started/templates/dashboard/. +View the demo at https://next.mui.com/material-ui/getting-started/templates/dashboard/. diff --git a/docs/data/material/getting-started/templates/landing-page/LandingPage.js b/docs/data/material/getting-started/templates/landing-page/LandingPage.js index 0ef02a93a9125d..e3d1172c1e9997 100644 --- a/docs/data/material/getting-started/templates/landing-page/LandingPage.js +++ b/docs/data/material/getting-started/templates/landing-page/LandingPage.js @@ -62,7 +62,7 @@ ToggleCustomTheme.propTypes = { }; export default function LandingPage() { - const [mode, setMode] = React.useState('dark'); + const [mode, setMode] = React.useState('light'); const [showCustomTheme, setShowCustomTheme] = React.useState(true); const LPtheme = createTheme(getLPTheme(mode)); const defaultTheme = createTheme({ palette: { mode } }); diff --git a/docs/data/material/getting-started/templates/landing-page/LandingPage.tsx b/docs/data/material/getting-started/templates/landing-page/LandingPage.tsx index aedbebb9a0992d..9ea74b32eabe95 100644 --- a/docs/data/material/getting-started/templates/landing-page/LandingPage.tsx +++ b/docs/data/material/getting-started/templates/landing-page/LandingPage.tsx @@ -62,7 +62,7 @@ function ToggleCustomTheme({ } export default function LandingPage() { - const [mode, setMode] = React.useState<PaletteMode>('dark'); + const [mode, setMode] = React.useState<PaletteMode>('light'); const [showCustomTheme, setShowCustomTheme] = React.useState(true); const LPtheme = createTheme(getLPTheme(mode)); const defaultTheme = createTheme({ palette: { mode } }); diff --git a/docs/data/material/getting-started/templates/landing-page/README.md b/docs/data/material/getting-started/templates/landing-page/README.md index 23250f5a04f07c..618f90ce5939ef 100644 --- a/docs/data/material/getting-started/templates/landing-page/README.md +++ b/docs/data/material/getting-started/templates/landing-page/README.md @@ -4,7 +4,7 @@ <!-- #default-branch-switch --> -1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/master/examples). +1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/next/examples). 2. Make sure your project has the required dependencies: @mui/material, @mui/icons-material, @emotion/styled, @emotion/react. 3. Import and use the `LandingPage` component. @@ -12,4 +12,4 @@ <!-- #default-branch-switch --> -View the demo at https://mui.com/material-ui/getting-started/templates/landing-page/. +View the demo at https://next.mui.com/material-ui/getting-started/templates/landing-page/. diff --git a/docs/data/material/getting-started/templates/landing-page/components/Hero.js b/docs/data/material/getting-started/templates/landing-page/components/Hero.js index c7da7e0072168d..6813d4c96f4376 100644 --- a/docs/data/material/getting-started/templates/landing-page/components/Hero.js +++ b/docs/data/material/getting-started/templates/landing-page/components/Hero.js @@ -33,13 +33,13 @@ export default function Hero() { > <Stack spacing={2} useFlexGap sx={{ width: { xs: '100%', sm: '70%' } }}> <Typography - component="h1" variant="h1" sx={{ display: 'flex', flexDirection: { xs: 'column', md: 'row' }, alignSelf: 'center', textAlign: 'center', + fontSize: 'clamp(3.5rem, 10vw, 4rem)', }} > Our latest  @@ -47,6 +47,7 @@ export default function Hero() { component="span" variant="h1" sx={{ + fontSize: 'clamp(3rem, 10vw, 4rem)', color: (theme) => theme.palette.mode === 'light' ? 'primary.main' : 'primary.light', }} @@ -54,10 +55,14 @@ export default function Hero() { products </Typography> </Typography> - <Typography variant="body1" textAlign="center" color="text.secondary"> + <Typography + textAlign="center" + color="text.secondary" + sx={{ alignSelf: 'center', width: { sm: '100%', md: '80%' } }} + > Explore our cutting-edge dashboard, delivering high-quality solutions - tailored to your needs. <br /> - Elevate your experience with top-tier features and services. + tailored to your needs. Elevate your experience with top-tier features + and services. </Typography> <Stack direction={{ xs: 'column', sm: 'row' }} diff --git a/docs/data/material/getting-started/templates/landing-page/components/Hero.tsx b/docs/data/material/getting-started/templates/landing-page/components/Hero.tsx index c7da7e0072168d..6813d4c96f4376 100644 --- a/docs/data/material/getting-started/templates/landing-page/components/Hero.tsx +++ b/docs/data/material/getting-started/templates/landing-page/components/Hero.tsx @@ -33,13 +33,13 @@ export default function Hero() { > <Stack spacing={2} useFlexGap sx={{ width: { xs: '100%', sm: '70%' } }}> <Typography - component="h1" variant="h1" sx={{ display: 'flex', flexDirection: { xs: 'column', md: 'row' }, alignSelf: 'center', textAlign: 'center', + fontSize: 'clamp(3.5rem, 10vw, 4rem)', }} > Our latest  @@ -47,6 +47,7 @@ export default function Hero() { component="span" variant="h1" sx={{ + fontSize: 'clamp(3rem, 10vw, 4rem)', color: (theme) => theme.palette.mode === 'light' ? 'primary.main' : 'primary.light', }} @@ -54,10 +55,14 @@ export default function Hero() { products </Typography> </Typography> - <Typography variant="body1" textAlign="center" color="text.secondary"> + <Typography + textAlign="center" + color="text.secondary" + sx={{ alignSelf: 'center', width: { sm: '100%', md: '80%' } }} + > Explore our cutting-edge dashboard, delivering high-quality solutions - tailored to your needs. <br /> - Elevate your experience with top-tier features and services. + tailored to your needs. Elevate your experience with top-tier features + and services. </Typography> <Stack direction={{ xs: 'column', sm: 'row' }} diff --git a/docs/data/material/getting-started/templates/pricing/README.md b/docs/data/material/getting-started/templates/pricing/README.md index f0c845cb6368a3..e5b41bad100a87 100644 --- a/docs/data/material/getting-started/templates/pricing/README.md +++ b/docs/data/material/getting-started/templates/pricing/README.md @@ -4,7 +4,7 @@ <!-- #default-branch-switch --> -1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/master/examples). +1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/next/examples). 2. Make sure your project has the required dependencies: @mui/material, @mui/icons-material, @emotion/styled, @emotion/react. 3. Import and use the `Pricing` component. @@ -12,4 +12,4 @@ <!-- #default-branch-switch --> -View the demo at https://mui.com/material-ui/getting-started/templates/pricing/. +View the demo at https://next.mui.com/material-ui/getting-started/templates/pricing/. diff --git a/docs/data/material/getting-started/templates/sign-in-side/README.md b/docs/data/material/getting-started/templates/sign-in-side/README.md index 67c088ce173215..a7995114143ef1 100644 --- a/docs/data/material/getting-started/templates/sign-in-side/README.md +++ b/docs/data/material/getting-started/templates/sign-in-side/README.md @@ -4,7 +4,7 @@ <!-- #default-branch-switch --> -1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/master/examples). +1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/next/examples). 2. Make sure your project has the required dependencies: @mui/material, @mui/icons-material, @emotion/styled, @emotion/react. 3. Import and use the `SignInSide` component. @@ -12,4 +12,4 @@ <!-- #default-branch-switch --> -View the demo at https://mui.com/material-ui/getting-started/templates/sign-in-side/. +View the demo at https://next.mui.com/material-ui/getting-started/templates/sign-in-side/. diff --git a/docs/data/material/getting-started/templates/sign-in/README.md b/docs/data/material/getting-started/templates/sign-in/README.md index 7ad1e840c69051..f9de61277a45c7 100644 --- a/docs/data/material/getting-started/templates/sign-in/README.md +++ b/docs/data/material/getting-started/templates/sign-in/README.md @@ -4,7 +4,7 @@ <!-- #default-branch-switch --> -1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/master/examples). +1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/next/examples). 2. Make sure your project has the required dependencies: @mui/material, @mui/icons-material, @emotion/styled, @emotion/react. 3. Import and use the `SignIn` component. @@ -12,4 +12,4 @@ <!-- #default-branch-switch --> -View the demo at https://mui.com/material-ui/getting-started/templates/sign-in/. +View the demo at https://next.mui.com/material-ui/getting-started/templates/sign-in/. diff --git a/docs/data/material/getting-started/templates/sign-up/README.md b/docs/data/material/getting-started/templates/sign-up/README.md index aa89e2950d0e36..f46cae4ad63fe5 100644 --- a/docs/data/material/getting-started/templates/sign-up/README.md +++ b/docs/data/material/getting-started/templates/sign-up/README.md @@ -4,7 +4,7 @@ <!-- #default-branch-switch --> -1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/master/examples). +1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/next/examples). 2. Make sure your project has the required dependencies: @mui/material, @mui/icons-material, @emotion/styled, @emotion/react. 3. Import and use the `SignUp` component. @@ -12,4 +12,4 @@ <!-- #default-branch-switch --> -View the demo at https://mui.com/material-ui/getting-started/templates/sign-up/. +View the demo at https://next.mui.com/material-ui/getting-started/templates/sign-up/. diff --git a/docs/data/material/getting-started/templates/sticky-footer/README.md b/docs/data/material/getting-started/templates/sticky-footer/README.md index 47ec73373dbbff..773a5d0039f2d3 100644 --- a/docs/data/material/getting-started/templates/sticky-footer/README.md +++ b/docs/data/material/getting-started/templates/sticky-footer/README.md @@ -4,7 +4,7 @@ <!-- #default-branch-switch --> -1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/master/examples). +1. Copy the files into your project, or one of the [example projects](https://github.com/mui/material-ui/tree/next/examples). 2. Make sure your project has the required dependencies: @mui/material, @emotion/styled, @emotion/react. 3. Import and use the `StickyFooter` component. @@ -12,4 +12,4 @@ <!-- #default-branch-switch --> -View the demo at https://mui.com/material-ui/getting-started/templates/sticky-footer/. +View the demo at https://next.mui.com/material-ui/getting-started/templates/sticky-footer/. diff --git a/docs/data/material/getting-started/templates/templates.md b/docs/data/material/getting-started/templates/templates.md index 8cc0416d6291b9..4d9a40ce5ef0a7 100644 --- a/docs/data/material/getting-started/templates/templates.md +++ b/docs/data/material/getting-started/templates/templates.md @@ -5,13 +5,13 @@ title: 9+ Free React Templates # React Templates -<p class="description">Browse our collection of free React templates to get started building your app with Material UI, including a React dashboard, React admin panel, and more.</p> +<p class="description">Browse our collection of free React templates to get started building your app with Material UI, including a React dashboard, React landing page, and more.</p> <!-- #default-branch-switch --> ## Free templates -Our curated collection of free Material UI templates includes dashboards, sign-in and sign-up pages, a blog, a checkout flow, and more. +Our curated collection of free Material UI templates includes dashboards, landing pages, sign-in and sign-up pages, a blog, a checkout flow, and more. They can be combined with one of the [example projects](/material-ui/getting-started/example-projects/) to form a complete starter. Sections of each layout are clearly defined either by comments or use of separate files, diff --git a/docs/data/material/guides/api/api.md b/docs/data/material/guides/api/api.md index 7350aa14bc73bc..476d40b1279438 100644 --- a/docs/data/material/guides/api/api.md +++ b/docs/data/material/guides/api/api.md @@ -48,10 +48,10 @@ to make the classes structure as simple as possible, while sufficient to impleme - The class applied to the root element is always called `root`. - All the default styles are grouped in a single class. -- The classes applied to non-root elements are prefixed with the name of the element, e.g. `paperWidthXs` in the Dialog component. -- The variants applied by a boolean prop **aren't** prefixed, e.g. the `rounded` class +- The classes applied to non-root elements are prefixed with the name of the element, for example `paperWidthXs` in the Dialog component. +- The variants applied by a boolean prop **aren't** prefixed, for example the `rounded` class applied by the `rounded` prop. -- The variants applied by an enum prop **are** prefixed, e.g. the `colorPrimary` class +- The variants applied by an enum prop **are** prefixed, for example the `colorPrimary` class applied by the `color="primary"` prop. - A variant has **one level of specificity**. The `color` and `variant` props are considered a variant. @@ -168,8 +168,8 @@ to that component instead. ## Glossary -- **host component**: a DOM node type in the context of `react-dom`, e.g. a `'div'`. See also [React Implementation Notes](https://legacy.reactjs.org/docs/implementation-notes.html#mounting-host-elements). -- **host element**: a DOM node in the context of `react-dom`, e.g. an instance of `window.HTMLDivElement`. -- **outermost**: The first component when reading the component tree from top to bottom i.e. breadth-first search. +- **host component**: a DOM node type in the context of `react-dom`, for example a `'div'`. See also [React Implementation Notes](https://legacy.reactjs.org/docs/implementation-notes.html#mounting-host-elements). +- **host element**: a DOM node in the context of `react-dom`, for example an instance of `window.HTMLDivElement`. +- **outermost**: The first component when reading the component tree from top to bottom, that is breadth-first search. - **root component**: the outermost component that renders a host component. - **root element**: the outermost element that renders a host component. diff --git a/docs/data/material/guides/composition/composition.md b/docs/data/material/guides/composition/composition.md index 12b9072d7956ee..9a70b80956a4b5 100644 --- a/docs/data/material/guides/composition/composition.md +++ b/docs/data/material/guides/composition/composition.md @@ -77,7 +77,7 @@ function ListItemLink(props) { ``` :::warning -However, since we are using an inline function to change the rendered component, React will remount the link every time `ListItemLink` is rendered. Not only will React update the DOM unnecessarily but the state will be lost, e.g. the ripple effect of the `ListItem` will also not work correctly. +However, since we are using an inline function to change the rendered component, React will remount the link every time `ListItemLink` is rendered. Not only will React update the DOM unnecessarily but the state will be lost, for example the ripple effect of the `ListItem` will also not work correctly. ::: The solution is simple: **avoid inline functions and pass a static component to the `component` prop** instead. @@ -123,7 +123,7 @@ import { Link } from 'react-router-dom'; :::warning However, this strategy suffers from a limitation: prop name collisions. -The component receiving the `component` prop (e.g. ListItem) might intercept the prop (e.g. to) that is destined to the leave element (e.g. Link). +The component receiving the `component` prop (for example ListItem) might intercept the prop (for example to) that is destined to the leave element (for example Link). ::: ### With TypeScript @@ -186,8 +186,8 @@ by using `ReactDOM.findDOMNode`. This function is deprecated in favor of `ref` a ref forwarding. However, only the following component types can be given a `ref`: - Any Material UI component -- class components i.e. `React.Component` or `React.PureComponent` -- DOM (or host) components e.g. `div` or `button` +- class components, that is `React.Component` or `React.PureComponent` +- DOM (or host) components, for example `div` or `button` - [React.forwardRef components](https://react.dev/reference/react/forwardRef) - [React.lazy components](https://react.dev/reference/react/lazy) - [React.memo components](https://react.dev/reference/react/memo) diff --git a/docs/data/material/guides/localization/localization.md b/docs/data/material/guides/localization/localization.md index c6268abaf45fb8..9f047bfea14351 100644 --- a/docs/data/material/guides/localization/localization.md +++ b/docs/data/material/guides/localization/localization.md @@ -97,7 +97,7 @@ The [Data Grid and Data Grid Pro](/x/react-data-grid/) components have their own <!-- #default-branch-switch --> -You can [find the source](https://github.com/mui/material-ui/blob/master/packages/mui-material/src/locale/index.ts) in the GitHub repository. +You can [find the source](https://github.com/mui/material-ui/blob/next/packages/mui-material/src/locale/index.ts) in the GitHub repository. To create your own translation, or to customize the English text, copy this file to your project, make any changes needed and import the locale from there. diff --git a/docs/data/material/guides/material-3-components/MD3AndV5Usage.js b/docs/data/material/guides/material-3-components/MD3AndV5Usage.js deleted file mode 100644 index ad1859b3fe3126..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3AndV5Usage.js +++ /dev/null @@ -1,22 +0,0 @@ -import * as React from 'react'; -import MD2Button from '@mui/material/Button'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; -import MD3Button from '@mui/material-next/Button'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import { Stack } from '@mui/system'; - -const md2Theme = createTheme(); -const md3Theme = extendTheme(); - -export default function MD3AndV5Usage() { - return ( - <ThemeProvider theme={md2Theme}> - <Stack spacing={2} direction="row"> - <MD2Button variant="contained">MD2 Button</MD2Button> - <CssVarsProvider theme={md3Theme}> - <MD3Button variant="filled">MD3 Button</MD3Button> - </CssVarsProvider> - </Stack> - </ThemeProvider> - ); -} diff --git a/docs/data/material/guides/material-3-components/MD3AndV5Usage.tsx b/docs/data/material/guides/material-3-components/MD3AndV5Usage.tsx deleted file mode 100644 index ad1859b3fe3126..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3AndV5Usage.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import * as React from 'react'; -import MD2Button from '@mui/material/Button'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; -import MD3Button from '@mui/material-next/Button'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import { Stack } from '@mui/system'; - -const md2Theme = createTheme(); -const md3Theme = extendTheme(); - -export default function MD3AndV5Usage() { - return ( - <ThemeProvider theme={md2Theme}> - <Stack spacing={2} direction="row"> - <MD2Button variant="contained">MD2 Button</MD2Button> - <CssVarsProvider theme={md3Theme}> - <MD3Button variant="filled">MD3 Button</MD3Button> - </CssVarsProvider> - </Stack> - </ThemeProvider> - ); -} diff --git a/docs/data/material/guides/material-3-components/MD3AndV5Usage.tsx.preview b/docs/data/material/guides/material-3-components/MD3AndV5Usage.tsx.preview deleted file mode 100644 index 0d31189e938a00..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3AndV5Usage.tsx.preview +++ /dev/null @@ -1,8 +0,0 @@ -<ThemeProvider theme={md2Theme}> - <Stack spacing={2} direction="row"> - <MD2Button variant="contained">MD2 Button</MD2Button> - <CssVarsProvider theme={md3Theme}> - <MD3Button variant="filled">MD3 Button</MD3Button> - </CssVarsProvider> - </Stack> -</ThemeProvider> \ No newline at end of file diff --git a/docs/data/material/guides/material-3-components/MD3ButtonUsage.js b/docs/data/material/guides/material-3-components/MD3ButtonUsage.js deleted file mode 100644 index f4154f9ace96f7..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3ButtonUsage.js +++ /dev/null @@ -1,11 +0,0 @@ -import * as React from 'react'; -import Button from '@mui/material-next/Button'; -import { CssVarsProvider } from '@mui/material-next/styles'; - -export default function MD3ButtonUsage() { - return ( - <CssVarsProvider> - <Button variant="filled">Button</Button> - </CssVarsProvider> - ); -} diff --git a/docs/data/material/guides/material-3-components/MD3ButtonUsage.tsx b/docs/data/material/guides/material-3-components/MD3ButtonUsage.tsx deleted file mode 100644 index f4154f9ace96f7..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3ButtonUsage.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import * as React from 'react'; -import Button from '@mui/material-next/Button'; -import { CssVarsProvider } from '@mui/material-next/styles'; - -export default function MD3ButtonUsage() { - return ( - <CssVarsProvider> - <Button variant="filled">Button</Button> - </CssVarsProvider> - ); -} diff --git a/docs/data/material/guides/material-3-components/MD3ButtonUsage.tsx.preview b/docs/data/material/guides/material-3-components/MD3ButtonUsage.tsx.preview deleted file mode 100644 index 0b4230ecf84025..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3ButtonUsage.tsx.preview +++ /dev/null @@ -1,3 +0,0 @@ -<CssVarsProvider> - <Button variant="filled">Button</Button> -</CssVarsProvider> \ No newline at end of file diff --git a/docs/data/material/guides/material-3-components/MD3Theming.js b/docs/data/material/guides/material-3-components/MD3Theming.js deleted file mode 100644 index 1b228594ee281f..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3Theming.js +++ /dev/null @@ -1,38 +0,0 @@ -import * as React from 'react'; -import Button from '@mui/material-next/Button'; -import { extendTheme, CssVarsProvider } from '@mui/material-next/styles'; - -const customTheme = extendTheme({ - ref: { - palette: { - primary: { - 0: '#000000', - 10: '#002020', - 20: '#003738', - 30: '#004F51', - 40: '#00696B', - 50: '#008587', - 60: '#00A1A3', - 70: '#00BEC1', - 80: '#2DDBDE', - 90: '#8FF3F4', - 95: '#B2FEFF', - 99: '#F1FFFF', - 100: '#FFFFFF', - }, - }, - }, - // cssVarPrefix is only required if multiple themes coexist - // on the same page like on this guide - cssVarPrefix: 'turquoise-md3', -}); - -export default function MD3Theming() { - return ( - <CssVarsProvider theme={customTheme}> - <Button color="primary" variant="filled"> - Button - </Button> - </CssVarsProvider> - ); -} diff --git a/docs/data/material/guides/material-3-components/MD3Theming.tsx b/docs/data/material/guides/material-3-components/MD3Theming.tsx deleted file mode 100644 index 1b228594ee281f..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3Theming.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import * as React from 'react'; -import Button from '@mui/material-next/Button'; -import { extendTheme, CssVarsProvider } from '@mui/material-next/styles'; - -const customTheme = extendTheme({ - ref: { - palette: { - primary: { - 0: '#000000', - 10: '#002020', - 20: '#003738', - 30: '#004F51', - 40: '#00696B', - 50: '#008587', - 60: '#00A1A3', - 70: '#00BEC1', - 80: '#2DDBDE', - 90: '#8FF3F4', - 95: '#B2FEFF', - 99: '#F1FFFF', - 100: '#FFFFFF', - }, - }, - }, - // cssVarPrefix is only required if multiple themes coexist - // on the same page like on this guide - cssVarPrefix: 'turquoise-md3', -}); - -export default function MD3Theming() { - return ( - <CssVarsProvider theme={customTheme}> - <Button color="primary" variant="filled"> - Button - </Button> - </CssVarsProvider> - ); -} diff --git a/docs/data/material/guides/material-3-components/MD3Theming.tsx.preview b/docs/data/material/guides/material-3-components/MD3Theming.tsx.preview deleted file mode 100644 index 610c1ac7a22b56..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3Theming.tsx.preview +++ /dev/null @@ -1,5 +0,0 @@ -<CssVarsProvider theme={customTheme}> - <Button color="primary" variant="filled"> - Button - </Button> -</CssVarsProvider> \ No newline at end of file diff --git a/docs/data/material/guides/material-3-components/material-3-components.md b/docs/data/material/guides/material-3-components/material-3-components.md deleted file mode 100644 index 18959de10999aa..00000000000000 --- a/docs/data/material/guides/material-3-components/material-3-components.md +++ /dev/null @@ -1,133 +0,0 @@ -# Material Design 3 Components - -<p class="description">Try out Material UI's implementation of M3 and learn how to contribute to the project.</p> - -## Material UI and M3 - -Material Design 3 (M3), also referred to as [Material You](https://m3.material.io), is the latest version of Google's design system. -The primary Material UI package (`@mui/material`) currently implements Material Design 2. -M3 implementation is a work in progress, targeted for completion in late 2024. -You can try out Material UI's M3 components as they're developed using the `@mui/material-next` package. - -:::warning -The M3 components are currently in alpha and subject to change. -::: - -## Supported components - -Visit the [All Components page](/material-ui/all-components/) to see which components support M3—look for the green M3 indicator. -All components that have M3 versions have a corresponding playground on their page. -For example, here's the [M3 Button playground](/material-ui/react-button/#material-design-3). - -## Getting started with M3 components - -The M3 components are included in the `@mui/material-next` package. -The following guide explains how to get started using them. - -### Installation - -Run one of the following commands to add `@mui/material-next` to your project: - -<codeblock storageKey="package-manager"> - -```bash npm -npm install @mui/material-next @emotion/react @emotion/styled -``` - -```bash yarn -yarn add @mui/material-next @emotion/react @emotion/styled -``` - -```bash pnpm -pnpm add @mui/material-next @emotion/react @emotion/styled -``` - -</codeblock> - -#### Peer dependencies - -<!-- #react-peer-version --> - -Please note that [react](https://www.npmjs.com/package/react) and [react-dom](https://www.npmjs.com/package/react-dom) are peer dependencies, meaning you should ensure they are installed before installing the Material UI Next package. - -```json -"peerDependencies": { - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" -}, -``` - -#### Roboto font - -Material UI uses the [Roboto](https://fonts.google.com/specimen/Roboto) font by default. -Add it to your project via Fontsource, or with the Google Fonts CDN. - -<codeblock storageKey="package-manager"> - -```bash npm -npm install @fontsource/roboto -``` - -```bash yarn -yarn add @fontsource/roboto -``` - -```bash pnpm -pnpm add @fontsource/roboto -``` - -</codeblock> - -Then you can import it in your entry point like this: - -```tsx -import '@fontsource/roboto/300.css'; -import '@fontsource/roboto/400.css'; -import '@fontsource/roboto/500.css'; -import '@fontsource/roboto/700.css'; -``` - -:::info -Fontsource can be configured to load specific subsets, weights and styles. Material UI's default typography configuration relies only on the 300, 400, 500, and 700 font weights. -::: - -To install Roboto through the Google Web Fonts CDN, add the following code inside your project's <head /> tag: - -```html -<link rel="preconnect" href="https://fonts.googleapis.com" /> -<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> -<link - rel="stylesheet" - href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" -/> -``` - -### Usage - -After [installation](/material-ui/guides/material-3-components/#installation), you can import any component from `@mui/material-next` just as you would do with the stable Material UI package. - -{{"demo": "MD3ButtonUsage.js"}} - -:::warning -If your application uses the `ThemeProvider` from `@mui/material`, you must include `CssVarsProvider` from `@mui/material-next` in the tree above the M3 components. -The following example shows how to do this. -::: - -{{"demo": "MD3AndV5Usage.js", "defaultCodeOpen": false}} - -### Theming - -Use the `extendTheme` function to modify the default theme. -The theme structure follows [M3 specifications](https://m3.material.io/styles/color/system/overview). -For example, if you wanted to modify the primary color, you would provide the [color tones](https://m3.material.io/styles/color/system/how-the-system-works#e1e92a3b-8702-46b6-8132-58321aa600bd) via `ref.palette.primary`: - -{{"demo": "MD3Theming.js"}} - -:::success -You can use Material Design's [Figma Material Theme Builder](https://www.figma.com/community/plugin/1034969338659738588/material-theme-builder) plugin to generate palette tones. -::: - -## Stable release - -The stable release of the M3 components is tentatively targeted for Q4 2024 in Material UI v7. -To follow the progress or contribute to the project, check out [the M3 GitHub issue](https://github.com/mui/material-ui/issues/29345). diff --git a/docs/data/material/guides/minimizing-bundle-size/minimizing-bundle-size.md b/docs/data/material/guides/minimizing-bundle-size/minimizing-bundle-size.md index c6dba59f9842da..cb2301cd5b3387 100644 --- a/docs/data/material/guides/minimizing-bundle-size/minimizing-bundle-size.md +++ b/docs/data/material/guides/minimizing-bundle-size/minimizing-bundle-size.md @@ -19,8 +19,9 @@ If you're using ES6 modules and a bundler that supports tree-shaking ([`webpack` import { Button, TextField } from '@mui/material'; ``` -⚠️ The following instructions are only needed if you want to optimize your development startup times or if you are using an older bundler -that doesn't support tree-shaking. +:::warning +The following instructions are only needed if you want to optimize your development startup times or if you are using an older bundler that doesn't support tree-shaking. +::: ## Development environment diff --git a/docs/data/material/guides/server-rendering/server-rendering.md b/docs/data/material/guides/server-rendering/server-rendering.md index 899c9ab638cbe4..061192eab6743f 100644 --- a/docs/data/material/guides/server-rendering/server-rendering.md +++ b/docs/data/material/guides/server-rendering/server-rendering.md @@ -26,9 +26,7 @@ In the following recipe, we are going to look at how to set up server-side rende Create a theme that will be shared between the client and the server: -`theme.js` - -```js +```js title="theme.js" import { createTheme } from '@mui/material/styles'; import { red } from '@mui/material/colors'; @@ -56,9 +54,7 @@ The following is the outline for what the server-side is going to look like. We are going to set up an [Express middleware](https://expressjs.com/en/guide/using-middleware.html) using [app.use](https://expressjs.com/en/api.html) to handle all requests that come into the server. If you're unfamiliar with Express or middleware, know that the `handleRender` function will be called every time the server receives a request. -`server.js` - -```js +```js title="server.js" import express from 'express'; // We are going to fill these out in the sections to follow. @@ -92,9 +88,7 @@ Material UI uses Emotion as its default styled engine. We need to extract the styles from the Emotion instance. For this, we need to share the same cache configuration for both the client and server: -`createEmotionCache.js` - -```js +```js title="createEmotionCache.js" import createCache from '@emotion/cache'; export default function createEmotionCache() { @@ -102,7 +96,7 @@ export default function createEmotionCache() { } ``` -With this we are creating a new Emotion cache instance and using this to extract the critical styles for the html as well. +With this we are creating a new Emotion cache instance and using this to extract the critical styles for the HTML as well. We will see how this is passed along in the `renderFullPage` function. @@ -189,9 +183,7 @@ The client-side is straightforward. All we need to do is use the same cache configuration as the server-side. Let's take a look at the client file: -`client.js` - -```jsx +```jsx title="client.js" import * as React from 'react'; import * as ReactDOM from 'react-dom'; import CssBaseline from '@mui/material/CssBaseline'; diff --git a/docs/data/material/guides/typescript/typescript.md b/docs/data/material/guides/typescript/typescript.md index 32b93202cd0b2b..e781767ed8d904 100644 --- a/docs/data/material/guides/typescript/typescript.md +++ b/docs/data/material/guides/typescript/typescript.md @@ -6,7 +6,7 @@ <!-- #default-branch-switch --> -Material UI requires a minimum version of TypeScript 3.5. Have a look at the [Create React App with TypeScript](https://github.com/mui/material-ui/tree/master/examples/material-ui-cra-ts) example. +Material UI requires a minimum version of TypeScript 3.5. Have a look at the [Create React App with TypeScript](https://github.com/mui/material-ui/tree/next/examples/material-ui-cra-ts) example. For types to work, it's recommended that you have at least the following options enabled in your `tsconfig.json`: @@ -32,20 +32,18 @@ Many components concerned with user input offer a `value` prop or event handlers which include the current `value`. In most situations that `value` is only handled within React which allows it be of any type, such as objects or arrays. -However, that type cannot be verified at compile time in situations where it depends -on the component's children e.g. for `Select` or `RadioGroup`. This means that -the soundest option is to type it as `unknown` and let the developer decide -how they want to narrow that type down. We do not offer the possibility to use a generic -type in those cases for [the same reasons `event.target` is not generic in React](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/11508#issuecomment-256045682). +However, that type cannot be verified at compile time in situations where it depends on the component's children, for example `Select` or `RadioGroup`. +This means that the soundest option is to type it as `unknown` and let the developer decide how they want to narrow that type down. +We do not offer the possibility to use a generic type in those cases for [the same reasons `event.target` is not generic in React](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/11508#issuecomment-256045682). -The demos include typed variants that use type casting. It is an acceptable tradeoff -because the types are all located in a single file and are very basic. You have to decide for yourself -if the same tradeoff is acceptable for you. The library types are strict -by default and loose via opt-in. +The demos include typed variants that use type casting. +It is an acceptable tradeoff because the types are all located in a single file and are very basic. +You have to decide for yourself if the same tradeoff is acceptable for you. +The library types are strict by default and loose via opt-in. ## Customization of `Theme` -Moved to [/customization/theming/#custom-variables](/material-ui/customization/theming/#custom-variables). +Moved to [the Customizing the theme page](/material-ui/customization/theming/#custom-variables). ## Complications with the `component` prop diff --git a/docs/data/material/integrations/interoperability/interoperability.md b/docs/data/material/integrations/interoperability/interoperability.md index f5928e24657497..4a4b7f0ab9e4a9 100644 --- a/docs/data/material/integrations/interoperability/interoperability.md +++ b/docs/data/material/integrations/interoperability/interoperability.md @@ -9,9 +9,9 @@ There are examples for the following styling solutions: - [Plain CSS](#plain-css) - [Global CSS](#global-css) - [Styled Components](#styled-components) -- [CSS Modules](#css-modules) +- [CSS Modules](#css-modules) - [Emotion](#emotion) -- [Tailwind CSS](#tailwind-css) +- [Tailwind CSS](#tailwind-css) - [~~JSS~~ TSS](#jss-tss) ## Plain CSS @@ -22,9 +22,7 @@ Nothing fancy, just plain CSS. [![Edit Button](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/plain-css-fdue7) -**PlainCssSlider.css** - -```css +```css title="PlainCssSlider.css" .slider { color: #20b2aa; } @@ -34,9 +32,7 @@ Nothing fancy, just plain CSS. } ``` -**PlainCssSlider.js** - -```jsx +```jsx title="PlainCssSlider.css" import * as React from 'react'; import Slider from '@mui/material/Slider'; import './PlainCssSlider.css'; @@ -101,9 +97,7 @@ The following examples override the slider's `thumb` style in addition to the cu {{"demo": "StyledComponentsDeep.js", "hideToolbar": true}} -**PlainCssSliderDeep1.css** - -```css +```css title="PlainCssSliderDeep1.css" .slider { color: #20b2aa; } @@ -117,9 +111,7 @@ The following examples override the slider's `thumb` style in addition to the cu } ``` -**PlainCssSliderDeep1.js** - -```jsx +```jsx title="PlainCssSliderDeep1.js" import * as React from 'react'; import Slider from '@mui/material/Slider'; import './PlainCssSliderDeep1.css'; @@ -136,9 +128,7 @@ export default function PlainCssSliderDeep1() { The above demo relies on the [default `className` values](/system/styles/advanced/), but you can provide your own class name with the `slotProps` API. -**PlainCssSliderDeep2.css** - -```css +```css title="PlainCssSliderDeep2.css" .slider { color: #20b2aa; } @@ -152,9 +142,7 @@ The above demo relies on the [default `className` values](/system/styles/advance } ``` -**PlainCssSliderDeep2.js** - -```jsx +```jsx title="PlainCssSliderDeep2.js" import * as React from 'react'; import Slider from '@mui/material/Slider'; import './PlainCssSliderDeep2.css'; @@ -180,9 +168,7 @@ Explicitly providing the class names to the component is too much effort? [![Edit Button](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/global-classnames-dho8k) -**GlobalCssSlider.css** - -```css +```css title="GlobalCssSlider.css" .MuiSlider-root { color: #20b2aa; } @@ -192,9 +178,7 @@ Explicitly providing the class names to the component is too much effort? } ``` -**GlobalCssSlider.js** - -```jsx +```jsx title="GlobalCssSlider.js" import * as React from 'react'; import Slider from '@mui/material/Slider'; import './GlobalCssSlider.css'; @@ -254,9 +238,7 @@ The following example overrides the slider's `thumb` style in addition to the cu {{"demo": "StyledComponentsDeep.js", "hideToolbar": true}} -**GlobalCssSliderDeep.css** - -```css +```css title="GlobalCssSliderDeep.css" .MuiSlider-root { color: #20b2aa; } @@ -270,9 +252,7 @@ The following example overrides the slider's `thumb` style in addition to the cu } ``` -**GlobalCssSliderDeep.js** - -```jsx +```jsx title="GlobalCssSliderDeep.js" import * as React from 'react'; import Slider from '@mui/material/Slider'; import './GlobalCssSliderDeep.css'; @@ -294,8 +274,8 @@ If, however, you would like to use styled-components, you can configure your app <!-- #default-branch-switch --> -- [Create React App with styled-components](https://github.com/mui/material-ui/tree/master/examples/material-ui-cra-styled-components) -- [Create React App with styled-components and TypeScript](https://github.com/mui/material-ui/tree/master/examples/material-ui-cra-styled-components-ts) +- [Create React App with styled-components](https://github.com/mui/material-ui/tree/next/examples/material-ui-cra-styled-components) +- [Create React App with styled-components and TypeScript](https://github.com/mui/material-ui/tree/next/examples/material-ui-cra-styled-components-ts) Following this approach reduces the bundle size, and removes the need to configure the CSS injection order. @@ -420,7 +400,7 @@ const StyledTooltip = styled(({ className, ...props }) => ( {{"demo": "StyledComponentsPortal.js"}} -## CSS Modules +## CSS Modules ![stars](https://img.shields.io/github/stars/css-modules/css-modules.svg?style=social&label=Star) @@ -431,9 +411,7 @@ bundling solution people are using. [![Edit Button](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/css-modules-nuyg8) -**CssModulesSlider.module.css** - -```css +```css title="CssModulesSlider.module.css" .slider { color: #20b2aa; } @@ -443,9 +421,7 @@ bundling solution people are using. } ``` -**CssModulesSlider.js** - -```jsx +```jsx title="CssModulesSlider.js" import * as React from 'react'; import Slider from '@mui/material/Slider'; // Webpack, Parcel or else will inject the CSS into the page @@ -506,15 +482,13 @@ export default function CssModulesPriority() { If you attempt to style the Slider, you will likely need to affect some of the Slider's child elements, for example the thumb. In Material UI, all child elements have an increased specificity of 2: `.parent .child {}`. When writing overrides, you need to do the same. -It's important to keep in mind that CSS Modules adds an unique id to each class, and that id won't be present on the Material UI provided children class. To escape from that, CSS Modules provides a functionality, the `:global` selector. +It's important to keep in mind that CSS Modules adds an unique id to each class, and that id won't be present on the Material UI provided children class. To escape from that, CSS Modules provides a functionality, the `:global` selector. The following examples override the slider's `thumb` style in addition to the custom styles on the slider itself. {{"demo": "StyledComponentsDeep.js", "hideToolbar": true}} -**CssModulesSliderDeep1.module.css** - -```css +```css title="CssModulesSliderDeep1.module.css" .slider { color: #20b2aa; } @@ -528,9 +502,7 @@ The following examples override the slider's `thumb` style in addition to the cu } ``` -**CssModulesSliderDeep1.js** - -```jsx +```jsx title="CssModulesSliderDeep1.js" import * as React from 'react'; // Webpack, Parcel or else will inject the CSS into the page import styles from './CssModulesSliderDeep1.module.css'; @@ -548,9 +520,7 @@ export default function CssModulesSliderDeep1() { The above demo relies on the [default `className` values](/system/styles/advanced/), but you can provide your own class name with the `slotProps` API. -**CssModulesSliderDeep2.module.css** - -```css +```css title="CssModulesSliderDeep2.module.css" .slider { color: #20b2aa; } @@ -564,9 +534,7 @@ The above demo relies on the [default `className` values](/system/styles/advance } ``` -**CssModulesSliderDeep2.js** - -```jsx +```jsx title="CssModulesSliderDeep2.js" import * as React from 'react'; // Webpack, Parcel or else will inject the CSS into the page import styles from './CssModulesSliderDeep2.module.css'; @@ -605,22 +573,20 @@ It works exactly like styled components. You can [use the same guide](/material- It works exactly like styled components. You can [use the same guide](/material-ui/integrations/interoperability/#styled-components). -## Tailwind CSS +## Tailwind CSS ![stars](https://img.shields.io/github/stars/tailwindlabs/tailwindcss.svg?style=social&label=Star) ![npm](https://img.shields.io/npm/dm/tailwindcss) ### Setup -If you are used to Tailwind CSS and want to use it together with the Material UI components, you can start by cloning the [Tailwind CSS](https://github.com/mui/material-ui/tree/master/examples/material-ui-cra-tailwind-ts) example project. +If you are used to Tailwind CSS and want to use it together with the Material UI components, you can start by cloning the [Tailwind CSS](https://github.com/mui/material-ui/tree/master/examples/material-ui-cra-tailwind-ts) example project. If you use a different framework, or already have set up your project, follow these steps: -1. Add Tailwind CSS to your project, following the instructions in https://tailwindcss.com/docs/installation. -2. Remove [Tailwind CSS's preflight](https://tailwindcss.com/docs/preflight) style so it can use the Material UI's preflight instead ([CssBaseline](/material-ui/react-css-baseline/)). +1. Add Tailwind CSS to your project, following the instructions in https://tailwindcss.com/docs/installation. +2. Remove [Tailwind CSS's preflight](https://tailwindcss.com/docs/preflight) style so it can use the Material UI's preflight instead ([CssBaseline](/material-ui/react-css-baseline/)). -**tailwind.config.js** - -```diff +```diff title="tailwind.config.js" module.exports = { + corePlugins: { + preflight: false, @@ -630,9 +596,7 @@ If you use a different framework, or already have set up your project, follow th 3. Add the `important` option, using the id of your app wrapper. For example, `#__next` for Next.js and `"#root"` for CRA: -**tailwind.config.js** - -```diff +```diff title="tailwind.config.js" module.exports = { content: [ "./src/**/*.{js,jsx,ts,tsx}", @@ -647,11 +611,11 @@ If you use a different framework, or already have set up your project, follow th ``` Most of the CSS used by Material UI has as specificity of 1, hence this `important` property is unnecessary. -However, in a few edge cases, Material UI uses nested CSS selectors that win over Tailwind CSS. +However, in a few edge cases, Material UI uses nested CSS selectors that win over Tailwind CSS. Use this step to help ensure that the [deeper elements](#deeper-elements-5) can always be customized using Tailwind's utility classes. More details on this option can be found here https://tailwindcss.com/docs/configuration#selector-strategy -4. Fix the CSS injection order. Most CSS-in-JS solutions inject their styles at the bottom of the HTML `<head>`, which gives Material UI precedence over Tailwind CSS. To reduce the need for the `important` property, you need to change the CSS injection order. Here's a demo of how it can be done in Material UI: +4. Fix the CSS injection order. Most CSS-in-JS solutions inject their styles at the bottom of the HTML `<head>`, which gives Material UI precedence over Tailwind CSS. To reduce the need for the `important` property, you need to change the CSS injection order. Here's a demo of how it can be done in Material UI: ```jsx import * as React from 'react'; @@ -731,15 +695,13 @@ root.render( ### Usage -Now it's all set up and you can start using Tailwind CSS on the Material UI components! +Now it's all set up and you can start using Tailwind CSS on the Material UI components! {{"demo": "StyledComponents.js", "hideToolbar": true}} [![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/edit/github-ndkshy?file=pages%2Findex.tsx) -**index.tsx** - -```jsx +```jsx title="index.tsx" import * as React from 'react'; import Slider from '@mui/material/Slider'; @@ -761,9 +723,7 @@ This example showcases how to override the Slider's `thumb` style. {{"demo": "StyledComponentsDeep.js", "hideToolbar": true}} -**SliderThumbOverrides.tsx** - -```jsx +```jsx title="SliderThumbOverrides.tsx" import * as React from 'react'; import Slider from '@mui/material/Slider'; @@ -786,9 +746,7 @@ export default function SliderThumbOverrides() { If you want to style a component's pseudo-state, you can use the appropriate key in the `classes` prop. Here is an example of how you can style the Slider's active state: -**SliderPseudoStateOverrides.tsx** - -```jsx +```jsx title="SliderPseudoStateOverrides.tsx" import * as React from 'react'; import Slider from '@mui/material/Slider'; diff --git a/docs/data/material/integrations/nextjs/nextjs.md b/docs/data/material/integrations/nextjs/nextjs.md index 044877d094ce95..c3003b6f553ee9 100644 --- a/docs/data/material/integrations/nextjs/nextjs.md +++ b/docs/data/material/integrations/nextjs/nextjs.md @@ -31,7 +31,7 @@ pnpm add @mui/material-nextjs @emotion/cache Inside `app/layout.tsx`, import the `AppRouterCacheProvider` and wrap all elements under the `<body>` with it: -```diff +```diff title="app/layout.tsx" +import { AppRouterCacheProvider } from '@mui/material-nextjs/v13-appRouter'; // or `v1X-appRouter` if you are using Next.js v1X @@ -71,8 +71,7 @@ Use the `options` prop to override the default [cache options](https://emotion.s Create a new file and export a custom theme that includes the `'use client';` directive: -```js -// src/theme.ts +```js title="src/theme.ts" 'use client'; import { Roboto } from 'next/font/google'; import { createTheme } from '@mui/material/styles'; @@ -94,8 +93,7 @@ export default theme; Then in `src/app/layout.tsx`, pass the theme to `ThemeProvider`: -```diff - // app/layout.tsx +```diff title="app/layout.tsx" import { AppRouterCacheProvider } from '@mui/material-nextjs/v13-appRouter'; +import { ThemeProvider } from '@mui/material/styles'; +import theme from '../theme'; @@ -122,8 +120,7 @@ To learn more about theming, check out the [theming guide](/material-ui/customiz If you want to use [CSS theme variables](/material-ui/experimental-api/css-theme-variables/overview/), use the `extendTheme` and `CssVarsProvider` utilities instead: -```diff - // src/theme.ts +```diff title="src/theme.ts" 'use client'; -import { createTheme } from '@mui/material/styles'; +import { extendTheme } from '@mui/material/styles'; @@ -143,13 +140,13 @@ If you are using a styling solution other than Emotion to customize Material UI <AppRouterCacheProvider options={{ enableCssLayer: true }}> ``` -This option ensures that the styles generated by Material UI will be wrapped in a CSS `@layer mui` rule, which is overridden by anonymous layer styles when using Material UI with CSS modules, Tailwind CSS, or even plain CSS without using `@layer`. +This option ensures that the styles generated by Material UI will be wrapped in a CSS `@layer mui` rule, which is overridden by anonymous layer styles when using Material UI with CSS Modules, Tailwind CSS, or even plain CSS without using `@layer`. To learn more about it, see [the MDN CSS layer documentation](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer). ## Pages Router -This section walks through the Material UI integration with the Next.js [Pages Router](https://nextjs.org/docs/pages/building-your-application), for both [Server Side Rendering](https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering) (SSR) and [Static Site Generation](https://nextjs.org/docs/pages/building-your-application/rendering/static-site-generation) (SSG). +This section walks through the Material UI integration with the Next.js [Pages Router](https://nextjs.org/docs/pages/building-your-application), for both [Server-side Rendering](https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering) (SSR) and [Static Site Generation](https://nextjs.org/docs/pages/building-your-application/rendering/static-site-generation) (SSG). ### Installing the dependencies @@ -179,7 +176,7 @@ Inside the `pages/_document.tsx` file: - Import `documentGetInitialProps` and use it as the Document's `getInitialProps`. - Import `DocumentHeadTags` and render it inside the `<Head>`. -```diff +```diff title="pages/_document.tsx" +import { + DocumentHeadTags, + documentGetInitialProps, @@ -209,7 +206,7 @@ Inside the `pages/_document.tsx` file: Then, inside `pages/_app.tsx`, import the `AppCacheProvider` component and render it as the root element: -```diff +```diff title="pages/_app.tsx" +import { AppCacheProvider } from '@mui/material-nextjs/v13-pagesRouter'; // Or `v1X-pages` if you are using Next.js v1X @@ -236,8 +233,7 @@ See https://github.com/mui/material-ui/issues/26561#issuecomment-855286153 for w To use a custom [Emotion cache](https://emotion.sh/docs/@emotion/cache), pass it to the `emotionCache` property in `_document.tsx`: -```diff - // pages/_document.tsx +```diff title="pages/_document.tsx" ... MyDocument.getInitialProps = async (ctx) => { @@ -328,7 +324,7 @@ If you are using TypeScript, add `DocumentHeadTagsProps` to the Document's props In `pages/_app.tsx`, create a new theme and pass it to `ThemeProvider`: -```diff +```diff title="pages/_app.tsx" import * as React from 'react'; import Head from 'next/head'; import { AppProps } from 'next/app'; @@ -367,10 +363,10 @@ To learn more about theming, check out the [Theming guide](/material-ui/customiz If you want to use [CSS theme variables](/material-ui/experimental-api/css-theme-variables/overview/), use the `extendTheme` and `CssVarsProvider` instead: -```diff - // pages/_app.tsx +````diff title="pages/_app.tsx" -import { ThemeProvider, createTheme } from '@mui/material/styles'; +import { CssVarsProvider, extendTheme } from '@mui/material/styles'; ``` Learn more about [the advantages of CSS theme variables](/material-ui/experimental-api/css-theme-variables/overview/#advantages). +```` diff --git a/docs/data/material/integrations/styled-components/styled-components.md b/docs/data/material/integrations/styled-components/styled-components.md index 5c7c38d7b1626e..f4535e06691d6f 100644 --- a/docs/data/material/integrations/styled-components/styled-components.md +++ b/docs/data/material/integrations/styled-components/styled-components.md @@ -30,18 +30,16 @@ To use styled-components, you need to configure your bundler to replace it with If you're using yarn, you can configure it using a package resolution: -**package.json** - <!-- #default-branch-switch --> -```diff +```diff title="package.json" { "dependencies": { -- "@mui/styled-engine": "latest" -+ "@mui/styled-engine": "npm:@mui/styled-engine-sc@latest" +- "@mui/styled-engine": "next" ++ "@mui/styled-engine": "npm:@mui/styled-engine-sc@next" }, + "resolutions": { -+ "@mui/styled-engine": "npm:@mui/styled-engine-sc@latest" ++ "@mui/styled-engine": "npm:@mui/styled-engine-sc@next" + }, } ``` @@ -51,9 +49,7 @@ If you're using yarn, you can configure it using a package resolution: Because package resolutions aren't available with npm, you must update your bundler's config to add this alias. The example below shows how to do this with Webpack: -**webpack.config.js** - -```diff +```diff title="webpack.config.js" module.exports = { //... + resolve: { @@ -66,9 +62,7 @@ The example below shows how to do this with Webpack: For TypeScript, you must also update the `tsconfig.json` as shown here: -**tsconfig.json** - -```diff +```diff title="tsconfig.json" { "compilerOptions": { + "paths": { @@ -80,9 +74,7 @@ For TypeScript, you must also update the `tsconfig.json` as shown here: ### Next.js -**next.config.js** - -```diff +```diff title="next.config.js" +const withTM = require('next-transpile-modules')([ + '@mui/material', + '@mui/system', @@ -101,9 +93,7 @@ For TypeScript, you must also update the `tsconfig.json` as shown here: ``` :::info -**Versions compatibility** - -To ensure compatibility, it's essential to align the major version of `@mui/styled-engine-sc` with that of the `styled-components` package you're using. For instance, if you opt for `styled-components` version 5, it's necessary to use `@mui/styled-engine-sc` version 5. Similarly, if your preference is `styled-components` version 6, you'll need to upgrade `@mui/styled-engine-sc` to its version 6, which is currently in an alpha state. +**Versions compatibility**: To ensure compatibility, it's essential to align the major version of `@mui/styled-engine-sc` with that of the `styled-components` package you're using. For instance, if you opt for `styled-components` version 5, it's necessary to use `@mui/styled-engine-sc` version 5. Similarly, if your preference is `styled-components` version 6, you'll need to upgrade `@mui/styled-engine-sc` to its version 6, which is currently in an alpha state. ::: ## Ready-to-use examples @@ -112,8 +102,8 @@ We provide boilerplate examples of Create React App with Material UI and styled <!-- #default-branch-switch --> -- [Material UI + CRA + styled-components (JavaScript)](https://github.com/mui/material-ui/tree/master/examples/material-ui-cra-styled-components) -- [Material UI + CRA + styled-components (TypeScript)](https://github.com/mui/material-ui/tree/master/examples/material-ui-cra-styled-components-ts) +- [Material UI + CRA + styled-components (JavaScript)](https://github.com/mui/material-ui/tree/next/examples/material-ui-cra-styled-components) +- [Material UI + CRA + styled-components (TypeScript)](https://github.com/mui/material-ui/tree/next/examples/material-ui-cra-styled-components-ts) :::warning `@emotion/react`, `@emotion/styled`, and `styled-components` are optional peer dependencies of `@mui/material`, so you need to install them yourself. diff --git a/docs/data/material/integrations/theme-scoping/theme-scoping.md b/docs/data/material/integrations/theme-scoping/theme-scoping.md index 762c8876be8e2e..3cb2bd9949497c 100644 --- a/docs/data/material/integrations/theme-scoping/theme-scoping.md +++ b/docs/data/material/integrations/theme-scoping/theme-scoping.md @@ -36,7 +36,7 @@ The Material UI theme will be separated from the other library, so when you use ### Using with Theme UI -Render Material UI's theme provider below Theme UI's provider and assign the Material theme to the `THEME_ID` property: +Render Material UI's theme provider below Theme UI's provider and assign the `materialTheme` to the `THEME_ID` property: ```js import { ThemeUIProvider } from 'theme-ui'; @@ -70,7 +70,7 @@ function App() { ### Using with Chakra UI -Render Material UI's theme provider below Chakra UI's provider and assign the material theme to the `THEME_ID` property: +Render Material UI's theme provider below Chakra UI's provider and assign the `materialTheme` to the `THEME_ID` property: ```js import { ChakraProvider, extendTheme as chakraExtendTheme } from '@chakra-ui/react'; diff --git a/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md b/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md index 6917beab82a61b..ab93a64152fd0c 100644 --- a/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md +++ b/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md @@ -26,6 +26,20 @@ If you need to run a specific codemod, those are also linked below. ::: +## Package-wide deprecated APIs + +### Inner element overrides + +The `slots` and `slotProps` APIs are in the process of being standardized. +The analogous APIs—`components`, `componentsProps`, `<SlotName>Component`, and `<SlotName>Props`—are going to be deprecated and eventually removed. +This improves the developer experience through consistency, predictability, and reduced cognitive load. + +### Composed CSS classes + +The composed CSS classes are going to be deprecated and eventually removed in favor of atomic class alternatives. +For example, the `.MuiAccordionSummary-contentGutters` class was deprecated in favor of the `.MuiAccordionSummary-gutters` and `.MuiAccordionSummary-content` classes. +This improves the developer experience by reducing bloat and cognitive load. + ## Accordion Use the [codemod](https://github.com/mui/material-ui/tree/HEAD/packages/mui-codemod#accordion-props) below to migrate the code as described in the following sections: diff --git a/docs/data/material/migration/migration-v4/migrating-from-jss.md b/docs/data/material/migration/migration-v4/migrating-from-jss.md index f6969d76befd9e..f63580f6d44eb1 100644 --- a/docs/data/material/migration/migration-v4/migrating-from-jss.md +++ b/docs/data/material/migration/migration-v4/migrating-from-jss.md @@ -14,7 +14,7 @@ One of the biggest changes in v5 is the replacement of JSS for [Emotion](https://emotion.sh/docs/introduction) (or [styled-components](https://styled-components.com/) as an alternative) as a default styling solution . -Note that you may continue to use JSS for adding overrides for the components (e.g. `makeStyles`, `withStyles`) even after migrating to v5. +Note that you may continue to use JSS for adding overrides for the components (for example `makeStyles`, `withStyles`) even after migrating to v5. Then, if at any point you want to move over to the new styling engine, you can refactor your components progressively. :::info diff --git a/docs/data/material/migration/migration-v4/migration-v4.md b/docs/data/material/migration/migration-v4/migration-v4.md index 25ee2d8984da60..6feaca110bff0c 100644 --- a/docs/data/material/migration/migration-v4/migration-v4.md +++ b/docs/data/material/migration/migration-v4/migration-v4.md @@ -18,7 +18,7 @@ We highly recommend running our [codemods](#run-codemods) for efficiency—these One of the biggest changes in v5 is the replacement of JSS for [Emotion](https://emotion.sh/docs/introduction) as a default styling solution. -Note that you may continue to use JSS for adding overrides to the components (e.g. `makeStyles`, `withStyles`) even after migrating to v5. +Note that you may continue to use JSS for adding overrides to the components (for example `makeStyles`, `withStyles`) even after migrating to v5. Once you've completed the rest of the v5 upgrade, we recommend progressively moving over to the new styling engine. This process is covered in [Migrating from JSS](/material-ui/migration/migrating-from-jss/). @@ -94,7 +94,7 @@ yarn upgrade @material-ui/core@^4.11.2 react@^17.0.0 The minimum supported version of TypeScript has been increased from v3.2 to v3.5. :::info -We try to align with types released by [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) (i.e. packages published on npm under the `@types` namespace). +We try to align with types released by [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) (that is packages published on npm under the `@types` namespace). We will not change the minimum supported version in a minor version of Material UI. However, we generally recommend not to use a TypeScript version older than the lowest supported version of DefinitelyTyped. diff --git a/docs/data/material/migration/migration-v4/v5-style-changes.md b/docs/data/material/migration/migration-v4/v5-style-changes.md index 81c19871e95c92..1f907c24ca3c25 100644 --- a/docs/data/material/migration/migration-v4/v5-style-changes.md +++ b/docs/data/material/migration/migration-v4/v5-style-changes.md @@ -108,7 +108,7 @@ Support for non-ref-forwarding class components in the `component` prop or as im If you were using `unstable_createStrictModeTheme` or didn't see any warnings related to `findDOMNode` in `React.StrictMode` then you don't need to take any further action. Otherwise check out the [Caveat with refs](/material-ui/guides/composition/#caveat-with-refs) section in the Composition guide to find out how to migrate. -This change affects almost all components where you're using the `component` prop or passing `children` to components that require `children` to be elements (e.g. `<MenuList><CustomMenuItem /></MenuList>`). +This change affects almost all components where you're using the `component` prop or passing `children` to components that require `children` to be elements (for example `<MenuList><CustomMenuItem /></MenuList>`). ### Fix ref type specificity diff --git a/docs/data/styles/advanced/advanced.md b/docs/data/styles/advanced/advanced.md index ca15e7021062de..f56a62d16ac91b 100644 --- a/docs/data/styles/advanced/advanced.md +++ b/docs/data/styles/advanced/advanced.md @@ -240,7 +240,7 @@ Read this section from the MDN docs for more information: [How is specificity ca ::: By default, the style tags are injected **last** in the `<head>` element of the page. -They gain more specificity than any other style tags on your page e.g. CSS modules, styled components. +They gain more specificity than any other style tags on your page for example CSS Modules, styled components. ### injectFirst @@ -399,7 +399,7 @@ function render() { } ``` -You can [follow the server side guide](/material-ui/guides/server-rendering/) for a more detailed example, or read the [`ServerStyleSheets` API documentation](/system/styles/api/#serverstylesheets). +You can [follow the server-side guide](/material-ui/guides/server-rendering/) for a more detailed example, or read the [`ServerStyleSheets` API documentation](/system/styles/api/#serverstylesheets). ### CSS prefixing @@ -417,16 +417,12 @@ It's a must-do for static pages, but it needs to be put in balance with not doin There is [an official Gatsby plugin](https://github.com/hupe1980/gatsby-plugin-material-ui) that enables server-side rendering for `@mui/styles`. Refer to the plugin's page for setup and usage instructions. -<!-- #default-branch-switch --> - Refer to [this example Gatsby project](https://github.com/mui/material-ui/tree/v4.x/examples/gatsby) for an usage example. ### Next.js Pages Router You need to have a custom `pages/_document.js`, then copy [this logic](https://github.com/mui/material-ui/blob/v4.x/examples/nextjs/pages/_document.js#L52-L59) to inject the server-side rendered styles into the `<head>` element. -<!-- #default-branch-switch --> - Refer to [this example project](https://github.com/mui/material-ui/tree/v4.x/examples/nextjs) for an up-to-date usage example. ## Class names diff --git a/docs/data/system/getting-started/usage/usage.md b/docs/data/system/getting-started/usage/usage.md index 611d8a91a22eba..487340ad2b1585 100644 --- a/docs/data/system/getting-started/usage/usage.md +++ b/docs/data/system/getting-started/usage/usage.md @@ -163,7 +163,7 @@ Runtime performance takes a hit. <!-- #default-branch-switch --> -Visit the [benchmark folder](https://github.com/mui/material-ui/tree/master/benchmark/browser) for a reproduction of the metrics above. +Visit the [benchmark folder](https://github.com/mui/material-ui/tree/next/benchmark/browser) for a reproduction of the metrics above. We believe that for most use cases it's fast enough, but there are simple workarounds when performance becomes critical. For instance, when rendering a list with many items, you can use a CSS child selector to have a single "style injection" point (using d. for the wrapper and a. for each item). @@ -299,7 +299,7 @@ Here's what that looks like: {{"demo": "BreakpointsAsArray.js"}} :::success -This option should only be considered when the theme has a limited number of breakpoints, e.g. 3. +This option should only be considered when the theme has a limited number of breakpoints, for example 3. We recommend using the object API instead if you need to define more than a few breakpoints. ::: diff --git a/docs/mui-vale.zip b/docs/mui-vale.zip new file mode 100644 index 0000000000000000000000000000000000000000..86fdbcc3f12bf09faac5d20ed85a94aada15fbda GIT binary patch literal 4897 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 BRSy6F literal 0 HcmV?d00001 diff --git a/docs/mui-vale/.vale.ini b/docs/mui-vale/.vale.ini new file mode 100644 index 00000000000000..f8b00bdb5dab5d --- /dev/null +++ b/docs/mui-vale/.vale.ini @@ -0,0 +1,2 @@ +# This is subfolder included in our .zip archive. +StylesPath = styles diff --git a/docs/mui-vale/styles/MUI/CorrectReferenceAllCases.yml b/docs/mui-vale/styles/MUI/CorrectReferenceAllCases.yml new file mode 100644 index 00000000000000..f8c94a1279bf21 --- /dev/null +++ b/docs/mui-vale/styles/MUI/CorrectReferenceAllCases.yml @@ -0,0 +1,31 @@ +# Enforce a single way to write specific terms or phrases. +extends: substitution +message: Use '%s' instead of '%s' +level: error +ignorecase: true +# swap maps tokens in form of bad: good +# for more information: https://vale.sh/docs/topics/styles/#substitution +swap: + ' api': API + 'typescript ': TypeScript + ' ts': TypeScript + ' js': JavaScript + javascript: JavaScript + ' css ': CSS + ' html ': HTML + NPM: npm # https://css-tricks.com/start-sentence-npm/ + Github: GitHub + StackOverflow: Stack Overflow + CSS modules: CSS Modules + Tailwind CSS: Tailwind CSS + Heat map: Heatmap + Tree map: Treemap + Sparkline Chart: Sparkline + Gauge Chart: Gauge + Treemap Chart: Treemap + sub-component: subcomponent + sub-components: subcomponents + use-case: 'use case' + usecase: 'use case' + client side: 'client-side' + server side: 'server-side' diff --git a/docs/mui-vale/styles/MUI/CorrectRerenceCased.yml b/docs/mui-vale/styles/MUI/CorrectRerenceCased.yml new file mode 100644 index 00000000000000..85853ab5146d39 --- /dev/null +++ b/docs/mui-vale/styles/MUI/CorrectRerenceCased.yml @@ -0,0 +1,13 @@ +# Write things correctly, please no wrong references. +extends: substitution +message: Use '%s' instead of '%s' +level: error +ignorecase: false +# swap maps tokens in form of bad: good +# for more information: https://vale.sh/docs/topics/styles/#substitution +swap: + eg: e.g. + eg\.: e.g. + 'e\.g ': 'e.g.' + 'ie\.': i.e. + 'i\.e ': 'i.e.' diff --git a/docs/mui-vale/styles/MUI/GoogleLatin.yml b/docs/mui-vale/styles/MUI/GoogleLatin.yml new file mode 100644 index 00000000000000..61bdbf909f4e71 --- /dev/null +++ b/docs/mui-vale/styles/MUI/GoogleLatin.yml @@ -0,0 +1,11 @@ +extends: substitution +message: Use '%s' instead of '%s' +link: https://developers.google.com/style/abbreviations +ignorecase: false +level: error +nonword: true +action: + name: replace +swap: + '\b(?:eg|e\.g\.)(?=[\s,;])': for example + '\b(?:ie|i\.e\.)(?=[\s,;])': that is diff --git a/docs/writing-rules/BrandName.yml b/docs/mui-vale/styles/MUI/MuiBrandName.yml similarity index 80% rename from docs/writing-rules/BrandName.yml rename to docs/mui-vale/styles/MUI/MuiBrandName.yml index ac48bba0ddbd88..3a5251f2ccf622 100644 --- a/docs/writing-rules/BrandName.yml +++ b/docs/mui-vale/styles/MUI/MuiBrandName.yml @@ -15,11 +15,8 @@ swap: MUI System: MUI System MUI Store: MUI Store MUI Core: MUI Core - MUI Toolpad: MUI Toolpad + MUI Toolpad: Toolpad + MUI Toolpad: Toolpad MUI Connect: MUI Connect Stack Overflow: Stack Overflow Pigment CSS: Pigment CSS -# Don't forget to run the following command to generate the package writing-rules.zip file -# Vale uses that ZIP file and not the YAML files. -# -# pnpm docs:zipRules diff --git a/docs/mui-vale/styles/MUI/NoBritish.yml b/docs/mui-vale/styles/MUI/NoBritish.yml new file mode 100644 index 00000000000000..6ce7dc93a3ff2b --- /dev/null +++ b/docs/mui-vale/styles/MUI/NoBritish.yml @@ -0,0 +1,112 @@ +extends: substitution +message: Use the US spelling '%s' instead of the British '%s' +link: https://www.notion.so/mui-org/Writing-style-guide-2a957a4168a54d47b14bae026d06a24b?pvs=4#25755bff02764565b954631236ab183d +level: error +ignorecase: true +swap: + aeon: eon + aeroplane: airplane + ageing: aging + aluminium: aluminum + anaemia: anemia + anaesthesia: anesthesia + analyse: analyze + annexe: annex + apologise: apologize + authorisation: authorization + authorise: authorize + authorised: authorized + authorising: authorizing + behaviour: behavior + bellow: below + busses: buses + calibre: caliber + categorise: categorize + categorised: categorized + categorises: categorizes + categorising: categorizing + centre: center + cheque: check + civilisation: civilization + civilise: civilize + colour: color + cosy: cozy + cypher: cipher + defence: defense + dependant: dependent + distil: distill + draught: draft + encyclopaedia: encyclopedia + enquiry: inquiry + enrol: enroll + enrolment: enrollment + enthral: enthrall + favourite: favorite + fibre: fiber + fillet: filet + flavour: flavor + fulfil: fulfill + furore: furor + gaol: jail + grey: gray + honour: honor + humour: humor + initialled: initialed + initialling: initialing + instil: instill + jewellery: jewelry + labelled: labeled + labelling: labeling + labour: labor + libellous: libelous + licence: license + likeable: likable + liveable: livable + lustre: luster + manoeuvre: maneuver + marvellous: marvelous + meagre: meager + metre: meter + modelling: modeling + moustache: mustache + neighbour: neighbor + normalise: normalize + offence: offense + optimise: optimize + optimised: optimized + optimising: optimizing + organise: organize + orientated: oriented + paralyse: paralyze + plough: plow + pretence: pretense + programme: program + pyjamas: pajamas + rateable: ratable + realise: realize + recognise: recognize + reconnoitre: reconnoiter + rumour: rumor + sabre: saber + saleable: salable + saltpetre: saltpeter + sceptic: skeptic + sepulchre: sepulcher + signalling: signaling + sizeable: sizable + skilful: skillful + smoulder: smolder + sombre: somber + speciality: specialty + spectre: specter + splendour: splendor + standardise: standardize + standardised: standardized + sulphur: sulfur + theatre: theater + travelled: traveled + traveller: traveler + travelling: traveling + unshakeable: unshakable + wilful: willful + yoghurt: yogurt diff --git a/docs/writing-rules/NoCompanyName.yml b/docs/mui-vale/styles/MUI/NoCompanyName.yml similarity index 67% rename from docs/writing-rules/NoCompanyName.yml rename to docs/mui-vale/styles/MUI/NoCompanyName.yml index 19e4d50efe0a1a..8556df60383e22 100644 --- a/docs/writing-rules/NoCompanyName.yml +++ b/docs/mui-vale/styles/MUI/NoCompanyName.yml @@ -12,8 +12,4 @@ exceptions: - 'MUI Toolpad' - 'MUI Connect' - 'MUI organization' # valid use of a regular space - -# Don't forget to run the following command to generate the package writing-rules.zip file -# Vale uses that ZIP file and not the YAML files. -# -# pnpm docs:zipRules + - 'MUI ecosystem' # valid use of a regular space diff --git a/docs/next.config.mjs b/docs/next.config.mjs index a0ec4b89d6df8c..825504dbeb620f 100644 --- a/docs/next.config.mjs +++ b/docs/next.config.mjs @@ -112,7 +112,6 @@ export default withDocsInfra({ '@mui/private-theming': path.resolve(workspaceRoot, 'packages/mui-private-theming/src'), '@mui/utils': path.resolve(workspaceRoot, 'packages/mui-utils/src'), '@mui/base': path.resolve(workspaceRoot, 'packages/mui-base/src'), - '@mui/material-next': path.resolve(workspaceRoot, 'packages/mui-material-next/src'), '@mui/material-nextjs': path.resolve(workspaceRoot, 'packages/mui-material-nextjs/src'), '@mui/joy': path.resolve(workspaceRoot, 'packages/mui-joy/src'), }, @@ -190,7 +189,7 @@ export default withDocsInfra({ // docs-infra LIB_VERSION: pkg.version, SOURCE_CODE_REPO: 'https://github.com/mui/material-ui', - SOURCE_GITHUB_BRANCH: 'master', // #default-branch-switch + SOURCE_GITHUB_BRANCH: 'next', // #default-branch-switch GITHUB_TEMPLATE_DOCS_FEEDBACK: '4.docs-feedback.yml', BUILD_ONLY_ENGLISH_LOCALE: String(buildOnlyEnglishLocale), // MUI Core related diff --git a/docs/package.json b/docs/package.json index b70ec2b2d37c59..c6350de8321b02 100644 --- a/docs/package.json +++ b/docs/package.json @@ -9,7 +9,7 @@ "build:clean": "rimraf .next && pnpm build", "build-sw": "node ./scripts/buildServiceWorker.js", "dev": "next dev", - "deploy": "git push -f material-ui-docs master:latest", + "deploy": "git push -f material-ui-docs next:next", "icons": "rimraf --glob public/static/icons/* && node ./scripts/buildIcons.js", "start": "next start", "create-playground": "cpy --cwd=scripts playground.template.tsx ../../pages/playground --rename=index.tsx", @@ -23,7 +23,7 @@ "@babel/plugin-transform-object-assign": "^7.23.3", "@babel/runtime": "^7.23.9", "@babel/runtime-corejs2": "^7.23.9", - "@docsearch/react": "^3.5.2", + "@docsearch/react": "^3.6.0", "@emotion/cache": "^11.11.0", "@emotion/react": "^11.11.4", "@emotion/server": "^11.11.0", @@ -38,7 +38,6 @@ "@mui/lab": "workspace:*", "@mui/internal-markdown": "workspace:^", "@mui/material": "workspace:^", - "@mui/material-next": "workspace:*", "@mui/styled-engine": "workspace:^", "@mui/styled-engine-sc": "workspace:^", "@mui/styles": "workspace:^", @@ -46,12 +45,12 @@ "@mui/types": "workspace:^", "@mui/utils": "workspace:^", "@mui/x-charts": "6.19.5", - "@mui/x-data-grid": "6.19.6", - "@mui/x-data-grid-generator": "6.19.6", - "@mui/x-data-grid-premium": "6.19.6", - "@mui/x-data-grid-pro": "6.19.6", - "@mui/x-date-pickers": "6.19.6", - "@mui/x-date-pickers-pro": "6.19.6", + "@mui/x-data-grid": "7.0.0-beta.7", + "@mui/x-data-grid-generator": "7.0.0-beta.7", + "@mui/x-data-grid-premium": "7.0.0-beta.7", + "@mui/x-data-grid-pro": "7.0.0-beta.7", + "@mui/x-date-pickers": "6.19.7", + "@mui/x-date-pickers-pro": "6.19.7", "@mui/x-license-pro": "6.10.2", "@mui/x-tree-view": "6.17.0", "@popperjs/core": "^2.11.8", @@ -81,7 +80,7 @@ "jss-rtl": "^0.3.0", "lodash": "^4.17.21", "lz-string": "^1.5.0", - "markdown-to-jsx": "^7.4.1", + "markdown-to-jsx": "^7.4.3", "material-ui-popup-state": "^5.0.10", "next": "^13.5.1", "notistack": "3.0.1", @@ -93,8 +92,8 @@ "react-dom": "^18.2.0", "react-draggable": "^4.4.6", "react-final-form": "^6.5.9", - "react-imask": "^7.3.0", - "react-intersection-observer": "^9.5.3", + "react-imask": "^7.5.0", + "react-intersection-observer": "^9.8.1", "react-is": "^18.2.0", "react-number-format": "^5.3.3", "react-router-dom": "^6.21.3", @@ -104,7 +103,7 @@ "react-swipeable-views": "^0.14.0", "react-swipeable-views-utils": "^0.14.0", "react-transition-group": "^4.4.5", - "react-virtuoso": "^4.6.3", + "react-virtuoso": "^4.7.2", "react-window": "^1.8.10", "rimraf": "^5.0.5", "styled-components": "^6.1.8", @@ -116,14 +115,14 @@ "devDependencies": { "@babel/plugin-transform-react-constant-elements": "^7.23.3", "@babel/preset-typescript": "^7.23.3", + "@mui/internal-docs-utils": "workspace:^", "@mui/internal-scripts": "workspace:^", - "@mui-internal/docs-utils": "workspace:^", "@mui-internal/test-utils": "workspace:^", "@types/autosuggest-highlight": "^3.2.3", "@types/chai": "^4.3.12", "@types/css-mediaquery": "^0.1.4", "@types/json2mq": "^0.2.2", - "@types/node": "^18.19.21", + "@types/node": "^18.19.25", "@types/prop-types": "^15.7.11", "@types/react": "^18.2.55", "@types/react-dom": "^18.2.19", diff --git a/docs/pages/_app.js b/docs/pages/_app.js index 05916ec32e58a6..7931cb56f13147 100644 --- a/docs/pages/_app.js +++ b/docs/pages/_app.js @@ -167,6 +167,7 @@ function AppWrapper(props) { name: 'Material UI', versions: [ { text: `v${materialPkgJson.version}`, current: true }, + { text: `v5`, href: `https://mui.com${languagePrefix}/material-ui/getting-started/` }, { text: 'v4', href: `https://v4.mui.com${languagePrefix}/getting-started/installation/`, @@ -193,6 +194,7 @@ function AppWrapper(props) { name: 'MUI System', versions: [ { text: `v${systemPkgJson.version}`, current: true }, + { text: 'v5', href: `https://mui.com${languagePrefix}/system/getting-started/` }, { text: 'v4', href: `https://v4.mui.com${languagePrefix}/system/basics/` }, { text: 'View all versions', diff --git a/docs/pages/_document.js b/docs/pages/_document.js index 599e5e0fd99be5..ecd5633bf5a5b3 100644 --- a/docs/pages/_document.js +++ b/docs/pages/_document.js @@ -226,7 +226,7 @@ MyDocument.getInitialProps = async (ctx) => { enhanceApp: (App) => (props) => jssSheets.collect(<App {...props} />), resolveProps: async (initialProps) => { let css = jssSheets.toString(); - // It might be undefined, e.g. after an error. + // It might be undefined, for example after an error. if (css && process.env.NODE_ENV === 'production') { const result1 = await prefixer.process(css, { from: undefined }); css = result1.css; diff --git a/docs/pages/blog/2019-developer-survey-results.md b/docs/pages/blog/2019-developer-survey-results.md index ca7cabcfd7a390..392206d8f8c1d9 100644 --- a/docs/pages/blog/2019-developer-survey-results.md +++ b/docs/pages/blog/2019-developer-survey-results.md @@ -270,18 +270,18 @@ section. Multiple options were allowed. -- Web app (e.g. create react app): 92% +- Web app (for example create react app): 92% - Progressive web app (with service worker): 25% - Server-side rendering 14% -- Static web site, hosted on a CDN (e.g. Gatsby): 13% -- Desktop app (e.g. Electron): 12% -- Native mobile app (e.g. Cordova): 6% +- Static web site, hosted on a CDN (for example Gatsby): 13% +- Desktop app (for example Electron): 12% +- Native mobile app (for example Cordova): 6% ### 16. Who are you building it for? <img src="/static/blog/2019-developer-survey-results/16.png" style="display: block; margin: 0 auto;" alt="Pie chart: 54,3% for my company, 24.9% for a client, 15.2% as a side project, 5.6% more than one of these." /> -### 17. Which JS framework are you using, if any? +### 17. Which JavaScript framework are you using, if any? Multiple options were allowed. @@ -300,7 +300,7 @@ Multiple options were allowed. - @mui/styles: 85% - Styled components: 30% - Good old CSS (+sass, less, etc): 24% -- CSS Modules (+sass, less, etc): 16% +- CSS Modules (+sass, less, etc): 16% - Emotion: 4% Traditional CSS users are still prevalent (24% + 16%). diff --git a/docs/pages/blog/2019.md b/docs/pages/blog/2019.md index 2e3cb544696ba2..a528508030a330 100644 --- a/docs/pages/blog/2019.md +++ b/docs/pages/blog/2019.md @@ -32,7 +32,7 @@ We soon realized that we could do way more. It was just the beginning :D. Some of the key factors: - The results of the [2019 Developer Survey](https://mui.com/blog/2019-developer-survey-results/) have highlighted the immense potential for working on advanced components and features, especially for enterprise users. - Developers are craving for a UI framework that they can learn once (e.g. few breaking changes, only one solution per problem) and use everywhere (e.g. comprehensive, customizable, high-quality). + Developers are craving for a UI framework that they can learn once (for example few breaking changes, only one solution per problem) and use everywhere (for example comprehensive, customizable, high-quality). - Bootstrap had successfully released [a theme store](https://themes.getbootstrap.com/). Following this approach opened an opportunity to capture a fraction of the value Material UI creates for its users, and funnel it back into R&D on the framework. - The market for paid UI components is in the order of a couple of \$100m/year, @@ -64,7 +64,7 @@ Some of the key factors: - We have fixed a significant number of [accessibility issues](https://github.com/mui/material-ui/issues?q=is%3Aissue+label%3Aaccessibility+is%3Aclosed). - We have introduced global class names. - We have migrated the whole codebase to hooks. -- We migrated all the demos to TypeScript (while also offering transpiled JS demos). +- We migrated all the demos to TypeScript (while also offering transpiled JavaScript demos). - We introduced [native tree-shaking](/material-ui/guides/minimizing-bundle-size/) support. - We introduced [built-in localization](/material-ui/guides/localization/). - We removed a good number of external dependencies and increased the `features/bundle size` density. diff --git a/docs/pages/blog/2020-developer-survey-results.md b/docs/pages/blog/2020-developer-survey-results.md index c1f493d9ec5872..9c89e737c62c3c 100644 --- a/docs/pages/blog/2020-developer-survey-results.md +++ b/docs/pages/blog/2020-developer-survey-results.md @@ -51,6 +51,8 @@ The responses to this question are a very clear indicator to us about what we ne As the answers to these questions were pretty different, we grouped them into different categories and counted the different number of times the concern was mentioned. You can see all of them sorted in descending order: +<!-- vale MUI.CorrectReferenceAllCases = NO --> + <style>th { text-align: left; border-bottom: 3px solid !important; }</style> <table> @@ -179,6 +181,8 @@ As the answers to these questions were pretty different, we grouped them into di <tr><th>3</th><th>grid - improve</th><tr> </table> +<!-- vale MUI.CorrectReferenceAllCases = YES --> + ### Comparison with last year There are a couple of noticeable differences compared to last year. @@ -273,13 +277,13 @@ section. <img src="/static/blog/2020-survey/17.png" style="width: 796px; margin-top: 16px; margin-bottom: 8px;" alt="Pie chart: 55.17% For my company 22.86% For a client, 16.94% Side project, 5.03% More than one of these." /> -### 18. Which JS framework are you using, if any? +### 18. Which JavaScript framework are you using, if any? <img src="/static/blog/2020-survey/18.png" style="width: 796px; margin-top: 16px; margin-bottom: 8px;" alt="Pie chart: 57.34% Create React App, 16.40% Custom Webpack, 12.35% Next.js, 5.40% Gatsby, 8.51% Other." /> ### 19. What styling system are you using? -<img src="/static/blog/2020-survey/19.png" style="width: 796px; margin-top: 16px; margin-bottom: 8px;" alt="Pie chart: 53.84% Material UI styles (JSS), 20.41% Styled components, 13.01% Good plain CSS, 8.31% CSS Modules, 1.96% Emotion, 0.59% scss, 0.59% sass, 0.09% less, 1.19% Other" /> +<img src="/static/blog/2020-survey/19.png" style="width: 796px; margin-top: 16px; margin-bottom: 8px;" alt="Pie chart: 53.84% Material UI styles (JSS), 20.41% Styled components, 13.01% Good plain CSS, 8.31% CSS Modules, 1.96% Emotion, 0.59% scss, 0.59% sass, 0.09% less, 1.19% Other" /> The response seems to be similar to the one from the last year's survey, so we will push with better support for styled components. @@ -303,8 +307,8 @@ We want to work on the problems that resonate the most with our users. 1. Provide more flexibility on the components, unstyled components (pure hooks?). 1. Make the customization easier and implement custom themes with Material UI. Maybe provide a theme builder. -1. Provide a second theme, update the current components to better match Material Design, provide more simple components and features (e.g. dropzone, carousel) as well as provide a better DX (there are good ideas from other UI libraries to apply to Material UI v5). -1. Improve upon the paid advanced versions of the components (e.g. complex data grid, date range picker, tree view drag & drop, virtualization, etc). +1. Provide a second theme, update the current components to better match Material Design, provide more simple components and features (for example dropzone, carousel) as well as provide a better DX (there are good ideas from other UI libraries to apply to Material UI v5). +1. Improve upon the paid advanced versions of the components (for example complex data grid, date range picker, tree view drag & drop, virtualization, etc). **We will update [our ROADMAP](/material-ui/discover-more/roadmap/) in the coming days**. We will run a similar survey next year to keep track of our progress. diff --git a/docs/pages/blog/2020.md b/docs/pages/blog/2020.md index c3dc375aa5c046..97e953f791cd04 100644 --- a/docs/pages/blog/2020.md +++ b/docs/pages/blog/2020.md @@ -105,7 +105,7 @@ It will be built on top of the unstyled components. While the completion of the unstyled components was originally part of the v5 milestone, we will likely finish this effort independently. -Outside of the requirement to introduce breaking changes on the component customization API, e.g. from `PaperProps` to `slotProps`, [RFC #20012](https://github.com/mui/material-ui/issues/21453), we can work on unstyled at the same time we make progress with the second theme. The two efforts should go hand in hand. +Outside of the requirement to introduce breaking changes on the component customization API, for example from `PaperProps` to `slotProps`, [RFC #20012](https://github.com/mui/material-ui/issues/21453), we can work on unstyled at the same time we make progress with the second theme. The two efforts should go hand in hand. ### Scale diff --git a/docs/pages/blog/2021-developer-survey-results.md b/docs/pages/blog/2021-developer-survey-results.md index d7c6bf180b33ba..e6b3eba54ff130 100644 --- a/docs/pages/blog/2021-developer-survey-results.md +++ b/docs/pages/blog/2021-developer-survey-results.md @@ -104,6 +104,8 @@ And what has decreased: It seems to be a transfer effect: people who previously cared more about Material Design now care more about the outcome than the specification itself. <br/> +<!-- vale MUI.CorrectReferenceAllCases = NO --> + <details> <summary>Click to see the breakdown of categories.</summary> @@ -256,7 +258,7 @@ If you are interested in an analysis of the growing and decreasing pain, you can <tr><td>36</td><td>system - makeStyles back</td><tr> <tr><td>20</td><td>system - docs</td><tr> <tr><td>15</td><td>system - SASS</td><tr> - <tr><td>11</td><td>system - interoperability with Tailwind CSS</td><tr> + <tr><td>11</td><td>system - interoperability with Tailwind CSS</td><tr> <tr><td>14</td><td>system - simplify</td><tr> <tr><td>8</td><td>system - CSS variables</td><tr> <tr><td>7</td><td>system - interoperability</td><tr> @@ -397,7 +399,7 @@ If you are interested in an analysis of the growing and decreasing pain, you can <tr><td>1</td><td>animations - on existing components</td><tr> <tr><td>1</td><td>animations - performance</td><tr> <tr><th>7</th><th>fix more bugs</th><tr> - <tr><th>7</th><th>support other frameworkds (e.g. Vue)</th><tr> + <tr><th>7</th><th>support other frameworkds (for example Vue)</th><tr> <tr><th>5</th><th>autocomplete</th><tr> <tr><td>3</td><td>autocomplete - ?</td><tr> <tr><td>1</td><td>autocomplete - abbreviation</td><tr> @@ -410,6 +412,8 @@ If you are interested in an analysis of the growing and decreasing pain, you can </table> </details> +<!-- vale MUI.CorrectReferenceAllCases = YES --> + Have ideas for improvements? Please share them with us! Here's how to make sure that your requests get top priority: - When you create an issue to request features or components in the MUI Core or MUI X repositories, we'll label it as `Waiting for upvotes`. @@ -652,7 +656,7 @@ This year we saw a considerable bump the use of Next.js (compared with 12.4% in ### What styling solution are you using? -<img src="/static/blog/2021-developer-survey-results/22.png" style="width: 796px; margin-top: 16px; margin-bottom: 8px;" alt="MUI Core v4 (JSS): 45%; Styled components: 37.9%; Emotion: 30.2%; SASS: 20.8%; CSS modules: 18.9%; Vanilla CSS: 17.6%; Tailwind CSS: 9.1%; Stitches: 0.4%; Other: 1.2%" /> +<img src="/static/blog/2021-developer-survey-results/22.png" style="width: 796px; margin-top: 16px; margin-bottom: 8px;" alt="MUI Core v4 (JSS): 45%; Styled components: 37.9%; Emotion: 30.2%; SASS: 20.8%; CSS Modules: 18.9%; Vanilla CSS: 17.6%; Tailwind CSS: 9.1%; Stitches: 0.4%; Other: 1.2%" /> <p class="blog-description">1492 out of 1589 answered.</p> @@ -724,7 +728,7 @@ MUI users are mainly working with low-code tools to build internal tools, landin These findings align with what the rest of our users are building as well. But it is great to see that there are low-code tools in the market that developers trust. -### If MUI considered building a low-code tool, what primary use-case would match your needs? +### If MUI considered building a low-code tool, what primary use case would match your needs? <img src="/static/blog/2021-developer-survey-results/29.png" style="width: 796px; margin-top: 16px; margin-bottom: 8px;" alt="Deliver a React design system: 22.2%; Generate high-quality React codebase after visual building: 19.2%; Building dashboards for rapid data visualization: 18.3%; Higher fidelity prototyping and demoing for accurate design handoff: 13.6%; Building internal apps when pro-code is overkill: 11.4%; Shipping landing pages with my existing React design system: 7.3%; Shipping production simple apps before moving to pro-code: 6.2%; Other: 1.8%" /> diff --git a/docs/pages/blog/2021-q1-update.md b/docs/pages/blog/2021-q1-update.md index 02be67b4f9947d..2c7bf644eed777 100644 --- a/docs/pages/blog/2021-q1-update.md +++ b/docs/pages/blog/2021-q1-update.md @@ -121,7 +121,7 @@ We have primarily focused on the data grid components, fixing a lot of bugs, but The date picker is at the border between the advanced components and the design system realms. - 📚 We have fixed the generation of the API pages. - We now document all the props supported by the public pickers components, e.g. [DatePicker](https://mui.com/api/date-picker/). + We now document all the props supported by the public pickers components, for example [DatePicker](https://mui.com/api/date-picker/). - ⚙️ We have mostly focused on addressing the technical debt present in the picker components (ported from `@materal-ui/pickers`). #### Data Grid diff --git a/docs/pages/blog/2021-q3-update.md b/docs/pages/blog/2021-q3-update.md index 5c5e582482b0db..dbb57c07d4ea15 100644 --- a/docs/pages/blog/2021-q3-update.md +++ b/docs/pages/blog/2021-q3-update.md @@ -189,7 +189,7 @@ We'll do our best, no guarantee! ### MUI Core -- 🚀 We will double down on v5 before starting to solve new large problems, e.g. a revamp of the select. +- 🚀 We will double down on v5 before starting to solve new large problems, for example a revamp of the select. We have made bold changes in this version since v4, but until recently, only a small percentage of the community was using v5. In the last few weeks, we have seen a strong influx of feedback from the community. We need to make the most of this feedback to solve regressions, improve the documentation for the new APIs, adjust the tradeoffs we took in the light of more information, and more. diff --git a/docs/pages/blog/2021.md b/docs/pages/blog/2021.md index 23a3932e0f6def..61d81f432e1b7b 100644 --- a/docs/pages/blog/2021.md +++ b/docs/pages/blog/2021.md @@ -115,7 +115,7 @@ Here is a breakdown of our [roadmap](/material-ui/discover-more/roadmap/). ### MUI Core The release of v5 has introduced a significant API churn in the community. -While our [versioning frequency](/versions/#release-frequency) aims for one major per year, we hope we can iterate on v5 during the whole year of 2022 without any breaking changes, e.g. taking full advantage of the new style engine. +While our [versioning frequency](/versions/#release-frequency) aims for one major per year, we hope we can iterate on v5 during the whole year of 2022 without any breaking changes, for example taking full advantage of the new style engine. #### Base UI diff --git a/docs/pages/blog/2023-material-ui-v6-and-beyond.md b/docs/pages/blog/2023-material-ui-v6-and-beyond.md index 337669834d1bc5..24bebf7f41ee2f 100644 --- a/docs/pages/blog/2023-material-ui-v6-and-beyond.md +++ b/docs/pages/blog/2023-material-ui-v6-and-beyond.md @@ -40,14 +40,6 @@ It's tentatively planned for Q4 of 2024. <img alt="Side-to-side comparison of a Card component using Material Design 2 and 3, respectively." src="/static/blog/2023-material-ui-v6-and-beyond/m2-m3.jpg" width="1200" height="600" loading="lazy" /> -The development for this version is already in progress, though! -See which components already support the M3 specs, through the experimental `@mui/material-next` package, by visiting the newly released [All Components page](/material-ui/all-components/). - -```diff --import Button from '@mui/material/Button'; -+import Button from '@mui/material-next/Button'; -``` - ## From design to development In addition to the updates to the React library, we've also been working on a long-requested Figma plug-in to help bridge the gap between designers and developers using Material UI. @@ -69,7 +61,7 @@ Expect to hear more about that towards the end of Q2 '24. ## Follow along and contribute We've consolidated the v6 changes in [this GitHub issue](https://github.com/mui/material-ui/issues/30660)—please feel free to chime in and participate in the discussions! -You can also always check the [public Material UI GitHub project](https://github.com/mui/material-ui/projects/26) to see the planned releases and their respective umbrella issues. +You can also always check the [Material UI GitHub project](https://github.com/orgs/mui/projects/23/views/12) to see the planned releases and their respective umbrella issues. We hope you're as excited as we are about these changes! Have a wonderful holiday season and a Happy New Year! 🎉 diff --git a/docs/pages/blog/base-ui-2024-plans.md b/docs/pages/blog/base-ui-2024-plans.md index ede47e3ba5611f..12d33c4406a1b6 100644 --- a/docs/pages/blog/base-ui-2024-plans.md +++ b/docs/pages/blog/base-ui-2024-plans.md @@ -50,7 +50,7 @@ Currently, Base UI components can be customized to your heart's content using t ``` This API, while powerful, has proven to be less than ideal in some instances. -Most notably, it's too lengthy and complicated to write and read when used with libraries such as Tailwind CSS. +Most notably, it's too lengthy and complicated to write and read when used with libraries such as Tailwind CSS. Additionally, since the `slots` and the corresponding `slotProps` are not related in terms of TypeScript types, it's possible to introduce bugs or have the compiler complain about valid code. To address these issues, we're considering adopting a new API that would assign a discrete subcomponent to each DOM node—the pattern favored by many other headless component libraries (think: `<Slider.Track />`, `<Slider.Thumb />`, etc.). diff --git a/docs/pages/blog/bringing-consistency-to-material-ui-customization-apis.js b/docs/pages/blog/bringing-consistency-to-material-ui-customization-apis.js new file mode 100644 index 00000000000000..99f0ea1e843451 --- /dev/null +++ b/docs/pages/blog/bringing-consistency-to-material-ui-customization-apis.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import TopLayoutBlog from 'docs/src/modules/components/TopLayoutBlog'; +import { docs } from './bringing-consistency-to-material-ui-customization-apis.md?muiMarkdown'; + +export default function Page() { + return <TopLayoutBlog docs={docs} />; +} diff --git a/docs/pages/blog/bringing-consistency-to-material-ui-customization-apis.md b/docs/pages/blog/bringing-consistency-to-material-ui-customization-apis.md new file mode 100644 index 00000000000000..dc43c0bb98c9aa --- /dev/null +++ b/docs/pages/blog/bringing-consistency-to-material-ui-customization-apis.md @@ -0,0 +1,70 @@ +--- +title: Bringing consistency to Material UI customization APIs +description: We're standardizing two key areas of the Material UI customization APIs to reduce complexity and cognitive overhead. Read on to learn what's changing. +date: 2024-03-18T10:00:00.000Z +authors: ['diegoandai'] +tags: ['Material UI', 'Product'] +card: true +--- + +The Material UI team is working on two initiatives to standardize the Material UI API: The first applies to overriding inner elements, and the second applies to component CSS classes. +In both cases, the purpose is to provide a more consistent developer experience for the community. + +Let's explore how these changes are taking shape: + +## Inner element overrides + +Because Material UI components often contain multiple DOM nodes, it's common to need to modify the structure, behavior, and style of inner elements. +For example, you might want to modify the Slider's thumb element to grow in size when dragged: + +<iframe src="https://codesandbox.io/embed/nw34ry?view=Editor+%2B+Preview&module=%2Fsrc%2FDemo.tsx&hidenavigation=1" + style="width:100%; height: 200px; border:0; border-radius: 4px; overflow:hidden;" + title="blog/material-ui-early-2024-standardization/slider-slots-example" + allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" + sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" + ></iframe> + +You can achieve this by providing custom components through the `slots` prop. +The demo above provides a custom thumb component that uses the Slider's internal `dragging` and `focusedThumbIndex` states to change its appearance. +[Open the CodeSandbox](https://codesandbox.io/p/sandbox/blog-material-ui-early-2024-deprecations-slider-slots-example-nw34ry?file=%2Fsrc%2FDemo.tsx) to see the implementation. + +The problem is that this slot pattern exposed through the `slots` prop is not consistent across the library. +Some components implement the `slots` prop, but others have a `components` prop, which works the same as the `slots` prop. +Other components have props named `<SlotName>Component` for more specific use cases—for example, the Accordion features a `TransitionComponent` prop for implementing custom transitions. + +The same inconsistencies are found with the `slotProps` prop, which is used to provide custom props to inner elements. +Some components have the `slotsProps` prop; others have a `componentsProps` prop; and still others have props named `<SlotName>Props`. + +This lack of consistency leads to unnecessary complexity for both developers and maintainers. +To resolve this, the `slots` and `slotProps` API will be standardized across all components, and the analogous APIs will be deprecated and eventually removed. + +## Component CSS classes + +The most common way to customize a component's look and feel is to target its CSS classes. +For example, you might want to customize a Chip's primary color and set it to a different color when it's clickable: + +<iframe src="https://codesandbox.io/embed/d7xqr6?view=Editor+%2B+Preview&module=%2Fsrc%2FDemo.tsx&hidenavigation=1" + style="width:100%; height: 200px; border:0; border-radius: 4px; overflow:hidden;" + title="blog/material-ui-early-2024-standardization/chip-classes-example" + allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" + sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" + ></iframe> + +You can do this by targeting `chipClasses.colorPrimary` and `chipClasses.clickable`, respectively. +The demo above targets `.MuiChip-colorPrimary` and `.MuiChip-clickable.MuiChip-colorPrimary` to achieve this result. +[Open the CodeSandbox](https://codesandbox.io/p/sandbox/blog-material-ui-early-2024-deprecations-chip-classes-example-d7xqr6?file=%2Fsrc%2FDemo.tsx) to see the implementation. + +The problem is that you could also use the `chipClasses.clickableColorPrimary` composed class, which composes the atomic clickable and color classes. +These composed classes bloat the API without adding significant improvements: For example, this pattern adds 26 possible CSS classes to the Chip component. + +The composed classes also reduce the predictability of the CSS classes API, as the compose order and which props get composed are arbitrary decisions. +This adds unnecessary cognitive overhead for developers as well as significant complexity for maintainers. +Because of these issues, composed CSS classes will be deprecated and eventually removed in favor of atomic class alternatives. + +## Standardization process + +This initiative aims to improve the developer experience for the Material UI community. +To provide the smoothest migration from the inconsistent APIs, they will be deprecated first and removed later, giving you plenty of time to adjust. +With each deprecation, we'll update the [migration guide](https://mui.com/material-ui/migration/migrating-from-deprecated-apis/) and provide [codemods](https://github.com/mui/material-ui/tree/HEAD/packages/mui-codemod#deprecations) to simplify the process. + +As always, we'd love to hear what you think! Please [open a GitHub issue](https://github.com/mui/material-ui/issues/new/choose) if you encounter any unexpected behavior with the standardized APIs or if you have any other suggestions you'd like us to discuss. diff --git a/docs/pages/blog/callback-support-in-style-overrides.md b/docs/pages/blog/callback-support-in-style-overrides.md index 1af851aff3e328..77325f31e9dfbc 100644 --- a/docs/pages/blog/callback-support-in-style-overrides.md +++ b/docs/pages/blog/callback-support-in-style-overrides.md @@ -68,7 +68,7 @@ import { ThemeProvider, createTheme } from '@mui/material/styles'; The callback is type-safe. -- `ownerState`: `ComponentProps` interface, e.g. `ButtonProps`, `ChipProps`, etc. +- `ownerState`: `ComponentProps` interface, for example `ButtonProps`, `ChipProps`, etc. - `theme`: `Theme` interface from `@mui/material/styles`. ```tsx diff --git a/docs/pages/blog/introducing-base-ui.md b/docs/pages/blog/introducing-base-ui.md index 67a493a3c1ebed..54af0f0943df7a 100644 --- a/docs/pages/blog/introducing-base-ui.md +++ b/docs/pages/blog/introducing-base-ui.md @@ -42,8 +42,8 @@ Base UI offers two kinds of building blocks: unstyled components and hooks. Components are more straightforward to use of the two. Place a component on a page, add your own styles, and it's ready to go! It's important to note that you are not limited to the styling options available in Material UI. -You can, of course, still use [MUI System](https://mui.com/system/getting-started/), but if you prefer Emotion, Tailwind CSS, plain CSS, or any other styling engine, they are available too! -Check out the [Working with Tailwind CSS guide](/base-ui/guides/working-with-tailwind-css/) for an example of using this library. +You can, of course, still use [MUI System](https://mui.com/system/getting-started/), but if you prefer Emotion, Tailwind CSS, plain CSS, or any other styling engine, they are available too! +Check out the [Working with Tailwind CSS guide](/base-ui/guides/working-with-tailwind-css/) for an example of using this library. In contrast to Material UI, Base UI's components do not have any default styles. They provide functionality and structure, while designers and developers are responsible for the visuals. @@ -65,7 +65,7 @@ See how it works on the live demo: Hooks take this one step further by extracting the logic from the structure entirely, so you can build from scratch using any DOM elements you need. This requires more work to implement but gives you the most freedom to customize. -Upon calling, a hook returns an object describing the component's state (i.e., whether the switch is turned on), along with methods that apply accessibility props and event handlers. +Upon calling, a hook returns an object describing the component's state (that is whether the switch is turned on), along with methods that apply accessibility props and event handlers. You should spread these props on the components you've defined, as shown below: ```tsx diff --git a/docs/pages/blog/making-customizable-components.md b/docs/pages/blog/making-customizable-components.md index 23359821c116f3..071e08af31bf0c 100644 --- a/docs/pages/blog/making-customizable-components.md +++ b/docs/pages/blog/making-customizable-components.md @@ -30,7 +30,7 @@ Usually this means that the selector with more classes applied to it is more spe For example, if we look at the Material UI `Switch` component, we have multiple subcomponents that we could expect to modify. For each of them, we assign a specific CSS class: -<img src="/static/blog/making-customizable-components/switchHighlighted.png" style="width: 692px; aspect-ratio: 173/80; margin-top: 16px; margin-bottom: 16px;" loading="lazy" alt="Switch component with highlighted sub components" /> +<img src="/static/blog/making-customizable-components/switchHighlighted.png" style="width: 692px; aspect-ratio: 173/80; margin-top: 16px; margin-bottom: 16px;" loading="lazy" alt="Switch component with highlighted subcomponents" /> Notice that each element is styled using only one CSS class—the thumb style, for example, is applied with the `css-jsexje-MuiSwitch-thumb` class, so any CSS selector that includes more than one class will override its style. @@ -59,10 +59,10 @@ you can play around with it in [CodeSandbox](https://codesandbox.io/p/sandbox/fa } ``` -### Let JS generate the CSS +### Let JavaScript generate the CSS Maybe you don't want to spend your time switching between CSS and JavaScript files, or writing long, cluttered stylesheets. -To avoid these problems you can integrate styles directly into your JS code. 🎉 +To avoid these problems you can integrate styles directly into your JavaScript code. 🎉 Because the level of customization varies across projects, Material UI's components can be customized in several different ways. For more information on this topic, check out the [Material UI customization documentation](https://mui.com/material-ui/customization/how-to-customize/). diff --git a/docs/pages/blog/material-ui-v4-is-out.md b/docs/pages/blog/material-ui-v4-is-out.md index db433c50987ad0..dfefd5a27ec56a 100644 --- a/docs/pages/blog/material-ui-v4-is-out.md +++ b/docs/pages/blog/material-ui-v4-is-out.md @@ -65,7 +65,7 @@ import { StylesProvider } from '@mui/styles'; <p class="blog-description">The DOM output once injectFirst is used.</p> -- **classes boilerplate**. Early in the v1 effort, we [decided](https://github.com/oliviertassinari/a-journey-toward-better-style) to use a CSS-in-JS styling solution: [JSS](https://cssinjs.org/). The large majority of the CSS-in-JS solutions output non-deterministic class names, e.g. `.fHmkjM`. This design decision helps the isolation of the style of each component, however, it makes the overrides harder. We introduced a `classes` API in v1 to target all our elements as an attempt to mitigate this problem. +- **classes boilerplate**. Early in the v1 effort, we [decided](https://github.com/oliviertassinari/a-journey-toward-better-style) to use a CSS-in-JS styling solution: [JSS](https://cssinjs.org/). The large majority of the CSS-in-JS solutions output non-deterministic class names, for example `.fHmkjM`. This design decision helps the isolation of the style of each component, however, it makes the overrides harder. We introduced a `classes` API in v1 to target all our elements as an attempt to mitigate this problem. We have observed the use of this API for months and have seen many people struggling with it. It can be challenging to apply the class name on the right element and requires boilerplate as well. As an attempt to further improve the situation, we have changed the class name generation to [output global class names](/system/styles/advanced/), while keeping the `classes` API working as before 💅. @@ -75,7 +75,7 @@ import { StylesProvider } from '@mui/styles'; ⚠️ Using global class names provide more power but comes with responsibility. We encourage patterns that increase your custom style isolation. -- **Pseudo-classes.** A pseudo-class is a keyword added to a selector that specifies a special state of the selected element. The native elements support a wide range of pseudo-classes, the most popular ones being: `:focus`, `:hover`, `:active`. Sometimes, Material UI can't use a pseudo-class as the state doesn't exist in the platform, e.g. the selected state of a menu item. Material UI implements support of eight different [custom pseudo-classes](/material-ui/customization/how-to-customize/#state-classes). It's important to understand that you need to increase the specificity when using a pseudo-class. For instance: +- **Pseudo-classes.** A pseudo-class is a keyword added to a selector that specifies a special state of the selected element. The native elements support a wide range of pseudo-classes, the most popular ones being: `:focus`, `:hover`, `:active`. Sometimes, Material UI can't use a pseudo-class as the state doesn't exist in the platform, for example the selected state of a menu item. Material UI implements support of eight different [custom pseudo-classes](/material-ui/customization/how-to-customize/#state-classes). It's important to understand that you need to increase the specificity when using a pseudo-class. For instance: ```css .MenuItem { @@ -120,14 +120,18 @@ Documentation was reported as the 3rd most critical pain point in the Developer - **TypeScript**. TypeScript's growth is impressive, the traffic of their documentation website has grown by a factor of 6 in 3 years. Material UI v1 was released with built-in TypeScript definitions, but we needed to do more. Sebastian has led the effort to migrate all the demos from JavaScript to TypeScript. This has two important implications. First, we type check our demos, this drastically improves our TypeScript test coverage. We have fixed many issues during the migration. Second, if you are writing your application with TypeScript, you can directly copy & paste our demos without needing to convert them, or having to fix the obscure errors. -![typescript](/static/blog/material-ui-v4-is-out/typescript.png) +![TypeScript](/static/blog/material-ui-v4-is-out/typescript.png) <p class="blog-description">https://www.typescriptlang.org traffic estimation over time.</p> ![switch](/static/blog/material-ui-v4-is-out/switch.png) +<!-- vale MUI.CorrectReferenceAllCases = NO --> + <p class="blog-description">Use the JS/TS toggle to see code in JavaScript or TypeScript</p> +<!-- vale MUI.CorrectReferenceAllCases = YES --> + - **i18n**. Developers come to Material UI's documentation from all around the world. We want to include as many people as possible 🌎🌍🌏. We have completed the effort started in v3 by working on the Algolia search support, Google search indexing, Table Of Contents and Side Nav infrastructure. We would like to thank [Danica Shen](https://github.com/DDDDDanica), [Dominik Engel](https://github.com/Domino987), and [Jairon Alves Lima](https://github.com/jaironalves) for their heroic work on the 🇨🇳, 🇩🇪 and 🇧🇷 translations, while not forgetting the other 348 (and growing) translators. diff --git a/docs/pages/blog/mui-core-v5.md b/docs/pages/blog/mui-core-v5.md index 0a03f8293c3f1f..3692acffd9c502 100644 --- a/docs/pages/blog/mui-core-v5.md +++ b/docs/pages/blog/mui-core-v5.md @@ -106,7 +106,7 @@ const StyledDiv = styled.div` <p class="blog-description"><a href="https://codesandbox.io/p/sandbox/elastic-yonath-uedfv?file=/src/App.js">CodeSandbox</a></p> You can find it in [styled-components](https://styled-components.com/), [emotion](https://emotion.sh/docs/styled), [goober](https://goober.js.org/), [stitches](https://stitches.dev/docs/api#styled), or [linaria](https://linaria.dev/). -While Material UI is compatible with any styling solution (as long as the styles have more specificity, for example, Tailwind CSS), many developers still felt the need to learn something new: the [`makeStyles`](https://mui.com/system/styles/basics/#hook-api) API. +While Material UI is compatible with any styling solution (as long as the styles have more specificity, for example, Tailwind CSS), many developers still felt the need to learn something new: the [`makeStyles`](https://mui.com/system/styles/basics/#hook-api) API. 2. Our React integration with JSS (`@mui/styles`) is **too slow** to unlock the next layer of customization DX we aim for. The static CSS generation using v4 was fast enough, even [faster](https://codesandbox.io/p/sandbox/nb05w?file=/src/App.js) than emotion, @@ -651,7 +651,7 @@ This breaking change is an opportunity to drop the support of legacy upstream de - We have updated the minimum supported React version from 16.8 to **17.0**. The breaking changes released between the two versions are [very limited](https://legacy.reactjs.org/blog/2020/10/20/react-v17.html). - We have updated the supported browsers. - - IE: **partial**. We have kept the logic added in the past to support IE 11, + - Internet Explorer: **partial**. We have kept the logic added in the past to support IE 11, however, we have stopped actively working on it. We can't guarantee that it works correctly. It's discontinued. - Edge: from 14 to **91**. The minimum version based on Chromium. - Firefox: from 52 to **78**. @@ -701,9 +701,9 @@ We hope we can reach 50% of the React community by 2026. This is an ambitious go ### A public roadmap -You can use our public roadmap on GitHub to learn about what features we're working on, what stage they're at, and when we expect to bring them to you: +You can use the GitHub projects to learn about what features we're working on, what stage they're at, and when we expect to bring them to you: -- [MUI Core](https://github.com/mui/material-ui/projects/25) +- [MUI Core](https://github.com/mui/material-ui/projects?query=is:open) - [MUI X](https://github.com/mui/mui-x/projects/1) We offer this transparency into what we plan to work on so that you can plan better and share feedback earlier to influence what we're building. @@ -758,7 +758,7 @@ Once we would have grown the team and made enough progress, we will expand to a We plan to run extended research and surveys. We have already identified that accessibility is something leading companies care about. -We are planning to cover more user interaction states for prototyping, e.g. focus-visible. +We are planning to cover more user interaction states for prototyping, for example focus-visible. ## Thank you diff --git a/docs/pages/blog/mui-next-js-app-router.md b/docs/pages/blog/mui-next-js-app-router.md index c6be880126d583..344fc0faa7b52c 100644 --- a/docs/pages/blog/mui-next-js-app-router.md +++ b/docs/pages/blog/mui-next-js-app-router.md @@ -34,7 +34,7 @@ Additionally, we've created guides to walk you through setting up an app using t We also have example repos for each, with everything already set up for you: - [Material UI example](https://github.com/mui/material-ui/tree/master/examples/material-ui-nextjs-ts) -- [Base UI with Tailwind CSS example](https://github.com/mui/material-ui/tree/master/examples/base-ui-nextjs-tailwind-ts) +- [Base UI with Tailwind CSS example](https://github.com/mui/material-ui/tree/master/examples/base-ui-nextjs-tailwind-ts) - [Joy UI example](https://github.com/mui/material-ui/tree/master/examples/joy-ui-nextjs-ts) ## What comes next diff --git a/docs/pages/blog/mui-product-comparison.md b/docs/pages/blog/mui-product-comparison.md index 2bf4a02a463284..5268b3104d6e57 100644 --- a/docs/pages/blog/mui-product-comparison.md +++ b/docs/pages/blog/mui-product-comparison.md @@ -21,7 +21,7 @@ Though our roots are in [Material Design](https://material.io/), we're branching Our primary offerings fall into two product lines: Core and X. MUI Core contains our foundational component libraries (like Material UI), while MUI X offers components that are significantly more complex (like the Data Grid). -We're also in the early stages of developing a low-code internal tool builder called [MUI Toolpad](https://mui.com/toolpad/), which enables you to build with every Core and X component in a drag-and-drop interface. +We're also in the early stages of developing a low-code internal tool builder called [Toolpad](https://mui.com/toolpad/), which enables you to build with every Core and X component in a drag-and-drop interface. Read on for more details on each of our products. @@ -93,7 +93,7 @@ Get started in the [Base UI docs](/base-ui/getting-started/). #### Key features - **Total control over styles:** Unlike Material UI and Joy UI, Base UI doesn't ship with any default styles or styling solution. - Write CSS however you'd prefer—vanilla, modules, styled-components—or integrate a styling library like Tailwind CSS or Emotion. + Write CSS however you'd prefer—vanilla, modules, styled-components—or integrate a styling library like Tailwind CSS or Emotion. - **Hooks for fully custom components:** When pre-built components aren't flexible enough, low-level hooks enable you to quickly add sophisticated functionality to your custom components. - **Accessibility:** Base UI components are built with accessibility in mind. We do our best to make all components screen reader-friendly, and offer suggestions for optimizing accessibility throughout our documentation. - **The core of MUI Core:** Base UI serves as the scaffold for Joy UI components, and future versions Material UI will also be built with Base UI as the foundation. @@ -174,9 +174,9 @@ Components include the Date Picker, Time Picker, Date Range Picker, and Date Tim - Enterprise apps using advanced date and time validation with constraints. - Apps built with MUI Core libraries that need date and time functionality. -## MUI Toolpad +## Toolpad -<img src="/static/blog/mui-product-comparison/mui-toolpad.png" style="width: 692px; margin-bottom: 24px; aspect-ratio: 173/75;" loading="lazy" alt="Small screenshot of MUI Toolpad's interface." /> +<img src="/static/blog/mui-product-comparison/mui-toolpad.png" style="width: 692px; margin-bottom: 24px; aspect-ratio: 173/75;" loading="lazy" alt="Small screenshot of Toolpad's interface." /> Toolpad is a self-hosted low-code admin builder designed to extend MUI's suite of React components. It's designed for developers of all trades who want to save time building internal applications. @@ -184,7 +184,7 @@ Drag and drop pre-built UI components, connect your data sources, and your app i ### Key features -- **Build faster than ever:** With MUI Toolpad, your development time can be measured in minutes rather than hours or days. Skip the mindless busywork of UI development and get straight to the core business logic. +- **Build faster than ever:** With Toolpad, your development time can be measured in minutes rather than hours or days. Skip the mindless busywork of UI development and get straight to the core business logic. - **Use the components you already know:** Toolpad comes preloaded with both MUI Core and X libraries, giving you the full power of MUI's components in a drag-and-drop interface. - **Extensible with code:** Start with only bare-bones JavaScript to get up and running, and then jump from "low code" to "pro code" as needed to add custom features and functionality. diff --git a/docs/pages/blog/mui-x-end-v6-features.md b/docs/pages/blog/mui-x-end-v6-features.md index 29c3acd6ad8d57..19dd6b95f107bc 100644 --- a/docs/pages/blog/mui-x-end-v6-features.md +++ b/docs/pages/blog/mui-x-end-v6-features.md @@ -91,7 +91,7 @@ The Date Picker animations have been significantly smoothened to ensure a much m ### Customization playgrounds We're constantly improving our documentation and working to better communicate how to use our components effectively. -With the new customization playgrounds, you can now tailor the style of [Date Picker](/x/react-date-pickers/date-picker/#customization) and experiment with multiple combinations of [sub-components](/x/react-date-pickers/playground/) to achieve the look and feel you desire. +With the new customization playgrounds, you can now tailor the style of [Date Picker](/x/react-date-pickers/date-picker/#customization) and experiment with multiple combinations of [subcomponents](/x/react-date-pickers/playground/) to achieve the look and feel you desire. ## Data Grid @@ -146,7 +146,7 @@ Most notably: - New UI for column management - Pivoting for the [Premium](/x/react-data-grid/#premium-plan) version -We'll continue to expand our portfolio of Charts, including [Heat Map](/x/react-charts/heat-map/), [Funnel](/x/react-charts/funnel/), and [Gantt](/x/react-charts/gantt/); and explore virtualization and other advanced use cases for the Tree View component. +We'll continue to expand our portfolio of Charts, including [Heatmap](/x/react-charts/heat-map/), [Funnel](/x/react-charts/funnel/), and [Gantt](/x/react-charts/gantt/); and explore virtualization and other advanced use cases for the Tree View component. We encourage you to upvote issues on GitHub to help us prioritize. Your input directly influences our development schedule, so don't hesitate to let us know what matters most to you! diff --git a/docs/pages/blog/mui-x-mid-v6-features.md b/docs/pages/blog/mui-x-mid-v6-features.md index 07f8a1c8f6f7a1..64c7da76591a52 100644 --- a/docs/pages/blog/mui-x-mid-v6-features.md +++ b/docs/pages/blog/mui-x-mid-v6-features.md @@ -131,7 +131,7 @@ Think of a file system navigator displaying folders and files or a navigation li Keep on the look out on our next blog for the Tree View migration. -We decided to migrate this component to MUI X as there are still many features that would be great to build (e.g. checkbox, drag & drop, virtualization) and it's usually not a significant component of a design system. +We decided to migrate this component to MUI X as there are still many features that would be great to build (for example checkbox, drag & drop, virtualization) and it's usually not a significant component of a design system. Head to [MUI Core vs. MUI X](https://mui-org.notion.site/X-FAQ-c33e9a7eabba4da1ad7f8c04f99044cc) if you would like to learn more about this decision. ## Feedback diff --git a/docs/pages/blog/mui-x-v5.md b/docs/pages/blog/mui-x-v5.md index f77faf8a26e622..3e68847e5763e1 100644 --- a/docs/pages/blog/mui-x-v5.md +++ b/docs/pages/blog/mui-x-v5.md @@ -92,7 +92,7 @@ These changes improved the developer experience when using the `apiRef` methods: - We removed the `state` structure from the public API. Access to data in the state should always be done through `apiRef` methods (`apiRef.current.getSelectedRows`) or selectors (`selectedGridRowsSelector`). - We renamed most selectors to have a consistent naming convention, making it easier to deduce their name or infer purpose. -- We restructured our state so that each feature has a single sub-state, and the feature hook is the only one to update it (e.g. `state.filter` is only managed by the `useGridFilter` hook, which exposes methods for both internal and 3rd party code to interact with this state). +- We restructured our state so that each feature has a single sub-state, and the feature hook is the only one to update it (for example `state.filter` is only managed by the `useGridFilter` hook, which exposes methods for both internal and 3rd party code to interact with this state). The work on this topic isn't over. We have several developments in progress or under discussion to improve the developer experience when using the advanced features of the grid. Here are a few that should be release in the following months: diff --git a/docs/pages/blog/mui-x-v6.md b/docs/pages/blog/mui-x-v6.md index 8ee0e1926b58fb..4b666d5c82b42a 100644 --- a/docs/pages/blog/mui-x-v6.md +++ b/docs/pages/blog/mui-x-v6.md @@ -66,7 +66,7 @@ And if you want to understand more about our view of the open-source/commercial ### Improved column menu Another significant step in terms of customization but also usability; the v6 [column menu](/x/react-data-grid/column-menu/) now provides support for icons, menu groups, custom items and actions, and more. -We've redesigned this sub-component to make it as extensible as possible. +We've redesigned this subcomponent to make it as extensible as possible. <a href="/x/react-data-grid/column-menu/"> <img src="/static/blog/mui-x-v6/column-menu-custom-action.png" loading="lazy" alt="Column menu custom action" width="1636" height="808" /> @@ -176,7 +176,7 @@ import { DateField } from '@mui/x-date-pickers/DateField'; ### Improved layout customization -Combining the slots concept with the grid layout, you can now rearrange, extend, and customize most of the sub-components used in the Pickers UI. +Combining the slots concept with the grid layout, you can now rearrange, extend, and customize most of the subcomponents used in the Pickers UI. See [the documentation about it](/x/react-date-pickers/custom-layout/) and this quick overview: ```tsx diff --git a/docs/pages/blog/mui-x-v7-beta.md b/docs/pages/blog/mui-x-v7-beta.md index 7d785326c1b22e..258a29b7fc57e7 100644 --- a/docs/pages/blog/mui-x-v7-beta.md +++ b/docs/pages/blog/mui-x-v7-beta.md @@ -74,7 +74,7 @@ While string values remain compatible for these types, any updates to the `filte ### Smaller bundle size -The introduction of a separate entry point for locales has significantly reduced the bundle size of the barrel index when tree-shaking isn't operational (e.g. Webpack in dev mode). +The introduction of a separate entry point for locales has significantly reduced the bundle size of the barrel index when tree-shaking isn't operational (for example Webpack in dev mode). For example with the `@mui/x-data-grid` npm package, this change led to a reduction of approximately 22% – shrinking the bundle size from [114.2kB](https://bundlephobia.com/package/@mui/x-data-grid@6.19.2) to [88.5kB](https://bundlephobia.com/package/@mui/x-data-grid@7.0.0-beta.0). @@ -184,7 +184,7 @@ As we approach the stable release of v7, our roadmap is well-defined, focusing o ### Data Grid -- [Improved Server side integration](https://next.mui.com/x/react-data-grid/server-side-data/) +- [Improved Server-side integration](https://next.mui.com/x/react-data-grid/server-side-data/) - [Column management panel with support for pivoting](https://github.com/mui/mui-x/issues/5700)[<span class="plan-pro"></span>](/x/introduction/licensing/#pro-plan 'Pro plan') - [Pivoting](https://github.com/mui/mui-x/issues/214) [<span class="plan-premium"></span>](/x/introduction/licensing/#premium-plan 'Premium plan') diff --git a/docs/pages/blog/toolpad-use-cases.md b/docs/pages/blog/toolpad-use-cases.md index 3c6d2a307fe899..30879950a648e4 100644 --- a/docs/pages/blog/toolpad-use-cases.md +++ b/docs/pages/blog/toolpad-use-cases.md @@ -30,7 +30,7 @@ Let's delve into four scenarios that Toolpad has successfully addressed: ## 1. Support key validator -We offer a priority support service to our MUI X premium customers: their queries get an expedited response within 24 hours. +We offer a priority support service to our MUI X Premium customers: their queries get an expedited response within 24 hours. They share their issue through a Priority Support template in our repository where they're directed to validate their license key, and once it's validated, the 24-hour countdown starts. <a href="https://tools-public.mui.com/prod/pages/validateSupport"> @@ -61,7 +61,7 @@ Your browser does not support the video tag. </video> We opted for Toolpad since Metabase doesn't support importing data from REST APIs. -This is possible in Google Sheets but it requires writing a lot of JS code, and since we wanted to embed it in a [Notion page](https://mui-org.notion.site/KPIs-1ce9658b85ce4628a2a2ed2ae74ff69c?pvs=4#3974cb6ed12b4c5a9013bac63113e3bc), Toolpad was the ideal choice. +This is possible in Google Sheets but it requires writing a lot of JavaScript code, and since we wanted to embed it in a [Notion page](https://mui-org.notion.site/KPIs-1ce9658b85ce4628a2a2ed2ae74ff69c?pvs=4#3974cb6ed12b4c5a9013bac63113e3bc), Toolpad was the ideal choice. Toolpad handles state management and routing, and simplifies query building and data binding, removing the need to write glue code. You can explore both of the aforementioned apps in dev mode on your device by running the underlying [Node application](https://github.com/mui/mui-public/tree/HEAD/tools-public). @@ -84,7 +84,7 @@ Thanks to Toolpad we've managed to bring it all under one roof, dramatically imp ## 4. Contributor payout -We have a script to fetch monthly payout data for contributors to the MUI Store. +We have a script to fetch monthly payout data for contributors to the MUI Store. Our operations team is responsible for paying contributors, but the script proved too technically challenging for them to run without help from our engineers. We solved this problem by importing the script into Toolpad and creating a UI for it. The video below shows how a user can select the dates, run the script, and receive text that's properly formatted to copy and paste directly into Slack communications: diff --git a/docs/pages/careers/design-engineer.md b/docs/pages/careers/design-engineer.md index 8129a647f7a9e2..5523f7696b87db 100644 --- a/docs/pages/careers/design-engineer.md +++ b/docs/pages/careers/design-engineer.md @@ -50,7 +50,7 @@ Our products empower React developers to build awesome applications faster – w But while we are [the leading](https://tsh.io/state-of-frontend/#over-the-past-year-which-of-the-following-design-systems-was-your-favorite-go-to-solution) UI design system in the frontend space, the adoption of MUI is only at 25%. More importantly, the challenges of developers and designers to solve when creating UIs go way beyond the ones of design systems. We envision a future where MUI becomes the default toolkit for web developers to create UIs. -It's why we started Base UI, Joy UI, and MUI Toolpad. Design is key to achieving this goal. +It's why we started Base UI, Joy UI, and Toolpad. Design is key to achieving this goal. ## The role @@ -59,7 +59,7 @@ It's why we started Base UI, Joy UI, and MUI Toolpad. Design is key to achiev Depending on the day, you'll: - Work closely with our designers to prototype and implement new components, features, and screens. -- Design and build entire experiences for sites like mui.com or products like MUI Toolpad. +- Design and build entire experiences for sites like mui.com or products like Toolpad. - Concept and prototype UX components and motion studies for components like the date picker. - Extend our different design systems (Material UI, Joy UI). - Create appealing new demos in the docs. diff --git a/docs/pages/careers/designer.md b/docs/pages/careers/designer.md index d72c3701d2f656..00a476a3f5e160 100644 --- a/docs/pages/careers/designer.md +++ b/docs/pages/careers/designer.md @@ -43,7 +43,7 @@ Currently, we have five main projects that are not at all related to MD: - [MUI Joy (working title)](https://github.com/mui/material-ui/discussions/29024): a second design system as an alternative to Material Design. - [MUI X](https://mui.com/x/): as mentioned, a growing set of advanced components. Today, the flagship is the [Data Grid](https://mui.com/x/react-data-grid/), a game-changing component for presenting large amounts of data, which integrates perfectly with MUI Core. -- MUI Toolpad: a very recent endeavor aimed at exploring how our users can visually create apps 10x faster with the power of low-code and the flexibility of pro-code. +- Toolpad: a very recent endeavor aimed at exploring how our users can visually create apps 10x faster with the power of low-code and the flexibility of pro-code. We also know, especially due to [our annual Developer Survey](https://mui.com/blog/2021-developer-survey-results/), that design quality plays a huge part when developers are considering component library options. Therefore, we need to grow the design team to help us push these initiatives further. @@ -57,14 +57,14 @@ Some criteria for applying to this role: - **Level**: [3 or above](https://mui-org.notion.site/Design-levels-aa01996ca7e0481e80479ad47c8f28a4). We need someone experienced enough to help two different teams with hard problems. -You'll be responsible for ensuring that the MUI Toolpad and MUI X teams have spot-on design and product work. +You'll be responsible for ensuring that the Toolpad and MUI X teams have spot-on design and product work. Given that each product is at a different stage, at this moment we believe that one person is enough to oversee the design function for both teams. You'll have the freedom, trust, and help you need to balance and tackle all the work. You'll also be the second designer of a growing design team, so we'll also need your help to shape this growth. ### Here are a few initiatives you might work on -- Help design the first version of MUI Toolpad, from early strategy to its look and feel. +- Help design the first version of Toolpad, from early strategy to its look and feel. - Evolve and refine the Data Grid UX for features such as filtering, column pinning, row editing, and more. - Help set the bar higher for MUI X documentation, from visual design to copywriting. - Support the design team growth by promoting design/product culture, and hiring new members. diff --git a/docs/pages/careers/developer-advocate.md b/docs/pages/careers/developer-advocate.md index cd3e3f79d6bba9..917e84fa830b79 100644 --- a/docs/pages/careers/developer-advocate.md +++ b/docs/pages/careers/developer-advocate.md @@ -77,7 +77,7 @@ For the right candidate: - Build example apps with MUI products and create how-to content around them - Research and write case studies - Overhaul the [Showcase](https://mui.com/material-ui/discover-more/showcase/) -- Revamp the company blog infrastructure to empower less technical teammates to contribute (e.g. HR) +- Revamp the company blog infrastructure to empower less technical teammates to contribute (for example HR) - Contribute to integrations with other popular libraries and frameworks - Create a learning section in the documentation for hybrid written and video tutorials diff --git a/docs/pages/careers/developer-experience-engineer.md b/docs/pages/careers/developer-experience-engineer.md index 658bf24da113f3..74294e3ffb173c 100644 --- a/docs/pages/careers/developer-experience-engineer.md +++ b/docs/pages/careers/developer-experience-engineer.md @@ -66,7 +66,7 @@ Depending on the day, you'll: - You will collaborate with Developer Advocates, Designers, Product Managers, Engineering Managers, Marketing, and other stakeholders to identify opportunities for improvement. - Inform the technical approach and architecture of MUI as it relates to developer experiences. - Help contribute to the MUI community by providing code review, mentorship, and support to MUI employees, community members, and partners. -- Advocate and support improvements to MUI to improve development and integration of tools and plugins, e.g. Storybook, Tailwind CSS. +- Advocate and support improvements to MUI to improve development and integration of tools and plugins, for example Storybook, Tailwind CSS. - Work on issues and improvements critical to the success of MUI users and the broader community. - Foster a culture of learning through iterative improvements and strong collaboration with UX research. diff --git a/docs/pages/careers/engineering-manager.md b/docs/pages/careers/engineering-manager.md index ce8bc33dd99698..3882bc0300ca6d 100644 --- a/docs/pages/careers/engineering-manager.md +++ b/docs/pages/careers/engineering-manager.md @@ -1,6 +1,6 @@ # Engineering Manager — Toolpad -<p class="description">You will grow the small engineering team currently working on MUI Toolpad.</p> +<p class="description">You will grow the small engineering team currently working on Toolpad.</p> ## Details of the role @@ -42,7 +42,7 @@ Our mission is to empower as many people as possible to build great UIs, faster. The faster and simpler it is, and the broader the audience that can create custom UIs, the better. We believe that the best way to improve on these dimensions is to eliminate [80%](https://www.youtube.com/watch?v=GnO7D5UaDig&t=2451s) of the code that has to be written. -A few months back we started to work on [MUI Toolpad](https://github.com/mui/mui-toolpad), an ambitious project to deliver on this objective. +A few months back we started to work on [Toolpad](https://github.com/mui/mui-toolpad), an ambitious project to deliver on this objective. We have found the beginning of a market fit in this low-code segment. We need help to structure & grow the engineering team. @@ -68,11 +68,11 @@ Depending on the day, you'll: - Act as a servant leader for the engineers that report to you. You will support the career growth of individuals on your team. - Develop a great work environment. - Work directly with users and the engineering team to improve the product. -- Improve our processes, e.g. the lifecycle of feature development from design through testing and release. +- Improve our processes, for example the lifecycle of feature development from design through testing and release. For the right candidate: -- Working with the Leadership to construct and execute a hiring plan to grow the engineering team on toolpad from one to multiple +- Working with the Leadership to construct and execute a hiring plan to grow the engineering team on Toolpad from one to multiple ## Who we're looking for diff --git a/docs/pages/careers/full-stack-engineer.md b/docs/pages/careers/full-stack-engineer.md index 2e2e9e5420380e..99184d1e26896a 100644 --- a/docs/pages/careers/full-stack-engineer.md +++ b/docs/pages/careers/full-stack-engineer.md @@ -1,6 +1,6 @@ # Full-stack Engineer — Toolpad (future role) -<p class="description">You will join the MUI Toolpad team, to explore the role of MUI in the low code space and help bring the early prototype to a usable product.</p> +<p class="description">You will join the Toolpad team, to explore the role of MUI in the low code space and help bring the early prototype to a usable product.</p> ## Details of the role @@ -40,7 +40,7 @@ We need talented people to keep that going! ### Why this is interesting -The MUI Toolpad application offers a wide variety of engineering challenges. Including +The Toolpad application offers a wide variety of engineering challenges. Including - In-browser sandboxing and manipulation of live web applications - Drag & drop visual editor @@ -54,8 +54,8 @@ Depending on the day, you'll: - **Help guide architectural decisions**. You'll join us in defining and refining the initial product and also help bring the conversation public as the MVP grows. You'll also interface with other teams at MUI as you'll be building on top of their work. - **Contribute to implementing new features**. MUI is a complex codebase. It's built on top of cutting-edge web technologies to build the low-code tool for the future. -- **Reduce friction**. A large amount of the work on MUI Toolpad is reducing friction and making it easier to use. As our MVP grows, our focus will shift from "making it work" towards "making it easy to work with". -- **Collaborate with the community**. MUI Toolpad will be open-sourced. As the community grows you'll act as a steward to steer it towards success. This includes reviewing issues, pull requests and questions, and guiding aspiring contributors to make meaningful contributions. +- **Reduce friction**. A large amount of the work on Toolpad is reducing friction and making it easier to use. As our MVP grows, our focus will shift from "making it work" towards "making it easy to work with". +- **Collaborate with the community**. Toolpad will be open-sourced. As the community grows you'll act as a lead to steer it towards success. This includes reviewing issues, pull requests and questions, and guiding aspiring contributors to make meaningful contributions. - **Experiment and play**. Great, unexpected features and heisenbug fixes have come from a number of sources — relentlessly methodical processes of elimination, free-flowing team collaboration, inspiration by adjacent libraries and projects, and difficult-to-explain individual strokes of brilliance. Whatever your preferred style is for creating new things that others might not have thought of, you'll find a welcome home on the team. - **Take ownership of features from idea/mockup to live deployment**. You'll shape and guide the direction of crucial new features. - **Ship. Early and often**. You'll iterate and ship frequently. You'll have a real impact on the end-user experience and you'll love working on a team that builds stunning UIs and prioritizes delivering real user value as often as possible. @@ -65,7 +65,7 @@ Depending on the day, you'll: - **You'll be at the cutting edge of application development** — working on one of the fastest-growing UI libraries on the market. - **You'll be part of an active, open, friendly community** of developers that are excited about building awesome applications. -- **Your role will be key to making MUI Toolpad the go-to low code tool** for internal application building. +- **Your role will be key to making Toolpad the go-to low code tool** for internal application building. ## The worst parts of this job @@ -79,8 +79,8 @@ We're looking for someone with both strong front-end and back-end skills. More i ### Required -- **Expertise in the modern JavaScript ecosystem**. MUI Toolpad is built on the shoulders of giants, making use of technologies such as ES2021, TypeScript, Node.js, React, Next.js, Webpack, and Babel. -- **Expertise in backend development**. MUI Toolpad interfaces with multiple databases, both SQL and NoSQL, as well as APIs such as REST and GraphQL. You'll need to be comfortable in learning and integrating new backend technologies fast. You'll need to have a good understanding of distributed systems and some knowledge of CRDTs is a plus. +- **Expertise in the modern JavaScript ecosystem**. Toolpad is built on the shoulders of giants, making use of technologies such as ES2021, TypeScript, Node.js, React, Next.js, Webpack, and Babel. +- **Expertise in backend development**. Toolpad interfaces with multiple databases, both SQL and NoSQL, as well as APIs such as REST and GraphQL. You'll need to be comfortable in learning and integrating new backend technologies fast. You'll need to have a good understanding of distributed systems and some knowledge of CRDTs is a plus. - **A track record of demonstrating an eye for product and solving real-world user problems**. If you have a knack for solving problems at the root cause, shipping beautiful user interfaces and intuitive APIs, we want you on our team. - **Experience building and shipping production code in a team setting** with a passion for writing tested, performant, and high-quality code. - **Strong written and verbal communication skills**. As part of the team, you'll interface both directly and indirectly with community members and enterprise customers, and contribute to user documentation. Clear communication is fundamental in creating intuitive and compelling resources. diff --git a/docs/pages/careers/head-of-operations.md b/docs/pages/careers/head-of-operations.md index 0957c857312be0..500387a1b66e0b 100644 --- a/docs/pages/careers/head-of-operations.md +++ b/docs/pages/careers/head-of-operations.md @@ -85,7 +85,7 @@ Depending on the day, you'll: - Strong analytical, and problem-solving skills, with the ability to make data-driven decisions. - A high sense of curiosity, enjoying self-learning to get things done. You are [versatile](https://review.firstround.com/the-adaptable-leader-is-the-new-holy-grail-become-one-hire-one). - Excited about the ambiguity of an entrepreneurial, growing company, and able to juggle many projects and responsibilities. -- Bachelor's degree in a related field (e.g. computer science, business management). +- Bachelor's degree in a related field (for example computer science, business management). - 1+ year of experience in a similar role. ### Nice to have (but not required) diff --git a/docs/pages/careers/product-engineer.md b/docs/pages/careers/product-engineer.md index 829700079e6b27..9a217e087d9ee6 100644 --- a/docs/pages/careers/product-engineer.md +++ b/docs/pages/careers/product-engineer.md @@ -61,7 +61,7 @@ Depending on the day, you'll: - Define the product direction - Review new items submitted by the contributors to be hosted on the marketplace - Fix root problems raised by store customers on the support channels -- Take care of operational needs, e.g. automate payouts, create sales reports +- Take care of operational needs, for example automate payouts, create sales reports ### Here are a few initiatives you might work on diff --git a/docs/pages/careers/product-manager.md b/docs/pages/careers/product-manager.md index 2061c3dfab5f0f..b91e6ea1982679 100644 --- a/docs/pages/careers/product-manager.md +++ b/docs/pages/careers/product-manager.md @@ -64,9 +64,9 @@ Depending on the day, you'll: - You will coordinate with the engineering to ensure that the product being delivered at each iteration solves the problem. This involves growing a deep understanding of our technical choices and constraints. - You will drive the growth of the product by owning KPIs. -- You will grow and cultivate a deep understanding of the problems that developers have when they create simple applications (e.g. admins, prototypes). This means that you will observe and reach out to the community, run research interviews and share your insights with the team. +- You will grow and cultivate a deep understanding of the problems that developers have when they create simple applications (for example admins, prototypes). This means that you will observe and reach out to the community, run research interviews and share your insights with the team. - You will keep a close eye on feature requests, issues, and general improvements, to curate opportunities based on our strategy. -- You will build a strategy for your product area and contribute to the overall product strategy, e.g. establishing a go-to-market strategy. +- You will build a strategy for your product area and contribute to the overall product strategy, for example establishing a go-to-market strategy. - You will assess the impact of initiatives through telemetry data and qualitative feedback to help us develop our understanding further, and decide on the next steps. ## Who we're looking for diff --git a/docs/pages/careers/product-marketing-manager.md b/docs/pages/careers/product-marketing-manager.md index 806403770bb2cf..5987cd5bc985fb 100644 --- a/docs/pages/careers/product-marketing-manager.md +++ b/docs/pages/careers/product-marketing-manager.md @@ -64,7 +64,7 @@ Depending on the day, you'll: - **Personas**. Build deep knowledge and understanding of MUI's technical buyer personas and the journey of our enterprise prospects via market research, prospect/customer conversations, and internal conversations. - **Market research**. Use customer and competitive research to tailor effective messaging to our Enterprise audience. - **Content**. Plan and create impactful marketing materials mapped to buying journey. Generate high-quality content for customers including website copy/landing pages, blogs, webinar content, use cases, customer stories, showcases, and more. -- **Paid channels**. Build and help execute marketing campaigns from the bottom up (identify goals, risks, audience, messages, and tactics), e.g. we can explore SEA & conference sponsoring. +- **Paid channels**. Build and help execute marketing campaigns from the bottom up (identify goals, risks, audience, messages, and tactics), for example we can explore SEA & conference sponsoring. - **Sales**. Partner with the Sales team to create effective sales enablement materials. - **Events** Potentially: Lead corporate event programs that result in relationship building, lead/pipeline generation and acceleration, customer engagement, retention, and brand awareness. diff --git a/docs/pages/careers/react-tech-lead-core.md b/docs/pages/careers/react-tech-lead-core.md index c077a706b5ad8f..506ea588f59bf9 100644 --- a/docs/pages/careers/react-tech-lead-core.md +++ b/docs/pages/careers/react-tech-lead-core.md @@ -66,7 +66,7 @@ You will extend the [React Engineer](https://mui-org.notion.site/Software-Engine - **Nurture community contributions**. You will provide guidance and direction to unlock the contributions of the community. Your time will often be way better spent doing this than fixing the problems yourself. - **Shape the product**. You will be laser-focused on the end goal. It's not about solving technical challenges but about the problem solved for the users. - **Enable quality work**. You will: - - Embody and foster the engineering culture, e.g. rigorousness, push for small single-purpose PRs, encourage peer reviews, create strong feedback loops between decision and outcome. + - Embody and foster the engineering culture, for example rigorousness, push for small single-purpose PRs, encourage peer reviews, create strong feedback loops between decision and outcome. - Empower the team to aim for high-quality outputs. By doing such it aims for the success of delivered solutions. - Push for consistency, follow what's going on in the other teams. - **Keep technical debt in check**. You will make sure we can keep shipping features at a reasonable pace, align the team on "one way" of doing things and make sure engineers follow the conventions. diff --git a/docs/pages/careers/react-tech-lead-x-grid.md b/docs/pages/careers/react-tech-lead-x-grid.md index dac3f89432dfd1..d0dcc6b75fa8e2 100644 --- a/docs/pages/careers/react-tech-lead-x-grid.md +++ b/docs/pages/careers/react-tech-lead-x-grid.md @@ -44,7 +44,7 @@ We need to: - build an headless version for Base UI. - build advanced, in browsers, data analysis features like pivoting and charts integration. -- build a strong integration with backend APIs, e.g. to handle >100M rows. +- build a strong integration with backend APIs, for example to handle >100M rows. We also need help to continue to make the components easier to use, make it more customizable, [improve performance](https://www.causal.app/blog/react-perf), make it more accessible, improve the health of the open-source by engaging and collaborating with the community, guide developers to answers, and just generally being a positive presence in the community. @@ -70,7 +70,7 @@ You will extend the [React Engineer](https://mui-org.notion.site/Software-Engine - **Nurture community contributions**. You will provide guidance and direction to unlock the contributions of the community. Your time will often be way better spent doing this than fixing the problems yourself. - **Shape the product**. You will be laser-focused on the end goal. It's not about solving technical challenges but about the problem solved for the users. - **Enable quality work**. You will: - - Embody and foster the engineering culture, e.g. rigorousness, push for small single-purpose PRs, encourage peer reviews, create strong feedback loops between decision and outcome. + - Embody and foster the engineering culture, for example rigorousness, push for small single-purpose PRs, encourage peer reviews, create strong feedback loops between decision and outcome. - Empower the team to aim for high-quality outputs. By doing such it aims for the success of delivered solutions. - Push for consistency, follow what's going on in the other teams. - **Keep technical debt in check**. You will make sure we can keep shipping features at a reasonable pace, align the team on "one way" of doing things and make sure engineers follow the conventions. diff --git a/docs/pages/careers/senior-designer.md b/docs/pages/careers/senior-designer.md index 3084ef228d1961..8b350abf778317 100644 --- a/docs/pages/careers/senior-designer.md +++ b/docs/pages/careers/senior-designer.md @@ -52,7 +52,7 @@ However, despite Material UI – our biggest library – being [the leading](ht More importantly, our challenges go way beyond the ones of design systems. We envision a future where MUI becomes the default toolkit for web developers to create UIs. -It's why we've been expanding our offering with Joy UI, Base UI, and MUI Toolpad. +It's why we've been expanding our offering with Joy UI, Base UI, and Toolpad. Design is foundational to achieving this goal. ## The role diff --git a/docs/pages/careers/technical-product-manager.md b/docs/pages/careers/technical-product-manager.md index 9415e266af253e..47dc7cb5233c90 100644 --- a/docs/pages/careers/technical-product-manager.md +++ b/docs/pages/careers/technical-product-manager.md @@ -62,7 +62,7 @@ Depending on the day, you'll: - You will drive the revenue and community growth by owning KPIs. - You will grow and cultivate a deep understanding of the problems that developers have when they deal with enterprise applications. This means that you will observe and reach out to the community, run research interviews and share your insights with the team. - You will keep a close eye on feature requests, issues, and general improvements (mostly through GitHub issues and occasionally Zendesk), to curate opportunities based on our strategy. -- You will build a strategy for your product area and contribute to the overall product strategy, e.g. establishing a go-to-market strategy. +- You will build a strategy for your product area and contribute to the overall product strategy, for example establishing a go-to-market strategy. - You will assess the impact of initiatives through telemetry data and qualitative feedback to help us develop our understanding further, and decide on the next steps. ## Who we're looking for diff --git a/docs/pages/experiments/docs/codeblock.md b/docs/pages/experiments/docs/codeblock.md index 4ee6cefb023b9b..00c353d3de3a88 100644 --- a/docs/pages/experiments/docs/codeblock.md +++ b/docs/pages/experiments/docs/codeblock.md @@ -4,6 +4,8 @@ ## Tabs +### Codeblock version + <codeblock storageKey="package-manager"> ```bash npm @@ -23,22 +25,23 @@ pnpm add @mui/material @emotion/react @emotion/styled </codeblock> -<codeblock storageKey="license"> - -```bash MIT -npm install @mui/data-grid -``` +### Component version -```bash Pro -npm install @mui/data-grid-pro -``` +{{"component": "modules/components/HighlightedCodeWithTabs", "tabs": [{"tab":"JS", "code":"<div>Hello</div>", "language": "jsx"}, {"tab": "TS", "code": "type A = {}"}]}} -```bash Premium -npm install @mui/data-grid-premium +## With header path + +```jsx title="PlainCssSliderDeep1.js" +import * as React from 'react'; +import Slider from '@mui/material/Slider'; +import './PlainCssSliderDeep1.css'; + +export default function PlainCssSliderDeep1() { + return ( + <div> + <Slider defaultValue={30} /> + <Slider defaultValue={30} className="slider" /> + </div> + ); +} ``` - -</codeblock> - -## Component - -{{"component": "modules/components/HighlightedCodeWithTabs", "tabs": [{"tab":"JS", "code":"<div>Hello</div>", "language": "jsx"}, {"tab": "TS", "code": "type A = {}"}]}} diff --git a/docs/pages/material-ui/guides/material-3-components.js b/docs/pages/experiments/docs/custom-components.js similarity index 60% rename from docs/pages/material-ui/guides/material-3-components.js rename to docs/pages/experiments/docs/custom-components.js index 656ad19c0f7a11..56cac9e3ca0c30 100644 --- a/docs/pages/material-ui/guides/material-3-components.js +++ b/docs/pages/experiments/docs/custom-components.js @@ -1,6 +1,6 @@ import * as React from 'react'; import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; -import * as pageProps from 'docs/data/material/guides/material-3-components/material-3-components.md?muiMarkdown'; +import * as pageProps from './custom-components.md?muiMarkdown'; export default function Page() { return <MarkdownDocs {...pageProps} />; diff --git a/docs/pages/experiments/docs/custom-components.md b/docs/pages/experiments/docs/custom-components.md new file mode 100644 index 00000000000000..18548cb1b10dda --- /dev/null +++ b/docs/pages/experiments/docs/custom-components.md @@ -0,0 +1,19 @@ +# Custom components + +<p class="description">They're either custom markdown components or passed through directly via "components".</p> + +## Header chips + +{{"component": "modules/components/ComponentLinkHeader.js"}} + +## Feature list + +Available through the custom `<featureList>` tag. + +<featureList> +- Manages modal stacking when one-at-a-time just isn't enough. +- Creates a backdrop, for disabling interaction below the modal.est +- It disables scrolling of the page content while open. +- It properly manages focus; moving to the modal content, and keeping it there until the modal is closed. +- Adds the appropriate ARIA roles automatically. +</featureList> diff --git a/docs/pages/experiments/material-next/menu.tsx b/docs/pages/experiments/material-next/menu.tsx deleted file mode 100644 index edc8eb85162937..00000000000000 --- a/docs/pages/experiments/material-next/menu.tsx +++ /dev/null @@ -1,339 +0,0 @@ -import * as React from 'react'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; -import Button from '@mui/material/Button'; -import Divider from '@mui/material/Divider'; -import { useDropdown, DropdownContext } from '@mui/base/useDropdown'; -import Menu, { MenuProps } from '@mui/material-next/Menu'; -import MenuItem from '@mui/material-next/MenuItem'; -import StableMenu from '@mui/material/Menu'; -import StableMenuItem from '@mui/material/MenuItem'; -import StableList from '@mui/material/List'; -import StableListItem from '@mui/material/ListItem'; -import StableListItemText from '@mui/material/ListItemText'; -import { useMenuButton } from '@mui/base/useMenuButton'; - -const theme = createTheme(); - -const MenuButton = React.forwardRef<HTMLButtonElement, React.PropsWithChildren<{ id?: string }>>( - function MenuButton( - props: React.PropsWithChildren<{}>, - forwardedRef: React.ForwardedRef<HTMLButtonElement>, - ) { - const { getRootProps: getButtonProps } = useMenuButton({ rootRef: forwardedRef }); - - return <Button type="button" {...props} {...getButtonProps()} />; - }, -); - -function DropdownUsage(props: MenuProps) { - const { contextValue: dropdownContextValue } = useDropdown(); - - return ( - <DropdownContext.Provider value={dropdownContextValue}> - <MenuButton - id="basic-button" - // aria-controls={open ? 'basic-menu' : undefined} - aria-haspopup="true" - // aria-expanded={open ? 'true' : undefined} - > - Dropdown - </MenuButton> - <Menu - id="basic-menu" - {...props} - MenuListProps={{ - 'aria-labelledby': 'basic-button', - ...props.MenuListProps, - }} - > - <MenuItem /* onClick={handleClose} */>Profile</MenuItem> - <MenuItem /* onClick={handleClose} */>My account</MenuItem> - <MenuItem /* onClick={handleClose} */ disabled>Subscription</MenuItem> - <Divider /> - <MenuItem /* onClick={handleClose} */>Logout</MenuItem> - </Menu> - </DropdownContext.Provider> - ); -} - -function LegacyUsage(props: MenuProps) { - const [anchorEl, setAnchorEl] = React.useState<Element | null>(null); - const open = Boolean(anchorEl); - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - const handleClose = () => { - setAnchorEl(null); - }; - - return ( - <div> - <Button - id="basic-button-legacy" - aria-controls={open ? 'basic-menu-legacy' : undefined} - aria-haspopup="true" - aria-expanded={open ? 'true' : undefined} - onClick={handleClick} - > - Legacy - </Button> - <Menu - id="basic-menu-legacy" - anchorEl={anchorEl} - open={open} - onClose={handleClose} - {...props} - MenuListProps={{ - 'aria-labelledby': 'basic-button-legacy', - ...props.MenuListProps, - }} - > - <MenuItem onClick={handleClose}>Profile</MenuItem> - <MenuItem onClick={handleClose}>My account</MenuItem> - <MenuItem disabled>Subscription</MenuItem> - <Divider /> - <MenuItem onClick={handleClose}>Logout</MenuItem> - </Menu> - </div> - ); -} - -function StableComponentUsage(props: { - MenuListProps?: { disabledItemsFocusable?: boolean }; - autoFocus?: boolean; -}) { - const [anchorEl, setAnchorEl] = React.useState<Element | null>(null); - const open = Boolean(anchorEl); - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - const handleClose = () => { - setAnchorEl(null); - }; - - return ( - <div> - <Button - id="basic-button-legacy-1" - aria-controls={open ? 'basic-menu-legacy-1' : undefined} - aria-haspopup="true" - aria-expanded={open ? 'true' : undefined} - onClick={handleClick} - > - Stable - </Button> - <StableMenu - id="basic-menu-legacy-1" - anchorEl={anchorEl} - open={open} - onClose={handleClose} - {...props} - MenuListProps={{ - 'aria-labelledby': 'basic-button-legacy-1', - ...props.MenuListProps, - }} - > - <StableMenuItem onClick={handleClose}>Profile</StableMenuItem> - <StableMenuItem onClick={handleClose}>My account</StableMenuItem> - <StableMenuItem disabled>Subscription</StableMenuItem> - <Divider /> - <StableMenuItem onClick={handleClose}>Logout</StableMenuItem> - </StableMenu> - </div> - ); -} - -const selectedMenuOptions = [ - 'Show some love to MUI', - 'Show all notification content', - 'Hide sensitive notification content', - 'Hide all notification content', -]; - -function SelectedMenuLegacy() { - const [anchorEl, setAnchorEl] = React.useState<Element | null>(null); - const [selectedIndex, setSelectedIndex] = React.useState(1); - const open = Boolean(anchorEl); - const handleClickListItem = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - - const handleMenuItemClick = (event: React.MouseEvent, index: number) => { - setSelectedIndex(index); - setAnchorEl(null); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - return ( - <div> - <StableList component="nav" aria-label="Device settings" sx={{ bgcolor: 'background.paper' }}> - <StableListItem - button - id="lock-button" - aria-haspopup="listbox" - aria-controls="lock-menu" - aria-label="when device is locked" - aria-expanded={open ? 'true' : undefined} - onClick={handleClickListItem} - > - <StableListItemText - primary="When device is locked" - secondary={selectedMenuOptions[selectedIndex]} - /> - </StableListItem> - </StableList> - <Menu - id="lock-menu" - anchorEl={anchorEl} - open={open} - onClose={handleClose} - MenuListProps={{ - 'aria-labelledby': 'lock-button', - role: 'listbox', - }} - > - {selectedMenuOptions.map((option, index) => ( - <MenuItem - key={option} - disabled={index === 0} - selected={index === selectedIndex} - onClick={(event) => handleMenuItemClick(event, index)} - > - {option} - </MenuItem> - ))} - </Menu> - </div> - ); -} - -function SelectedMenuDropdown() { - const { contextValue: dropdownContextValue } = useDropdown(); - - const [selectedIndex, setSelectedIndex] = React.useState(1); - const handleMenuItemClick = (event: React.MouseEvent, index: number) => { - setSelectedIndex(index); - }; - - return ( - <DropdownContext.Provider value={dropdownContextValue}> - <MenuButton - id="basic-button" - // aria-controls={open ? 'basic-menu' : undefined} - aria-haspopup="true" - // aria-expanded={open ? 'true' : undefined} - aria-label="Device settings" - > - {selectedMenuOptions[selectedIndex]} - </MenuButton> - <Menu - id="lock-menu" - MenuListProps={{ - 'aria-labelledby': 'lock-button', - role: 'listbox', - }} - > - {selectedMenuOptions.map((option, index) => ( - <MenuItem - key={option} - disabled={index === 0} - selected={index === selectedIndex} - onClick={(event) => handleMenuItemClick(event, index)} - > - {option} - </MenuItem> - ))} - </Menu> - </DropdownContext.Provider> - ); -} - -function SelectedMenuStable() { - const [anchorEl, setAnchorEl] = React.useState<Element | null>(null); - const [selectedIndex, setSelectedIndex] = React.useState(1); - const open = Boolean(anchorEl); - const handleClickListItem = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - - const handleMenuItemClick = (event: React.MouseEvent, index: number) => { - setSelectedIndex(index); - setAnchorEl(null); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - return ( - <div> - <StableList component="nav" aria-label="Device settings" sx={{ bgcolor: 'background.paper' }}> - <StableListItem - button - id="lock-button" - aria-haspopup="listbox" - aria-controls="lock-menu" - aria-label="when device is locked" - aria-expanded={open ? 'true' : undefined} - onClick={handleClickListItem} - > - <StableListItemText - primary="When device is locked" - secondary={selectedMenuOptions[selectedIndex]} - /> - </StableListItem> - </StableList> - <StableMenu - id="lock-menu" - anchorEl={anchorEl} - open={open} - onClose={handleClose} - MenuListProps={{ - 'aria-labelledby': 'lock-button', - role: 'listbox', - }} - > - {selectedMenuOptions.map((option, index) => ( - <StableMenuItem - key={option} - disabled={index === 0} - selected={index === selectedIndex} - onClick={(event) => handleMenuItemClick(event, index)} - > - {option} - </StableMenuItem> - ))} - </StableMenu> - </div> - ); -} - -export default function BasicMenu() { - return ( - <ThemeProvider theme={theme}> - <LegacyUsage /> - <DropdownUsage /> - <StableComponentUsage /> - <h4>Disabled item focusable</h4> - <LegacyUsage MenuListProps={{ disabledItemsFocusable: true }} /> - <DropdownUsage MenuListProps={{ disabledItemsFocusable: true }} /> - <StableComponentUsage MenuListProps={{ disabledItemsFocusable: true }} /> - {/* This is not working at this point */} - <h4>Auto focus false</h4> - <LegacyUsage autoFocus={false} /> - <DropdownUsage autoFocus={false} /> - <StableComponentUsage autoFocus={false} /> - {/* This is not working at this point */} - <h4>Varaiant selectedMenu</h4> - <div>Legacy</div> - <SelectedMenuLegacy /> - <div>Dropdown</div> - <SelectedMenuDropdown /> - <div>Stable</div> - <SelectedMenuStable /> - </ThemeProvider> - ); -} diff --git a/docs/pages/experiments/md3/buttons.tsx b/docs/pages/experiments/md3/buttons.tsx deleted file mode 100644 index d36e8616b41e6c..00000000000000 --- a/docs/pages/experiments/md3/buttons.tsx +++ /dev/null @@ -1,219 +0,0 @@ -import * as React from 'react'; -import TextField from '@mui/material/TextField'; -import Stack from '@mui/material/Stack'; -import MD2Button, { ButtonProps as MD2ButtonProps } from '@mui/material/Button'; -import { unstable_capitalize as capitalize } from '@mui/utils'; -import DeleteIcon from '@mui/icons-material/Delete'; -import SendIcon from '@mui/icons-material/Send'; -import Button, { ButtonProps } from '@mui/material-next/Button'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import CssBaseline from '@mui/material/CssBaseline'; -import { customPalette, ModeSwitcher } from '.'; - -const variants: ButtonProps['variant'][] = [ - 'elevated', - 'filled', - 'filledTonal', - 'outlined', - 'text', -]; -const colors: ButtonProps['color'][] = ['primary', 'secondary', 'tertiary']; -const sizes: ButtonProps['size'][] = ['small', 'medium', 'large']; - -const md2Variants: MD2ButtonProps['variant'][] = ['contained', 'outlined', 'text']; -const md2Colors: MD2ButtonProps['color'][] = [ - 'primary', - 'secondary', - 'success', - 'error', - 'info', - 'warning', -]; - -function DemoComponents() { - const [radius, setRadius] = React.useState<string>('10'); - const [gap, setGap] = React.useState<string>('0.5'); - - return ( - <Stack direction="column" gap={1}> - <h4>Enabled</h4> - <Stack direction="row" gap={1}> - {variants.map((variant) => ( - <Button key={variant} variant={variant}> - {capitalize(variant as string)} - </Button> - ))} - </Stack> - <h4>Enabled without a ripple effect</h4> - <Stack direction="row" gap={1}> - {variants.map((variant) => ( - <Button key={variant} variant={variant} disableRipple> - {capitalize(variant as string)} - </Button> - ))} - </Stack> - <h4>Disabled</h4> - <Stack direction="row" gap={1}> - {variants.map((variant) => ( - <Button key={variant} variant={variant} disabled> - {capitalize(variant as string)} - </Button> - ))} - </Stack> - <h4>Colors</h4> - <Stack direction="row" gap={1}> - {colors.map((color) => ( - <Button key={color} variant="filled" color={color}> - {capitalize(color as string)} - </Button> - ))} - </Stack> - <Stack direction="row" gap={1}> - {colors.map((color) => ( - <Button key={color} variant="outlined" color={color}> - {capitalize(color as string)} - </Button> - ))} - </Stack> - <Stack direction="row" gap={1}> - {colors.map((color) => ( - <Button key={color} variant="text" color={color}> - {capitalize(color as string)} - </Button> - ))} - </Stack> - <h4>Extended buttons</h4> - <Stack direction="row" gap={1}> - {colors.map((color) => ( - <Button key={color} variant="filled" color={color} endIcon={<SendIcon />}> - Send - </Button> - ))} - <Button variant="filled" disabled endIcon={<SendIcon />}> - Send - </Button> - </Stack> - <Stack direction="row" gap={1}> - {colors.map((color) => ( - <Button key={color} variant="outlined" color={color} startIcon={<DeleteIcon />}> - Delete - </Button> - ))} - <Button variant="outlined" disabled startIcon={<DeleteIcon />}> - Delete - </Button> - </Stack> - <h4>Sizes</h4> - <Stack direction="row" gap={1} alignItems="end"> - {sizes.map((size) => ( - <Button key={size} variant="filled" size={size} endIcon={<SendIcon />}> - {capitalize(size as string)} - </Button> - ))} - </Stack> - <Stack direction="row" gap={1} alignItems="end"> - {sizes.map((size) => ( - <Button key={size} variant="outlined" size={size} startIcon={<DeleteIcon />}> - {capitalize(size as string)} - </Button> - ))} - </Stack> - <h4>sx prop showcase</h4> - <Stack> - <Stack direction="row" gap={1} sx={{ py: 1 }}> - <Button - sx={{ - color: 'onError', - bgcolor: 'error', - border: 1, - borderColor: 'tertiary', - borderRadius: 'medium', - }} - > - Button - </Button> - <Button - sx={{ - color: 'error.100', - bgcolor: 'error.40', - border: 1, - borderColor: 'tertiary.40', - borderRadius: 2, - }} - > - Button - </Button> - <MD2Button sx={{ borderRadius: 2, color: 'error.main' }}>MD2 Button</MD2Button> - </Stack> - </Stack> - <h4>CSS vars playground</h4> - <Stack> - <Stack direction="row" gap={1} sx={{ py: 1 }}> - <TextField - label="--Button-radius" - value={radius} - type="number" - onChange={(e) => { - setRadius(e.target.value); - }} - /> - <TextField - label="--Button-gap" - value={gap} - type="number" - onChange={(e) => { - setGap(e.target.value); - }} - /> - </Stack> - <Stack direction="row" gap={1} alignItems="end"> - {sizes.map((size) => ( - <Button - sx={{ '--Button-radius': `${radius}px`, '--Button-gap': `${gap}rem` }} - key={size} - variant="outlined" - size={size} - startIcon={<DeleteIcon />} - > - {capitalize(size as string)} - </Button> - ))} - </Stack> - </Stack> - <h4>Material Design 2 Buttons</h4> - <Stack direction="row" gap={1} alignItems="end"> - {md2Variants.map((variant) => ( - <MD2Button key={variant} variant={variant}> - {capitalize(variant as string)} - </MD2Button> - ))} - </Stack> - <Stack direction="row" gap={1} alignItems="end"> - {md2Colors.map((color) => ( - <MD2Button key={color} variant="contained" color={color}> - {capitalize(color as string)} - </MD2Button> - ))} - </Stack> - </Stack> - ); -} - -// custom M3 theme -const cssVarsTheme = extendTheme({ - ref: { - palette: customPalette, - }, -}); - -export default function App() { - return ( - <CssVarsProvider theme={cssVarsTheme}> - <CssBaseline /> - <Stack sx={{ p: 1 }} alignItems="flex-start"> - <ModeSwitcher /> - <DemoComponents /> - </Stack> - </CssVarsProvider> - ); -} diff --git a/docs/pages/experiments/md3/index.tsx b/docs/pages/experiments/md3/index.tsx deleted file mode 100644 index c67299fe14ec18..00000000000000 --- a/docs/pages/experiments/md3/index.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import * as React from 'react'; -import Tooltip from '@mui/material/Tooltip'; -import IconButton from '@mui/material/IconButton'; -import DarkIcon from '@mui/icons-material/DarkModeOutlined'; -import LightIcon from '@mui/icons-material/LightModeOutlined'; -import { useColorScheme } from '@mui/material-next/styles'; - -export const customPalette = { - primary: { - '0': '#000000', - '10': '#3e001f', - '20': '#640036', - '30': '#8d004e', - '40': '#b70b68', - '50': '#d83181', - '60': '#fa4d9b', - '70': '#ff83b3', - '80': '#ffb0ca', - '90': '#ffd9e3', - '95': '#ffecf0', - '99': '#fffbff', - '100': '#ffffff', - }, - secondary: { - '0': '#000000', - '10': '#2b151d', - '20': '#422932', - '30': '#5a3f48', - '40': '#74565f', - '50': '#8e6f78', - '60': '#a98892', - '70': '#c5a2ac', - '80': '#e2bdc7', - '90': '#ffd9e3', - '95': '#ffecf0', - '99': '#fffbff', - '100': '#ffffff', - }, - tertiary: { - '0': '#000000', - '10': '#2f1500', - '20': '#48290c', - '30': '#623f21', - '40': '#7d5636', - '50': '#996e4c', - '60': '#b58763', - '70': '#d2a17c', - '80': '#f0bc95', - '90': '#ffdcc3', - '95': '#ffede3', - '99': '#fffbff', - '100': '#ffffff', - }, - neutral: { - '0': '#000000', - '10': '#201a1c', - '17': '#2e282a', - '20': '#352f30', - '22': '#393335', - '30': '#4c4546', - '40': '#645c5e', - '50': '#7e7577', - '60': '#988e90', - '70': '#b3a9aa', - '80': '#cfc4c5', - '90': '#ebe0e1', - '92': '#f1e5e6', - '95': '#faeef0', - '96': '#f7f2fa', - '99': '#fffbff', - '100': '#ffffff', - }, - neutralVariant: { - '0': '#000000', - '10': '#23191c', - '20': '#392d31', - '30': '#514347', - '40': '#695b5e', - '50': '#837377', - '60': '#9e8c91', - '70': '#b9a7ab', - '80': '#d5c2c6', - '90': '#f2dde2', - '95': '#ffecf0', - '99': '#fffbff', - '100': '#ffffff', - }, - error: { - '0': '#000000', - '10': '#410002', - '20': '#690005', - '30': '#93000a', - '40': '#ba1a1a', - '50': '#de3730', - '60': '#ff5449', - '70': '#ff897d', - '80': '#ffb4ab', - '90': '#ffdad6', - '95': '#ffedea', - '99': '#fffbff', - '100': '#ffffff', - }, -}; - -export function ModeSwitcher() { - const { mode, setMode } = useColorScheme(); - const [mounted, setMounted] = React.useState(false); - - React.useEffect(() => { - setMounted(true); - }, []); - - if (!mounted) { - return null; - } - - return ( - <Tooltip title={`Change to ${mode === 'light' ? 'dark' : 'light'} mode`}> - <IconButton - onClick={() => { - if (mode === 'light') { - setMode('dark'); - } else { - setMode('light'); - } - }} - > - {mode === 'light' ? <DarkIcon /> : <LightIcon />} - </IconButton> - </Tooltip> - ); -} - -export default function Page() { - return <div>Empty</div>; -} diff --git a/docs/pages/experiments/md3/inputs.tsx b/docs/pages/experiments/md3/inputs.tsx deleted file mode 100644 index ddcf6005c1c2b7..00000000000000 --- a/docs/pages/experiments/md3/inputs.tsx +++ /dev/null @@ -1,212 +0,0 @@ -import * as React from 'react'; -import Stack from '@mui/material/Stack'; -import { - FilledInput as Md2FilledInput, - FormControl as Md2FormControl, - InputLabel as Md2InputLabel, - InputAdornment as Md2InputAdornment, - FormHelperText as Md2FormHelperText, -} from '@mui/material'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; -import { FilledInput, FormControl, InputLabel, FormHelperText } from '@mui/material-next'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import SearchIcon from '@mui/icons-material/Search'; -import HighlightOffIcon from '@mui/icons-material/HighlightOff'; -import { ModeSwitcher } from '.'; - -const md2Theme = createTheme(); - -const md3Theme = extendTheme(); - -export default function MaterialYouInputs() { - return ( - <Stack spacing={4}> - <ThemeProvider theme={md2Theme}> - <Stack direction="column" gap={4} sx={{ p: 4 }}> - <pre>MD2</pre> - <Stack display="inline-flex" direction="row" gap={4}> - <Md2FormControl color="primary" variant="filled"> - <Md2InputLabel htmlFor="md2-primary">Primary</Md2InputLabel> - <Md2FilledInput - id="md2-primary" - defaultValue="primary" - aria-describedby="md2-primary-helper-text" - /> - <Md2FormHelperText id="md2-primary-helper-text"> - md2 primary helper text - </Md2FormHelperText> - </Md2FormControl> - <Md2FormControl color="secondary" variant="filled"> - <Md2InputLabel htmlFor="md2-secondary">Secondary</Md2InputLabel> - <Md2FilledInput - id="md2-secondary" - defaultValue="secondary" - aria-describedby="md2-secondary-helper-text" - /> - <Md2FormHelperText id="md2-secondary-helper-text"> - md2 secondary helper text - </Md2FormHelperText> - </Md2FormControl> - </Stack> - <Stack display="inline-flex" direction="row" gap={4}> - <Md2FormControl color="primary" variant="filled"> - <Md2InputLabel htmlFor="md2-primary">Primary adornments</Md2InputLabel> - <Md2FilledInput - id="md2-primary" - defaultValue="primary" - startAdornment={ - <Md2InputAdornment position="start"> - <SearchIcon /> - </Md2InputAdornment> - } - /> - </Md2FormControl> - <Md2FormControl color="secondary" variant="filled"> - <Md2InputLabel htmlFor="md2-secondary">Secondary adornments</Md2InputLabel> - <Md2FilledInput - id="md2-secondary" - defaultValue="secondary" - startAdornment={<Md2InputAdornment position="start">$</Md2InputAdornment>} - /> - </Md2FormControl> - </Stack> - </Stack> - </ThemeProvider> - - <CssVarsProvider theme={md3Theme}> - <Stack - direction="column" - gap={4} - sx={{ backgroundColor: 'background', p: 4, color: 'onBackground' }} - > - <Stack direction="row" gap={1}> - <pre>MD3</pre> - <ModeSwitcher /> - </Stack> - - <Stack display="inline-flex" direction="row" gap={4}> - <FormControl color="primary" variant="filled"> - <InputLabel htmlFor="md3-primary">Primary</InputLabel> - <FilledInput - id="md3-primary" - defaultValue="primary" - aria-describedby="md3-primary-helper-text" - /> - <FormHelperText id="md3-primary-helper-text">Primary helper text</FormHelperText> - </FormControl> - <FormControl color="secondary" variant="filled"> - <InputLabel htmlFor="md3-secondary">Secondary</InputLabel> - <FilledInput - id="md3-secondary" - defaultValue="secondary" - aria-describedby="md3-secondary-helper-text" - /> - <FormHelperText id="md3-secondary-helper-text">Secondary helper text</FormHelperText> - </FormControl> - <FormControl color="tertiary" variant="filled"> - <InputLabel htmlFor="md3-tertiary">Tertiary</InputLabel> - <FilledInput - id="md3-tertiary" - defaultValue="tertiary" - aria-describedby="md3-tertiary-helper-text" - /> - <FormHelperText id="md3-tertiary-helper-text">Tertiary helper text</FormHelperText> - </FormControl> - </Stack> - - <Stack display="inline-flex" direction="row" gap={4}> - <FormControl color="primary" variant="filled" disabled> - <InputLabel htmlFor="md3-primary">Primary disabled</InputLabel> - <FilledInput - id="md3-primary" - defaultValue="primary disabled" - aria-describedby="md3-primary-helper-text" - /> - <FormHelperText id="md3-primary-helper-text">Primary helper text</FormHelperText> - </FormControl> - <FormControl color="secondary" variant="filled" disabled> - <InputLabel htmlFor="md3-secondary">Secondary disabled</InputLabel> - <FilledInput - id="md3-secondary" - defaultValue="secondary disabled" - aria-describedby="md3-secondary-helper-text" - /> - <FormHelperText id="md3-secondary-helper-text">Secondary helper text</FormHelperText> - </FormControl> - <FormControl color="tertiary" variant="filled" disabled> - <InputLabel htmlFor="md3-tertiary">Tertiary disabled</InputLabel> - <FilledInput - id="md3-tertiary" - defaultValue="tertiary disabled" - aria-describedby="md3-tertiary-helper-text" - /> - <FormHelperText id="md3-tertiary-helper-text">Tertiary helper text</FormHelperText> - </FormControl> - </Stack> - - <Stack display="inline-flex" direction="row" gap={4}> - <FormControl color="primary" variant="filled" error> - <InputLabel htmlFor="md3-primary">Primary error</InputLabel> - <FilledInput - id="md3-primary" - defaultValue="primary error" - aria-describedby="md3-primary-helper-text" - /> - <FormHelperText id="md3-primary-helper-text">Primary helper text</FormHelperText> - </FormControl> - <FormControl color="secondary" variant="filled" error> - <InputLabel htmlFor="md3-secondary">Secondary error</InputLabel> - <FilledInput - id="md3-secondary" - defaultValue="secondary error" - aria-describedby="md3-secondary-helper-text" - /> - <FormHelperText id="md3-secondary-helper-text">Secondary helper text</FormHelperText> - </FormControl> - <FormControl color="tertiary" variant="filled" error> - <InputLabel htmlFor="md3-tertiary">Tertiary error</InputLabel> - <FilledInput - id="md3-tertiary" - defaultValue="tertiary error" - aria-describedby="md3-tertiary-helper-text" - /> - <FormHelperText id="md3-tertiary-helper-text">Tertiary helper text</FormHelperText> - </FormControl> - </Stack> - - <Stack display="inline-flex" direction="row" gap={4} mt={8}> - <FormControl color="primary" variant="filled"> - <InputLabel htmlFor="md3-primary-adornment">WIP Primary adornments</InputLabel> - <FilledInput - id="md3-primary-adornment" - defaultValue="WIP primary adornment" - startAdornment={ - <Md2InputAdornment position="start"> - <SearchIcon /> - </Md2InputAdornment> - } - /> - </FormControl> - <FormControl color="secondary" variant="filled"> - <InputLabel htmlFor="md3-secondary-adornment">WIP Secondary adornments</InputLabel> - <FilledInput - id="md3-secondary-adornment" - defaultValue="WIP secondary adornment" - startAdornment={ - <Md2InputAdornment position="start"> - <SearchIcon /> - </Md2InputAdornment> - } - endAdornment={ - <Md2InputAdornment position="end"> - <HighlightOffIcon /> - </Md2InputAdornment> - } - /> - </FormControl> - </Stack> - </Stack> - </CssVarsProvider> - </Stack> - ); -} diff --git a/docs/pages/material-ui/api/autocomplete.json b/docs/pages/material-ui/api/autocomplete.json index a15c9e431d869f..e0e4d60b0a7e5a 100644 --- a/docs/pages/material-ui/api/autocomplete.json +++ b/docs/pages/material-ui/api/autocomplete.json @@ -332,19 +332,19 @@ { "key": "tag", "className": "MuiAutocomplete-tag", - "description": "Styles applied to the tag elements, e.g. the chips.", + "description": "Styles applied to the tag elements, for example the chips.", "isGlobal": false }, { "key": "tagSizeMedium", "className": "MuiAutocomplete-tagSizeMedium", - "description": "Styles applied to the tag elements, e.g. the chips if `size=\"medium\"`.", + "description": "Styles applied to the tag elements, for example the chips if `size=\"medium\"`.", "isGlobal": false }, { "key": "tagSizeSmall", "className": "MuiAutocomplete-tagSizeSmall", - "description": "Styles applied to the tag elements, e.g. the chips if `size=\"small\"`.", + "description": "Styles applied to the tag elements, for example the chips if `size=\"small\"`.", "isGlobal": false } ], diff --git a/docs/public/_redirects b/docs/public/_redirects index 38466c7863360c..017dbacffa4757 100644 --- a/docs/public/_redirects +++ b/docs/public/_redirects @@ -10,7 +10,7 @@ # To add when we finish work on v6 # https://next.mui.com/* https://mui.com/:splat 301! -# For links that we can't edit later on, e.g. hosted in the code published on npm +# For links that we can't edit later on, for example hosted in the code published on npm /r/styles-instance-warning /material-ui/getting-started/faq/#i-have-several-instances-of-styles-on-the-page 302 /r/caveat-with-refs-guide /material-ui/guides/composition/#caveat-with-refs 302 /r/pseudo-classes-guide /material-ui/customization/how-to-customize/#state-classes 302 diff --git a/docs/public/static/blog/bringing-consistency-to-material-ui-customization-apis/card.png b/docs/public/static/blog/bringing-consistency-to-material-ui-customization-apis/card.png new file mode 100644 index 0000000000000000000000000000000000000000..1b97fd598c06f47015e96158969c244aa36634d1 GIT binary patch literal 196042 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%0A$>D8#B7MRdAn{r>B?%q^IT~-~c^Hyz*_Z!w( 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%0DWAlY5 z5&O7s7-JI_x1(@`#Fc0vi#$m8{m&~}Uhx;T{r8_nhigc9Lmquj{T(g`n69;?5k`G? zm7HLeQY{x)9;__SvEl9v;Cw%jo3ytBIQVb|(AR^$oeBVcWHG>F3?M7Q%tSK)!G1D} zoNuR)|M{H29p2tqZnoU2C28QUeGyC}4~oBC`yv}YXoV*TVVGJ*uJC$qwR<x?N}#y% zMqq~1=t14bA9qgeC+l&klo-ELz=VO72pX<6aGK(GFojBs+b(oNv#@=t7aklIzX6pi z{-?>me@}DI-~aOdsCWeg=sjq{*^r0jJz_BM3(`Y9A`boT%m5<q!+zueJ8!qUK8pcV z4)9$O;Lpd*0|ee3VE~sh5`jO5eaCsgD=p-F)Su**XXW5@mm@<gIq*5ZN9}mbz*53~ zP6wc-wfqskPv%#+BJyFGJ+t_mWu+(aV;TjB;h@iXo<9DbKy|R-o4Ml5G$PKJHd^}{ zZ-eFhsCkCpe=JA%-#h-nzkdUR>HDAK@5g{SD7?;U&g(AOy|(NhjizB{d43=RIHv&N z{lIL?CH+$vKrw%B)er!la~j|g22cc`iX*#%ylG!(I^h3pICy&q{(HIke@-h?qyi5n z62Stu!{O~tRdfSkX}q~#iq~ivQz(rgkREuqgpO5E8KW_XAJwel3Go|Z)RE$+k7{1( zlE6Xy%i@-4u{jrnNs+-r9!4Zq&mE$H=W|qy>mT@cj~_?-?;pkD4;_&bhr>I!v1kf- z5igcXolFMgZLmqmQe1R3iiz`kD5qio*#KnKAV49z0}S9{4p7G^4`d7g|GxKzAjn4< zKwR@-V4a!-2mt&tW5NFu1h_p!Ubg*Hxn+8w|B)+TD()^t>5JS3uA>4QdZCxf*+~z` z6m+|>bqyDT2*a?i{5gA~*^GbL9lXQuH^-ZUwr;(_qcQ|RH9jY;nVnO<VYLj`(m67i zrds^X{v6%r*touL{LR0ACn!~678fnX>Pt(Yj)IQXl=wrtEie$^$C}V3|3mjEf;kpA zy3;X$0(`q6zHgPE$N<6t&J%&pxEjnc29VcBQh|TrK;&!X1#jEehJo9I<VA9`;VT<V zDwKr75;U959TM{B#VxsQ+Bl@#!aAloKjlXB^!sB!aT^q;_R@e7j*gbFjL4i*V+>?S zo|Ky7CkP7qS^P9uoc7sbT277=%#NFg!l138DEinV16gGYIC}gxeCqGJ8ipypbNv0k ze@}C-MGY=5YaO_~IFc(ZBS81!5$Q+NY6kz6><><rhZp}l&Hz4R2uwbU?$dQRkPQ8b z`O8wRMzA62!H<xI`&PLhf_MA5zm~RMe+%=eR9z9?Iv3ZcbD4WBPssGxUwf%)S9HF~ zvWL=V0skmHc1tggnY=D}c}0FKdq4Kq%j@=LZQJXvY^&Nt>b<HK!h4Zlr#wO(TQC2p z_-7S@L~3IBziplBA#3}kef>2872I2Nt5l%B<0o$YRo)6&a!U=u=W;XLWJnMvN)H3l z&q&_7-H{O%HAjV!oM{lqkyXHqTvfr9AxDe7R(2zrGL*t}d`L?qHTw7+qGoO+{!wvI zp&EB(pMMI%xG$_k`gpP?x?>u@3my+DqXr7=`%UAgp&k_>TDZjwlzRL#<PCqDcdWYe z{w{Z+9oR>!rTt<{)iFVR_zp0Dpg@%i`EiM?U(ih@7*0<4RqF*ED1~0E5-0`QZ-D4j znt#1&!y^9#0^#?B0T<NMA@i+Vis3&MKi9b*0)UMF28xe#-=9ktD8wEBzm@^S-YbE@ zb<*tv^7=1%XSW<X4Fuu6ps0cZ@8x;_H^rHB?Z?nG9pe-~Cu3Wh8Oio)6G)#Pjr7w` z0(+-S_0!<M@jZ{hfTwz#`q>ED8;w*C=}#d+^eKaYSab>C3D}K|j7u#7Na{cC|1Sr4 zfNv4Ek3Ie=<ejSsi2Qg2Y?0zC=$O{c3I~kz4Q!O-G}_|oT^Rx9Emv_lE;WLVI$$`f z65h2B=bk-m?y~V)JhqPCzJ>ti?Rf!R)ErGu6Y@L#?fyz`waBT{Q0?=|2spT87deVn z^)<(z{xOjE*ubv4ycHB{XM}}b1YEsFx7Pz49pg&4_e$Rd((cXy+$eAq(BPCC<zYMY z0~*qMykvx|IB>yy6#`-j{G};?IursH08|auQ+>L|?+8$VUO_%Mz_!}Upu9PCK>@11 z00IjGc*>O0MjvzJ?*l+VzQKQUj3RwFz_;=_tlzh@d~r$Z)U_!<tV5cMFpVLaUqOd} zq*14Mc~t!`A>eU9WNx05@x%VnHg~E9MQ3@c5_Eqon#p)RXOJ)*gat6@<X9pE0$|+y zh<yfXXU_s~@xQp7cme?X@M^PoF{llD*^_JxW~!V0w95GNUw$WCF?eGH{(JcOD^lSR zZ|m*Qg`SC~4&S4FO$7uPo}zNbleVupexJ^;-;{Ec`|m%!d2fj!G&+-_OzBXuzCtJR zj0=PT#B%}oeJTTpMlZCX0~G>Z6d>>pAKDaaHlV60Il24qtn6}$AyoKRmnz}Fg$03s zAVQa)g#j_<8r|hbRSNc=+6H~wxXuPvC@8mrzktK*Vc>Th_!RYbn$M?<Zwdv!;lD#z z0ygPv!{;FH2_PJk%H&1kVJIL9)*HW!7Xc^*{_tOvUr?*O#xf@tK-R}E8GSLQnFEBb zg`Hg?;5H|Q!(04jRDTlAU8KKcI2RI>k!gc4*GWfwhLb4qGjpu_amDX12Wy>fiSV|t zvp#+u30k1jvNdZBE$OF=BjS(kLd_tCdOxs@NL6@O{O3gsEq>+kyTU<bpV~iL!Gs~4 z8(vE7X8594H8oI_NHG5e8?-~eG3(DVb?-5Ngnb7K>I4C*B$BI=lU7~8J;0whKpMyn zp$t-y9!7wMU_MI&Aqu=Hz(0e2)SLD@Z8;(c6!trHxIcNWA-|dhOvD#U1uUTae}@Hp z4(E5C$WxAq1P=zB$nYGS_ZtV<28I>;JV2(jfsIy_7olZ=zPVrZe|nWb4H)}X0`F}6 zy1_8t4A(?IlR3u#13q$sM&Cb)CWnxTj-w@lMaLW=giFs7$k_w*2QAHBkLnCh21Gub zI)%({j4;}vtRvJk{+u>RnSQz)K2+|`fF&jw73AZ?_P#zI-#{bB>_aS@Bm8961HbC{ zL+TT!y|@59dvcrUnlZCG+?Oz;t;~H2QDAUh+Xl{^9SmaJU;xK>b&@$Qa32C+XR0dT z-_-P>-XKx2VBy4jG2lEAkd6X>kbhu45TA_9{|(tEwol4gs0#3J4$t@)`)t1m{Un(I z9Arm#slY$VtD-RQPXqQu1P=UDHEAHD!|g!{KAm<FER-%Vs5A3`Tnp7Kq(_)o<d~r- zdjA)L!X&Ri?*Z<FW2eyAZT#|f*|7YjKJuR$l{CbE&ZGA~5r!QC0;0tq%q@;V;v;ev za7KhEdlVdP8ko*)Q@iFJX!IBRkHBAHmJKwpq&(7|TIZ~_nP2%S)^JMf8)lA`UmiMA zCQgm^fH!o>^+m@&j)VX?dR{!Hckv=;eQZ4!UhfVX5i}rQI>>H%-W9MOn)JJK08tRq zfQbQ<+p*NCA^cS6dedn*g&pE>jQvScCjxHbPdHGuqQOAIL<0`elPcnm%;3D>pP=BB zV8PL^Qh+L*0hA9aaB2ZQZW;6M0zl0KoJC<25tdh|@OZvY0UQu`>Oyj-9He_p0oqfg z0%A&ZRLTcNnR7upIS7=hJDmufj`g470-5p$$ejjC`q=m({)*^FqS!Aq;J6<?D?KM& z<cL(rz6cBvN=F-+fwCx-;kiWBMXDrZ$l{3kj^<px0!qVtu<&pL<Wi~IIvqKg7oUI5 z@bfW<Vo#6AUx)k=KjEV#rKwP$`+&@sGBH&7yHl|JtByaglZD<}b%~z0^kus}^<NfM z@Iv<Sqazg<4H-w_0xJysR0a@mIGXMj>>GO&qW}&5?E)+)8#fqoiY1s$0?9`k4P*k( zan_px6bc0MK?SOHqdy@%sXFAOPVmoGf5B)1xa$m{3SBzM2e~6G1T*I(0{<TV9WtMa z1S_a$jFTBpT~u>hTqO`_w5Rp~{SI9^LmI7m|LfTyhb}7Cx>@~puY>WwJRsW-qCgco z4l#h0zj8=HO?yIk*0zo?Uwh+GKk1Cao{woCHN3MRSMOAEBFa<vS~GWQpS<94^zg{U z5AMz_^60&LtPO8Y^QOt*RA8As9mYRQnfRCn;O_`y=J>MX4@_nnLP$Yh<IfBsMgF){ z=7}4GI7(c5f9U3s>7v>K!8ca`H1#F}2+Vttet<wdV06g61@Gi>7Em?Y0%Nf%G>8xF zI_0-3X}Hi}06~BQ^MHI~>J%cR6<I66un$OG@OkP{xpZ{&N3F6lfTRSgnrljm>+)I! zaAY=ylO@Mgg98tdc%zbEdK_FFpb=2xD6(TTn7j`-vz-{M3Y4V6AR+oS<aS^9C_95i znuA1^Hq`jXBK0@JJ@lvh2*5vnPcTqKgo8pCtklm<RrMKtd5MVRI09Yg@8rN8bBYVQ zM@uBz0>_b%aSn6oDiAdXlT}>ZU^UV+ovoo`P9R5bIV$;6P^Is_E$JN?;?VU=j(>{s zTgnmV5tQ)<;2K}8x;~v6FUpi``2h^fNgtmW55CU;Qqeqx0O9NLOTdA$SD_~G9;S@N z34#SLNJ{v?4sGK8JPnwopd|pnQMbVJu^fy%eLWb`fT=D}fV*}E5NlL`;P`-oH${MN zg1`cU<56|OhqFFrZlKnOmV8^GUc!x3_@}1RoF;Hx0HA|PI=~NHB~buXH{<;01WbD1 zQSFl!!Fq6=d~(KrRDpmM00(Ca00M{FoUfYaiLZk|yd`musuC3FoH?xpo4cJziTJC~ zijMv;LRacOdmX>G?A8{{%L~lV;NkC@s)}hXZT;c!qQ>3i2)B)&qc89izSrdbdgISP zm4?2U08ZNdmh>shkHBA;hvD#&?Lfr(jgu?ky^t7kc?H1x44}ZCpx)a#mzr1$aI=0W z=z@7@c9-}+UGhBSyC%-=Q6~6z%Ac};fIS3ePzZ4eao-jEl_SW&y(s{JKtDM$^h0sv zhj<v6oM7<Z%PaG`kPtk|$P@Z+W&xhZtPMSh!K8T1%s|LKBcD`>ECiU)JEn%b_%E0; zDdUuecT6%WmZ%+WQN1YP6-RI&LYDr4q@|=~H9_k+P*iLfo$)vLx4vdJY58NH2ibiV znCVli-9<OY5|WHoazb7Xe0ujDdF`K}?}gyA3j^1aHt3?@q&;t$;Afh))rRQS8g*Hj z(Fq*)a+?f%IWy?esLFC80np^2YRZF_nflTE700iph{w3Qtop*~*;@Yg@^36WHEqdZ zD<<z&%-42?0}M7Ci2lzqfIMp@0P8{?av0->y=&HemNqWbhfevfBR>=jpCt%3ga8By z0s$@Biyn4_lMEEZOrkXaN7E@B=p<mrfWW>y2T<^iQ|OP7@GW6iI9n7(GXT%}J^Tx9 z6PSZ}!hi+@=Zz`@uo4A$2O-7@V>XI9s^{n}7OdOU8g9hL9fu=CUJg=(kK|qqW&>KP z3-C{-3Ev`7bt+P_HvkMXX2R;T&PbqoL67pk`t;UJER^tcNznaZ4x+8rS6CDZ!=HIm z;z)AjiFh)cXbi;WKFb~vDE=;gQp1H<p6g%_r6`Cs4y4VRL(RX7nN+1;Oue;Rzx(lr zfNdg}>tc!}JYF+_y?44J?p8$g!fhmj=sum}pl=6(S7-krHyJ?f`-=_yLnr7<a)-x) zjRFKJa*QniD9;V@54cBz3?~8ijv!=uDEAi-h`47UpV+@C&#Iz8GC_m@=aQ|npg-5r z96)*u?t|+iE%;N?L0${MoEA}FH4G2<Jl+*n1YAU(Y@LX_!huuL6gfZwz+fT4E4)&; zAkxNi*hfjtll4$qfPg4d>kgW*?Zxu)rqS1wKgIxd49E|Ylp9?F+Wyr(`1f^OXU}!7 z9YyezN)qNbI?m~F+z|i8kJm|;!W?4P1!`?9%P0`Q@6Ul~bo`yY)tTTy{ha&3g&`~P z(Qu&J$V=o-W{7L2ZaGemk$`XBrsBIEKYkktRR)X|<fZ}Eq3^bITQ=eRK!D==1obF^ zhBqakMBv?R22lE(r>L4fP=6cw;h;=-@If0qpsoLm+2Nmx+J{a`1>PZG)#QVK1>ps^ z7~-3HJXwVJEDe~@aE5>x+&Kfd%@^$_CxYO<kr7Pzcgb;#TOjT<fp;6t0Y+B-FOZR2 zQ6dVY1PKDXkYi&l-D7L*ZDP--5^{$Lsl16Iv{*Ga0W$p~Fs>f+1!$)xzJGoKkCxET zmd(%JxaP2Xs#BirF63Vtip;hKGe>?fo}9Ch5g@gBFnf~aHanMOH(Q;9hk1fw4X+nC zye`}^zPG_^esG@rX!su`0iK-y;~iJ8?{xf}$>A@}R>6ff*l-J4Xl_3vd@pQg3?6AA zqkg6cx9Rvq1`wd9R{#Q~Pt#uX1sw;GUx+?yd!awj-n(&w2$ZTrZgAq9aDd1=)B^(w z^GTKI3?S=2%N~c!zs>{E^+JJw!$JZkJy<ezu@DSMQ1u;oPXm6tga9We7#46^j^h;( zKCi(Usu#?gLW1Jk1=lz*dta~=jP!-40Ep@e#9DM)NEg{#ZfwpGa_^9ei~*SA4p5@6 z=rTT=8CX_(fjJ$0;c02)W~(P~@DV2R_5U^2HJiiqa_^VeI#)ingkT9}#dKY1HOkrN z4eo}r<`@=A$7A`Xjz2ltlDl{_Wt2%qN^ijvW8j=Ed(&NeTyDm0`0(PgvrkJ?_5F>1 z3XTLe@gz3W9+63DcQ$%3j4<bq<!)%8Kxu-5GEgXQP=EluTMXb-qs+xdaDsqiI@;S& zUQXE{J<S8elyx`z<A)9NN`Kw4GeH#g1@H+2%9Lk1E>Y(1%d8^&XF*>XQ#LVZ7v=!k zv3S1ea9>CWe#3lAo+~kU;Kh{SzHbNA-)LcFz*mX_m&&;(@mhja94ja*EiS*u9o+I# zC`sg4HaE%(A8A2fnS3U0Ak9P8!RuD5MqA~HhVIKR(%})8%Bq_KFU#C{uAtwmBR{mS z=7s4F6(Mle<FtV}DVTYiR&*?#H(imIfnph4{&LIA$pu>c5gDz=LFLH+c2wpFH4wbv zhgV~QC8%j6T)<f0*Z8H&jGr`j_IH%%t`~vMw>}rLf2A=6SixPnWa?s3z|W-sQEtxx zq=EosdteD$fye~}1kNEl_k(7(h}&Diss9*v1PCay56}ns$9`}+wFtDRK9s2?*eb~b zhVEO_0Y`UXG3XHP?IGao#=xZ6Hw8vPy}<&`HRV(5!t8+XF*s1kzo)hEhyy>+9gu_) zQ*{#7ftdmZklfl)xmKo#1nzLE-Lhxq>8U(^$q~?Di0|sXbRn1wbJ+yOG_b6w_y?V9 z4xlqWqv3C?RgdA6{o)PYG#E*@vQS}No}{vtv}T-}aY2cHx|&H16ul~R%u#Tw$^^g; z*ei8vZhz}40+Mx~zm&W4kJ%d2O>LQ$$fD-opYLt_nTZQ*U|mAftvchc&+zbOe?`Q> zw&xyTP#zccx`_EqHln-50ICw^!ybn~p-=ZyfyWkr*=~WpvFRo2Z|?`Zqq|*{|4S5D zmF)wpLZEO|p9ZK%utb^+%m4xn?~xMRgtXq#8v}TnJN;MI_*LH(tp~UggJA<n2|iBq zDFP74aauc6>1&jbz(BJ+(jx-&MU{!#f)O|Orh=Mw@YjZd+u(IJgO9gStHINI<y5hd z=tpP%IcjFIB{hAh9>!CoRJ!)RUCe-{&^jc@3?xdQ+tfFh3KT-6_8JLuA<?nWJ&}0& z*bK9Lsy3r!7x<XVfVNoxxcTW%mA~yy?NnA)m%q3sRQC|%cQyWXpSP7AkTZnL@X+i! zkrqdwwrc=mbk_T0b6}kzc5A*{a(^8_N{Bx~z#|ZVKtKQ=K2>rO{q>#U&Pz_Z&2@hh zV9ar0Nrn9zl^hU=zOV`K@@Wp>V*F&(bYS*`1N132d^HN}zCZVl6S{9~OqKT_py)pl zfRfkrz@hzEBk=SHXpAKe%7{KL5FbErEge`epo{@@aXuiR%@CeUO76lWafE)5cpi0D z;rYw-<6{y2T|=w#+%qUit&VJ}j7N-%^tpW)A@SC<#3B^3E^a<j{~DKjsapuwPl^`L z%3mQp60obWI?ldHSteOJ>Mdu@P{z!EzompD;VAD>HeTr~m?T(dO3$KM>US}I`scQN zR`$DQ`@7Zo%GeYpwK!&l%9DQ-)dAtD`Le+B1@Aou5D&Wng8@N;D|$P+M3&0AaC5Hs zFc45HLEqm8&cPmSyL}2oh))=BY^Xp8LAgPIgn|)4!;%af(tvRzmh_7UjRV_xK$1X! zexE7B{k_9I@Sjcvf{FfqJIgU(^Bjk`(~!*SoSDFd0cHClfVOdnEL_G0j)e&BVR8pA zq*T3VJ^(84lD;27em0?RN;Fy!2-R=0$pS7Zx)f9`yc4uSSCpjN#<G;n>>^0Y`>^KE zk_U`lQY|`FLls?tw?u0nx<HcvWUM`-jr>6DhBPzc_f(5KMqFiOM7bL+Y*s;ej+p*; z<L7MbXx}~=PkCgCNxM!hy)f0G*T6d!OA!vlf#CeyJ%Mj9fU1S{1picxXBtKW-oXQ+ zV6Mequ)&Nh04)p%47|tu;ihdgmE?dtRE&W9DCd-cNLL214yFQ1FkG}AG|-Rj;V4~d z4luU!f8!(EtLn35z~3hSuwK<M=TqxMvm*4HYXu<}F2UvwyiYo2+3CQekt0HW2Q3hE zlHEy&%)+-)aC77IrUxB)^OU5EKv=5N7)?OW5PoY*l>`{oWs-Ij0*=I%7r`zO!_>K8 zv9brQ-;-Y-kyr@C)%il1&@gxZ$iZ3v%yFNoWv`Hu>eyA8VJccV5-?p)?Fcm7Y~@E6 z?1>noGREOkC;az69)H~<dLLAcufx2w8?d<wjZIyDu@wZ?WiXI5Ux4D>;IC79@O?KJ zKpaAXx`+kK*z$n#!$F0K%?===j|K^-vP$Ozr2zUcY&*C&L%RX9C&)?&9>1KUujK%z z7)y|^?hEqT5#)sip#(Gh>p(CwfTw4w0{>{Z0|BA@rqb*{WN^r!zc+-q7l=N6zBXV^ zW?f#uO%{+8RNx#c;O|BO-_eVxGOQgpjtR2k>9iiYR)z{Mq7Pm;%trWu_clDXgqi>t z*N%NKfO+w+zPfP_Owwe0ojs*zDV8+>3#2K8n&X=`U#JsxWo^SU-ELJ&uMD6?lRNF^ zySH@AA4X5jnXx==8C>Qh@qma5BIBqCXc-kb#~sG3_y27CtG73Ylg?%66gkF_<MPc< z?I(w20;`7IlZpTkBkt>Mnee;G08%Lf1QQqt>RTu1k9G?dkEY@w)0c#~C^_JZsse*M z;Qki>Cv9}#A0-y^0H@r00*|9?3%quLQ-!Nzz6JFv0|*nSTTI4>|K4msPXm@uqICdA zhzloP3e~r=l{tseg;~hDcp*0#z^%piafdoELywNOMl<SLQ`kUw8ZG7wQsvEEoG!({ ziwA*<9T+%2pGV{<`ZfSP-4xUB$k~+MVue>R>}&=~bUElsEGAlC(4xnb%OV%y6&=>O zt1a_~Yb$+1VaI<7*HaYQvGY)E#SrX_mVL**OVbL>mw3GY=J;0+-hFjDkJQWBN+Nrn z=U7*K>9{=K#ujgafJ^{H?P%}%_P<oO89*w9es#W>KX4#a9^yH`yOw`v4eJmE=;YrG zESOs;2>nu#2W-G^6k$YMKtRQRM-dx%VL?EGzSJE82FVPdJUiP1k6_{F$#arHBo-Hb zJ7%WY6chR#Am~&2psga9QN21{5@9Pa+CgdvDa6u%ic)}u>bU`-RG;Ig4)m34v5u;$ z`1qmmDBXIZqsno-0l>7in~bX=;F6PI-7jqbg_Utt1}#)|WtW5~Iob|)8S>I8zs0=f z=$qTu#Ti|(4`@oKOq|N{!_O2iuXzdU=8_N4gWmnO!QVuYUlP)t`ohxx+V}&-*Klv< z3Ht<q5+yI>ePO55%@HfAV3)VSe^Lk!<GUdp6W(V45t2Vl)ba>xzJLhhe89CG9H!vk zjah(B1A_R_)du}H<r3gs6mJi!zTgXaz#J?CGrH;`P<?`UjR6$kn~RlUq@#U`Jl`-N zOyFo^AcuAd2L8P>Pe4DX^FpHVHbW%1fIu`)03N?;wTd`YP3DLU0Nf~+BTP><ZV(y? zzb=eFuLWpJ=<uzt`yR%~jgo?w9I5?-|4uImGNYn4|K8gu3N);<_c-ZXfS5TsBcbRm zbzCK8msu<yQC(IsooegdOGZfq)dlI-bkQC?%ry75Iodm{b4hWqnSB4o_*YZeFr?&@ zN5q%ML0xFV^1uWU*DxFWHZrg0`rh9J4rn(9<^4H;GKBS51`upFmNS8bb7iPhv2T$Z zLJ%JC^W_IhEF}SN0H_RLp8$wVk1eo%YzQa#S44PkD#E{Kd4mNk4B(vbeS(5>00uu( zo`QZ!v@pzBh!GSRL13taPaN*-UQd|9>%tm+2dg?$fck)W0>Of@AL%qUzu-pdMMt zqhFUae%ndeu%TLozLl7L789BOjk|N(ks^kHC^sSTG>`lLe+rlqN0BKGt?5AmcTZwF z>4xlrwzeEQg#qlbD?eNbAG(KsJ0`=F!+%SbiSd06ggN2q=u+$JIOinCM(hTKyCO52 za4NntO>4*qBDUJu298lB>~A=#U_jw5{o5ObOOW*6Uw>zvv87qBwg9jvo$O}6!XpiP zikC@3V$Ypw>=O8Wp7{HK`4j_)avc7fZk$KjM|wJj4<xwZAW#rG5cDVEV5<flSu|iw zoI!po3wRp#`lTCa*yK0`WGp0OmJb5u7khL)1TIP}V*n$8e@g&_cgh2S|2u-gp`iwR z1cjtbG#DlzMrs^mKQBaqVd>@u0WR8a#=(ALpV%+TTx*M8-f>xU{f`43_6*>G;Shmg z0DF1<p4%u1s@taw;A-qNi3_#Z7GQN6L&}{=eB2Uc^o<4w^xQ7p*zfpxD~>7AF=Usf zvdmiho5n~;VXNt}B)xih1cmICE3@|6_0vm^(bXLf7z_*E^?WN~0Q>289&q61QKG#< z_}vTr7TUXk0X#6`$hbxsBYObjJP==cbRgn+5J;>;1xg|k1-=*%{=|m~90|9wK|p>8 z<ls6acqDrOk38V%L4~y*VDf&)RN&`)Add6G6{H6W{BGcl>sAQ(<uTphzXJtQ6nel( zibCg(v>Jbp(y#i^u`dgd3}{C*c<hYFcjN(+-J3Z?b%_JBiSQZm_2b;d7Q?tcrXtTE zKo09;51{NjQ?w;>ya);ohZvb87=y<6wyV)%r@Op#w$q?~cCawDTopAc!gCbharmO1 z7k)?4-BL6y=)W$%My>VBitRgJH;#Gf`W434(Z);fW%ynQ`F~M=&-5fDub0#L>0IIb z&~_b|8Nh!N2t>P+0h|lO2MLHAv>pm=q!fWk{s&+X5O{}t>NckW#~=`l1m2AjiGn)A zSk!|7|D{FKRNz$(aExdh+@Vd!kTQTA4Ga_bYI*zsfYXr={2)JMARvWGz$l@k@Q$() zS_5+8Vixv)E7#W#=zDu$j6L6oDC2uqqCH*{u%i_j+qtHlbEyuGCd!^~&q3`eD4iKQ z$22s>Gj*O<^Emx-TiqT5>01|6=*%TfjQ~sjx90>EL{?teAGgJ$3bQUe<L&D&sa2B6 zv2H<*`lb%d42+{|92u=4&G_nz)`4(&L}))5ad{a77~Hqc|Hyn4;9iud)O(Ia>IXw0 zMcxvU@LA|b4+l6svv4f(?;T9vD*gOhL<q}ARTzZPb|!GNZ}J2O=nNpnIt4B&AN)7S zaDXis97s4IWG61bP7RH<$g6#lEkNi2p!{z!;2*C)3WOgo7+>IEV!%1L@9mcipt5r_ zM=HZej8|qE)q#N~Sk;fL5NO!YoCa?tkcsU8*nfWZV4D}gRqR6J=g?X$4rLJayOv%i zdy0UQ#FrpS<u@4<kjt#Q*6$T36?6%cyL18h{?=l(=RD}JN1gZ1RzK?hjbtnyx?4lR zbayg<#C>BWD7-R!sRD>I+A)b?oQd-ub)@4=P3R{;pbT>!aGF&P@RaQV{SgDs6)F%O zP~{-`y5Z<AU7(&Yfbf4a<aqD-oM15EgoTIt+Y(?BIFhu4!7ihgykAPh{O5EZa$)@~ zWjcWG@uMg5p300P?`mlhW9Kj~ZZF5D*~N13oIXG!E2_&u#pRez!NOsCQVF-9r`4~^ z%NaVrSf0}Rx5`r?y!47g7(Z7G#U95gV88#7yXisn+t%No5$bz8vG%PQP@RF3<CEz$ z{wqNSuWinA3jxk&CX`g*jNeuC<-=_ZAPQVxRLnd`csK1R5&V3(4wQJtf-gWnMS1I5 z3?RpW?1Iq1yHPPCs31V<K2L)f6b#rSCwO`}6hxANg#kRP0b<~KMLNjb=^z3TD#pyw zAmO55tq@qP(;h+nzvIC7t^X~jGH2)C+zHXc0NP=rJ#Z%okXv?^E7Ze*%j&=Mc&ld| zHioUyd%`qX?kq1iTw4H%|D~j8TF=&&v>(H-eR>BLZ6#L@DD+HEnTS`|O6SXbXD13n zcdWmR>u*EC?sc%H$gFdrhCa1GjQ@wD_gr;g7@CRC@hZ@~YwY_Nz)T?hcUnAvJi@;? zi3M*9w71}(#D6bke<=G$_;>m(KU0B`cwhs!kO;g_1#vkmL4*EO>Y*SWA^tM`DLqH{ z9t;@xha><D5*!G^k#KKsR45)usN5Dd(PHfX4+maY|FO?<K;9#M08~J$zvhn@ME=f0 zITGP4tcI`@mZqvZ*D-A0nUtQhpi!q0h2s$Tx(v@x+>2<K)k~Z$uyOt5W5<P_&C!7% zO;aRXkLu~;Sh`8BOcb)betG@d{N(yOA?tKtOud}}E-+EQil@hud;LBBt0(G#{G!uE zD=gq$Ss))^0O_C00wxE9hDdOx0^LA_3k)tCm<E->TTB1t!YT<Og9AUELJmlpv`3A= zE#SRFO_Np%!H^XU97Mb*435eGVp0qcc_jp7H{d9TaC~sGL<XO{O;u><sfBSKQF>?? z$pio7Zon+-=Jn?QzNCG`Br|9FcV(x(ZW<C~r`rH8?r2-{r63xXP)yEF;q#nNE)+q; z+oJK>eX>NZhK7|l61J~Q^L%Rw0!y)ADF`C;9X6WQXVG`!t=X<#)v~Yf`ugb$acyg5 ztY>v7S#3v@czBvi|8ysUJ%I@|VczMtFp%yp`B+@&&+Gx5o_&BpeNo{30CG4s1IVly z0`Ji<IL$qYz!()Fg8eoKENQ<ur#ZbufEV7&Ko!z`>C)q>5^#rIW-XJrFNiPBmkB)J zBRvuZFwo;CEd?7GYat)p8HgEYIXt+=Rc}CR?(?^xV&1p@w|qqhXn(he!ABc;XOUBG z#dX^$8=cZi=m*^^H%RQtc4hsRa%iUft-R8ir_;(vr~Jyq`gd@)sSMbDHYb2<B8#6! zElAI|koM#795Z$hCV^#4)W)A)UB81}`lc?XOz3vS+5J#ARO4VbXYIHVg}Z0&pFJ{Q zP=mhrBf$>`xZ4JDj-f|TiT_64r$Hxv3)qWq0lX+N7eHj%Z35v}B;;QcjsZRpU-T$3 zQTq-+VE|9C7voa=mk!MtKm<8~;gj;1J9@z1G*xEj89;E?xQhy-6({PZ!m#+<#S!{U z7eB`B_3M9&WDDlw?GXTI$$)!I$UiBgcX5|Ft_wfXI=bYdQA)DE$abH5nz3VwJ)KKK zi2eK)|Jm;7e>Z(ib*a8zOe%Ib2jzJbwf89h#(Q2g((`639QXC_8P-)WCP315IXdUp zdnGP;HQJAcl6~msYlGQFJ72=|7Xmv^{9WKK76v$PK5>2T@e%9A0QG_SP(f?wF~94u z-(y$bU^~Dc<2(!ZcXK2-xgMiVpHIpEn5)!#K?_7Y1&~MYl5~$HxEK~vkKk|T38y_7 zKLm4IE?~Uq!Fu1C>Dc|(WpGCrsU#;Hr_+{$EDY2A7EvK(5-PbD*tnPBQ179@^6#ww zEs4M1%)x)iyH=xh8ZIRQXxFVJp5-iwZC;5OcY0_$di>GA-@+#n*FC#(D-Cubo0<!U zP+At*r1!G{g+wb4`ae6xclfn9jU&;TsOuqMJ-o%JG>@!%M$Uz845e}0TFwMQlgdGo zTGGy^!Hg9oNCzq$6)S<<c~Tg^TTmXG`U?ER(@DSSA?-#TM+{_)_-6D+vjk@#932R7 z?)l6e%$P}_Kpq?5muK#mfIL9pPXl4n;JR$~ld>;sq(MG*0j?>Zr#&HFG%OJay|j24 zg#qL_3IV1g8a&Mtq#r0S>VUv}1_-*O5J_O*iC+L<igC~d9`ObMS7+z;h#<K~=HNl0 zZ%cUj`lk%{Tk>rX7{G;MO5~-1Hjm!bbVe@QH|E3yYD1%_`tzhOuRYjdiv;J^Ro<uh z9VJ3mSV>1}aw<jvJZoW6zgW(LQ~c?8d(F3MfF0Bduud<F^{@Q3tpjw%(e^B0r6M)T zx;<XQz4Pj}GXt3Si`3nW1kpu}lF?CVGcsdOK|e@5nSerzAFVKevBT@d3bxA!q2yoO zi~S3nJPW@N&qbvH_DvIokT7#zAe8$f<2Tyo{;>BkZ=`}`q@co-f%)ESB5-u<0Kb4h zWBQ8!a#@(<{sIqA2`>mW_?5PES(X}xf#^_dV8bl=|33vp!zrWcI%ObA#+GC!lkn`@ zvSdasAt3Fby|h{JM<+rxI+A}^8pH~4g0vWj(eM#A(O-J*`m^h(@SRDAhk4lP6kam& z%=?r6JKzsF7{D4`Gd5<~tJ{k2gue%*(QHTzU1`@Zy(+d*CU=w|XOC@m+E(}{!3Kh{ zqj|P<dm`K0*`I{YKyCN8%l5BOxv6d+=UWA1mpa*AgtMgP_ZzqS)xXW<jWyM_5rDoG zB7+s`a#akNz4&4#nIk3xRCNyY%QV{b4<vPnAC|8$fCz5lz_FL)ekVYTg}pOD_{>2! zYR<Uh;uuNykdX5cS}^t&wgNeWzMK8S$3UY1KSfKkqrZWH)2x~>BG7-x<^abD`!^$7 zAOfSsx|R2Q-gImSH~n`@guIYsDQN7IsOnpYxFsD-VZ}z`W1^;&FuUKWqR?mG|BY-0 zu!e~w670Ka2+6R<N<hsC4QFcfKYvGX9DPqY!&`?N)vLYqC>t7$ZPMY11oHgpa9KHG z=g@3L=w;N?>xK&wZX=S@q014luht_vMTgpPD`TC@<g)hmvMb3sU<e#T&~62V9DqE6 zzSIBEayg?QMXn>icgZ7Qa6`I)KVbklkO6bheCcAK;HM2TJW@S4HU)UD?*r?t5-=Hf zSO^GbSzy$Y|HA|Uru~HPM1yh0KG;5(K(w+5FrCN^>DSi);@G>uq0lmZBkvs3w8RhG zUB0}tKn)<{X|o`%QYg7)+M>`Bb;UP5)3I?f3#~jevqk>v`+rUMMz*|qd5Qad*nE3~ z#`k$pP--tP^`=ytWD8pCuMB!-G>^It<M5)QF+-5MKq7hQ3e@faWdkAm&5Zt)y&^O$ z>j;dgsGJ=&c4k<?xDas_MhP75ET(dGx*l*Jg6!IlV>Li6`{p2H0P46tyxzE?jXP=r zz&Rh2k7>3Wv<K9y3?SZ-F*fUm;OdSSEyjy3yS(L?6C)PcBGGd^B*A>y1mu+M*j!&6 z%bOMpk`JLS3<?4YoR(N{G)`pz;rtkAYXIYNWcT;zFL7TY!65`knHE^+GMfRv16c&e z0m)oso=|(!+E#2#L;)7!Mqvgg=D)vx0QLNmHh}>wVO<M=;%@BL>SY0#r3qq{PRb=) zU`(sOIqT;cpQ!cn?x(3mG3dPN1#XJ~cV$0ZU(JQ$>(DyEXVsF8T|=oTOux|!^!UDQ zf{r+ihLnV1rI=Cb@A=)Tfr{)5kt)H6`R60Eu&*J&+$1W_NS<UAVBiDtQ71S982zm6 zOg?ZL3Om!UNlSLxZ$RI#ewX%R6<{ooW3CzR6Ugt}_&4F-$WllJDVZRG2rJE(ed5+i zz-;?Ah;MXcbTr8XmcdmSCU(?C1VRS>yA02l2yi}Z349|dbN3Us*z&nIj+5C-vlUck zIwEx80l7EQpBZhH`TO_xp8`9RbZzqTnz*`q2GcqzAb?4{T6kg^Qp4IQj^-_Q@d~wd zV>%0qOCqkYH}doncsG(tt6vw$<9|v#;N2gNMnQ`QI6XgJe-5JYjAkjX=(oE0Va4`N zW55~l-uZUE_J>^SW&RfMUB*xd1499Z?aKhfU;NkL&W7mdnXxpIg&PA1^G6QIOks0h zGC)WKKG;9(MFF0hfk5Vg3n}r!2or&lj|jVf5C!H;Ab3T9N%xoz)nTBPe@TwWl3=}6 z2=KQtLsA8hBeEF4>1jVRrQy9bawjbRg9Z!*NRN1tnAy34P2t{>dfT8LItC6IVMsh> zLEw%f)5pCd(CE1T=Ka6^D)=I@vVaS`(oK|{m55rviiBTl^pj?J1YtVa#58OJt_?&v z62nsFre;<ONhGD}EVHH4QyQ+9=k+q+J?Q8Nu#eV!45@1=Jg(qMr4MO*qVReZbYsb1 zdVJ+Ygh0pR{Gmh6$`l5=0RVx3^zeIoc7PuYAnjkvNB8w~Ulg3BIt+L(LlA%IW0%Np zW<e32*e&P}RJVA)k(m``bT$S74nD;JX4zkCa2`F%A%AZEI}VH@75Zs4z#r!{cq;01 z9uT<4l-*6=0=>x##>BV-fFyxzv^OJH@->SBTV`!+leary(m-sEysk$03g!*p2~KQs z`~E*GYrn7L^Q--5Cabg#tqj(sPZD(GxY`QBD>_STltzh0OoK!67P8{+wzf?lsEDdB z{G_X8!1bc?6m_wtq#AI&Mo}$4M!Dbeq>{}{FLfX#VRucJG2ti`mic1|18iKKZ(FVL z@;!COdm~&CBd9JPh`?Agmo9?#lzePdALE??<YI`w%mVFY+~i0CriaBto%XB@cu;`x zMI#aT0rZC!AUNl-_uFesT?iPPk+JBIT?7y_Ixb29P7maZfk))52l%-#fFMHrF+?&W zaPcM;IBLj0qQI1h<`Yq&fN_hLcD%L5Z?Z22n&G1Zyj3P{vwQCi<@QDXfd2jve_!8s z^0HJ2Y~MCZe{{F$*hcSZ)=4FedQ?&>)$}}H&*3Opp~7+rRaio6UVvG0S33jf%u{(u zEjBGIldQG7OaYcR=v*qD4&@X)s~EBL@|r!tqy367Snk^SI|+7cUbR*Nuaggf(&KWv zydXIrqmMsGkMVe(5Ms4Rt?(D6X943KBOmg7YrSWP!U)sBdH}vT6BAyM-&af=REL33 z%%xA}fdmC6D%>(aaBlQx3}E(y6S}XbfKUu(A;E$H4H5Q0@to<#gOL4;i|$TH_8>ci z1%NXe6XOB~5)1Z>4Hc#q1ZE7NjQYfqyGyri4?~A674okoXupnsaOoOnfP;xv`X9Yu z4Uet~P-xf(!*}Uz07qC!#YCtTG59xOa|%gXzN`(V^GxCUQs7oa&cAP;9GxxF6bfI6 z=b7j|m!;pV;8{Vm<;D(N4QHo!hS0ysE8nbn9WHCpMdWmX<iK*{o0H}s0&`7f<5588 zUiSeDx4-TM92pN$-*LrWs4rdyklf?N2PF;mPaWtNT*#IoRE-I2e6|?iU^*hfEc}Z^ zj~3%Zc85JW1p|+I&_B)^0#2szhJb)PI@)Mb<2en~9x79~D8yfyX=6r5G6Ca01{{PJ zEk?9CNe@Q6N}xbD{L8dr%br$|nZaqJj<0+H*C4(%Nqvd8LRT2lX)e$o>PvR%l%?K2 zB1|7zyQ|mW?YPJb)fgZ-4d2eK+}cw#+FhL9sW{fx?sK+!an5({B;O5Zh1fE3%a^3$ zsdwmz1I3G3M(;@4|DYPMT%FufV)jO$7bNWr;BkS$O*&)C`_-lH3q9(GRpl?P7{P#m z!Wl|~{-AtBJN1<VQfD10z_9^$l>#J0%rFX}-ZR-3V0cu4hV#Q47t8;^h)-a^puTC! zK5#_<eI@LR8x{Q5N<z~_$O8U;N<lydJq?&4tj`=7-hd7ds8Gm2EeV6Yw*^MM<^hw{ zP!LzK)D4!x6nIut1ZYWvNXcn?;{N_WAu{Q{F8S?=`tp!Xde5KTrq^V~#CsV@7NjQ( zlN9*)#@iX~NK(U4bR`fHJW!AQ|9?tjE*u4OIYc{+t2(irRLx{(m$_WWjsTc7T?*|Q zcC&nl7?K8o-7&lnv&QcjS9?x$PHL^P)r=H~ahYGy5wd)z7?rp3;&#SbdqqVd)C~Vg z-nzU_(xkZy)|A?e!T=(Nm2qOc<IJ;Z-#vFExDekbw)yQb+{?et4BlD5%!+fJ=|MIy zW|0Q}aEJ0E9z54h2svi{p?{JATxq~3>Whtu0PC&<xYx5lu-FF}Y=?4~P6W=C?tptG zDEu=6>j=0nmTUl=K2@UQ0r|E1(3sGL$cs3q10)xp>S?}c3P}Ci-P;NsAf6Q90OZGn zKio43M3OuG*8_(0dYvKIuaYuqr=z_|cpC!^jwPJ*Vc3ydS&`Fs3PG@1R)_PFzG=V4 z_zO`e7!|Xxw{K+B(=z0|4NWPjqsHyom*1;dGZ_-F*jZJtd}PHl>!^_5eZKv>#De4l z>>+W$5u%Jv&^NP!`bPa~j&DJ>bPMopZ`lyYHJERm{~PD&ofd5gBUAt!9VHg`$hZg- zkL&``nfL)oOcy#ZEMS@_2(}h}L72Swzj5HM!hvwY{oU^Y#Df2O+d#t^HVpU`=rB1T z3Hinbe$de~d?FR%z(brt1qztkQ{3kx^3Y=k0J}UD20n|DoZ$HD0RftvJS_69pFSYj zg?=*aWm>JI1&T|~_F9H-O5?VpC%i_9pctQCoV{|1{pywNh^QI0WqPv}E3Mc0W(Q{l zyzJWxXlum1Ynn^5|A>VfrdL#J?6MZubQZWWdS1n{uMo|T{h*IKeZV0_V%YBoI0WmF z1QPwLWg)vL4GWgp4C3Pe;C06D*)d@W5E0-PeG!4o_?8ag{Ey(nJWM3{BLeUP-l5@G z_CECE6o#`Sa8&5O(JwMV0`yh^n2ikmP>^_H$Jjdu=guRg>K_4N7mox_d&GPl00uCY zoq)-29rW|knkZhYuec<$_&Ml5oGzZ<@jN(eWdr<P(S{hkiu}%pKlE=x<e^<KfLaUB zze0Vbz@^eis&jYlLCFNJsPPs7S^zk9XDuD^p*c&2({mM?xvjG9jyIJx^%5lbZAWI^ zOeklCD92d7%}eU81eP1t?|9njb~;DvyxS9iq&TMjSLUSqj_jY20KQD&_3^UVZxp6| z4annzbEg3_w6Wf3u^L7U`kOW*J=U8^REJ?2>Old1TQ#7gL+g#naNOX-O#B=K%rS6@ z_fihJ2{2GHf17^?e`$-520ZOi6UN0EK)ku|{uBAfQE^|!|B(qCpt0abd@~gk=<<37 z0uBazH}hHYoKVf=0d?u#K5;y>@(vFXI|dv)9QYRuc$fYWk~RQPl3Vcz{;ReZNHzpH zoozIiO;^I($laMEEdSk#;ae8Dgk2TZvh3KhpPRxlXFGeOlFo@dF6;&y^>~zvYRAIb z>aGCYSg^fZnnEReCuBpWll-1=?pa-)7A<q-ycPgpMxpSK0eNJEL>ry5fE0^K11p#( z?vbDiFb-utyVPG{0HdZH^jLe}AHoT6b2WkBqhVhl6ONcJDIS#mi*?vDjC^$&n1I01 zH{v5Ij5ylEfpygV!%zg6w&OmBgkkkCa<!oFP=3h>@e|+}?GBfRZGG$mOtrw6f>D-m zyzT3caIkQ8jmGnb7{#%yRytY^f_voa%bH%(Wc`0|q8Fu~gUR`k5CWE6O%@}@lyItV zDzAlp<xE%Er6sW|P37!^k_{ri(<<lcgiik|7jaUDvBspb7OnRu6p-e0C?den@7cL+ zy2@(S9OEA3wOycCt<%4l&YP8aw#U`eu9Ovz7dOLgZOSbM=jDUMBocfufah6ofqe+D zF~;ZQ2f=#LHUl_XW<-rP(vJLffdM>uK>SB;?{N&sp)i~meF^~r0izBK1n^Y>+&S}? z648G=)3bo1Y=9WzkM^hqgCf`u9dqpg#9|MiBe^eterdV5IVWz~pb~CO1Z()e-<#1A zgH-`=xp=My{NmH*pSUzntZ^jsmr_vWx&9$VUrzXy!TCXbvvMMPR1bSyevgIO30|o3 za{opY!_hNU>w-uSc&mOUuOBASv7*vzoh(#}ZaaPZ{|y{rx~5ieJBj3V2PG%MGA#rE zdkG&+O2PCem0#@H_ga(5%NluD-4lMKf)d~lZ-5-AkAGC@8Kl%7C@<<fc%Z?r7Eqk$ z3vX126*Fj$quqdhSg3*x$pD@%1}tBiSVtyE6kIX+ARj=0fqm1rQ{ZL-E(!bgAw3Ij z3&G!t^TH3#t-)XP*y$LVE;E3Ep#?vd4#Hs|e;GXy;q+uW<49tjBhi6P=@xuKK~*x4 z5gBs#{Y##W0~7US{iDwMAH#o9FovInmi<<at{2*@rUm+5r<+8R8qBp*?463lS?3qT zVDyA;(`Q8jIodOLI<D=cl&57Fl_ViqT%J;}AL!mrs(Nx!9r6%xRLd^cibj%gqKB$U zDv}`h4hOXYw`QJ{ge%QDDNQb;=sDoyB>$lN65b^ae5L_Y0y=Z?VSH8amx%3h2G&)$ z$Bc7i93Z(M!HmTjbsNaSia<c>07lsFhdTX%1E&QKW@-fEGm>3^C+zD6g`Oi(VxWzR zXY+mFKvWDW!K57EjkLXfYhVS1lL3;P;8F5HfPyF(XqPNtx+gmDV_Znnw`&-{P+-JF z8`MJ>=*A=S9#E#nh*5bfz00N*gZJh$>wg6WdN5tmalTz2MITig`bvvbh=%LGB2PyX zMs%IZp1+3c?HkpY<}kYRMVhD~Wpku7wN!G(YQWS&=QIwhnODEtipOt~hB~BLjlY=Q zs?KzT<k4YNZX7jj#3*<#@8=r@u=dpQm5ukKkKo`q-}yviJ0Fo>AY|sp<Q$NXORyTU zBEH4|;>->7BfmqW&Iq#0Cs;3L2BG_+jS(}#HozDk%>~XWz?%THYS4&C@M5C?odXOF zD0v_<u+RKmeyrfVvA>Sza|FniztDgO0+c)u=APp~KMJ9Ik384&9b&pXIZ?!(<0KN; zPS#)0pAdk@&de}?9@5&TM+co4+>;H?)cCPM&6{esLM83H!4|~^m4j3)+7W|fs2JcW z=nhNYxNil$@;n2!O>_Zc!CIPvjTfu9Dk;8P_lFv<Czrde*#D=*|Dd)mKhUy%$?Z1k zdH3Y_9_@4h!6PM@G~c`kXYQGa2ooq6ui3b0QP6u9Ff3pu1oK6K8I;O@kb~Su-M{J6 zt)MPcUt}gh55@)U_i+d~Em^=+f~gW%$Nd(h%fa!rTo6=Pz(Ik4JUW$C<OLI@rG-!i zCT%26?(Y_k1fD(@2g0@5fL-7KfqY>E1By5_xSX_Wu#Wp4Li(}7V8=ea%ZuwpEtn<* z(bHhE12aF9BMaDqz#h5j<jzG&kxl_QG%9!b9ZIKlj~lJhtK2x-h#`2n#v<i}F)bd_ z;dKWCJr0gbSYLV?&2aE7s<<7X+Vfn3<SH@NncMq=pks);{nRB($;kOa!g{h1idCcW zpDhaZf%s5BctF5E#|iq<M;$E#G+(n3uoHco6l93G5k_Kj2ps7k73H}T2Z_KT084pj zXueegCJJgu#m@=(A3f^3WyO5c;_s2+!u;V`qwMhm^4){Kh4)a`tWidf`j%c2I^(De z+)I*w0VZRN1|B>*msbj*AVGnt6I11JCtyvpe~X1@>Ha*ciJE0G@ooIM{^wGTusc5+ z7{Hr?Zo&Y{&iDe!u$*mZGuEXrufrN4$%Lm8if6e65h>acK9lJlt1U<9UKG?8C|KTK zp7E2LD-LRWq5Y|QI<yygUcK-A6HZp>ITSg=hueucVaok~1dW~dbgZ?YzQ;8h#&NdC z*wMzoek*k7yzN%NX&%+3zhnS278@*gnt0JL^hh`eK%&6x?~C^Uf`>P*>8Agj5Cl$l zN<7gp*x$Eh{AEybfr|w1(R2t{cM5|1x_>c%#DK%|O(OyjyqA!F>A)Z>J#}Vw$g*Wf zzf{1R&jwLL4p!9lV0?WNF>m*o1p=lW!jpxJK1=qaKYngL_`tyNZ^C9tG)Ka_8sV}% z{3Q{@OQhV=WAxkb<#@KU;|pO{ZHuO7)^x-xDGX(Ss24b6+5)M3ADjbUhApAF`z1_i zsWV<}+zX^{kJmr%C^mzZ8r@IPlfpAHaeq}$FY+vyoePqFY3bqd(i8m!{V@oa_;o@d z=#uua&|+f%nVsz4(12kDeVd=-KRWgVMp=Pe;=&QUz`|)h9|TT0Kn?;HDKKw5_)&2+ zoF|6@r;}}eR}5f$<#4!+t&M<Q5b!JNM2Gt$3LGzjaX|wn9RkFZD$%1ZJar?B43tEU zE0ovc%q-!3Bf+{^<nxEoPx@%MFPtCF#Q@6LU&elTEio=siOyZ-jrP!QBON@vSiz%# zv%Xe&f6emEps%0aQKthOuT<?p$G-pHYX{d=^i){?E4o(I5u|_=Vao(};qkBPcnSNO zgq3`&`WMCG`2Mwx=<h!tHvXX=r~eY=Jqbdv-lqK+#=4Wu<<*I~qOZ}y0K)$T@+AZu zef;eRVAwyrTV2#&w*RG#%J2#W2l64H#!pJ<y=aU0!$cOs6JerYz%oP?m=y%Z!XfFv zi0lEJei46f`ySyj(S>w_X}}|(kPIRP$E@?=i2A;ccHk1gQ24xOsV)ziO!C3uFJIAy z&wR!ax-aV)<Tqw3{NnYW^V7&Z>Hxs+_;0ozZnmc>dDVZ-8&t%zyuu7R31dON2H#Md z<phrw45Ya_UPZ!w<qOnXg1`kcVABb6R?vZE!=h_}$$fkMzz&wRo{(1_uXWceO)rN@ zYZbsNZhb$Df~Mb5X`}!Whz~QK@qyw30B7z-e6jE#I2HSC2R-pxTrG6$3?S3U^`Oq* zo$&ag0Mn+Q&$-R-VET|%0%rREAqbos*r!qfpbJ<py@i8Q17@_s|Ir%;TvulR$AAo6 z^d9EDct&s{y~KOtM8GAZDl_<c2mq5CJD<t`GHc71RE&Nk9Bk)&gu+Ze>tjTMzXkxO zn~RwHyBh#l_JvCge+iHowd3TK6*_Z%QqnM(V)+<WnO7Eo$1|02`k}wxbVS$hnld7I z-YNrIB(%S@=TFe}6o(4}P?AMFQNk+|3N0%DO)}<XgWY!SM{RcnbMf1$AQ(l}VpKgW zd>-L8EhV2LW5t{SoOubpbfX|V=9~Y@z_!@F7nLz*0OO*rGlH(t=|HGIK;SfVB!rw$ zFc&goI6t@>kQ87O@FB%rDg<N%4G?Iu(m;a#-o2y&e;ot3j|3Ux-5^>R8tsk(JR`q@ zLDC2GF?1uZw*>D6)uAsb-ltef`eB;%Zzd%TC%HF3Hkc3N6)jyQMcV*bH>eNr{9DHW zdLp}@V&7|XSmf*yJ)u;rDL6CzUTq6}(K9Lt(F*d>&W)x?8-{75ezX>Uk)Nj34e^>D zj=L}$iI+<P$%51EM<&P285^c-8iv;g>gh3(CW3DGc`rraXQx&x8kd5Bf`XTvk5DF> z(?4MTPMjEH9>+&}FB`sr^g2+?6lDMzikl1&BEqN(?cvb_L4x{#eY^DYD)D@PK~55h z@iEE1Kehvoy4gSyhK1!5ZV-+yiiL!K0t8yPSHK~ZUf%;4Ki7DI<bnuIe2&wA!GO=) zfWR0|VqqXd0{#x3sE{)GA5(ILjER{?wy+lU+XUi+cMC7<aFbDL*97D}XI3+iFaBiz zj=la|R!_`*l+dlY<AQ|!$31{{EC)V`<H#wvbRIdX%tXgp_r<JrZ@he?vTI!^COXY| zmPHA=z2#spBEkCw^<m8$Zhr@~fbePf$#U5BUHxf=fWB`5w*vFRwxQl+(&9F7>VGHu z&I)WN$}Qc$|1BpI1cTBrpuHKQ5OfA80uvoU#}=`&e<<w#gA2T10P%wGrt_jNE|7oJ z0E}zynL>1zZZm)xi-+zrlGKm%NX$|RFa-cjn1mb<Z<;0yQ3r<ofVcc#A7i{_3eN!8 z?sta@v+o7uF`r@nI2jlOh&N*L26rO^k|s(Dhzf!Nc5#~A3?QM}&>7fBu+xf9nl(rU z2l>gwt_Rs=W)^YOwTY*tex%<fc_GlQ<Yz~|RU*d&S3Q^~RGya>_VQ((03wxaTO&Zs z5)bs86aVF@zcYofmHN`cz3Rzh_c@({lyX_Vh?guz^Wux1pP{ECdj!}P>H%X<uXj$a zvwlhvubIAm)X&}a4Tm0Qz>E;)4eu9y+B^eTEN~9jvY-Z+SaD~%h3x3>8Nh?{JFuUW zfWE+he6SB<@90ksFhk(PdHiz-5CSmW%KT^|cn>WZ!<7PL&mWH12+SX-5A`l6OrSU2 zsF-D|;QMws#{_VqKj9FNLd8*uQG!@5BAf}3J}`Xahz{rj8jc+u!A1W79&>~T)ZH<c z&3h+V`0$F2E#DXOg7==eUva%9xgUG|{7K<Og${%Pgos5J11RYjwk@ZD1=RkcGAvH{ z&E%C_`r7)6*F>QS6~k>JWUa$W$Mpr4y4=Dr$%!r(MHiQCTpM=ZPk@O_f6Qf0E{5dW zDa*8)cuuuDrz+Vm22#u0Bt3j924<y5W?P!X`1?OEFy7x%e@|)<l-RR;<6WBuUG^bu zr=4ptfQNxJ29W2{tyEqRAR?h(ivn{t9LK;B+&z(9t|b7(PfCivbksL990C4FFFfEc zw^FP8u`}?B4D1YGeI~S@@A~_~6Xbv*NlHcH3tl<Ujmp5LS7^O_PBh4Fy1{Z5(<L=G z)4`6UWbh@9A?CvA;U*Hjz#w_dIVLWS5v%w&Ee>!rCo_Y<57~PFJtx3eARYBzyqXaz zP)|}eF(r7Y-)MakiiAHezSmrR-Q@u<b*xB+uHU<NUX;G#4m71r9sm97oI7wu*Iu4a znWkARjGT+UjNI~s=AY1)Q7rn^-EsOCdC6su;aj4|Oap-9wQ-X45n~GzQ1dmuBlNo& zKnzS2WdP4De;f#6f!~CTF`NR#q~7z~!Gp9Bpku~9E|7q8x4>__jM?eGqfs$1Ez-6C zR&3Y;zqnl3<rgCt4&3Vs22>^xkeEkeeGe4~76JW%UDEY4La>nmc>zqJ0Eq@4VDPNU zH)ae3)-Y%>xNMX>8F)5#T=`O<JF+|wtbOK3ct5DW5l!;InSGYPY!KnNwtTa#{8k}G zwv0Pm>Kp(5(-*!X`7C*(EJL!KE$&D!c=x7wpT;qx^u1zM=bJZOvX7R>;mHw&EG^)x zcH2|&5WQ`ulvCZd6V<_587;FrL~0YQOt<F`OL|5FvVBp$8xo_fnjDYZkwC!!IwpYo z4vZL(S45DXqP|t(xiEle$^gy{qyyIx7{1I2?hr8T+#C#ybL9aS`fm??Jp~95oW_M1 zuShEny)d813i|evFzC(#Is=FV#DVhCsCaQ`&k13t-?%`15ex=81qK4l9SI%`*kVF_ zuT^XLnHFyvD)No~04<;Rnfzpkm%k;?v*i!anIeDeV2nKM`m^<;0Gx@s$5rH8&q~~$ z9;3qa%3)<*p%y$_(2Mj6oU}bk&C^wG=bVIK>2#5w0b9|Hplh5GwALW-70{Bvz3u&^ ztIG$#NJrBjHiYjuSu<F0*#6i4TAXY33+rph+4Gn@LZHEJp|;nG&WsNhIcl79lkXAN z*wI#t#DfaBGv*Jc*Nxr}_(>oEZLtfCbFkN+BJ>OEmk=;Xz$p3P;-dP?=j%ZS_6-<p z01!V2@B;J^BHkMTn=nQg*x5hd1DNK50i*(SfFP}dKDzIU2Pab%zA9E^sd84_>wO1C z{e|`-zXH-gTF;S`3vw$tytpRb%zWOH_F>augb45hBqSm{eX^M$D>wD`8+B*2WJe7H z(Jjaeyzt2X|0#)R>Xb`#9n9XjESkBtBztDp$)>i`vgE&CKgrY!Ganl;qa3mdLE9mo zo$nJGNC6cc!?`3nd2!M&qXu0hl7>$T{@xv^)r-+-VLcD0N`%}tgn8LI$3C^MRBX)# z@gTJVw5TW!#U)fO#H4Vzo;bnnQD8gQQL>^mvqhMD`(|(nS$|IG#4NCHK4~In_!ot_ z1?nA!!aGa^I`2?Gb2|vkWei}DTWUe4yCeKuLia^u&)@j*0sn9<p(xCg5`0Ie81RDk zyC?=InZQW{Np0vN1_6Zu6uBV&n1rF?)nVWOg9pb$2Pj<JV6P5HC>AadSVkQ9p!rsQ zFvbAJN$ChGgTLD)T&9EdV{9^kgY803f(PeLGibI6+$kQ?Xrh7uMLz0TZN7Jmk=5DJ z3A@{doZ8{ZxC}UcaW-e0f~?BwE8*q@5K61K=6_3$9E3qEU<RdgdAK~=*%Mvu_+7!s z!j{^%Xh^)T1x;WMy&j{3?HgiuuR-E8vcBcy5^k|Ar$F7dr6UhU;{${vM`St>9urWf z10gc#Tl_HSnw){&&H!!(kTTF=14kk78+0hh&qm1ojU5a(zI_@npH($_-t@Q-2(E_# zZ(l)nm{>(l=5KT~97qVi^i}}SLEz0EKoodC*gymm>y`3bE{+OezuE!(Ts9jxD2r`> zV88Dp(SJV~zj@|}|Ax(*&qkV)<V*RMw843K`DmdHA^tKtJ>c)hJJ*_I(SYUj5Q6zs zfdSO8CQ~HwPa=?MR*#M*akf1yDFRC?%hVwac^TuBTOz&|!tM-eU4rA;8ZD$Td~0N$ zfDNH#m+41jRMpagJl09m7vPDnb`~<1U~0k9opTgyCjS6#wmSO1hrK*c@fpcW!GhDC zsUlQ^&V7KqLyEULT_E(*M%xVFG`<IL)X|UXFJsf+OWz2H9xNB<Xh1*ASZo7)n|uon zl%qi;1T17=Aptw~!z=*r*H4@avUFzv7Y%6g01#xW8Yv>H=t;?Z5D5-EsK9Lai#HIA zeSt^*#rNJ$-%SuW*pBzi(~}}XpgHEBZM^d>!udhpxp1<|wDFMnYZ3scRgg<$aiK3E zBsvRoRXF%!(@+5$B}%ICJBKki?&v)~dM;g=v(3G@{wigFeeD}V$OcP2XW33d+hbCs zSgEHD^lpx6>+6&sg~~Q++6K-2e|$VE-Wfm)<Jf5|+`s2&4$w%tFnzR{Fv=;*d8Up0 zyAy!%g6U-I-~scT0bD`gXdDL|Q-n#50Y`(^oA%rS2>ipH4%g#YFvyQ!a2GiZ#Dyn4 z17vf6@PHT0pPm7?)OG~}N__WC0u^|a2L$Oj?qj&{foEKo`ph8%<Nx6*@79tuN#8j0 zMvkh1Bk4eXH|rw^jy94%M8p}u9&z+MMg1?jvxPlO+s`TWok_mS?VbTN!_ZR)2o=U) zdHj^iOQCpfh=f{|+>!(Egt{`EZF?~t%whD2bao&xhKGFV(X6&k@h&`Nxw{2t*MHgu z_ME(yEryc9FEtZuocml9wBI;Z=0|4X%;an452)8i^5Kd-15C3$fDA374?3`cJI8{S z3w+jmh63FC0CPuBA5R0J9^iabJjUxVu<ZG3sF1yXbPWbN_%m_lu+)b(xqQU4gMm4R z)!>nXKyYCAJp#joZW933+zXe9{|@>KlqBcl&$@yI`L!kN^}`Y(faNRsD40ZY8p~WG zS!g=0@Lw=B^TGD3l}QS79B}r-IOsL^)%YL>ZA?(??YZ6;=@vbU!Fe&K;QA&;{{|{R z_05sAvI%i3TpnQFgA!)l4!Vwx8}Vv%BtB=~6<@EnI78|oX>K?`;2J$JFJa;+2P|Za zC+~3}pbjgVGl0090c6YHD*ucs$HU>Slh_0Z7=+qOvonDKdt4cKV;TtkC`bn5rRJYW zpL0V2vJVg{P{k?0&i&yjzEFWtsyxdOQXraA#zZ;U_xA54^jnf{r?_9xpagsG<UC+5 zQRNVkg7?ZO8ns=3esHB6_U4ihuoFft1sL@%1Z11@aauIXLsU-gXyn%M>$Yov&DQ1s z)kBzNZQJk)KbjAs%fgAO*t1WUv?Pg=T7I;#*`*YT<k!jp9D<c^$}S$uE_PoH7QJqQ zWXMD0UaL^PA0_rV>ut1S<>Q^6@Geu)=`lr)5`Q@3-_#xhc<vcMs=!Pa$4g|Bh(}Jx zQQjqY^nADwxWl*~JrX!~L}lZ=i`sxd!1<U!0tN#jCd?IT?_TynaTm1#Q9dv1X$t^E zu$SO>IV!{e%3dk+;(SIVy;p_x9~SA<_wupOop0f<aK$q)CC#%CVGcNiEaKiy=NvKU z%W7g4@(0b6l&cSY`321h7iJZl{qN6+VL$uDcU~_4dO*@#xviX}$AIsDOt^7rZaKPV zb9WNBbW3r%`2J)Y=huA$hHuwELgF2zVb~40OT7d)#(#`%-Ng?GCO`Ny3}7|^j=47g z-eEzn2uKU#mpf7zK+qsI^PZC7{D=W-Q5cT;{dU9sz2a23k_&u`-E{Y#Uq?C!>f7YE zg#yEE?U*PgQ!eEP<p(NzMuIIBVgAycScj2>8S3anE`XphfIFk32OlpZMp8gUH$iEF zgp)ANDzHQirS&(;ez4WZ5}X4RVyiQMZq^Dve*Q(n>%PL+n$nS5E6gmP!;AW0za&>& z<J*etV|6sv*Vg=8`yEcd_jm3_Pm6JNe;qDrK36b@ji=jq{Uo^45(W_E-|qoTf6{;G zFP0C=yQD`6*6R#l=3qP~$pHcKsr)md&!*k*e~tdbaVMVt>(c5w$NLl{?BIpECV(Fr zfmE*(VAs=90PmrL07pgc@ZTx}(|atg)4*jQM1dHFmwD65)$6(KM|F^~fmg*fx4EGx z#*yeQK&MglaOY0D_94&6MKA2H0$7Q=d$~ww$@XL1#Y<OzGJ6=+VYi=BC)w&T_xfak z1^o+HX5Kl&NYpf<wtu+@^<PF}<l#tM(KpiLLl;A^AAw&Sl>tO444`EJlM0NC|5Y(y z1O||R@X6<m+V=ov8jn3I!u=Tl#3LdkphG~KPa!~PKdkF(p89_Cc~%?=o=1cv+UY3j zRXFT<@vpqsA7_AI0e>dR0>0aK-^rp5pBTi$jVARd{HQgf{yeHJ2tf`m^@Dqxe#hf! zMP-9PdzzIZQNGc^G@h%ilQesH`wg9xS%!I+TVOw(GRfOj;a`7)Z`s_2m(Rwrm$T<P z3F=8kjsRoY7YF-63C2t1mzakKFFFdA5rD{J4B$XCs6g744)QuE$VR{)eXPL0%>a&Z z2(D*^5`fb<3e?c=w<soO7O+YnAMFc-deHYzh6W@YO#4<mNHW7T@NeV30Z}&F--YsK z5J`CZTt777r=*8~otHqrP8yU_XmU>NF`d?Tv&rEZ4ha}vBYt{UNiUG!yJZE6m@CvV zcu~B#>%Kb=ZPG}BC%vb6%b#>d9uiy>EGy^dSo!cqGkOTvW3GM}k7f=@W(SI(;x=eI zL_}>5BY}NDIoQ8UAIYTHIIz&}cl<rg0FDIlrQ>|y=Y%W{TCAgw!9}^i=Ou`bBSB0I z<+*%w3UC8ML4I}2Yr<S^2xKxR1>eY{01-gc@E=2rzgd2H`9zYR>43+dmXG$!OA2nG zXUu4w;?{>ehTL-^BzMu%C7epbx5t(tw244yVMz+2-j6E3wKRPJdh__7dS9vhbou^M zT~0xlOYKW&RF}t^@p-8mW1T&X%i!GIE=ehLZikHA5`ccn4&GqAG!TFb)xqg$#s`Cf z`{?!_z$mX_0CNrSi}z&ho`QoU1Fthd;=nv5<)h!T4e%Syt9Ai;ez0M{UKDVPWCKem z7~?{KfK>z?J^};b0H^0ejPy;RT)-Hnd_f-r)iMb^%V+)5zd}B0B-hV_?KNZlfC5e8 z)p)CilpI`i*2ODVNZ5Sc9p7=6^wIRjl6dm77Z?!?AVil;^y(#&YvaYF9V~hY6Rd4t z)GYSuevbGgM_TClPKztzl5~z+%H5B6xlaKuaFZ-x3c;k0b6Qe5K!)qgUo-)MkaRWt zyXNhYYX&e~5MC4#f=$Xi7a-hu)-MthgA4ED0_oSw0N&Sw_Br>5f`I8>5)20V&D9CO z#sKD1D?|Th`N5oaG0MS_VLct8^(TG2IfL+WN8+FUrSfsY28Rlc>@aR?^a*z(rIG!3 zGb}lROxcy%V|eVmU`T~H3N`4t9a|eGT2gK86-NISCR3K{RcbDf;{Jx5V6}?LsN2H1 zUB@FRp3STo`2u9?7QRLp%5G92@h`rC<v}U<`Zx`4#do7IFt*TqK*KE{H8ckB({ClS zt{K3wM_IM|P+lgy50Y;hs6EWW0M5|+5a3NFupm1b{3HW-kNRHD03txZ$PaA*;eRec zM*)J=hyLuIJ$d9)?{nNoPZx!SPWxI2_`&~;3)=GNPbXbE<Kh&N^8=0>Yl>-qqsQ+` zFQwydP^+wWyZXDFJ}pzdrkjAzeufLC|8+a%XmzM90T;_*GFN5IugEk3d`vT9U7qa! zIt@pb4YyihhOQqb?%#lB0~h2MgV=7I`J5RY+=2l`D(#259F+lN*crgUGTO+~TLO<= zf6f4My-U7P5gIm75a5es5r8U51Aa9O+zg;!z`uFNmw<hT97hIk2pCDWER(*5{Bjvt zK6gL9T3`TcePEAU@r;vOTr_r4s1sI=v9NSrV@t5S>Yd<8Y+ZjRnFJ&-Z8<yMcAUwD zA-bO{hqEq<#&@?h`#!c`z5d?`+!6xzJ%5e>3kxe>%6Ed|@DOk@6~?Di?pPV`;*C%b zki=eaA<ga)91iw+gcmd?3?N22V*n4xmoCb)62h+({=-rDzc<!_`M96}9Roha0bb{P zxO{>E<nvXSzYzpb2cm*H`|m5c2=K$Ry|j1|^?td0v|kgp+57bF%P1i!gkgKvHDYQ< z>pG8OWG77&Tcu;rJ?3>-@c4;xUcFcUH-K62ab2<`qF&~Y=4yzwEYLD@C2#s1*6Nfu z=*sqc;`5!Rl8~YKH62h(`S9A<&m3L-1|%+lI>O{L4P-hLEC8ZHKZk<pE*Zc)F_;di z3?T0KOBHC$7Ym8MyUSo90N<1b{B1tu06#zh_Qa5T1o#N_`#b|kAowfdKvE($XvzrY zqiBv!W@`=zF8sedBp>Yrc@$4u^;%_8{;d`<6NW;<$NP(6I>hh_efCRIi|Yw%CW)0B zr|lQ1iveFdUVKZtB0VqRU;joM#f0v=5hNAq0*T#MA?*BPsm^S*{3K{B7<Apjg5IZZ z^R!jO>1zK&K)_KX1;+#q;#BD8`M<~}{G0*&X>Am=EMTek_aF}Z;2@A)2N2*_<OG2j zFNT2Rd*t(}ZGbL3`|G{H0e%?<FbBjTBJanD4z%FW45PWo!;%4<2E+$W#Y2}uh!08_ zKu?a{JM8_}8)^d_*Ol<N>uFf%#zC$|VsH{9E^2v=GEEIM_k<lh0(h!hQae4tR?1sT z2GMQ79xqTRuNJb9Thno60ey-b9(-~BO8;UGs~fBC&>h?JLF`LAP3omPN7ndYzfs@= z+bM5=#CsuaW4a3lkjH=ed4S+g88Zej<^L!KNcS)r<T&4SX>AW+#-cnfEeZHTqQGb- z^-#F4f&U5vh_^F<yx>mW3Iqov!IXin7T$8q=QDf~coVgr?!jb?8TqSwQicJP80yWw zzBMeYoPh=3Ew5ye|CWw=Lsv<>@vbmUpX3OfP7-hB3mc?uRLxqXU}u^Oip&#ZcjsBn zhEwPa`r<PZj9V9|plAnlFHauy0)R%fp)lETYUw5ASNB+)QF-J{@dyya0rLl4crClY zGZv6d7{HOif0-FJ@MmKH6a1y@-@<=bk3@m#2Ge2Yl04ut<IQQnP65iuYjT0VkN^=! z`ypR32t4u|vEaGKC>RYNhS7LF!f5@{fpshFJ3cx_-yk0?bGH&in$D<51kE#iZxEeR zeHy3sJ$l?8hW7YwTcIEa0hRPhKzR(<D{Om}N5dcVmp>W)_vrNy9cgtqu6g1px@)dH zYNyOOe|bzWT!u+snPm%HFWbM70;o~-Cmae83$O?IS2I4)P(1ppS-?Lx29Oy8{`jK2 zR~f*cDKh<9s?S+Kh`=!ccuoOk=yCaWBLRAIzZn5i4(1Ugcz+rQj|KvMSp*na0BtBV zJtA&9dTz1o<8$b^lRFj^oRz#qI10`Kuzmurb7UT9)s0@)#f$rkJY0##(Lyogw_HWh z0vs6X38l`jIgk+~dsP@(m&AydO5QJ*+Rg$Z8&);DqkHF;%MJ!9K`4n+wG@%ONtkP# zEnf+vF5im(Eu|qqLxqvCk1~GOL?6>{lY-Hl0gU$3v|Mdt;n6<``<wy{l}EF)f}a+r z0P`6!QM7l30RNvXAjl6u$O*w;90eW`)oJ4ql%^-~_M_(FEUuNGa!%yDW6ZY+CxJM# zB8C4h>ds+#3|N2j9;R2y^WTeJH3Zw!1}@9yVLM%B=YttL)IqV-%JX1tnbKzHnYLIS zzV+fXs2W+tvOn>wBANK>yW1CbbqqgdN9xv8_Xd0z7R&W@YvA82R2@qr3Ov>Vcar#l zUj>MVB@EzFe)`^h-~RVQfvcQBuFV{d5rrLq$P7h$F;4sl@dpqbIka}YOQsIs-uKr$ z0{@=g2nYatem~$J?44V7+b|47Rk<Hce!BnNwnfIi!9fw2v0{=DVVMLUQjFtP8V?BY z@9Tr*fiWm#%!Dve0^}lpG!oP76|gRMC)ZA6@Td|ygC{vU$d=!+>uj;LFR~yP>kx&e zB}?^!^E&#IqmDBE^bRJ4H@hIA3c{$2K`1v!DW2^SYlPtfVHe2-L%Xhv$lH3o8Fide z&{eg2G|>unJo%`#N44Z<tDT&43+kP=GjN)N-o(~;YR%*`_bTe)LNI~|!ND4H!|lKj zz^5`YfF8st>O&OZZUZVb9EB0`z$B3FYc~a!m=3r@i}IZ455S+Q`#Hd$eM!K$>+h50 z1+1vT-iH3Ba0Mf*y~7(E*t)Q-aP21M6qvju$QaKwH4b2Fc9ZR%4K~uJZIcVQ4zj2p z`?a@@-r8>)Y%kzp+Es(W;;)1`6_PBxwdDi3Eq<Gh1Te<oI+Q0K$;_ITqM#}qnsop| zvHa{#KW4D2%#c)ow~}3~W34NfE3e|(4gcIScYXXoO?+?dlGmVsonk<ClK!crYNY@H zajMbj2~f~!R_?X}a0m5Wc0~l&5;pyKqDL602dwjI@_I4{2;GPNXfnV%roN8r27XCj z8Vd}d`c1~%BIN(_KB(5<mgnSX#Nd$}0lt9j;1`*aIg0HQTt7{B*(%Z{r*axSz_I<A z?T4GwEl1V8(nf_7>Zf6`Z&pdG<m6Tg#EbLu?>Dv9Yx(RP3f4Z&f)C0W%o>3g$q-Ap zUH@RIv*#p5L#oBfM@v_gg|pFlAE6Wjxphuy2pOq9W{P6{w8*yu^`0S@yBI*P`4He; zLyXd5zEOVa=qeimxZ8$U0SNYM67byv=<jvnb$~A@AuWtR-#q9~d+FjAPNY5L_aPDd zqwE3*w>_poC3RNVGRZpmA??q*4R8(eZ*c{L$BG67Z#1Ne6S*=6gUmqq?eCuhFaV#k zf7c~b`}fb=ufLGnZ?9-SLL2+zE~pfglG1(+NBilz*M9d`_?Xx7zxK!PKU`UofBzKB zFYGrt+WtM-AHV<Fek8M>kEZt{-@jR9rI!c)|Np4||C^TBj}rg=f2mw}eU<@~2iI6Y z&b|QuNbd4}usirO*?2_;u>1NyJ6aLo0rGlN#?A+mMghMhiJ`|welTorm`c?^`@#|a zar>w}Ji!8h{szo#Slv4_G|p{1AQ!ZqQQwFF4sm#n)W)Xy$C*SOQm#mc=NHqNF9TQ< z^nm^QIQHM>HQ7&BtNn~W!G1L|`=vkh{qHk>|C(rxXaD=(NBI8vv&r}WRdRWp0c6T< zx?Z9LHBMn2k{^2al%W|wljkGAS38UV9M}n5Fo0QN=;09{xDUmoAr$yf<`1v2fS(S1 zG`)K;fZXIgDMRR-4*X5=S9ah}><E+tQ<J^fHOy3vh02p4*Rh!F0U}P7=JCAxM)v<W zxBdIc?LYhX|AGAzFn}Bgp&$z6CFKj_{awHb12_l;C=&4L6d(@ZU!z07`x!vZodE*E zsQso(+@MqtUA12v-OY|<04H>xU4V;8=@SL)Z?9HMr{$j#-Z96SxbDI}n|AQn;Vg!O zcT#h8RrN9MQC|$_)A@;ceCTEWvVYls4*Rb%AOk2(KH%E{N+jUd-r@iUaRRq8fHnRb zW*Ok81^@4a>i`P|a1sc3g<C!dSRn8{`=)*uC9G+*H3J6EooT9z@zAwsO5TM9U;BGT z0ds@IXX5-niHio_g}LF5AC6?jo^;3Ms6aG^H;i61tDph6tXkpBW&g5&*?$K6y>w*& zJr~HFosQk1L~x)gt1kn%e}(_r)&TZ^fL##a>sv7$2Us$I6D$Pua>aY5K)bQ&E-#x2 zVdwM1IQR$#e0P#2`V7zDM-?7T_LD6xaCc3f2sFKQT$BIzK7MawjP8_f5v7r4BLqxP zT3V2hZjjh0Nl6uu25ArklxCw-Kmln1>F#FRm-p-Q`|a_#|K0t(PF&~Q_qoqG*Vh_R z#&)I5*<55--xr@8+$o!8QVogdJz_9y-*~pj{A1hvROD*d0?DQI9;~hdSH@i7?-K<d zj?TQKgkU&|EC7#$9_<fS_3&ikW=sXy0;>>+FT`R98nDBe8}l;6=LO*N=8<0A9oHPG zD49f?Bm3zl`Li>A<4I1P9bVrdyqZsuTfL!@{8*d$3mx)}yMKQWZ#-4P^{yF(SUe>& zRR7=YQ?LS6^L=(?3+Vn+9O@#5Y{7}kf{5&ort}2?MCSSqh9?E|ERmux4U|CwAvs^9 z;SZmmiTqoesgn}<YE(wIjd3#|itwrDHwXVnNmM<mVsN+qThi)i)j9RqQ6@;`^7z$Y zGSRJ3U7LM^NJ=oVp9nWK$i*^q0CyvMJlQEadDj7uARGx*5#I31XnRRxrx%mqK%N7_ z2)l8DgGRf+DL11ug+TM94LN>zAyg0`A5fQ3*54s#(m(M-&B-)^QVci|m?{l8^Jkh` zQZyh(U&|pQJ6#o@xkeppqS_v2^xGeAsCkJgP8Uqv*cy5po{hB<L&-_MLXbaP3<)D7 z1wCpqXp}(dD?N2RyWjhK3y*t_(vb<CVaB9fP;u_Zfa`te;n`k66G<Ccw`$oDg?qP1 zE*?%2hc&rec8&lA4i~yV-IP>pO(ybnJbWlPKI$K<m2@qIlllv#VE+e{cv~Z`JE%t# zb$GaYns-&vrbdnYLnFM+K`gEWIj00pAH$q(y9NljN<d^DzQEiTdzz!mF|x(Q-N8Td zF5{}wf&{73#ngnBovJT+v=r6m#PFQa0mG~(jg@m<62(j(x+Y?@?Z7<tx5SkshQHs= zf*by)+M;TFW$XYzbC@fJBur$17blu&R%L>8Pgn(W=2Hkm3`<NzX%QF(Lf8HR6cP8r zDkmod)yNqVP}eus8AGOVDLXa$bG9!+z97x)%bV9G8dN?j2({(sL2hdyyoJwckJT2e zt#2f1eu_k1q1A<Z)Im$DUhEcnh(}fIsoIc0>zmC-GMEZ2B|9rzEyt{+5NOX9uswmu z2j@b*=ccNEsg#-{kXs<0>w>AHjH|<M%lnFVy8a+Fo<&VunKOwXvwQS5ejB-bpr)>U zo^NOI-@UR&aZ8*F2V^-?)Ru~)<*#Xr^#(ftv%(x8K8k=EFWphd$?rtAaJQ*L@vfDQ zU-kzl%0A1InC!)jcjx5xvQkB0&kE0iL=Tk*+^;H?%BH8h&;NgG<Pb*GFEJrTO~sks z{|?SmK7>3fTk#}YI30b_<{3#~3C$6d05CR$`zyv(sG<+|X1Ss;q&{1j(RH0#Hkw}b zG$0h2>l>3VAFfx<#ZvpcQmH>>{LjY*aC<KJiM|6ePL}pGssm7Q*tP`u_Cyqc<IUEq zEbdO!aTN52Rq2P3r(_FJh8P!q<7WS!?AZ^;VfRBii`xU_eI5Q7oXq^0q1KO7Wz)W} z9Q!}!Vz^3#e4GD8ma|L<R%D4Bp73OQ|D1Ep3Yez>IUB=7L^bzwV^LaNsP|+GuRyjR z?+sjFUSwH6Ih$irPHjm1rz&7iU*^;9y&tZ+p*r<{#%jNV)D_l{Kov@)5#NctIyZjq zV@^2P3*;$2Ccw&ZAp+VelX=6Pt!|2EG9`}wu1%d?2+zydiU*%W?sz^Tl;93IIDXmn zqChvl;Xi`YvcjkI-XKpdD5Y8VFw`QySvN;Uma{+aF#$><Q@7-la6?s)Eew1}56v*Z z1sNGLGxcW5#_jY~d|te+YZ+fre;NcYIqhK+7+tZs`=2n6Ch^Y*O(Ax-%IA$?!Ds@4 z^sg#d&oS}NTbMO2Wo2{)EsO}rxWn;;79z8QbG4-l%%16~>kW`Uf7AHVUzJ<*EfHui zOJ}UOfO%b^?bClg8nullk<9|liez9|nGnHhrcie6_b;FI05@HWe|Tt?8Ax93`Q5yC zF}p{$P^U?bt>|iH4fN-tv`E5cm*S#K`B8J3w`|X))XyrcUdd(d(>CDk{?A<darw(3 z?vjx8MGkq~rW2oWaFv*7NLC+{1Awpc5w3UYKv3zeU^L(2A$9EJ*Nv0alJ|@fH`xYe zDNA@~fFaNGU(UJ-ee?DjgsDb3gZ4}d9wZh!M6%-TB2JI8PbqN)c5`QwOABsm3VNLa zbsC=fq*4@u$$csWBdMk6&C1L9JRCz@F(AxNrwz*ENJ$z|90<KyIw#?z+h=w>*S1(Z zx=ZKkN}ls-Ee6a^6I@e6yqqh@{#Dke8%5x3o93%wu)wZjqF`=aBZtyCF~I8K@#gqd zM1}v&vt#7D2+Y(MSOk%FN2ZwRy9pp@bO}+2wGB?h&0b$CkV9DY0mb4U1RN9wHE;}_ ztC}3*hb&?G73>dHq8(0jXLm%(mqcyc{J+1I5w6q+#=C6+9+*1?S=h+I*#ZH{0s;1l zg0Oe18_=qy+mDh$Nl^wUn6w=6SiA{{OHMP+O#MY^L!}<Od&;v*nUTg4U3^)FE5);I z&a+e1?$9HevMyft?zAgYj9tkafSz?Y08w%>?GWYrpP@Fl41d}`3-eO}m4|{T34+)_ z+$absO1%?>+qg-jib67+o=BWrA^yMPF+I65D+hjeQH#C+9&SKktQIJjG9U=mA`OYT z+MpTA2gTh%EjwuKgIrrmf7(%DBu`XRwd{c7rRCPx;Jq-?)xxS@r^VGbDk;NBmmr0# z<<9OvN4`~d2}8E8*w@)l^$>?09J;{SCjjnfa2HG`H+;eQf``T9FHkKb@R=}39mHEO zSS|3y``tE04SfB^n01g`BN$IIL%4v1RktYRJ<ihqp+|3WsDJSg(F1r69$&{zZgNib zRj`FH&9nipHPf5>tfzfA?hFr|{p%nHbDn!%3BEbIYGgL*YQLvTGdgm*;PuMCI^=FQ z$g=a~G%2wzmCsCNJ1eqQne*BzCfL+Z?f#n>*lx7Yv%#$ssS2EnJMu@w4}9#%SRIK6 z(ya9V;FTg-8Cl7eohYH&B44jk#5Zc%(zdN~-debmwK$BzoeivZ;*{E7kAP(#qv}Yi zWKBZr!{1$=MOn!eOFQm`##mo{ftwyI*q-M+VVNi}<<y|lM&V_#9&_Flzvt5b1x5z^ zIiqXarfcf>_V)qS8+L?_JATZi_#PSX`zO&h(dx6knyvP$%$)yDQ;PgRv)fEaMX#-N zh0OF}l~N{Cc$(t$CCJ_+&-dV&tUlIReeN+Zqw~drC~aCcbc04!S^?VWBIRdeaF0%l z*vyJW@lc^sg;|_{SReZYnxfDC?waJei0!Md5-p^}wcjGxZ&RrdIM^>pgRokugz&pP zXBQ{q!3wiK&(8$-$%t?D=pk>c2>lrQ)P$WGWX^MSXo^uQL}q{>^kYz1tg%Xt_Zv4c z(MF3G{c(L+FDL}0QaVLHdeylZ6=A8{$i|a8+nR3nZ%PuY9UIqY2i7RAgq1^VI_Fw^ z-%7||*E9L9yx)uvw$tM2nPk6j`;dmeP@~;F$uFCwE$h|_dY5sE266^?rvLe=w{vVI z6?aHBq#Cdkdd;MV=h8lbzdH(8>iy|zUJi6?peBv;_$bImOo97__mh?UmjP4o@9ev` zaui3KNSj?04<J-6r&Mnndemt#1JKmd*Kud7Mz=gk<sB;_bbYD>AJCKatRoGCU$!@1 zJ0#a<btlceApR1=u*$dumtP2zw&-w2P&UavfSQwe9DQgxd&b%{kD1I@So;Z2zBu?3 zu+s3c;y(|5y5+L)`=MHCT_|`RLs5fv|DA@?3>s-lc;C;#&YQ^a%r0pyfQklMqa>hU zH%&9RcK>o#mHRHf={tg1#Uaprd!|ib{2k@nMC!hpqQ_AnlPOT%s`od_uw``%2N!dV z<Cp{UZE>#@f}$pvSXla4VS=Ko`AGzc|F9Y~AuShzwPm*+yzU%+rmKhjPkRto`+j6S ziFqa9&Ms-Kt<=w<Ur{c6(7pkavsexyPa6jPU6v_72)!*A;eP<AgRd*vVyntcrK*(V ztKd>WVr-5l@!f^Q6#mpXp+ih$7}z#Jg9O|=P$ue8FWCWMG>51ak9Me-1@gYKYY2s$ zl_*A}aSPOL96WV)8v|Y`Iw1adf-i6h_qFcE>nC=(<o};-qcxyz#GD=?H`G}uyC}nQ z*I^narhDgiGwZY4C<5BIToC$aL*gfow}Q{UnB@4^8R21<Y^(l<$I<6uyGF#dJdA%P zy0rL1$8ViFT7-sCuLi6J-@QyuTmra@XGt*UW2Xu9hz6fq>w^YYtW44*l~R<=Vo$ml zIR=89*0@kd`T|O^XHJ39Tayq_>HoL~nS5!$*&ie$FNk_8)Tj6?j2xZY#wcEpF3!k{ zqNnM%8mNR^oYlVaY`@l|TGI$F)Ih~V#N!0Y(v`>XPw>`h>w18p;G_ZQ0i@#(Wlk~W z5J+-9dCP$~>V7(b^YTVp-x0bP)!LOMhmqP>cK3RDOxP<fMBB!-%(;Gl6;jb>Bc{E3 zYjD)yzn~)j2QGOQ6H`|Q?mdJb+62j1(|1rN?z0|v2ayOa5M9E4B|j$^_dvTl+uxs~ z;7(2+=`07~UK6HXm>t874qpWgN${$^$zt<!EA>ev+)-?Y-aARd3(~o=(zrA;j98k3 zn%hOgx`@k06#iy4EhjWET+QsZacGt`0-a6LP=$EUuYQNLzhqXWto~zWWcp=x4^)NF zTMr;^9+3X6YK<1%dMpa<*Z7qC<>$h(hmZ%x*%C$AVSzGMA~xg|wn-plsdEh+0b3)~ z5Wf_PE$jllet=^prZ(B)LJ}v|kd4bx%MJ9jo(l=gi<Wsk1YaGIpJj?>MbY&HKZvwi z_w#^WED4q`no6nvd|@2)Na83?V3ZrB0gqecExw#*(ZeHyA5)BPR{)=nx+-JAy(upP zzEdeStoq0VlRmQsiIL>w$XkP5!r0$#zi!fKq?W#57g4`3qeXONf$%yI@~{goDjaPg zN`XSH1c)#t?7f5VY#sXyc5Y@fyyvEF2GKNQE}+Uo1ej>5W|x|B<=3=q|4{Y|PlEZ; zmnvrd>uBOp8_axuG==y5&(+YD*c8iuFtO0IopL}0SD$#m=$}E7JVChXggN)@E9xNM z6aAQ><+pmjmm}iAP0kRQ*rrskEE5XJaoVeC#pJ$ySSt`B3x4TRR8PnVRyho~XWBZ( z=VHMjdY1zJKEEr*GhQKff8d$+_1CtGyr^W1@~_gOo?x=L2`<A!`wuVuzO6aYVIqQ( z@XJl?YXT*g@gY8y$Q@Z!eb|){Dgaq|rb8k)5-2Ux2Ceg;4Y-|Uh6&ui>1npD&`L3% zMU#1<Lj)eqkJyDFOg(Q)LLHxik4zGJ$()E@BKA8T&!Fh-Bro_UcLvC+EXpXLy9PDm zZ6j%`P-`d^1xouDSv4X|kA%&+7l3@N|9$ku;PHGH*H*J&DE#HLib=5i?{#<8rW z_g`KDt3;F6zm%NM1%7907AC!ORwp4SUnixp&<CRWM78TWb|k6fiiPZuaob-zVAn&I zFf!aeZ`Cbj{vGK35M0lAE@}k9K+vCh*${qwvXO0}aZeU~j|i+zzF77E|BC3>0EtB? z-CMKC55m7OwI_9BH++LYKV1WwM}&`u-!~OWapWEu;bZWy|D3<_GQHlqg<|zxQ#^Qm z#Cq7Om#N;~SaTup%UB_K8`$Vbk=cCvC}E{*y9#oiGpj5WPhgq8Ips>Z?}u`R8Yj#1 z!iS)`Tjfo1J&<Y=(HAXD)Sln=6$X&rHFTP_Y_010<kO+tg_)eILXPBwVZMW$j2zdr zM5SS#g~I%6vN^`TyyW&We(q=}_At^99pvj{i<HOlG?;<?WU~~)y;4BDX8@v-dZgEz zK*o!y=irS)9m{$WZmyw-EkSY*ogH`P&viu@WWt5gUn4NrB4Lyfs2X67KXO7DEQ{lF z0kt6=<y~j@%g(d7n%=FoQNEH+GFSIk<eG*29M*r&{c4RHewQY7%3N-&vMu$w$vv## z@|hS`1WCb=vEedEtbKx(#^EoK-+b=N#?d{ex^H@~ES=UBN`ZL?jj;HcB&G6C)PymT z<a$H!Skqu8)#kcg?*dn?kf})1sfHW#{z7rDBk&7YlhP(+fLN3Xjv~`!d9p_r>q6=N zH&r+5I+mb5y;^f$>3Gje_ULMpCHcEJi1j)?5aS3*{I~?xt?Cdy_2Y=7#n>DE^I{dv z(e37rs;<kE#ibE^qB?}lB=j6g6yzq4e0H@S@?d(>Lm=_)*zJz=U6#wbrDF_UtT{jo zzm|3|d~SPfzI$Hx?<hC&N)uZ3yX|QTI4)Qj=TTT+CI7v%jrjSsqv^g8(iP6f+FQT7 zCNey0bgg<W)L8|Kz!d*tqna^B0~CaI(w5V<|7^`IQL|uCxj3-eQPWc5$X*QD!3)sx zgSNpZX5ids#2p5+2-B=gaJPcUD|1{DtGK}v>7npnw=iuMqP|Lw8vG#=2j+KBAp<O| zRYR0l)tu|Kcl3VMBv*p?V@j?RN3g;V1!n2MnunrGHBKvv)d?zUs*ftUa1VOR;HBL~ z;52A!9IYh@Yt!2W1tw+*xbK45Z83(LZvXeCIcw>~@Rx;_VJne#Hezhx6{?!Zw3C3Z z5_wGrj2~+|ceQ)Jza+|~!wjE@9H#i66*3+OWRa%-Cm<hoe8mT<L%m|NJ17xK9e^4i zkUrPY$&Dd}dGec+70tA=f};&T`Z&<mRu1tul~XmfW3{3zS*nbc?{lsiXu-(tdZ~CQ z7&VM2F$ZV1)4g<E=15nz2s~{dD<Xc|Y;N>Q&-|$dqg@W!;=J}<S{nL03<Yya<MK;P zK5-l*PorbZ*&FYujmQM{Gv{c<G2>&A-FUJkMi=f)Iz&c~Ad1_EVE1*aL4Yc*WDpFw zdH~eIj68e6i2DS|$?a<zvjcJ18Jj?-@en2GBtf&eD?<9+1g?ahdb>C~-~_*xZxN<= zTbss%d=f@8fIZdv`PbG(wDV^+LKRxmB$(YSuF717KjwlC4fjE=4h-I<!~jnHX()?A z6fCsOqJb=-mymJI%>fXhs~myfx(`00kXc3;nvQR?PS&%$qa46)D!9;t2eK?Lq=^FA zgud9ArBG5wZKe^I5;tGGy^{An{^VMs$_mZzZH?KcHwvu-k6se;?(YR%l3ss(pa&c| zrZ&<NDAfq$O7CiP``!ZkTHF-U2gWL!<*6|#G}l`9*;o5sOW<w<gE#8NTk39}64N0r z(zS7}Jx~%OrrvII$b5JV7pFB(?`~NYiv~rEXmOI;fV^@7D4#)LUn=^I4Coz0>W&+0 zESS^$!8OGzKke2@QpYVN(Jm;omMrpbQB?*NglNM(gHUC;ite@udN?pGHY}w#B|>VM z`qNWhJ9G1PS+O-5wp(zt_?<(G={M>iRx(1Z_=?J^2L0~mn#j}$Ar#?!_h+~)1u@Ct ztgU>*W_;4Z$W7h9$6zw!jctLFCpYH@;pDV9GV0f|ii1a!#~{8ja+A%)2SFz%9NLPs zXjQ5uiTGq=+c38eDGm1jCTs+cR%qI&Kqc+Qbh4J1wGT?PAD`9oD}C?Jx3i=qu-Z6h zWxG}0L)lfPIC~8RV@o4$95vLo%e!3qo^?#l=N=){r(;c?6G2zI+wL^b1XTB@fjXrT zc_Ur~#QF9xul52+ii1Gj|Dnv&kc7|WF84$3-VY`c2hp{&H*^o<?)9``4m|U>Kq{`G zhY`Pav^L^5sI|U{KmC|T+Wn=fCdZK{P9>uBE)7t6rY1Vk>b`Y8d_wNe+O0_7D#}Fm zHQ>b9z4|-&HrwulU357><NRjjIf^U~k~H|URe*KM5VuCMyp!}@WH?+d^T~sGH`<Gu z2*DK_enZf)mVp*1y$<s+XW96ijq{L~fS)ViW<T^1Ip;F(wP23xhpX2Nw6Vf${jFC& zc^v`oudFT+#u*_atxpWNRE1@Tsw}zD)F}atKc*yXt=PN*=<fEoh`j6Dn>KeL`=8!I zp-H$df;|;bz?2z#6=^Cw@i1sHDz;I|<k_M~QP`r7o_INnT<W5ifVg{q49f2x17bsS z-8r#bI}Xs?|9#WoNGoU>JzWRpflh;5QtPZF+LUm^&lml^R$DDcD&2$}5(^U4MuTd| zU22!enjUV%+8ooM-(iNKd6kH-Y5Dv7uSs+LJhW15ECz2ftG3-`>&!#bS6I<lxgddS zfw$VKz>VuIj5NDyfhI_D2Ce82w)f4TWQ#(*WXD6ou91)7`ezsRGMpqifn#bmL8eM7 zCzoovA{=jN*SlX!XaWp&?zW7sF9t6wTt($&Gf014BA9z%{2@;Xp2C7l&obzO%o(Z9 zHAh|`K(SG;8A*gje_HibfD>=NhAQ5)w0zefHw<F`VHM^n{jkQ3j0tY)etnIi%N$1k z;6mLTO!iyFfbo+n8q)&6`49ew#%&ZupfYY*zB32-y+rV9Nk^se*9n;35EfY{APeoD zo3ni1%0Wh{N+VO4$R*OIrQSf`aJ?**qn3^G%Y5JA{wP-Qd{Jsa4I{ob)5>wqPz@su ztt|jYRs_CPO!&+cXC=W0!(8OZy&kGV!ks^Cch>#>2lf525tcV`J5+CVFg)roE^gjE z=gog|%jx=2`~v+5v|JK#8x^K4f1c-;JqzSzDe;b@9uaNngfOuSVaXmm5S7%q7QN)b zX|_}8fI5q^Em#uzHAWu>ZZq4X+Sdpj%&BEKA)6tyJMYN0_pMMX)Kv1Sd{bP$@$ofd zcaUPK1fM9^TUj=(Ei{7p`DpLPP<$y756;wm@2SshVQvzS53s3?Zk&gsH~%Ly3!tx^ zAM4(%3z+^OIKK@Yp=f8%<8USa%mf2e_E{&jSA+A;WJRyPn1UW?yZRmpFFct^dY;^9 z87O3;1+-9pF~VygCPR2~4#HT@LQOthDxU)j-`(n#*up78<N46K0El#g)0)I0_MP}( zx7;|V`E3KKx|eM|zzYTT@B1MNU(Z5CacGU1c=NBcD!+F+uc&UK^1j+6#dvQyJp$%m z#Q3fHMv?c^Z_VEyB<jh-R2PKGOlEBkHJ~hC)Ez2)znS1lNZw3Qd5^p^o<>s3>55{p z>m`DMV7t!WH!#O3zJGsnM%lT2Ud~)xVGKdnZsoi87xy?Rl7TNl_sWbaX&ch5M`c~g z^)yqh_f1Q2Qq<ahs2cd3=Z<Lrx#f8*|Gw5zWwao1+%|D6ScZv=knP1T&ywR2KUM!U zr2K<51}2F5(I0uJ41;=A#}E<?3$Y+GzkUU<5~DRu8m@>slJL2rJ<I0yt&r`u)c_sH z@v(*yb23IBygZIZ1-2Um8wB(Aa&%4AJ<Jig*jG#u{0lY=!`_X!C(-@z7Oms^Ud3;E zdn!z!EP8)BQ?zTn!dn-bKY=b<Z@Y=Kz7uB`c9RbZ3bsSONp^tvF#Yd@S>(nk(_c*? zkq%ZuyKA8qp2!mfl<F01JQPq_1O#8sg*OP=KUT8wfBp1&`Jz?fP49&Pf3RT0ye4W8 zRH+Yc)o&R7I;}$9M{t>(Ghc6i(m?p+uzlYG^$`Gv3oV!2Q)GM6Xi1usTK}F>%RTmy zhiEL@osx8AKv|f6@MHK~uz!7N0gaJdg8hQ?Ns%(u>uWGeLBm*ne*oM6MW^U{QEb8= z3&5JBX%c50^KrLP>Ve)KPlKw}Rt&KMIoE9@(6w{jop^QGSrd?N)Uym|M@!M`3yKEm zwkN_-oCeixPtcp{5+*2n^F+jC=9{MQlPL$_8!3(E3Xes+NHe9P_OE-slm<Uw1Ygqv zO@vUqLIu%I-&}JR0tY|dU%zvKZB*6md>57ir-)Mco%ZOJ7;bsJk*!yt{BVI00n}Ay zu}I+7x?}NgbZHn^DPcoDbb-fZ+oY|RRA#Pe30{t^h2Z3-keFw(_73@#KWNEBX^BuI z_2Y73tz3)ByN;0`{|Q%Y6{4g$i#(Ls56GIV8YiIR*5oQl3}+Sr(XT;M6iO?@0!_D1 zKY}@DM{+s%9%;@jm9JT#(sI_^4kv_y1%aeJfHtv@Qb1*fZuv^=#Z8;gF|R`9s%1WG zib69q{89>T@g_YsTWOAK)O%J^jfM2tMRr}RvURoe>iV0lCu$T;uldMZUJl;m**932 z@qd!rc8bA7^q$x*!PY$WLO*`5=+j2`I3tVzdT6^5MsA(Rx}BKicKZ0^sZ8C<mxO^6 z!`p_#*ifN<9wq8|k{{j)xj=PkyvPvV<0&y>+uqcUofvq`ds{RnSxC1&=z1K}(4rd0 z<D2#JGfTbs9nH?kcSJN3BKDwyU`FU;o)<0pxsHnA{f|1^dRe{U>U3@pWtSNZ`W4iO zriX#o8<ZsDP|%w=gl57|FHz#nL)SwV%pqxI&kr)k{~=(;C&!|fieyuFs=&9`_^Q3X z$=LagNh9IcBUQE&j9Q2@uy8Nu(aL>`WND+c*_N_r*2}@<!ztt|@@)ce4Te2>wuGox zNriq)H}hIlNyG4Z+72Wss+lW1;M3dS#n0eJzz2G3KyV<8W0vT5>r};>N2hg#!E;^? zlAI=Pm%jTmsAQy6<zQqHao+F9a42sc%BsVwD+D9OCi!61r~e&Q0Vt@uXceB;Y3<4| znRmvZ_-AsT$l4&joSy3K|7oF!1v%%#5*~jW^%>C5FgeZJ)%#RL)-mwV%zQnh7C}uJ zK@&-xaNwB1M}9FtAKZL$j~<~QGe1YfsrI$7G=<V_dwzwmFf<pU&rp1ot*mJI$o847 zDyoQogi)BABZH!$(vLo}eQ8}f<rVZ1-%J2~CS>opN>km|&^#ylt#80nPw$zQaPpIH zuoJx!f$Xp-n`_%%Cp_Z=LTyDUJOxEU!7#&=uiuL{bwyWA)WX&4O6((*K}a`cYwQ71 z3W=8p>NY><>lvoiwJ%{c0F;~lB)}Y(cjscX*N9Y@mW5XoO@oudyNIicgT9`-tMl=@ z1(xiRs0}l4Yp#6d=B#N{24nsBe!jrK-<LI6eRz-gisJR##_{nyX01A`o1eo`{<i)$ z;1j$~LLk*C3)XGy4H+-5)M*s+w%>qhSZCEVE7$e=Qy`+l?~#TIs282AnoV^jiavhH zq-u9Rqh5mUa~V%S$@h2Q`W05n5={<`fIwaGsGETgRzebjLJF_0=L4>0wBWe&r9C)) z{{-)Mjm90$oy{ZhquCv)XgqvUKKrjs^zBGnW<jEXPYDNH0u-8w?Od)1Vj5U0%0+2y zJ+!N>nl1`LbYXSAmGmJRyYXCMdMWMUpWx}a-LoRwiFl)>M_y-x_!H8FE)jHAb~YY& zeG&tyFjq#Erko5L0L(m<2F4!^T>R&ayM;Eh{RLJMt$16MId!TOq(73h6K3)kec&EZ zw24^^DgoMQ8G%PBoOcRs^N9jhl7{yc6%l2icl|wld6_&Jh=ox)QTCQ6iZ)M))3F<g z%ao-4T#SQWHlBT=Iqp~$mFkBwZaZr~s66uDSy|q6?bh~U(bgZhHGF~Y?<uJzTi5Gv zu+hiTZ@5x;SfB~`frh8YWp<b2!eYV*Di2W#<n1?VAFY3s&DS@`LtM1H<7;s<q^0K@ zm&^DgJQC}#5Eyv6!oD`o?utz|V=j|?wyR2y5j;yx?Y11aJ)>Za;LiNCv(CQ4kJck3 z@OI^Td!J;drgklXXJ4N0n`FX63!k?ZZ);6^7r9*}>K>iuM0S1Ca^Rw3Uyo(Y0TCJm z5O)1aP>YH_>FaGmelfCn-?L!m3cw6>#U$Ifl8iBzaz85{1)hf;Y0L16h|5hEGN*;! z3_7?K{!|wIR!*ih6c$9W6S3bW$WUnCRde`e4D;dSIFgoDAbfOW4@@X4Cccsy)oKp@ zz&Z84YnW-s-BBOy#(N1fGjlS-tG)5VI%{Q3hzDI>xDMm5E-9C<@zR?}H{9#CivzAd za6Sp%W;IaQc%P6#NbuH<<;hq5UC!z(Hs)AL8w0%#L%62S#KofW>Le^gxLCBN=^2aC zAN@P1>TPZUVZxW@p5sL9>n$8Xp9%YQNk4!_WXa^uJF8wPWDI>8Z)sywVVXiYVXD)v z3aFE)<EZ<o$G|u1azTl{Fw}g+&vT0)S+Q+__ldEh%7q0%GOCM-(*G1a3?BXU=_DIr zzT?vp&c?Wm49s9X<;6+IF2xP{da+oE9#kEkv(CgvGMCcS2l&HH@NWJ`u}gI&c(ukU zpSR8atvx4&CyZ1XOes9wCvboJf3UBVC)dLjc%3U8a{m%7wFj|9?@)XT8mrA9>(0B5 zxPuB3MGy4mr0vTH6EbBytB76j{W3niiE59SwjHgkFOCWRNi<afwtGz8f%#n1wtY%o z+FM{|fvR1k&9}Y9G#R?|i}EzIn6gTfii&BE(2|5n2=S_oEqIQP=ZSYjT0dQ+^uJ)^ zb};L(Jwf%CH`IM5zRWv5@e5?ZpJ5gOZ^hM}?mctL&;8_~+#h#&(1I#58}0RN5B;Vr zbM4|@C2p_aMIb!1y705i#pOpCe!abRIKiXE5_^$S&e0jEjLrKNJQJ#b_t?UZwc<Hi zag63*riHd~4hZuHLpO5Hp2(E(OwMl;2DhW`ki8Wpx1!5|)VnG$PafXk={$l!sVcNg zli=mz9^3mbHX};9b7^b@B28beaR%M8EqkFt{yoJ#OTp2fm?nI^>m>$72)#u}bX3Dh zwEVm1y|lWspq(%{QnfISSD)}_1E*Jm;AimZ>-#KstNKQhPc%Az?W9Yt(>k&rAZUUb zA4<hXd<H(GA!<l{14_!7LKz=deEWQ6_aeY`xhFLV2$Bv6J~F7&m>EObPxl-tIy2Qd zV>t^_f@aUJ@t%aKgGzFPNAOCV*4`C9cnkjLdT%3zel6g%@ML%%ukhMS?b9weD^lt) zWT1)Yf#(MnNoRvK--m5%_Kzt@S18*xv<rz%rw(%o2A`IwK$yV+p1(lMXfkbu7K9_& z*AH4tuQ&Q^5&}N~z!f>iL-0q!D&_CTd7f^vOt}di2-eX(7{zKtczzLE>=hToDxd#j zn0oI)P72vKOV!+0Vn(G!Tr+9yOTPU*w}WM0aOGKP_bYs9$okHy<1}@cR4alI*CH)E zssJ<L>sTY9qag(vZqmm=RD`Ndi;E_+@i^tau)5*<#rV!`%HEcluR(u(VwwJoBmHf3 zO?D{`uZPR<qc;}3aA_WcgMPhVf!{djJNRaH<!l1~7O=OzCWP9A^AOTJIs5ZHXurMr z7TJO*7p10XblH_9$;$L_Epi3~mi(8Fp}YktEsHn(h*QQ^ZQ1Fz-Z@~F>l|hmZ49e< zx6Q7N&@>g}mU0l#)L5q5OQLL5@RT$6GziTVYFK!0xERctXY(jp%%fk)^dGt8W-to> zUP+abu=5TgC66ofL$sFMu-Z)ppAK4urn%GWCD~$#$ha9z>_T~z&%dro#jW5&!t~DD zr{*oz<z)2%h|c*(DO^Gjd_W|5QWMX|i@7(9|8O!|u7?L9@f;dh8h{q<u&#d-(^g^Z zl$+9*mpT{z`@#!y4Vm<}qw-Vaj(P=iSiM&rWJa5)fNxXhTmxLBDDC^+Q8_>m`}s6; zj&$jtTjHFZ@kAt?veJXArQa8Q^ZwpT4M*YckIwK0QXOlJ#SK76+&_UnGdc?US=w}? z7P_`8GM5GVNc#BvV<p*L<QP~MpHo#%>C|;j1<PL%vb#RME4<qtFW3_meeFU$CBu;l zR@W?k0+MEQX2X4C(P|9Kf5%QL8c?dy!1HO(!?Lu%W@JmSXTr{?E@%$`omSSBA-QJ~ zLNM#s9GOz}bF0^(<sAM6mz3vMc&1HTs0CH^&mcStSf&_G+8IGBjHa;gAAJ?_WM!8R z-sM!(2-`P;*o}((j(RI(E>AH~M|*}v9ZuB%GMuRH+C%cioIu~^5%AB8oUke-1mV{S z?KbW6{*xSky1(`2(>Gg>NcfL9BhN~<x>}W=!G<-4{{CZZx@7K%ZR3o5Js>OeGc+Jv z7&gJ9MJ4isX`ow;`Z&Fzascb9Fq|&&{X6?zh3F!ebM{pP$ni_TwU2`OQ;-RF+IDyh z0Y|1`DbUzdZ~7g_qtvD)@zX%=Rr;kw^g~W~D_+)l=$47I4=$Yy@~0H<ipIz1<I3WI zpr)D4Gc+G6>Y9~^C8&s1$(M28Hu~EH#43qvh6DHs1uRFL4n#Lq+PrCrDg-TjOqtY_ zXSAZvrBy-Jt}>75F@MXHSmb8u{%X+n5)g<YO395+tV*(3!!Qh09c`HEHAsh{4|1Qe z*&PiF21-5)OqPR|=>v##Z}r|%j^r$qsI^|UAYoldZA;v5c|@_}F0&k8DtI%^-6QYA zlY9!&<lbcG&4Li$>w)@t5zQQ}6chAb<zs5D05Lk8Z#!;5a%&tJT7MfMSy!Ab;|@Le z@u}^?<r9!~^UDlG&+K0_#?ToS;UG7ox!+y-pgjrpPrT81MbP$^)}M!8B7I5eF$tWi zG<WxSQ_;#x0oKA7IuIGd9j4p<<se?wKihkig(gGsHk-sb@tRK@o&gj7?UvIwuX{b4 zdHgzjo$=qb=;kWhsB793$06G4;%qj%o4tu!cTnC|d+JpmG-K&886nU9McNv)1aS5! z-#U`Y5ZC9Yt@|=v!L?^<%h~t?_<pWTl~5Y$*uX_&a|fm4pUj&iyGK;`dzGP{^)vDT zZR`M%+yA2EJlE(P+kwQrQjU$g8>?O3C!^9x(xtnU!+5X7z@j~4=Az*`MZAsyoTIAw zKfKYGNJrv@|Io5dWRq~txkD3lma;%U_~oDclVcLPcGcW_`A1{q>9JkFPhks@^CPSI zsk{HOe|`d<Z6&p4f&cLxsV;?H_Pvtgd>6ql`Po`}<1rCTWRDa)*WpD(Wpzzu2FTDr zi0?J<usT2a`EO10u;6i=%I&dx-zVp&>sdueZ1<P?dBO+oiZx~}n3#l5`>6|7P}aFf z3z~9o^~B3DTSJ=k0A-vd=qE>5>cuTH5$S1#x9w5Zsu2W43x?;9p(DN>*td*hA__+W z1b?^hmvBfbzdT$zLOLAU{wq>HEt~ta#&LY26|)7O-NP>x<IRZ{?xXQ+I|>cv*p;Q^ zGo|ZioMe_v3|Z<%TcLd=--x*!b9pV~A4;?SW@HR{_FDTii+}+^><@K~=E+G48!Ny0 z_9UShp0NU#B3UP<*B$cC;R^QlNX#+iE933e1Agwk*xwTChqdW+x*e&y_bX&j5+*)u z)En(vUVjTs2!3nX?S{>hZ7yy77$cm_AGz^i9NrURE4%yLJva40XBAna`2br2=--si zVp&Nnt{>N3t4<m*pLObH_*%VGyyP*4-i<e@BCIlDSiJ}DNV*j8*6Mtg&~$Ih$Fu!S z7{&LcBXkq>oV@%;uw8HC?CG`Eml^w^t`m;^GyL<h`LCdBzp{D!H?YiC_|)rEN<F~p zWy7RSNt9lI_lY}Ijz*H;MHLI;>^clG{d#wT-THZ3|CSuMw<@gqEnbffLw033Ub)Pw zeA&T65c$buXzxx)xFR6Y>P`D-bdFK91^D$-n_liL?ai(U3m0Kh`?XJLiV<`5a&J|P zb)9YW56Ogtxxl37II1aDXKf9~PxgBsX2<3e_htpzY`N(WI2Znzmd~BK9AT<?kz!xw zLYybeP)o*gp3lS4ixRE{XQuja6S~ugz?PXR91|)+%Q^rT|Kwt}IHha;TyGdI6MLzg zg~yv<zh5Kgpv~iZNDTmX0%N=DMOAf>BZV5ZJtX=(+on0!eoaO2nmW#*d7&t3U|&t| zBK4|3dbI4Q6;;*ZO>Ut$o&AG9b#*1t9#k^Y(|dP$_*uwpGjWA0&O^V1wE3X$?0b-{ z;1?D*V>?g$78$V>N^gTAK9YT7zg2C{+P|I)j--yIjA)p{a&oWxRaJjZSX_KKS{kZF zbtmN{`4K&Jf|v5-YjTTgoli=i2u|MW++oZQ->gOD%WIfge-cFX(Pi&^nh?P^UUVk! zY}!h?tMM3H$Nqv2w;XGsV7SisZ+#cpRdr=})D2DN;pU7y*f95y_$$GM)}TgvRcM0p z_Hrb4tncrkKKrfL3G4KTWwpj=Lwtml!D?1>+tlGH$J?Lv6c*q3XZh@I2#Di!`)i0h zXYiRi^9-%xNYNA}nHv5Nlo9!t(7e{ISDkxR*gomcxEl{N;XoBi<>Al!xPyzMTnc%E zw0C`Za3LlaJXs;jEuza4djG19r!5s13Z$7h?Ma~|vZ9(^pM>V8*aFRSr8wX0gYQe~ zYe@LbW@peZF=-jR;bb6ov||fCa^s#QcrSnYRHvKR3f(VTQpQ4{+PGIxSJ;dMmS;Wk zy3dGtUp_sYAjy^VRfH>rnD;mGNz-+dR!)fc$qOb+N~<?SoG&cPiM|-yvP2rI9Lucq zqu53Cdj>Ks7<DP>T?Yxi{ay>gh|@l@RM3US>LEem64V(4EG>e!E$hAo9|-<i0R}Hy z!;H-$Pb}G?^-o*bKc6J$|GX^wC4^GlkDod}3S%ZU-`{XkZN~^6^t~s4Qck~1Kr@_f ziB&g7&j=|&n-!*<bdjBe(gF1%L31wGUIL)~jUQ_qWtALcn=V?j|4W-5V4LRlszmn# zX6Er6T^Gl$0^rGsc~`rtJg}zFZkO0<P~42Q{^axY@G(B|=hp``2>&m=sda7wG8$|K z5=U~V2TBBwCqRYo!Rs2Vxskp(EabboaI(Akwt>uhUI@6qcO8LNyt=$R(+Hm!O&?C> zH_HnSY2VsDn|~71{@GFl+dM+3gM&1f6V@8>N9%SlI5lN%7<r&CiOk{_8hv`?;=r8= zPH<sWiBi3dQvIlvnDm&~RU0o^I{&w<rgPl;q{orA&8FSm6F1I=^}KcdOwt>B6#u|i z8y@@|+m}pne1(tPgv&n0`3TGX__>Fs!}!GRv9$(FL=(4e#a&(2JZgS6Y#by&CI3cK zEZ1RE8e>72%E=$dYB5JaFB+KTcv^9jybvwuva`Y$pA1ZK#5xxPaw4yX<v2<5thhst z7Jjyz@dvo7i(31!BT^c~pkpf_<6D|GPP*NpQpTWVUq|Up9nhAH?j6*xVgKdl8~VJ= zP}bRrJ-QQ0I)qpK>)JnnxAtxplD_vxwHn$v@UX_9o9Lrun5)POcmFG&*|`=jET!jx zjT`oUd?yf!chZN81%#j!`hXHzrkrrhURI*{)wR|8P1w?<lQlNt5{tZh!jUb2ze$!j zZfy5VTPO!m$A&gxDoO$VkHR{wx?4GhIiq@9AKrJ3?Q5Q}2%%h?Z@uC+%#xeTqCMTK z1XLKmsZyFyXquHn?;>uz%u})xO`YQdS;nLi{V*bCyB2k3cw!Uw_97+Bh<mCFRRVnp z6(vyWrk5nKD3CMki&OdpPTl_cXM@RZOG{#_hg(z|P@R4=(Q@X{LA>^&+Is+j>e!F` zV}c_q0nD}mU+=4Ewnjwm0Bk(FQ&>8rnc8i-8mAj}I81Y~cu_~_>-SO^d^CGGf4K%9 z;Fy5F-xAP0g53apa<UgE#X}nO>mTZA5dJw(aMj{6_J*XsGW$uN4R-4`ip(8Vrs$7S zkWdEQFYbnrz3<t`iQfUCk7#Gu%M%7p#aIcY8)<v#C{_7~R6HeG>Q9s~?)9`8D(AJR zFy>c75D&f7HdfaZ(GCCkt&&23!<OQfmkS-(<X)9V0(s=m{UA<y*MeC3uLdeP4WFOL zpOHjt^I=T<Bc6VhS0V++5(!U^Y29innOBcpHO(eFt?ug7CF9qKwVuXie^E<LEdo>k zh=4(Zm74FyS%xXxZLDVRB5K!}ZMGEWJa2`%Ubxoj9#8}Y1TAqaB8KqJx^UGzoYvwM z{?``rU@Hroi9J+VB;Tq7*CSgY1frWRN|tXPf7X8vU=Jgs&aCcF{5X6w<7df{;ugz` zlEppr)+1576!L+IErkyK)CB^|pWZzj2tGELOTFhE>x2<ErkEtKMAcA7L=u#W(a&1T zTA<e{8Eg9DLH9_0mGB9AhX_Fj!0ycYPO<tR$6a1=#-)yG&VKM1<@9B8>-`g2d*b!v z42C?1?{~g{@3!&x1s9IkG5(~H|L50v7fO}Uj9p{tQ1sWv>NxY$Ie!&K<8fD8Geym9 z-Y08*gS72cZ5L~z{pZZVYltB<Bww!gRRGOAKHdnuq=gK^F3Fp;`1Yf|Z`pRnXI*^< zivJg7{u!Z_ErP+CabgUo%M0#xloT14O-+lIypu0x^S93{K69|2U83q5(z{6(G=bXh zG`p2RKlc~@Q$Mmq9+r2E_nk4@T9@-Q|6pEE23yG%4ig{#vv6M8Y5XD+-4g(%N)fhC zm1ie#o7Sc14FNj1KP)~OG62XO=@4=mHhRCsp}e-QrqB3$*Pnqp*l(kPw@N|P+Acv@ zYRdf@XFa%>T*zee(f+j+kmAa@hOEo^Pko77E_db&gS0>_`<SJ#xP_&gG^q*!S{{9q z8`Nl9C|&1dVfBvGW{A22w_L*YX1a(-%qY6)yR0fG?1^(1IPvmQ3gvACtw##VWaEN! zSBr|woFR~Qjlzcs+xwCtq@idKmc@jl@MaLi`gklo!m^fd#7tYWxiBuA#6qHtl$-8E z<CCEQ8o`O-PY4F1q;wIV!)@}B9?~QTR#n2fNeEQ*xfFuqrW}qF{(F5fmlrgs5Bk>; zNj?mJ*Jd0zb`)P%jO)3^;**<e?41h8I$S#vpKsAQ4&9B;<|KH*C3we4?8CoyQ`7PH zG38%W*8geCj68^0waq{~9G*`O91JZb*GWA8B`<IkgqE=4F`3<)KrdsZ(>$?gI27P4 z%|Dxub)EKLgpEWSJLw-1bYOOL5Sjz4B9Wf(kSkFjS5<{!pXR0Tg1#uMss#umDv4vr z=^g`(Sz1^rqgA}Q`?-~UxIl#=OWwtf9gKQwbD;bhloZe3?l6~SfQGz@?<v<)P5MFn zKnj%*GEbkhFX=4hAJc_Oy?*zckgT|wK<nctEX5iJ3NVx!LTQD9vq=gP^L13Y?rh?R zQ_^(X743UA&euPV_bMy34$v!mTFvKJJS`fJ9Jq?1ZAm|i2SkIA)l#7h*m7`Tf)qQ0 zyF<L_86_2+G*!H)y4qK@2Rp&$WQgjQ^pn)Lc(k5{4zMA{UZKsx=QU!rk84jEuREsv zTj?Xm4ITyGC8P*ca*wi?OZlB?`j&%Oze&)0wO3m^_Ic8)jo3kb>IS+(inPCm(Ig3m zIh2X&piT9d7^zSA*QeGW)Yt%Y=hD-*cHOs+6=2mKurQ7Msu&0p>!kPG3bt=KpTYu% z(^a2VGGd^KNfE+d<%gi*oBL;4du4lCCh8RpYJ2f-FR(E;SJ|V#@J+ijJU&0<fAFrb zqw@C8wLE}e_<$SXF=|PEAM#M=ZmK}q#iXkZ@bb$J@KTtgvi%=r?FuV`4aZj|T>{bk z@gkR+Q*vGJA3Y}>cjAayS^(j~9R}ZES+><X?Fx4oD+!|3>IK{FAkh{AZ``So&tF|~ z){<!oxdSB+)UxG8X`YrqQjH@#Gn>(WG{gy5x10EqEGzY6f7`|R`~!L`3LK0w5RGFX zwX0Yh|5NSjS(wkjiu-(WF(y>mY|X;YLoU67QD9g@dVt57g)4YaeI6a)btscg7V*<@ zylaK}9{BlZMUx?)@`l!i?57j9kqTe47EzD-=Ci9_{+)#nZ8IhP!lwC9Lt>B94N@YC zg&76%r`{GdZ8%Ccn$AA2SU=P9S<5dInz_5@Ha<7$esng>@m;R9r7kXg>(|b~^Iuo~ z%}PqfaFd$d<;}NP&y)Lje9Uh=zG0>lrz1(*WGvPD7w!e<iJY&i>MnKdq;EZ}c{BfH z)kRpiB4rSycEk2d6E6J89VrpXTNe&{<5yPe6X5%i)a0e+K}x1RHNP9PvymmWalCxN zV*pv8ipc9Z;i9$zpcqWDqD_VFvCR%TUOeLnXx&^mqExy#*vfA8`;+FXrm6E(AHqWT zj;AGQh<<d=Tcc+~81=1KXOh=^<FUT^Z?A@7AHZvi{tZoHtsJV&&|wpJhaEK<c?^ST zPe9i8w0OtNB%$xhttB;hgQD+0z>wHaTT>8m(Obb1dbk&Ri*Kcltn_Uq(_S??6Xu03 z(e9Bn<|<F_YnE=~za5X4+dFu>OGeI{lb)Ktzb31*S^u0l`~C0FFoyQt4^XdlDE8a& zu8o}I)`6bs@fq5mZ!J;b?;ptVW@p%^L)EF~kUtP?NxX~gUZs>t;2-?9{L8V>ym@%7 zoBv6xou>`*=QcKe2w(mNRa&F5MP{1GV$8p%rT)F%<*$aPhspQ!H<e2szY1F6)lDt- zj#sVpe&6!vuV~Tfo4Ip%HPAkJ!EL$mA<5QM2kB%T|F99zv4N7JVjZ$<Z?lnRzj4>M z%TdGtzg_dYbYePFQ9#iMoM(%!VBgc0^jAD}|FxwvZfG}>7~jP?y?#Plpr~iX*Q>$t z;|p*u8@{fS;1b$;dE7DlJzGl&o9hdW{7RH~V7Uo|>>h-Vtx4&=gqu?22`&@nWB{`@ z(PA#};$*2FMcOXk!lSdWTLlJ|>}=fntH0H1|EA22fOx6*2#fn|f$sl#cB5M!5low* zZHmta=vxidjCxK(IWfZT0}!|Lsl!CmvV9t%K<n>kiATiYfCbNIKjM1@{h-(Rgt@<7 z;N;bixlPoVvB@rG*S;q5r*@K}xu2_ZC|WrCx4Co$ybjL<`~?-O_;MY1=U@~}Mw&%= z<F=!Vd;2h48hdcq&C?S_vW|lTb`sVP7VJ3VAWi>eqwtm^cWY@IyD0Wnd(IgCE$(|? zn(ptW7Hdj)O5#HLdmk2bz$e1o(wK*nn*kZJsGc{7!{Q^=4)|B^G?oT2$w4L2H~%U) z_AW(7mh*ywN77ZtO~?^;L+(Mp)Qj|2He?b`=xcV9>$3AH&t3&yM~!B0CTk5)g8SGI zn^g}ad0P1L?t`};g~NK264**MHSFR0F;6CSHkDl;{{voxb9w+Dmo<s#OUDfJO%ZQ@ z^}252`6w(-aV4n`CK=RQN1fKM@taYvnSMRKAu}d9lpLK}tQTgK@M%Bbiku?+-bf1k zzP6CSRdDSdbG}8sQa8)t;3BH$1K2%5h<z=ub_W6^l2pXDLpxuN5RqnFM3MT{)7#lw z|7dUIh3L$~7VI8}I^<e2ZOCCKpFRdu6}PxEy=lE?p4o85xVc#`0@OC8ZLR&7A5Ckm zT3Gg70}Qi!oP5JHNl+0H-%}I@4ZdbKAiWRv22M_Dy(eqRgcmqpm`X-`D;G%2%_vSv zMm*Vuc$7K?9lZE+(^hFQ51fx{jBu!M1f0L-px_iMSt76lX|xlsQV)#32Qshzd<!|4 zdu4IlV(w|R&Q=Z^l6^Fi00w1n!xCy=@LT=^k_y2-cU*y*k|>6qRUj#O;VzIWkJ1D^ za=Vu;saf57^YK@RWz#Dd6)?sx3%rn_CashQHWit>O&_EiH2W+AyV-~UcAj6UkUD>$ z+`0GpPvLykJH6%PJFi-zfdsQ?Rz&C>0SQXF=|aS_!z=0Qqry#qkSLM{=1M)n!uv@E z<(a@^;RBc~WglgOICnY3&K;z0gz_Sv2?Rp)G<|}cUydvj*gYq``{#N#%g@4`(vV1; z>Uw}`Bz=iFFS}we9Hc4wk$<#^*#E6fPmCFm5WFCQL2Gk-<jj#nX|f@b!+lYFhy2G7 zuu5>(R<LCQ-A3^b2S!ni_8bjE0TuW<aTFwy`+qE$OfQ-O9rgz7pLwqW?)Q9vsMLRF zYk|G7wJ=#XJ|U(#n#Xaz0<bDm9V>ZvU|up$S`ZilWD;sBxh%fnUvjRI`6kPvd}=K7 zj9NSZg?{#bdgo_8_X<1I;=PDao*IZxU#a%xNe(X}o`mLA1mRCw>HrQ?Pngt4kVW|a zqv$H*qI%ls++ARiSVB@jN(H5*dubHu?p36e`qM~VKtd5gML=2sQ4m2uIu?`=MUZZm z?(Tl~{kUKDH}}rmC(e21oU`a%s|mBc@5aA<j1K^;G3anyjZyrUyUq?cCyGXtXGwuv zdHhN@8}9fx&;AM+c^ii9U4df5&N_%NjaE9J*dHI3nZb<1OeLAbl>tO4+<0&Hly#u# z$L>Bjj2fjRimgSFN1?O5vP3zk&G=oJ4QzCxTrSNhJyB?1=RwYo2zf)VxGZM_TF}{M z9uI$_IgGtf336P|L)q9cv4vUf*1QxGS?Na0Ahm-PSHY4j11~FSigx5i%JE)*hCCch zmHd!a1bJNK>i3UBo9!Rzl!JqaJrDv>)gO~(NpN%CDZiXJ2Z~I!luRID86?-Cv*^bw zN1+_))^5Zm&nnp}?vFxGNh7~K2PF-6o7QAFFyAf=1S*Jd$sl`za<+_P;DH7(>#B4e zTO>!qB)+X745Fyb``d}?Yr8kw&fFB{i#(}B22bl5U5yjp+wvx>;3eXLV#(Rg-K!Ql zU0$GZqTwi+m<Jk7e$k2Mpm)Zp3p0?S_XCwR73fP`xH!I;aY2%IeC8F2Y4;is-Vud* z^Ds@`b750|e$oy$%<*)ZG`yC?>(Hx=(o1dWFopQGe0)KUx!L=T{J^T$%rdY-hI<Vg zxGGjKSD}K_*mNlN0`WI~yw^Q=jKvs8Lk*=jJ}1c%<MPyGsIkNY*NJ$+VS^OhR9*8_ z;>4Y7S$cEJnf_+!l6yss{S8j)n2{wlW^mOt!aYanOWA>zHwz*=VzM{4p{&zy$8#wD z)A9oz;6F?!7RrXeyB{ua<Ipfwr_AfP@ncR8R+65YhUl~8N86hpijLkdPe;Ch-V8}0 zR<H9DwGvsWWs<`K?|#$ThS+wWN4S$QAW=MFnrvnX^f>j9+%FuKoc!OZec46sBxw0Y z8{_P?>Bh)RPn#2-5uIwj;q3qUjL(di59M_2-mNn&6BNqh@{+T2$sM%cory-XK!Ft6 zKmq9v6Mq2@u$S`Jh7G4AiSii!g!V+~jqHmP$+;}kUx-Y0HMHl0+b06kjRHQEqTuO; z0co6(Ji4-XHJcb-F=m+t7AYAYYQY}~8Ca!5WKFq0-h>{NFu-g#2h1eds<e-U1_Qn` z15+l)Qeh&TXimMvR#-&A4jaI$nV%5QW=yY^;9lwl?a>#xbj5v6&l_(PUN_V+4j$@> zm(!M`CXSkE)_Rjqn?r(RW#HB8XPqc}`Jh*pFwrtt=HyI-!uGEhygn^g#p#Mp_l5=Y zo6T0|%G|s!-zg;U>p$U&15Z=}Z<q{x*XkNMD<)U|@_ZRbpf`B-sMjj6W}BMpo~~q` z@-$HA`Fr{8S3(HYSJtC|Q6t&!s64^7N=Cpc@n^4)$^jj+-{nljlhAJ69%>^nSiX(| zU35UGGWcnPQufJIMXGTQ&!N}Qb5`8Gd%3m0Ls1i<!jzx>{!t_JSE%WLR@i!B5tOhs zshZ4oM3td|#$RqRcbTEyd^a3=U1oy<wm$3{tG~krvdgj}ANTK7tV&D>pRUOHTj4?v z1D&dteCfQtMCUU>>KpRRk*g#>_B*?XF&huqQWVbW%46dzJOMF66h+POb4zRBJSvC@ znQi^!E$8d_ON*u_bTP-H-}&B4F0ZR^Z81EmSKE9eMoE3sY>SWme#sb=8ztonWk#W~ zr5|hb2wj-?({Th!Z~N&<uB}!JnuFP(k`>sZxN*$j^>6^`VHdf#>mG?HusmHL&P-i% z+_~`Vbn`8z!pgIK?D<P~%d6<UtJtxCgYC|;&O_@v%b;2H@2eZRmyc3&8s`-s^=4gv zEgg6hDEKc_r+)hFIqv2QLQhg&Y)b(A9O36a1kas1=G0yty$845u6CCjelj}{1rHN- z(@Oc<A4^-4c#=7H3^%f<!wb5HZG#Hvh_gS?Y2t?lQtIW)_GpfuW(D<fhEsx)7YVQ} z!Y7fMr{|;qf7WnF?W@IcW5hRJQ~iw?t?HZFha}n8eo@TriJ2OFLk``rv`8!%gI;uZ z<xxks=RE!pG9HIC3!>c&G3k`CPFS7=SNf{Q2brjv0!`*Q1FkU<y=1Mm>ScrQFPuK$ zep$JVjtxCpP7wFLw-c9bhti9`B3t&67|oG`gD&1aF27zB7vmtava!UF2D<Cyqr4!D zk!~i^BUS+tUQn!Zzpg8t5Q}W5wLP(69P%v^g@;*=@B5p5opE7qFZmD@FI0=s3atv| zV?MY);gf+Rz1Y)*rC^S$uZE!Ay4%a64GQ@<`<}Z3{%=$#3-s;=_f5Pa>UmoHLE7WV zOO4^bzsqmr$33q`eq#4I;Jqg43&@j<lAWj+Mmr$7-IaMjO9%qhri`cr@w!_%1$~g5 zlX8C~XvYe9ppaahscznWE<^#fRLi8W;}3M|KPpd@$ODC1nU4WKfBiX=@B@UDN_WMc zUJA~vEATwe3HZ9Buv#;Qs1sQE!CCxS5^&fxGL*n7IvWB;h6ki@Z0>gIZ2EB!BcoRO z4Rp4(R-ci}$@HX;-V7&$(;>%iOy9LWCF}_iH_imTx3wmR>L%C;w1x(zqeurYEfe1A zcHHShk7WN(@}2rbPdCVjmqhk9GD7=lI8W~DRC8oL=x7q)=JNrObY3WGA0IF&;0s>O zQVaEER&jks^4?39h5MefVLS<;?o@e%5yC6TN@<<ZfdfbFYNXT{1x>K{qNiSm*+2 z3VLjAkYL0FQ-IT$xHtFT*9>0N-p8?Q7%m|WRi29b?~zV5>@IzKUIA<TDBQaIfQX=t z_6^tFomgV-y!V8(s4+(n3if|<X+GO&f*lOJT5#+tc`3F1?Z?i0<3nkHb6g3h1Sq1i z1s{o`*S(l|q2@@O4C1u4unv2`cygSF1RsRVFZ4gTS6W>PpJ9}OvDqkHK3L!(vB%Vt zgs&PAyw}1*)nuEg8OXy=EEAO|yy<7sYwWKQmiR82hpk7YwAgkYY`^b4Jc?z-`CYgE zGz49X6w{zWzUMLMyUT{TGKc2)>?jSRezW{g>>oD){=IZEup9m+sE|0ld?KV&xr8yY zY}#k<b0w{cSVRlFJw1fO7{tvPfKx2yE-Qw_Sc?_sbdV&<7n0O^6ZlqwF3ABFftR=V z6l&xeZ!dZck13q^5mGP1ZKCumaOY8YEis+cIyvC04pW+#3fj^$KRVp|>33j>x+)&A z@!k=nfB$>--9xy_-_5<a%$}o21gq{JL4zL<Qz8k|J_8&ljfu<1eI$Q^AVo!rKg__? z4}~`eyk30<c6!6s3UVvSiA}Cn%NqZU+%&h+J`}XLt9*k?@$J=_q&~`(SFhf7t2nlx zIY>WwfjfH}pn=x|{D0%6X)^wXHDZAjgmKT3f!CMKor3ZKX+%K3$8IujjaGV@Ri~mN zksRx-4}TCdT~!@inKL>Z5shCjhc$Z8m$-w=((un1j#jlmu8P82D_>;|bjl69OumaO zbd>xZiVwZ)1N4dDxB+LubMMMKSZE&!SI*Ydwux?%{te}83XL8lJlb*Ods`~hhR<T> z$+M&|D~j+)T>c{-nPa7Ax^zQn$U~apmg9#6!;)RD{K(BM&n~Z~@L<x=Vzbs@Uvm_8 zQpbEV3)v7v>T|h{=6XazJdDMI*LmZw7w3Wc<CizHZyRjp8CO3*2`em~vwH%8L<s?_ z7HBoQ*Sknl5XMIG2xs4cx_?)dlY}|O=g{!y*63$bh}W3&C;x8g%t>4wgVN*9kHs0n zASW}&x`U@dy#&bRuH!+x&QriV>i}XrQ6-tpFTH0~D~?&@2xShF;Mk|Hi+B62^j~xb z+ypk(@#=%u*=*eY8)C7nlna0zI_7}?D<LS;rOFZu`64kSF!SVImrv2jPmIc+O-nii ze)fq%)@h!zBhg}`T12I@RiIQ_m*bB$lCZ*VkG@D8%X+|w=Ot#;`akG&yI8(~N?}oo zlKQ$MRM>aCqMg8A`p1k8;U8Lf{_Q<B<yNt&p2;kD#6Wx|3ume!?9<47jn={I^+8B1 z63sFFH9WNEnG8IaYMKFg40!_PesS?K)*&Zh3lp2AhfJEsENb+Obt6p%SeIlp{><9m zr{MRSqK$wS<#^7V`oibQb?U)^_J=$wWm0euEo!sQXIIEtywa|5>R)pS<=C~`6Cuwc z<4Q+TW?b)u&Cn9;#!YRO<d(nv(tu4>KR$5^vfsFJZk6MS+LW78e7Qu*k2{|B%P3M{ z9JJWje-N?w#IwBzZ95%U^<RKSnc!P{7GfZE(5No1Knc<8bnzg2-tX^QwjBF6bh^Pi zt5Q;n>&cYjMF8bdRDnEP(hzXPNRj>1)iWXW&k8+UN;a8E%Tjp37hE}*Jkg_DPG0`y zczln#Booc*cB$SFA}c&>R4NtZGE*H*MrxLZq7oz?OqxIcTXwLK{SwWw=y077jE`n} zG_TN3Y5lm$hLh~iW&x>Qrbt-8(%t$hGTT%&rxucl8T$Me+bL#EF@k{nDB*)W(0|ah z^Xt!T4MoXw*53|x8IPjxFNB5OWyL9J2g<M>m+-Rssu2&@UblU~Ii7*q`)UYsm;K>o zQaSc;`djJKzH%5Rm&3}h@GW8tUp%B27FXNC|Je`8t9IHN{!+T$mEQ8wc`W(}!)S@b z*X<3)P?y}^WhuF|d#!{_<iMnVBB$0PKiRg~JpA6pPRhQ7eZmk|$bEK9yjOw77Vla< z&2aFpaiPUi*mh%0&vTze|5f4~1NLjxK!Xy;g|843_1Vl0B%5jr3@6bXf1c5z^w=Xk zG&YI}Er>#B1ae5w;Lz}NWf*!W_niWm&&V=YfP}d^?Rg+U#J~A=Jfk<FPr_^3u6`<| zv=IduM~%e>>EeXX(jiy(g<Ep2DgwA2!r=+(*JHz+b@@6Ry|ZV8$2DteDdcKr0O&1x z$2hvTFyz?eK2zI|&2gi=un4N;y^0vPGBr-^cR3@8@X_S;va?j)3)e7n8LPgI!d!3h zcW#?YJ-NpO7c2=CmL|Q-|4b~^H_e&HiYN^r8v1?4hdMlte9PGa4wY~|FGCk&vBgXS zVXLhuqE*fP_kK0cS~x;0&#zCgD=#I5EGOxk&4O*t-s#-T70{zpxA1$t9Hp@qgvPD| zPfInO@NsGQO$(kGIQ^YibO`X%3z$QWh*Sz?BBkiWH*Fh4w&T8J=*gQHVR%AO;3{QL zYU>kM)z4uKli@BJGxsG?n>dzv%WZ3kqy5gk{gYawgEXE!Vvyn&P)Er#AY?xJSl@Q^ zeZiH#hnt()OVpbYMll;VjJsq3N4)>yOrtP%2zxcm8VxT$K4)dei)_Z0r8pf=kpBzp z{e!5}Y=aM)t+Uzd5Ts7&2M<ro@wQqhVD<=voQsH;jz3W7OAY4cq%-?+eh{w25AOH{ z95sqybNAf@9&$b>?6C}YY8TWuUa;qUAwdACh8mGhgX`$*!cdflx1KjPMqst1Ytc>d z5zoTGM=!8-Vboaa7g8K&q|fTe#4P@@B<?;<Xt-VqskZ*o^ZNvz+%S0U%@^upj_~g2 zB<qV8=e4Q5Z-Q96(xt24-IMuzlJRgo<ImcdZ*3KO<3f6%t+=|`OnxU@<xF`fu&U5G zd2wq(MvQ-OktZV8zDZsSo)JW6Gr`{@tyL$2Vx?M-gJ+vtA{Zgi97=HBJk!5rulW5S zGwh*dqQgA{sN^}It}ie1A;zTW{AlcH^gx+v$IOB3=x2+%ZaL%~un0h&|4sxEY*WuG z^1!8aT1efiKJUJ*4$k)PNVlvsoOSKX`nK!N6A|lDFvB(-emg<XsGR_(legZ;zKUX? z;O%_1nLPvUqK-@TttO$_XIB55ZDkUUu3&e*J@f+3OWcM*uTqfh(&DHR!W-0Swg&J@ z+-M>Q6}oNv@yn0@z}$aR6HGbgZ)zE9o`}Y~XzVmVo2k&E-PS4H#jNXTP4D;5lD?)I zN^@TbIbh<n#fJ^|fJ8QGM}V7-PsJ?u(q<oo>Eco4=D+LhV0cnqp_1&8`S9i4v}sdc z5{zF(hf*y7BD>VLloXv!i*Gl#)D#GS&4z!ytbwXY-%Z?JL|%9^b^NR<>r(N{R3Syk zh~s+GPZGmRu9K1@ZZX_I;@#uUCsQxBhma37s_N}H#wn@qQmw9}P%=HRP2rX5xrgyK z<0;W&MzC^Sp8i-(C_UQSd|E}P4Fwn5&>W&8j%+rgGO+lq6+%00B|0cO2W~r~fSiyD zxTdcw`{t7Q18dM-;Bqd}d>bEty^&fSNQ?{EiT|(6_ndd4=yI2nwkI{)$CQ5ZH^>wt z;?<wp<CCk-dXBPj5hYu%McyLeEx$vq!~Na=ltX*Srj0|+$3nl8dI6OJFUK^gvH;*u z3$oGqUyw-Q7R0G22`1(CJ!HDQhjmgYKJ`h=zqq7n)Y{X-k)*IX=OEHncA@8r5(lc5 zvT(_Gv9nff(237wp>_6iGol1nB@S5~>pdIC{?}k~9!XMAcD10*mW5JI;lI}h6O})T zkebnDJL7V%gHU?9qq4B8G;F>IJTun8@;`ahrd9gwthz)nT$Wrwq-!U32f}%^mD&+) z>&G$6XY7;1q0$#W2bLvr4J8S*xQHsMtD5#H85=Gg{rX(H+}Gxw-CM&4u^B1*b&OjH z8}+I()rBtk#&vs~d6=vh38ICyX5%OyW)UzkLSs7GCE>~?qa;I3twHs^>@vLs-gUk( zMO6e#4l5Wk@O0WU50fBlB{?6UxBHD>Agx#{WuLlHZe~mrW<9<cx=hjWEdqIH{K#NZ zvMI`=UJ53Idr0n9fZ7$25+NIQj~IsRSDXTb!+y2&r=vh!%p8-(1<jl^%rI3W10QX& z;e3fstr>O=N8{yX|1$qe=W>27OMubhFT+LZI#A@hUnhkAK5K4x<e0m6BmbkEM1l?} z`LLQ@jS=LCp?9*Vz<TkOyj4PE_kU-fh<n$&Y&7ULaF;3!*MrE8xo#sDyIpZy+@T8i ztA8Z0IsWK*lMFcaEqA!>Rkdkpbe}_$QNZkmoa>J54>|X%!kyOlZ+@u7E1=)oBIQ~@ zpF?Mu3o7~Yr6h<Gv|i`1{cm!aF`DVb8_XHRpe2Qsf<-wdY?bvlFGfk94iiRTxoREA zBbK<L9uG=|w}2|${$((KM;hMy>Ik97pi_;V#xd#=nNWuLl*#8?cR2C2fBxMDxD{-& zcuubZv^l05h}@W^*u<DovmLO-IS@|PgL;nYPX-hi$J3jQ+y`Gbq}8^@J2X@a^OF3W z@}kr43iaF#pERFK46c3-B@F8wyLj?Awnv6fE}!aNp4}CIFqi-NG~kwfh~TgJml8N% z)PMQrV%G|O`WIp)6HM`0>FK1KsYgtvqiDhZn3>pKglM-`cLlzLkYV%T6uA2kK9Fkm zU{M|aPoKj(S|Y|_Ze!qa<(g*a919w;J_>n@u1W9D0f#^z@bAbeZMOEAc$KHuDjF^} zV6BB0+(s_Gty~6->t9^*B?jCm9iJ{N$5j16P`OH?s8NCygVeYvvc-N8(94er!5gX8 zTL*;F9o{W2zwy3*cw1srUAm3XhV{Ejcm;f$SGbRBhw4f}Rr^SJ45F?Vrv8!d>Ko3@ zB6J%-&9&IP<id)W(psv`C6bGny=ClXe9@HeO~lcN2)O(^Ds+{A*q-H9>RvYnwU$5b zBhVaNK@|UNxoh1-y%!!*9P3+JDL3LGqA5wvO-9m?Lox9Ip=b`jgC%g+%+IC*iH~5E z$|U_r+}iRLESg-4M}Tx<8f?x#LI@uI7#wx5c#YsEGe9cqE}D=nrEL5h>EsD??E(b^ z$BMFlwC2xEOl^*96TyNlbXN}OoekAvYd_sE9VDN+eIcvk2a0PuKT^K2m2+A8UDyw$ zi@CP^`?&pcnID0>HRt>V!4%~p05zZq!Q>DgDy7;)%-Ts~1}LwQP@tm#(`Y~=iO2ps zQ}Lc?WK28Ajm9Z|J>kJT<Wsu(jwHI&(CYPzz=e(5ZSa$w5IN{<YAD3X4<*d<9GSAw z1X+oWn?1P;3xW@~SAId>?Y}tdz(U6t;~jYfe&ky(?MvB<EHgFPmskEX#!Z@az4>CC zLxk$C8G3`W5w)n0@~w2<ddPkS2_8yP=9GyayuZ!+r)XvQ#C}e(Vc#~43DkCdbX)<M zE>b1ChCrU6Vx?(B1X6a(?}Z6ImROw^&T7K(C$qO7`#Fu3vZa?%ZSF9iMk7Cowa<N| z8=52&EXtkv4gNI9w_9{epoA@GyH7Sl4aq#mIHwMJH#ktMeTL<BD2HSEPZ7uzua-@2 zZ_u`Vbw#T%aDkf<u#-3um+Wx+`T@r~bxH5zGaSEH=`NfgFxI2G*Mt{&8iosEWj6Rr zc3Q9fn{&PKkInXjYIQx*KMh;ll2GXsVzJJyLs8{KjFY(5@Nnm)k726vE4}0cq5jOr zo3pQ3ajTvS!>X;2(P57CO+yo(cUI-Ui?#?FlbO}2!~Y(qSk|+6tbMq-t>r$(G^1AB zoCky+ZB{<9=H2`?+MI0?tf@sO)d`*UKLh!#u;P@PJDP*9nP_ZK5;V+k;W-dEjTd1= zu5lIob9r;Lls#L8eEm?l-?10a_d|J1WTSfHJ=_GE1A75XaqUNu5NcEGvlF+9@l4q^ z$0ZL%z&pl+Kj^wMvGA^tc|J?nk_{wx1Ov9;i_*sp|DZnvw3N@!kxH@sk<U#(M3TxD zi5nqBhWq@9rWh}Zx(Oxe;|HjEWY(?7fS}@YAJ5X%HQjdto&Zuj<mX*lEKrjLHqYMY zhnCT;i}kv@X)JuwEP#CdUjB~IXb=&{b!91bPpXvF6jx-Hr_E*US*QsSQX9GKadN^R zh2X*GwI@j2-?xp?eh|Bwni4nL(j#g!g@pK*yu1`dJ(Z;~ZEB}|P0iR;WP8RBX*i$& zDOGuz%xM%<CzTaH_%2i`u*cE@7)?@Xlg;~f_8^h?=FiYnt(dHUt{(#DNC`fD4()TV zBV|}83<@(2KtVK8@bPsyktXO`c5HZV{pbEv>h)0rkkRH~uP0^j>v^yXzA&;ykTPve z_Abkf2^-ViQWn5)?qbnZE4XMcasG!DB}{^muv__hQ}<7PTx~5B+7W*I>n&SGj%EBn zQ2jd#`hb_~W04QaNe-(C61w*>OMf2)g2!1^LMYQ;mQBIqDTtK7Xm-hS-;KjI%kf4# z4DO$urjxEt)Ft|JY*WrHs<5HOVYb<-S#IfnsDmieo%`PYDB;l@q*J4qAL#CFy8&cg z9ei=JLUr@^_q;*+H}HXT`wbfu8v7jy6ugvixMZ;+5QF5MH9=ukfX*9%@+vYWf3ce1 zz5~w9T*AC&WHlB-i-`X35Vrh%s+`E3gIHDY!g2J=99OZ0@ka1lX#=3j2_~{u)ryhe zkL1sTjf-ASAj~@q&m-)$lea{LGPQ<GMLhOu^d}6bFJD@vAz5Dq>*d9x?kFp=)b5RM z{_uu;N2W&t$Bpby-Q@C5DdQiV;4j{(bGJdcwS=(4{v7$~c(YC?bVT~#Ifm)U6cfzL zjsOk9jn9sKs^tOg7@MLOC?N@xm4yis!XBwd#2*v@*TqKJz3A!0AFVthKXjqzsXxvN zu3&CL9a<A(I!Mf2uU>BnZ3HG%0<UY+qxbiHWl9k@Xto{S?V*Co?)|y(P4~Zh3Xf2j z%*bt>QuA=WpC<>t9%b~qpLK{x6sgw@N4SaZHDq07W5v0rJ;r!mY&I51e%|A84Sil1 zhO#6#=>Bf7{H8Kpqepfi??Gmr`T#^k^|BfIvl6@l?rLic6wDEwMnL2!@E>GL*$t&j zAhOT2vt1#*XZIrNw9y<iW=q~4;Lc*~KJ@Di8^){MTV2wegPn{=+s5XTmcgm_$x9h9 zeOeTOV*?5ON3Xq2))DX@l4F_;`uW}l!SxR^eDUN!{5;KF#&d!gI0i5d-===HH!-IQ zUsM|{=mzd|J`eQ|0a-!a(x}Zgzaj;yu+>^5#DUKKF!^5!98Zt>n8R~V@j)YZ0Q=f4 z5nVH?fmNq=G<KjLdiblMn$5WOkh~&dGsF*2pPgTLg6~y+arO*g4?R+eTr9pj@eiuo z!i$VU-VxQ+q0~|Xb@F>hZmFyoY1Is<r0SH{Sg4m!fc#vRy^pNy|85ggJ~eIc2p;m_ zCm_9FT|XVTLC`P-Y(AkDnds{jlC|&t3>5D19OR193tyWk>_c6eANK-^?j*RnHBv;I zJSJNmo^$cWkOVol6Mfp{aD0t;OQg2qRMKE->Mrepv5@_)+XIcc^vjCWZrtV{p|oQ_ z;B5mvLE%*(>;c|*fo~Uy9fb9nfW({#r2pD1*q;TNPc{iOa}8i}M2Dy*9=s2H<hkgr zDG5|td~QQF49`H!^FRjoftcdE9@M81p!oR=gyYX|AkbOHekic|MV7Ns9DnDeLlypq zy6d|qfG>aB(2%Dz4heUM0X?U98R|SmX=X|RTDcc3*r(gM)>_OeI@q>Rcys1XC<{wS zXn{05<WE&!Z5Ot6$td!i6!L)4`0^?9Fr=BQ{*+~D#c$+%02(_^JmyHhdD{ad>o!A) zDmLum2#)Xd*2=iW>qy#W+%M4X!jpJ*3%XmZ6SDY)i2~@%u;Mz`ubz&-fHsYVz@hpl z_c&M?p6Qe)Xi!x`iqcrcue1IgXuRx-$sn9Gp+v10hvOzk<}^2cWH7U+e&Y1fxdoTc zDeiRXPx05WTlg7Yz;kf}_iP0;W7D>B5rXmmAQ%4uZ9LGqx#kLCxXIx!xUpf#YDDeq zD-yL?qlLy(v*NV>i|(FWuJS8zs+EO5)vYc=tstgD^zbFaFqI(c4YG<9B(&J}9a(ze zIlaI?vtgY}9zIXXo`I}(9;X8F#_BpLXVcHRBgi>|cF#dL5<EW7%G{0YPUs=n=Nc(x z2s7qp(YJlS_vgRZC#!ox4;Q83aaN3(pfAXv9QYgP0RmCDo_AeGTaoG4z7~G7lom(g zt6Cq<5NO?{V?*!WQ&Z$+zjkBX;5X#q8^O)Go6zUWHrMY}DFW5rGF8NB88L{QBm!r{ ztV3SA3wof0Oa;fMf1yMw#SGd4{OS!(4N)FG3WJuSGoV2Tmu$CviQ0e`amrRfCZ*^> zzZrFK^+7YuDP;^o1DViFyfvx)BRG=Abz+|Qs%n~?w&z76GV39l!_v3?<{!v=UnPw_ z2WfU~mG2L-zj9P6i~tHLBqIGEf1Ih90Xn|kfbD=_)`#l0?7p6O1s;j9*<A)7T7s`Y zCJf-dVu9q?uueC_H8UT8A@MV1mWKJ3WZ*tz8>o`{c5C}@jdftCfACE6Gcbv<s@}eO z0{sYDSqUOQO0~G(E2+{$Y!{j&V{VEGN_ohaQD2lT_dbx4GrDKsZ1TqP4LBS*H>PjO zz{(-KgmpBevE?A!O`1M+SX{SD9GUGSf<39qS7cEonnT)@5AS(^9~qb%giZJ5Nr)FL zYe(=)6w2k;cM*1fss9=$<tCvT!E4yCgT<~SU}GvzpuOLI<p-EAidXJq%7irPF1Mos z{Kn-aQ#;~Vc8(A^@nqN~l~6tQ@}Xd!1a@UqLS**8c)7A@VPOfpENU>YaPSQ$rspp& zbrbtT@>iA2VfC8NEHA3UJU-xb%g9pZta9JE%m4%V*q4M#$1Sx2A>t|V&RF#8FqCL{ z;suxmW(ZJ7SDHfuwkm@a=X|_ge{O>#N)%6FQS3xy4#BZ!2rRC&fBXD)C*o<8xAsEk zQr?b6T*2T$GSBE|0s~mg;v}%bF-Z8kW#qgt7QpQ3W&)nJd><Z?SH5lH5wg1?IIz!N z^Y|s`dU`4~M58nDG<+_B&-e-mp^dLNp0X~?I?xG83@s5zU+ktPMb$i6Fe5GD0Q^M~ z$DARFtznZgVS)SS6leOx{8!x2Si8?W+OuH#Lc78T5LnZzIY@stvv*1Kt!eZ&syb+_ zB08I8rvA>j&RV`8nq!}{;5B8Np_x&#*T<q#toszB_-<hLS{N%XeSK}WYlxiA5UuT_ zt*FC?5*|{wGAeS26~GyR7tXGkd9k+1NZ9Ct_Po;ysPdQ;M<A#Qq;$%^J4dt}R_p&` zh5o9g9_Lq8Iw+WgCM7+3wObE|e$Qz{v*CnN8%WnCFJ|_1ESNW`X%VPzTol{n5yU!J zMFyULTGz43)r_vkEE;reWmPVQ0aV-SiPO6%B8ri4`Vo%kEBBu)3dmniz8ZAvCKLPJ zrtXM)J&Hdr+;Z|OPnKra7M;sWYkW_w0~{S3%U85VwwDe%(}ttktLLfq?HC%%(D=+5 z5FYX&jtw_0C<8m%D^oDJ7ynut%0QZZMd043>bl9TPJ?-s!dRELf1^y2XCEhYywAF< zcxlKfajgSN6iv<QbZe(ja_M`;g7V-rIq+h|#b^08c;+SD-XMW@k}O9i|0<4MOSo~s zD603h9Kw=()^<7Ao4B=BR^>IbZC#lc##HYb2O8aPob_L2M@?jnqowFU=nz(BvN-D` ze&}4Ld+$}!r5<vkRX$|Fh6Z0b!pnprsGWR?>yPPK#7}GD^YRHxl(3w{e?a527mx>X z)!b-&w$V4g(YqU~o-v8z$9VAa@}L3dYU(;jog~3&Kbd&9Kc~CgEtKZRDvic&{aof9 zeUEH!Y%U0rlU*294WMFZGuKpk@-_(F1`*VUFC%q`F-9^ul`~+#?h%@fEV21Nknj4Z zmhp1axD4P=Ek#~(=1?CjqQMd?Ry^;O@w&h?;x!SNk2}}zA%b?t1tk#>Qt>o_q~|fw z=_>kXIBH5vtdx3@)9MamtojO2dzYth-NNeZJXWQJJ#&``7rp)#8B;z}uKL&!x>)Ok z{{KuIXM2Cyz6E=8%F4r)qrM;*{LxU!(Vt@SWRZo3<LsoKG&V?ucvb8bA*+qJLW!U1 z7YRzDa7%<y(-`~yYA#!+P5FQWeNw4}4bYm&AcB}ILc%-z1%|RU9i?lsF#mbJ0ot1t zGtc|sz}r&$@nKG1_O~>W?S|`!)AU)}7nmdLsdKcZpeli@`CHNOY-()MFPZ3%7^V|3 z@Fexg6oMch#b~Ac_68|$KZn#8j-eOP-br)vj+hC<0xhNAU%nRoND4<v&*6L~7r;j; z*x-pd&e!M=e4^1Vtz85TJag2?y;;3-4G{CAp&}GI`~1QC^Oq0!{YL*lb($}rE~Q4Q zww`5{se(sDW^bO@Q-}@T;`-`TheS~`Yi0PH{Jgl_(R<v~*PXDBfCJOy*Rs`meZVGv z>~cr-Tm^wYQNr#+=50`rtu393?(+!l8XA!qAx4etfS)(TN*uXyC-)AyJ5?U9l5rUr z^_`mlxga{mouh7czOdEGF24li1JiI$wrj!ixCsudu3ZyH=6O4mrOAT-xAi+|U*d)4 z&V&xzi~2^ZL5H}GW906AD1;2nVUBA+GT94z2lJG{j7`0sM^Gz{3h65E1Md9sNqLd@ zN)V0rPhmiYeAbfSJG=9C%kJGi<0GToD{zj>Z!||}+5-E^jo1RI0eTy{joF|X8Q3Xv zzuXA?yz%1E7=xX)>%MlJ!(%D<-Z^;>Y{`bB(BtF8$&~pSQ&b)I6y&nZsffBcapUsC z1Nf1=j=tcdDLOO_Efd$Eli7VBHpMbZ0Ttq#RL?Gm4xx+VWkd~^##72aq+xfXy}h&* z52A>YH@XU|H>g&d`pXf)WN7TWMW7&5^imFdd9nl?G_HBK$5(~%h@6ksAY&wmFb{Ah z#;f8oGVn0(O{yyYe%z$8!D+IN1~s1ADweMes0z7L0k$DAX}WtGy}bcHAtIoEe{n%d zD@;5fl?paBHwsu{ic+_WRwi72$RK)g#z+a}a67p)g+4$w>@ggY_pdW4R3cp+G$WVt zL$X+XQP@|zAm&o+?zc~6jSOOG(W{>9)RFS^L&<+b<pCxPr!Wfhx);t#S#|4T4<qLX z&&+gXG^3RaBsX;FtRI<u2<tk&Bm>(QAkBmsGIj&e*|?$72PQxHw6b;Nt1ow5tgHjX zvkWL<m2MLG=T7@jH#GJ*)FR!`;Rk1sX&Ov?mOL>Xz^icXG74hUZ`3B+6z)VgykmSz zOlT)RyjuEes?fRUzy{K#4tBG9s6<ZC+5Vy1|DN<gbeNw#mR7V2?*B2cl#Crv;v%rm z9QodDEG++s2#>>;e>ROdk17M-=Mfp~1l<7*zd4+q@9F+oBldpEg*ACftzQ|0{3h$s zO`+N>Y}Q^UC74rdk9uIYoq1YKG+Iskg-5(-S@#nP)%}9Z-FVSdIU~})mk;G}$=64K zRX9aV1n(J>$!Pv*n$+a+Sp9?)tZJ`fz5H1CrL2z!=<GEi{!9LG|ISV7LmBnQA=Fi# z6{ABkph2SEm|-x|=g`dnZ2Ay=l;Ix@nwukwF6fD}v-og(2THKdW}*JW56JMu%(Vg4 z52fXQ!;5!?kFF{IM^o0_`yG=x5_A~2imF7d6RYy(&~le63RBc?DYw4QRH)GozLB4L zn%jdUT~!YCdkVPaKIL2KZpN7?@7fhg6MvG$!M{!4|02@7iTg2dFnHhdQO5oO9)w%b z&<|c$t|o^s-XMADJ~G{4S9x#+k)(4|KsMOt&YwyJqYO42VOsV|44Vl(^A*v8H%Lv2 zAu7Z|+v+GEPpqCsktQQ2?kGlNv6+|<F@b+xHNgmDxP?Nb;tBaBk5mNQd+nsif~Q7y z2hkEjaVXXa(rdt{DO-KR1R{glpx?L7ZErkf5?tX*(WD-_v_cC>JXt_u`=17@S@8Vj z!=V@@vSZYe6LL?c15rz?=QI12{GZ&-bMwP@Q4qUg!;fAl(s`G|l!#rpq!q8arvG}G z4~cGOxA}ZNTeMM(*E%rRi$tM>DZ#}w`4k;4(*YVh@ujZlRGzy$U1HI@^RLA}(=p&b z-vFZn5j5RuBr@iv<L*IAReR|J8*9&GLa}o=S{B#lG@*XEm}Nni9Q>Y6)rDvDSMbve zPzmj3A!oPf&%dm=!k&}ZYanQW!oXPPYDu8!J<Y;zRB+9+R~9fc>{owH@rZX}?O8jr z^-P5t`_RYHyA`}4ndWtDQAFULhs}}{n%z_noCtFBIppG`Btm$%zP1l4q+PeW#3Wz1 z?jTV@jUUg&?3Fv=eA%_5$m5zv6lW3-++u0YT=(c&eSZaWP7T*xfKe(sI(aF01&KYc zQ01K16C-$B_9NVBgwX1VW%|QS&=>?BK*!4|#C3z$GJvn=Rr620Hd>5VXyeru)`*6} z%^4l&4r+_2Jp|n{vnQ`G;J-**beofb`H!Q47OV|e1HQ5u?aVjruc?UyI~FNa*n#;4 zGf|z5#go)&KF28P0)i~XMVC`E@T(gq(P2|e(7lw<%UxfPS8o2XYq7H|KCUr_n6zKM z%n~lSwE5Bi<HA~XhiIwA3fE@YjTgKYNw8x5_~ZQc8+>yjR!&r|$>t05C!+iXTyLL) z%C|bZ*)uhGl7PMv!AFW3_V?66*>I&42kxT9dj;iaju-hw4^h<Bz|0K^V*hpbo3LWe z_RM?Sw{i?mY#7+zw=LeOxgWjj`yzra&b*3H@!%P+@gYuhS8@IfYybUhB7B7T!;pyf zDt_!;Zku&1#adU#f4__2j(|Ybkm;(}iQB@Dpk}{8M2KgfPL}p6#;Nq;m7)KYOEb5x ziBryI#aF_HBoso6F*V^-LpsjUP+suP+WnscDwClC^@~*Dza5(K0$P=jGqs+`<^>6+ z3!n2%jwIsA`K0_aBEGZso8Cvce`&)Lg-zYLnqZrhT4jN9l6NTQiOJ<A$i;`yCYLWx zuX(A<kXHPbhL!ya7l0s?5NX3DJ7a-Lc)WzpE~pDeCCIL~Ly2Arw7`f(udvzNLpkKX zryG@5TD`OTe~VPIXlOv1<I1>Z|LvPE*8DkWk1Z2Lg-}Fnj#ohqXaDdq3I20sEz7O^ z{<`+<vj88T=WUE-;P<Vvf-3RU_E2ZKRW2~38u|{j+u*d5q6d~6o^)Sx=&t8UKe?LK zEeVKwPsI|Ry_n`33bVpyIB}1E?p)r?cMl2KS{yD@4Dmmfzi-KpDg2SIczY4m6DgYy zLX%`doDV*un3}WtFs0X%|Jn4-aT||+){eTP6-sTUVH=!gacmtx&BPO#p2tO7W4QUo z*Nc9;_az;T&Cj&(HVn;t-ZyvVcLl2VX>Qi-hAD8J3Nu8#H@*8D@)6vjcinUThFQ@V zsRUG(6QS72tlyo+C+BhE$9JUQv`_868{pQ++aEs4xKHH~f|YB)Giwxu@h(?E3Crql z_nz8d-2H<@3k#N~X{|(WeF<h}ldXAY-M%)h^0I6l@IKV|Qb}rR?s7;)QFn8oGdtrW zgte&{w_D4%R%7tp5|=0g^|l}Itwki+4o}LV>`hiJpzvB5S$99FI0{~QAMee8t&JLV zpET~TT^Rh~e--ufV+u+A*^)Z^Zm!LGnR|$fiDA}%zgh^w$<v0-{0O-?uH9%jIbKRT zOvtwxGU20U?TM;?*hsYc>@qLCd}P5A`Yv)STSEFV%XS1=liXVJr~!HPf0*|!jMQ1- zoUhSG;nUecJ<JJ@(8<o)mLxW~lI%EZ4Ju0ej!!<4F=D79D>aQ!2L#DR@dqXAzpfkc z)=UkGp$}tnA9$1;^_+PFjq4hz!oY7Cpy|(MSEXQTYXO-{h*~s<>EM4LU|tX^VOskM z8If=LOHteo%zHMeF{NrJGAv>8j~-AxLY3UvCv}&$i?9{m^moWivu(k3pfJ%5Hw1Lk zdgA7FGNZ_0%%^_uOc4kE!-c<Kj2zOtCm;6UcHo@GP5#=bb9DK`babLfnls|V{gFFM z`C)aAs|I3u@F^3u+^UB*P)!M3k~tM=+K4zvZ=!gQ^8HEbqGRxBkjFqT;sh&yU3QVW z`;D?&uBq~*u9n+PVuJ$1+&9SotSMD)(xGH;nbIUi%2a)&#OQ0`Z;VY5j1xKk(i<M# z>!TwpuwSRAsjw4n_@4CuPvTRJi9-cUy40tfpY!u+9+Off@=>9&_wv;$3{fTKy#b5S zaGX5~e0Xr%Y7h$go`j6?C50a%PGpwnL9?04pT?@wU7Kx853!;TIaKDOu||l^>i&!T zT}(#f<!ITzv``5zH#EBGj8Cq$(RxJ!&&HW)|C}tEu+B*S#(A<g(D05Fl`v=R&CF!q zcu$+qPdGeVxPnZU=N(1$(f+*qcZ#Y+{fcHH+ocnOH-^@gUpX`n4H01d*OEp!?$@#X zPKuB)>dMFl^LnffAn<$Sw<_*jJ}=~?>eX$`Q~6%G{`gSn5Dh)}4t|)qQ~Ik0<=v{c zxNVlpP5Y3M^}ZyUg{w8dBD0*)>6&fwMEWYJJceaW*n`BnXPzbTenj;5Hf(^f^87a| zZ_FE>rxU!4T<MFAiVW;_c`$S5$>d{HNrdL+c#95)vh@{cpAGYP_}{bn$Fi`Opg=!2 z_#+tgc)8!-4pnkfNU2fG_?5P|B%O3A0X3i3OD^`9AAzhEdWT(n#p|VC#(f$9xY>N| zvHZL?xNR!y*T+=xi?A;j0iT!qk7v2RfA+owBzQ?dRKXGdPSWa*+Sd}1?J=>01>YX_ zD302uDPZtuCha@WYb`juTiUQcO!Xuu4#-<z-cSAPA|=(5lzsXsajg8KfON!@yJ03h zY8$lN@2E&s!e1Fgg1KA)|CiUOr<?Yu)$@M4iE5FN{QNj*_JJr_2*=Du_AcSWZ#5bh zQI0Ie^J(Xx>s3^`FSsn8GJRf*ZKWH^Xjrww>Iz<ORCNF3ij_;r`4!C0iu1L0a2k~j zqZkDx!+k1Yoe-`H8B-w6hI{kkejz9slLwxrw#MJ8k?mxmX^`ct_`<WwCS3v*{?s?i zNk66k3l(i7ltKYZ`zPOOE?yGBZ<&Nd8=h~Of5@o2t#u$^hclEw=pR~sXOH$qk!#rX z=Pf0sQ{8&fsf`>)9rcYDj#h=ym2l<kH)xYXZ!V>Oos_*EJoGsv>X@m?2G<cL#;zE$ zJ4BtG$axz1jDv$6kJ{}wvBkhIl3#W5(pPg>7;ipSfHV90IA#$kDK{QG_xCeEg!=l} z(IDH=<`PwWvr%kJW>y2?y$efC(P8OaeG7;Yjvn{Bn|vfs%C+5E_a`Q=JSThAH2FK& z2gokVsEm4nXp1Y|%xO<49hsThAfpWJf&ak8-%|U$EHu7`$p1lT3y*FOE3RH!yLQlA z)fjcYLJD_h^buy9D5)g+<seY2SmYsj-~s6wyQsr<mSj)R6F(W&ZCsog26B8{$ns zd4+XIYDdTyI^@UC*)_(_9K~jfYUCJQ_Yv<l;mukxFgBVFChIhHz`ze4)vr;`1ZUb2 zGw&c+QT>z036PDHT~6R^o+$g8%{=eE2hmhCWrdKkt@v0hHIrWJ(^RFMPk4{d{9fsV zt`kG6m-exXl7Hhu-WOEABQ!!|!0)5wHD5a=NrKk6!Zp@>5)pI!IjsPNqHYQeh^i1l z=^^Vn2=m`s-S;@V30D9iBl_ZL0(@^)xAI|C2srdREiU(*K93c43rDtpbr}z%?5t=F z1$ZUVYWF<kVnkm{lLKNs2&k65)YYoz1TpjW$2*Puu`;{l7i-4T=kKXHo!6+8ecsVx zS%3pb8aUYJSvr**qmrUkq0-%mHD3Bb{_PU0z>U-4I@-^-_dQ&Js;=Z65b%m3v~bp! z@AMl{xfr{ULOdtt2IAIIa8z(~Jr9a(C$dR{O*`2JUjb1JfPChLY%8y(E5R1G4*z|7 zH58r{++p^DNaCBOgs^n69)-09v6TffkD+xppq$HJJ^C`+{<z{Y`a!E#Nag-{c7<=$ zWhC=|?hC*z?xMyKq7tv+W0!UzO>qrWV_N_pra$#ViOM(Kw!V(^ngY6ny+hqlwL4Rv zlD92Kp<FV^7m^Sgy`u9{Jl}MxixMi_(=b=v*U;(@NTPs+olD(HKizN!DLU{MhI+>H z_yWN(R%N`A?2la=&gLp)N|Qbd4c-I&l{k|NUvaCVj{Rq#cFz6Ak7TyAq~{Lx(y){M z#ZuC#qHMA*->L9ZbnCBtw+9!$zDGj5r)@1Hh|knDEj>A?d2%z|+Pp|*kKC}7F&<;u z@H`UwW#f91BWI<uG<v4hHiw$~Y#(rs>>>ViN*NMk4voLUxt+cxzvu_Bv+rI|x&Ocn z3VK`BwVw<r)zx2|N8r!Ct2;4K(VhpIp{U<$9lECx7^#Ub1c^is!lLpv0K#4h=4#-_ z_XVPae{cVux)molYx*VGE$I#a!fOJfssv}il*Xeq<7R_i1Jih%A4oe21z5iuvVSjw zlAT_ifYg6-%;1byc%{baMP<6=ug2xgU-`{Pg}R1I*U%If-;o^Y*PD^|W)*uBHZKOo zC}(9-MB9sz%h2nQE}N#l;1t<w$9%S99Au}iA1-+Ef6|o0ijd+*iUK6BaZz9M&o?#Y z?OXl7pQuyJJvV2vP<WcWcFz6>X(TSj%Vt0|2(IlvE)AEY1I>R~!}Qy)J1=9&qkZo$ zhH3k&iBX2otS7#1dbNt>_!DD8lp}3oYm}>$v%Wz^xpR(ZpE`<`hD|PqJ#+mF(T(+# z1@VI()8D#?Emn;2mZ$Nwi?1ZmtNZim<vcSmE1u+e`w%5efcxYYR`!QxeQDwzpZ2l^ z$v%bueGz?i1Y*8cY>Vv*(tu#ZY3WVz?{)@HFW%`h5NdNyMFIMJ6KNQ4d!r?!M-KO^ zVIqQMYDRxE=qI9w0?n$Z+-#6%u1IPTwB?WynLVtn!}wIkf^t6p+Wt;X?Z1HVTp~kK z6xm0CBi&)$0zA9uaqdmy%S|oBxxbzW4Miu8y?J%3JKn^VM1mrw8ny8cPg@lCR>d3J zIt?S^1k^@ie6`3Y$ksojrcVr*o<HxL&f#=C5hn@^xR1T^pKtkWtL6PHx8w7kmbk;- z`O?0Cq5N-*o5wP@ZkMy6^zKvE#LB{Eu+Vr0<_mI>8+%i+;NP9hJu(nqW_*Q(D5&(* zBo9f(sW{$oaA%tpqfRHef*iG<FG#sP*4y&@V30oJ*Qz$*E)J1YWV;nuJum0WObSv2 z-L}K2FJ7uCt^CfZn^$nb=-WF#x|O4O(Q-Rh8eXP;WE@-hV#_<Da_uatoJ|+`E8;B@ zQYV`d|2%SPi`^N1PYwruN5zE9T4pv}%;=Yr&V+j=imfs_eOoUZg5j)zSt1Sv^h;{I z)8b95^++F^+!k}$&s;8;Qd!liV9n#TRg5%7QP5A{>G3NmD?^mFe3@0Y`L<45Wf}9G z>ww`UH-axOIra(D?Hv2Bt(t$;GuE&y#`9ZKCtmxLS$<qoQP|?~x{bzA@~l=H+(oIM zWYH@leWtejXXVPJq;@mPY{Z;HH0t{L|1pIJ`ppcy<t1WtduskrION4q&Bu-Oj;@){ zsmnTkuUkL!yel}dF}`(}wJ?_K<5Ma&^miUaZCVgx@PkXWYlQJ;nNXTT4W5OaVV6eW z(GIDW{>xwM9n{=7oW18`G}TC88nhkP<!zE^PSh2xdx?U4MYw4ZI-KK0Y`9HX$#+x- z$uC(Tjtkcx`Ef&qc0~TeCp9))zT2^?JG&qJ-v%8HKJW4*k~;4hM!`kjI;|~crqnoY z+Khhvx3VGr1J@SXA9s@AxDBC_!sOaQNtSxt(6PNiZ+D%Dq*g&sQ|o9`(Cle^nm)&= zp`Z#ef@RD{^83a$l2z(g7>?$`-s74yJ&}GB>esaE=JPCS+Qo1e@m{2b>DKi4okg0} zZ2Agz?3XIyC=V@>ZdLI&<VkbddbV#U_eq{Cz0V9_bYHl8^>yI!?TogD#)ThsX6GSl zAH{BCFLUb@!hGjzBaq9Mtq6YfKTzKS&-i#BpAz>RuLr3iPe|bS9mj;zmZRT}Z6d!m zmPK_IL}i|<5XhGx!X@bJZ}S!lK7aPE_BGYqz=xk{lv{nX1=!WCN=nev`~Cf@qlya| zoVo9@Hpi}UKId|kcpjK^W_g}HXto_Io~?R0@4J6`-8at$)vW-F{)SOdLbpln)sSyV zXMRH?hQvxs=QI~O3N{Zta-@pc*V!qotQC*!*}w}X;o$EBH^Sp9;6@A0QcoIX!C5BQ zFEX$hhY?;bPDOPP_Y)%;{?fTn!W@ozw=EXqd3WA<yqcdHzFPCv(1!bi$=27WT0;?D z-k)wz^YrRb(wF^v9Y*rg;9Je|Ff&gNG2Po%mi;#GSFV@yd)XDQhF_@<V!zgKeiCPp zY|v&o8!7o<yZ078diNUQ!wQ5&7Vnut>^$S8$i<~s5t_f>z8r~O=K%&j>)Vc2bWG+X z$N%Qh90D3to+mdZuwxLb3XZ-@;H#4-5eNva9VStKaXe*l82!-=0cU>fAL>0NF1~Q= zGpu6`d3JyRGZWg!k1LJ$TkKPJ8A+_G;CfDMkkb|HiUJ+N0aS1+Ep8t1o?CCPSfzER z?dD~cx9^~|cXmvE<%``o?fskBU;`+^`Hv5CL{3i*o3x3S@#%j&Rz@U&?wYKou#@nn za51sl9|v(wcPYlM`|?<`IF(MB_lgs?6HKk@{{)zO5aI=sB1ARBF(r9evRmYdO?pV4 zJf%BMV))h;>PNJicRt69@KHZ8?az($ez|vRup%TSeZzENE+r@RW+^hq>%i(GfzLiX zc`z3f_cRS)ImUm?P|gG8Iv-Zt3>)EAn)##o6C@mg&bCY%0125BI7sX&W67hf-S(KX z+Yg1?W;%k$TLrtWoYzNr0lc6J?sUr0ZfZzzYEm2O?p0t99*GPMvb$};Ok#A<pvqy3 zY<QajQ-!WQA?XWvm&BZctSTr+zIZe7Kc22SEb69fZ_ypnt#p@kHwXwyNF%MJGy=PX zw6t_dBa%`I!qVMH$I>m`u>0{o@AH2D?(f=bX3m_NnRD)Y`mrVSeGm-PuGGt#D$ZE% zjJZy^Ks|QW;wxlv&Jnh8-wvm-LvDnu68<f~;CGCp+nrfBD}K9<H<QkyN`(n}Plm7) zt{(jM>(BASe7((6yybkhca$)(OB|Y~?n`kL(8HXN=C>_radnYI#KiB~s4XS;r2!_* zo??K>*KP!~36gS}nwZ^`q{N_3zfc}Ii3(=x`jPi}Ul&~uno)OBXgV`4lk5}y@c{+f z0BPUTwVR`DJNE|_8aUa2O-n^Rw&?J0YB2nQ2F~=wiY9Tzo$znYN;6m#Nr@pQDDnP! zdjfDAhL4bwm4D=_yml6wc{c={IzGr$DsD4yUS5ZB#?hyNcU<F4fefbi7iY&h6gw^w zfb+VN0_`*MmT_oH33@@eOzyCX+1e=UQG)RATvWfPq-)z_+N{K&S@Q-Lm&O|)`010D zcI?O9qi%ZlX|qL-P&@WtgSv%-(xYRy=2jFWuHU+Xa{cW&7y>RI3MfX25#Ot3Vd+R| zp(__0-0v^QZT3{Zws1pg%<qKp`1ZbIH`KeQI<r4#7-?1?P?RI~Po{o3|9CGLuRonA zOlC4QzmlK$<ckx$wC_aEeFj0`0GOu;c6Ja$Q%l-TibcCWwqjJQ>gicm+2dS4Np4;F z7|+&B8H6A@)qCKjg=gBIL!X1G9x*khDesDp_?t!9{SFP*wQmE?3<{V0dusi=H4gh+ zx4%qjx$FQqvUqcmEk3DaqpcEcV@{eFV}dCLNfh7*eIv=ct#dZKZM!Q}LQIU5Ir8GP zpXt@qHZMvtGwyu(*b>>_>VN8b?4bOWsWk9%I0;H{{;Y@cVPnu-J4e#LYO-@w8!34K zx<tM=0E?4p^)=SJSyU^mGL>-;Gud&b4H>0Ev+vUy0>p0CKjio|u}o??QA9luAus^7 zK7PKFG9!6>HCxIztApl&ewoXt%|Tma5hfpYVHJJA^|+qO)b;okJ@f(Qg%$h>^k853 zY-dy)@mKq?mWHU7coAo^GV)GE2ZE<p7{gGr@(m`^tnlM(7PbaYrj@x`ux>|CLDFdD zdL!~UmJQ@sVpzD!tK^pwJ^HRlse61k^+$mEMJ@15CS)l#<@)CUsF7|Wfmt!C3|#u| zspZPBM63jCeJR3uIrWXo5PkmH=PU{FzMr(v9tY#$j~Cm1Z;s@db+o&@etrS7eXKFb zG3)Q@)c^U|W!zNY+~i9e>-9k2k4fQ`VIFYs=A+aPrTW>eKWs9GQ<FlXh6}*DvE9=T z!V{a_U)`qt+HY37H#avQ0qu#<mOs^}E|0ji>F}Uujer@`pGWCR^ioKA_&mI6BKQ^b zo9d-4P=ISUI%mtzqhCGwn^e2}s&Y_V;NA6l9Q5OP|4hX<`CP|of;hVRSo6b(2On>= zheEYCqdJUkxQk!9{7tzrBf;_!n;0A7y`vj&za5M(YC%u2|KfNFUW|^raDO^V<YgP* z+jMbpw!lNyo)hYB(b*hFq$^XX50ZN8p*48u260EW5crbQGk#uXFglz?JtVkmKBr&) z;eV_70YLElZrowA@@FSH`tbFv@p(2qxm!m>&J4a?X4{%$Z>}3rrdU-a<F(THcMzi6 z3qKFP&_@cxb#nFo;8rGa^+bh`m*ctrj}vt!y?qJT{FK}C!0<)1Q~n#D;7k967-{5C z@KL<^1z`0LSMLWKfjgb$G9_sOxJscyu{n$Kro)^{{zy{|mekc3T${fu^`A?>n;yx! zI1xTg8P9(zF>apf^;ujo0+da(m>v}59$zIIG7b~kaq0lnF{EK(-UV+n4WI8%*pJao zct7KMnlm>6z%W0#8Q9`YFTtf^FG`Hi7`|}-($mMX@Ut(RL&WbU5mr1PT%}{H)Y_KS z`F6c+Yy;3riH~p`Cm%0bo%8W<L*XNIU@QCpNvPAEce!n|E1<w+yMhKTz9=mQOJL(| za>sDJc6h8Rn2&esS&`GG)Qwl=LpZTaQzK^dew+wl@!uGR3SLAIjr?4PzviJh2;Svc zF2Ch(_Rj9|Ul3ODO(_02HNEr}K?^!KCHCfVH1Lj0N&W*qj=V{ss9yO(WZy~NEUj|- zh&#<=<WQ(tL7nRAq7foFHe-}_S7PLT?9<#~LKX=TQ&OhH9Ox>_KkAG5T2D<;K00#t zazf|%{GV}7yLXoM%Eec_tA{y9z0GcAu`7?Ol*6|$WoGhGuba}kyL&8Pk6r`hv^=+7 zbg!IuG{Db0eN0e04@<yl9L@{C7n{c7Fy;AK;8eK>(b5;`9UfD%p?W*zqEhEA<zT;a zhIb6+p0}!TEjQ8Mf|wafbgD9q7k@(e62KICMvbrWsW8^W3=R92`qv1~?$^W-S3k=V zWc@!Rz=M{e2r0<%EuM{MbNw2eV~cl_T-<pZe+<@yBvW8sDxbanQqi%Hq?vBY$H3vQ z*F}0jEGdDPkgHDgC*EJ0E7aGZA&TEWvB4Q~FF9quOF@d&vVEtpsFTF;g9YRGvpWDP z!(-%329!=;Y6FW($U*nJVDwj6lg}T!Cc#agz<uXQ^4_MNB**&#vm;ru(z$zyah7~f z&UGWkh03#LH_3hzS<DuTe5m_dFi1Vw8dTN;uDHcSX>$l%an_Ny71?4U^K#J`oe$cY zgoTaV7sLGvDPe~E*auH4=(O+$u-pX125``q{*>z*3f4-b7U8b7SKyL%u%$zEu6Npi zuQ%Cv{uHQv5>QS^>5RWE+~$C|4{XUVfm*NyN$nJ$JUrHpiOc~d7b14*iqAZkj~i&= zE(wb@pR>cQ@%Xq|F<d_Y8r=clOD<5&^3#~#2<c~R3^YYEo$qC_zm0uyOr8>^MkDd8 zfrOe-(5tEYGz4QT4I9HdONU^y0|D~AFnsh@g*mkO?50qvZ<Af$+cqxx>*}I~+uMW4 z(9a(3S8wb6xP`{-O)f)QW7h<3Lt*T-m1>|aZ+2Mc`e5A`Q1z$A{M<z1LS{VEBEpQ* z(3sbE?Z4|Xy7nESH*N`jzv>=P3`_!F7fZm-ln5s9o<9f2$6h`*g~UFkrR9FN-tG7S z@VE#GjIo)_g%V#@MGAQDwbgBcyj~$;&zA=yg!yyqB9GlAv|FKsqRK}Kl}i?PUgwl> z3ptu7I!lWhWLl^<!wUmq=Ie6bAer}40%*1r$#MK|A5=`fUk7L~VMpq+yP71S9pXDl z@!>oAn&om{uG1!7dNbh|5xw^2N7Ir_hNk#x2FX^iQ0VliM?aH%XYs~WpMW41hh<7w zs(aPNtTC`{b5ckQ=F$SDNCNywDF9mdJzsdl`I+wYh>JtN)&_{uLqz`3r%f7oX>`oX zib&YoHtxu~{NRlk1*LeLRQ3xD9|ty1%scxW?rxgRFP!BPAB11W=!O6k?Er$Pzssl) zZ)^@G4j$dJ*mYUu=@9!JAJG&ulMi`~Jj6QRRz*>ND1(ARFnPu*H4La<J`W@%*$w2x zWn+r^<LJ#wnn5ET{b@X%^FAtwqhnPlPS{7x7@shu7XGSxFEmifJkH1C7C8`*^a#BG z%m1YmEqEc+8|5&Q7T^l7(_W!vmAyBp&!U0%0~ZH?xWV9{;<MG(UxgyS6chSG7(@DA zv4@z~#z;s0Dt(#=#ia$8c#nDo?`Sn_-uDc%m2p3o2P;CIy~|faR!lariIk_&`O?8Q z8w*QV?L@#HH}-o8bgy_qtaswEu!hJQE{d9Z{h*H(II*v=@t-n6?K)&N`G#oi|2&;H z^rr%H+v^z)|G<sHmxA?29OOs#?5=udyq!nezD4u*pV$CAzRlkff$8QJeQyfh-Jb01 zF<!M|U+)Q4({TF*&u!yeocsLOv_FF>-dxpXJ>5hJ8uM{rfl}UzT+}l+48V(-iWEXL zWRcT^veR#11jGYnGp%$qG-6i9=r5i}B(!PrADb{{gPs%;yul0xy$Yj_08DZ$w=@ez zX_V=QBLz6$wqHtA#@>o|4SMDMSPP9&Fx38fpIxpP`blvC&*e$EX5&sNntb^P-6Q<C z3uVolIL6L(mBLcDZ;_W;=bvAc^aHbKWT~XTkk4k*+1yL$q^h~e(8$_-w5}cu_Z%LD z>ZgtdW&X1p9Y9DzSo8`d0P^93??hgMgK}O=kYX%Wh8*8qa8QCJl^H%AIQ6672>j^@ z9Sg*FzL0FFZQ7bZA3)+>wdh3s3a@?V3V)1q%RKdq`R7S)H2!sMlP1%48c58ZrQCpU zg{)Z~$=X`ZNK{C$u3<ucME*_1U@dl5avJ_<{k!uo<)9<|2{^k)_vU25RTtrK$gBE4 z>V(t>HNv0AfBQ`TXEzYXaW)J}tSdR(ag`o!UP#?cI`A=a8$dU^@N;Lo#E$$>P%C?C z;XUB-Q2%J?oET&5F4HFi9&8a<31-%Kr1dc!`XO{`E_*ltEAA&{UalnY?H-}fN<;i( zhZL)h?=tKc37cn2+tPfZJk-H|2f}|1KrVi!{(&dV6ra$KbV4KVzG<_SoAqxI9}EXW zOSSTav_Go7|9;C8T83S(q;vO$6rII2g?MWtNchV*)3-WdoR_rbaXOgCOy*wMbOH}7 z|8b#_JNMUH;@OAMV9~pWrOf@e{|gWH_Qohr8|=jN?G%mu4@8MaEy}`>8kjM+tc&<i z+)DW`h@(#>t_8XMx)K&j`(wb=9Ueh-e*^4_gV0bUQoopQ9-FGO_4Y0{Im-$7Gw5wg zMe%$<z1|1xNwtoB4o4|LV2}y_*-OjvMVbreblf1*IYnOHPP7mm63RM?zjQG}m6qk! zC{G8xU&cJ$xsI|!eNkP0bcatJ{&Aa+Z|>B6#E9Q%U?0B6cw1A3G?99n@gFN+5QEw7 z)@(hqbpwBx@tvLRkqxV!sHPKOYD(GBP+B72s*?eIXHFZ$CEpA@@7MH5RY}&#z=ydv z^UlrKJUCflKJyA%Q4dmA3}1QoTOa$30QapJ2u551E}J!cj)_}mb@|x|S5=+=S-<*H zD>gEn$}o=9_EIfkj%*=LlhwXF<{@I&(0}>+#jAuo-S)97O?<=#Aa>8*t>r5OcGU~4 zBS#ebN#_3Zv-1()^w!7Mg77l^h#Z`-1U`G+#=*@&(DB{R#5kKY0^7VrCAa6o06&{C zCP|jyp}x*>5Z)zZVPM~9jqFxUD3MqoB}DtRNf|gWkrUJn9J&Mj+E8M!ME;&92n+8W zTK}t8S?&!)xS~j%>98t|6>$#cRgL7{J&DrQ`<OpcN)n-2Gq3vR;qnFV#sxbd<Pdc3 znknk)G2Ug!rPk7fe)HeSUzyRZys#^0J?N!@gE9OazjcrAVNL#Hr+!cPO5b<#pQZ&Q z=nOGiGrr5(x{V}$|8<@l>z?^<(&QT7*wN(mkK2dyYT@Hv*C#+FSN9l^kP=&px&rwK z%=5o$s@na-&+N0nzrV}avmx)aR1!NHPTp&fGuZvclzpvoDnb<hUJV1S*Cic|?M=3f z)G5#8)##_`SA0hU9SbzO-FC`wXD-QtVoPI2Kr*4lGWc=MsK@>lAF8}3@VgDbJg)0H z)?>!=AJq_tUC4^w0d-&h5r7yt$#MHanMrY}EzPl-nPiTO{@!3;OaH!|{9DCvthA&y z&yY8QcE~>J1S=7eSNyCP8_^6-pPX@HNKS%8>PCe>kL@2Zj|DV_$<~|!!JtDtQU7a< zOK@;Uucx3~q$dj`V<~)9Ik<J>1<`!m(p%+Z1FKnIw5!OT0Sg7TzUK9f-CFX}$KdG_ z3vPOMb%=L!WC5jthfwoC#7sZS{1a4vgVUxjKJ0y<Rd`wXKt|b)onUtSD*kmG8qhku z(-Cy-fb-x*hWN8N1yJUX2n~k#(ZDg%ss`1i*+d<f`<5Lsz?<lo!6~e>J2}O`U^$sT zl(?961TW_BUj}`m=N7A+a<2pHuV2=i6NB|jKK)(nGWkg^jgUE5l5-YP!bL;GM&tu) zWkGIW#aat2M?!Lo-V88<zN#vhES$fIhjM*{)yha%kW#hNJnlv6;Rn6ANPDw`%5RbB zKz{`Rm>s_?EBX;P*hdckIHcr$We6~Wi_j2+8RpQ?|8`Y?5Y@bYVtPch<ydwGbRnD2 z_}o~rzz~)-voekCYK=<7ZjBbqkk=yE<wU>oP|+{{gjbM3gfaCJ>2^;3&>#pC`t^)S z?{c(+o|uW>XWxe~v@%|ZtgLlX3p*&f0jCc50y9tjMM!ma4%d(r`c)RCzkBu6q1FTY z-aDKrKHu{qg@yZf>$LX2^gNLMmox<AuCHEgUJV@`Bz#l<fsdE`F?5anK_(Wpn)G95 zg_Yng2DKnK16_P%o_i$NJ+TVpeoNJhZ{E_&Ygt+q@<gw5M*MEQ`aS~Hy*Yb-P~XZ* zk@H!Pie2VM{Tj*B#o;lb*}ct+nR~N%pd?e3s(D&Io|LND{rysHT%NZ>ljd|uj`7GT zrqj^Aj~p)Af<}qBZPb201F=7Bo)+62C%Y%1V=sg#18*rbwi!>b`R##G1m35GZ+m<~ zRRi_-w_}t6=*o5%Y6=6F#4ifWx;M!(BNs<gU2=GP)=mN(5A^=zE}L1Y@y0EDAS?cQ zuDdHxg*?Bd2JSiR|J__3aTMTW|GlS(+P*#-ox|HIxcl96RJuZP+_`lBFnV1@+IFH> zaK`dHFYA-|<@Ih)5KZ2D%MeEgACa<LPljIXV{^kSCEDVK`^L$Z>aju)cw7^@|A#zK zXmJ3dyE+$`DRU!6WuTRYzS9igY5a+}+PMqmL(6}fPOX({AP&NyX=JDn9f7=CZU;6t zMxcj&79oN!mKMmJ<8zf|P#82Qnqy@!Q?Y;fAu^Fk6Y%RqqG!i2`|}5NO!rb27zLc) z^b~!~UM05fnm5Q?|M}o&)>~|}IJ@#^ux%T+s}{509V;8jG-gOXZFU69=BoWPQq_AQ z-hXIZ%UJyaMT=TDu-dvpo}9I>=d8>8#J#`h{AblqWXeSVnz-2?bYXf5#hI?)z4BS3 z%|Hz!IvFkh(txAGj1?R)^dFmroK;ibkzcKw@tGBqq{UqAO5mOoC6>_|gwkqo>kxKZ zD%agl%im8}t~5Hx{hsZDVxbE2%4HHtmPM=M(n0W`Jwpqw5wuN-Qh*2rKB+}OdwPer z=@-tVz@)_WT4dgb-O5K^vmcwQ$9=663aLbx=3l+SBGl+}B3|t^QK)s6MF(Iy3&paZ z&eBPK>u9^0d_Rzvu`gZb{4rElJ9a?Y*{swJgJe3>F0}YMw-<^^7kBNwJL(<%Nbm2~ zcyxUvBUN^LC;G^fi!AT0Vg@#kihi6OHX;lub^&9adjEtxQ!_Uqk@2JEr9P|v!i6At zVKgv29H_YWuH)kXig`~1T3&WnDE7uYC5SN`0rLuu`~fBDzo($mR#*dcKIOz{p72LU zn1}kvRn>@cW6cJH&2>6Y=kx>EMM>oKUqKhMuBeq;g%-J`Z%u|N@zB!{&K&L%t+7wA zgvdNvT>I;V5EBgo$J;&(XaUHHaOzj2;I=ibRL7k!l=8Em7unK|9dJ&Ga}ID(ch^TK z795I}qwC160+*vpdelQnf&8%u!P%9}dYj+9$q3A)W=Vr7nm(iG`yzL0o8#z})I3I; z_{!7BJl(3PYy+U!)0_-gnLM1`D$zg&Mb0PncGFL7*ZxG?FbO40H%ox*WAP)X9C@hr z)yN=rux+vrz|fR{U^m@xFu=QCZ^$wk2jK#m%rx?LUpH(f=$^M=`xCIMn=%Lgi2hB^ zzqgHwkfIKnryEiZHO$Fs8FGx-_L}wJ>lM0P%A0YE;WMs7v?A#(ZbcZ8;iH0h6yJ|# z)d7L0-JMWoB(EEM1lih@+lHb<U94dg07Lm)8Nqe-Mpc}bpML)Slv37?ymsrwh(4h( zQ~yNtZ9Z&i<9&!~hSMQtC}YvmlOXOR?&$)4QF^k78`^q+aF{6IS;j3fg3(xMMHj!J z9Vhg`I#vaJr;75|f`tl&1$tmfg4F43Bna@WrR-^Nk>zsl<=_SNQz$f7&RPKB;m5Q7 zZcrH+JrQ2yZ()-qABl0IIS3-G7jmD`(*MCJRezeepl?7+UBr4+vmdCihT2_NX_G!} zEevqGLF^yV9ifa*WF$Y0Hw4^lh4(h!+~uB_%OKzF=xu(!{;D}OwqEsIKx3teSIDWh z?g)B#O=l_joIU=nWT-Z#I5Q}hd5NPf9am^cXYL8c;tPC^yS4Cs?Dc$xEW>xmQLj;T zxW$EYVmqG`7nsqV>F3_jm`mW_;-H*79IS<AgpxE#&{t<SDu=F%h(!MvKmFgbZaWL% zmyQe>&M;5*;<_NVR(P+vf0X}Go{%pqp&@c2*2x_YDEY{{>iRqaUjY5D2CE?kTSnM6 z^&S0iCMimrAkN(X@`j}K?qH!e{0?P*?=2H>bKOmzE4>=d8|Zt#8;C^O-J|61Av=W? z67}hdg_{^$r0LQzpV@UVEmtirx}MwGSj2MGhImp_VJS>gSWs3!TU(w6xg5)o#uFMk zn6u-`>|^~w#Jft@BkU8eJ|#N3n%r&np<7bZV;@Mfj}M3@<JmCCK6T21ztD$J13fIh zg3g~&($%NdnaiOX3a1Iy6%Q;C26ML)UR4C2{!EC!znA3yO#52P$Hj*Vh}<fk@=llz z8z(=&!YBh3{Fl8vrmZ`dP-g1-#=GtsgMuuPsKP8(vmq?@Q{;x8wEA_AXbchgIhDDo zXkzj4vCW6%sx&{LDkz~VI7MWhmnHC&WM)Afgi7A?g$FRJn*CUNPfy_(GximuUoc9! zD&?Ng#C>2s$y#%{lsT#J{E^9{1w%Az1?>G%+5^v`K!}!O&}UrOPxFZEna<I~fXMp= z`gqHA>|NEDl2Q85^CDBzeyfk`q8?k~GlW~EEE6%%9+~yr6PNDMFNew30f(Ohi~oK# zf2H(HJM;O`dII^ytk;msdH=YB!Vu?HTF9PQS5_g(Oqh+zIl)EJWwbIuM{l()5J#6l zMhp)4OP)t_n$(5E>zJjjup!oSxG8NT)n}0^`}+%-z)WEA7XKQ52KL27_V#@b`wJ=< z^pyhcLrPl!Mn>Mpyvw%HADioQ^SQz5qMof85ywiyPF#$#Z0>$KQW=z}e<n)bB@=wj zOSxM<2?G`a3Y_eME#5a7CGy{hwK!oJ=wkjN>_9%*pqOD?Vr1VO*@HLEE^`#^U9?p< zernt8AlA~rWCo{sB0_B7b7A(TVMea=Bj#xS8Wt>s&355AlPi49=k|>u`$6cq;LCG* z=0Mlg4)gCcQZAqxKc0u0KQlWH4UH;@ijI7Z!S9AvPjcS`451m9;1l8wLw`Xn{*mP9 zb&&j9N(%~^3J%-HGyRFqaraHUyjDG~<D2k%6W9(OT(9w6QzgJH#w1gob-q5;lc6zm zAqP11TUSnz*RzW+{+%tb0<-4<e-Ezx5^p4j4=;lLhcq$JBWtdpe4>t@0GGGIl4W<G zXDD+^<c`Mj<P{cLNNka$M|A$u@@XppK%q%==*-75)aba(8|2rRSikTl`d|4So#cYS zJyKfP)G-UO^qWaD?=uBT;-e-iax*x(CL=h0ISupiPsQVk2-wZkWOC(AxQqg7)vk&h zB`DA=HRlPQL#z$(c*v?{({ivyZhiSegT_B!1VNVONEHY=zaDpucl_D9WT|aS?Q&5S z;WDf2FXjz9LyTw}UGUXcywR4x&lXx{4U%d4XwGhb!<6R!uf$YNFuKeYg6L#Iu7hkK zIm9m6dqls34BprHFtmoZGFMoQ7V8ezTE5_Z?#@k<_^Y{|K>!AHxz!n$4QTw<nPxta zfHda)bpJ)Snwf#jGDelg@<53e_9~FQ!7;ebW>P@sPu^<ufJ2Z?1bB6?TJfinf{ZZo z%ysG7Gd3W#O*)Bv;bX@KG;ni2n*dbcEA9)vY7q2Q`(-Xt5cyxhMbV@G2CF>Jutnfb zc(WH}iN}Vs`dP-C`{ME0EaGdsPgmbft+`ND%WQ6*t1SdJglcW6u#UfkQy?CDvpC2+ zs%fzALkrN(B(Sd;@FN0k+=uJwOiWeEH94w(5%wZ53qH|RrSD1zxe=^jSDt;gHY0+! zmX}vNywYhd>&EG9L<L{I+?-=!bPMs?kr=BD#}@fsWc9D`8rOIaUY?NQ@}mQIV%i95 zD7@t!)Xlw~HRBXnV_s-%GluF$39t9YVB3<Xq$;O(O6fuGf-SJ?^X)MAGYH>GG4Sxb zx%ZR<ujhKntEBZzIc1$#J=MvZ&bI--w-i5vjlLipQ@EUrEw(_P3}G|~>+gT7#!S`y zo}vq?6w$WO`#n=p4&Y)!af`&f6@P7Ja-B34*?_*!7xoI%v890oY>`3wKgVch1SOYv zzJ_t4;7v*WB%nSm52~J6>HK#)@xk7l@qOX1>y0Br8+MX%E(vQN0~Y{~*@^oh<~n1U zsWzn2@H@my4L>cl0e=?X-?DD-EMm0=ct|S9qsIJ5RE1hF!a4v+9<#y@-QZqIG|K07 z(d*Z}O}0!=dTdYn0kJE|{lywId$rAvy1bP3;S0vIoq`G5`+s)0t$T&x-GJ-c=>$$E znl_{I4qm<AdJUD|xAXGe@&n`)5T(;V&SxZG2b{QfD+EViNj{_0RvN=7m!d#N3Ul~E z&`-MAc2|Tof{>s~9uUdJ%L6dS>^wv?x<MX~C)>Zgo&@^@oL8w(bwL>`>7m}H^PP8F z9qVIf6f7x|%YEaTLe;E53WvN9MXQj1Efwl^PaR;#bIfpcEiqQT;m4C^Sb;k@${2=! z!XRJDwj^$(TWt`0k(V%DQza-j^|7ELbo7hJJMWfLrl+AnJ6_7JaPfF>2e~?TpY9}Z z7WL#jsxAQa9Um*ybXDk<_j|kU@YTB(ZHkg3wSo`+KxEN15xmKH%Ay7DwU*XI3|*MG zu#xw2PShvaJ+t9z&&LGn9|yb=l^e}7fzB-{6KFuu6X(m_v@HKL<(%$=VUr=%2W(Wb z0kTh=XzbHRCa2ZbUS@a*E<<A4m`hQ$Y5pdNaR9!*UG1~UTX`Dz_5AACtQK;;=E+EF z`0i9v)5M^*6QyRkFY(Jh5?B23lNYX4&t!hlvpUy^?4CAUI)#wmx@93(#<S*rG{4Yi zonH{pdWEN*j9&joyX5Y9ITiM47gSEbT47g~pKkhN9TS9T_#OMqN@3lUDlen)OJ3s! zwHQY=ufiHl$6h-B=+DiAF|G#+gwRGSwWq`y62=&*JpjLeKJ(oIWSisUtRzxWU-Q-r zZ3yFW5=iD&9_Qf_yGetu<qP^TVq-$syeLt%4~2H;<Js7HHyEYUqypIwr9!Dn;jF_< z`0{W6wa%X}dADuznio+vvZyKl8r~tUJd1@CER9h&oUh2vU=j3*jQpy%?coXUKe(yf z!IR~Wz0mpW>DX>;b4@LE(e@HWH@NXVO<niqj6y#8j#S1Wp!JaaEOWx&dM0Qo2L{}~ zFN0_Kds=q#+1XjA;;+b20)ee!#gi*D+jQYt79xrmZ`#88obDeVlK&^G)z?I*%k0MW zs)x>?byLtA$B}SIqKMX=Z0}1rA@x{8!x%Y^BKu(BL;%oR)tI9y2j@QI_s9Nvs<3u| zi6Zka^c-RZ+Xy1TOE`Y~EQ$;;xcLf`SqSeX14^o-hE_ys`bjuy-uL@_@@}uMT)MO! zdO`5q?nV>oH(^GJ{pcC}DiFR7xX4(La~4BnKr=-<1*HJdX**T`zADYjQy(h;=I~wi ziF${8Yl!XSn|P_ywb0!rv^AJz7(T<+B7|{*iusDqBJY0XSd`mof3S7*ua91DBbIT8 z-V56}cM1?l^3G8?_h>()SP9*vJT^pJqL-Y#i2hs-v6%f9q9p(Ok3c*O*J48CR^IzO zvktMBh)W1~JH~}>7e;_Gu4zBOo+KdgDd!|G#By9tjm1R2Sh>cMx(P>`DmehS80JQ{ z+n?t<gA!OJTqVZ47rQFfR%p;>*xr{O16cFw;}q;bV*&R7;#HHMw*Tq@)c76{a8lsW z9lZQ{ZPN+g?D2Yjl|#3Om-ZRaZ!XZu>{v938g3@tl~vG$zXy}%+atlfv!D~%;(ES0 zim^8g&qJ`HJLRestSgTqnl212%YLc8|0td3wH;Kr5yt3!*xWJZtvK)Xz52Cjp~BjL zV&|4&y>}E;?8a%R=<npXbaYt`2L8R|OToextC)X{*`;U2OY8~M)BGsyPrkWO^<JMe zXW;%r!M^WD9$nZULH7f~LRuo(dfU1q<&R)}cR&RmOV#cdu{OQ~`LwktFsO_BCr8sF z0pAVX))yrM&7|qiz5Oa%I+Tnh6x;$kOBO=6f4x{1yl#F#p0q06|0JsW7IGsM0F}Oj z!oTzp(0R~?|CO66(7X`_{2OR_kw{%0FueO(?%qbek|l@?<j%d{GG2kTA`Z5fH|L=G zao;NTVYb3SRm!PxH4rBcrrv2y#tBuxHP*rB*~uHqlgtwgcIQl!eKU-gZDe+Fw!0Np z-mWqx$KKZRH&Gh*rMsJ*G?fC0WaD25t;g6;b6OeS_f??Ew#Fdkall~u`Xj*=vGFo3 zE9THYeuWaMa*tQHM*l=bfUDk6W)rkT8dBzvAb=$v!+mH-A8CwzgG)@DE<da5$+M^# zflwFk17u(~y|+bv4RAyIV5xzhhK|uu1_;OMl*f^9l?rOg`8nLu#yx2oGo}*QAmbj~ zGyPk)$?0zRB&jhCY-l0-MaivXS8eAz`-znPoNDsFTh&G*bH99D@fSXvbtV@dx*i<k zFu{%YH<|2w{;-MlC{dHevSqbPrqny2@^<rXLW1WHKLevM5h)*r{@;2`Yfa8Esu!`? zoROe)oS_D2`@>}s13T?2_|D&ArPs5n$>tgj^UhE%WK$miG_`*$p4v>5Y?QHl7>NBK z`EQIwiF7{#&(cDF6GN^vB41|pH<(YxR;bPJ^Xc`59t(eHN)8EU9=_s2O-si25wd)? zHpj$m{t}|j7uHmTxvW1JlmwPPmB)U$vtF$qR@s~UJ&wE5lkrTRDHfmaTT}U=wXZd- z{X|gLz6SeRZidTNcVTzHZ6_x5FH^kR|1xW+_Wf7{%8_XH{kH#j>FL@hOK+uK`8rE; zwPS`C&k$i7I<5K{>CWjmj@AGi8@v!-Xp>ns*0af0I>I&UczA=6$NNZ34Qv2yU9686 zA#q*v5T-}|@oK2>m`uvFCvHnZnE#8)hm%ID7p>VdU8k;7D@F4?>07kxO`f9v6GLhb z!NUod#u20QJln)n_yM2ZT4k%c=`9yi`5lPDrlEU8s8Jjj^34|-cmQ%*qu*+@rdAb; zS=Gs~l%eOrY4eRVXrbO0Lbp0>)4@4>>#n3mNQQTkYGGogRUY!W$ciRqxt?hOn9}3# zC{@@_k`ejfef7Uo54Clz2VH+cuE}xXN1%vrQ$R*3s|<dL(~tLkglGgAkL&%TbCrq` zzNUz<mw!Vf<!PZ0=nYW?4IGP+J<B|h$HcJUu1}$l6o-^^7h)XOkI32Qg$o7BVYpe` zFKAorK2-khblYKnx4yRIBkwc(UkgBGue&rG2OH-QzVN0nHgat<?ZUgT{?RP>O6@NW z;n}#f+CbwxPW1@TmXrE1que?$I2mR!g@G<Z-#9|n{p0xx%Z6%+JjbEI1t&BSo-YGW zU^IWQlf2%$3L(TWjSY^xB89%?OC%G_x1nq{%$Hc^vhI3p{`Q7w<^HSus81)I*Vl60 zBIZ-C_cQ@|whD4X=hmGP9;%-V$)j{sA+K(3isxiv;$ldOi6~=O+(Q`i{2^J@pfj>A zg2oW_aows{)kwzau<*YPVi7}#Q|{ao8Un_BDvRcqUJXAzdU;*Mgcok)dVgx3Z-vCb zA^yH*DBJ9RQ;RkU<fr(mcQSCtaVcwp0EIQoxmvn0$yawVr84x5oZedjkD&Df0D(Cm z-}ka-G=vxcF~?sLgtKqs$NFyG36>N2$ITae@25*#6ZQ2@v1U3k{15=T3I2L!NhRV= zR)BXL<xJ0)orYgSv>3>Qbe?GQ7*e&RA_)JL3%EKH`PU7hR%oGl8<yDc*+;;Un?^Ir z7}{yY;p}r=p=Slf-xYZWx#J*69)0<!VpKPCrJk7RM|qdG#tq%AV?N7n=^`}j3Ns&l znD`##zjsKnVNAXmwN%LbuG+KHVSDJ<+H}<O5KOIX#m4$n0ms?JhLUpRtr7s*;JEHl zB-m@UT%UaA`M;QfDp@mMON3}+%@SLLay?0@b=63f`R-%o^%H1d-F3#+xD6|R&V$Qd zWZFmx`$%1d{sKK&5|=~q%~`M6IJ17o8+26%8j{8w=Ft68D#4s5se^o0_%?!E%4xJW zl>QXqG#~Fmz1oyX8_R{a-g*R*yEkhbS=%wiEH#;zL;v3wtwcaaPsDDAy<oPGuvhhY zokDW9-*MAKg#}@{NsKismllX`q$cv7K1Ved8mi;;Z#|0hd2nyD@DhFbl0Zdwx^8BR zfWLn<>1OzgHd8dzMtFY6PC1pllbaysXdL*>txENaS;qR3b%b^?=#sXp&9r;M^I1tT zO4MU)PKWd7uPo<(!}<}Sj7iK&t;rm`>bd{eMr#2sL&;kMxs-K1`i<Y?I@pcU)YhZ> zHVPZW16ud;vKX9EDnK(|#p8xqaw>4$Gtp1h!DM(>W1_ce6OFnH;NATLMIewQ|9Q~p zlm6DGcLI0eWWm>r+3v0>En&s`^L`e~56QWA0<HEFbV`#lp_lWj|3*#}c@HYU;^W+H z*$6<|@Mut#`DJX3Odp__g7-?Q?hV2;x?N_GtJvt8+Ez^#0kC+#^BuDAM111~;9Eb7 zzDo~_Ulx0|;oXr4JMX8YX9I^jZzxV`^E;x4>(^I$Jq=XTjLc4k1X-i2$>@L4=NoE~ zvf8)d5cKHl$sYZCg@k%_!n;ZRbn%P5iQ|N>9U#si#={1XplEhy^;eSImAQ~O)zQ5+ zcY|K5^q%@d{ui(iHQ$Z77U+8{W&=g&&v!xf1)>^vQpb{_k`4uX<F%H)>D5X3uf7XA z5oDYkmCM>kwfp3j<VZ<333=%Kk>dr4(p{K0zg8ZrXnY8j_<tAq;{HS6jUO9UUrcZU z_}lOESCp~`I-LcrcP@)$FTe3-HuV83aUDK;m*ciDW5vj|^4!CC(G4i&e$UH9=z*mj zS8RjH(93}?FA5LBU0*LRso=P*hxM&OHjt9+m8IWwI*oug_e4pw{e7vK{?XE<b;aJz zcf7ae8}u=#;oe4iI+?(GWaqv&_Y7138P4~3w|3QyI=x7`%0&@LK~L8<BgYf}_Mdzq zxgp-G!F-ebtANCqQdV{DSh7$2%5^2s|HmTX`)uIUx10Rna5VGh8BDG%r^Jh+x(M$2 zQ^%%!O<w!cI+?g1>B_z40JDf&IoGPh{jwWB=+b%>PN}(UIfzCVJ6^ymQ@nT(t@2)8 z!T_kSuS|REjOvzLr}tdXEUBUk=w2sBnck!BjvjCS7?5_O^zBKh+s+~@yzT*(Osh7; zArx=0hjH^#f=S4X+i7rn?lqeRKQn^UoG>pzF$QedZ6BaiFbao34LDO=VZb<zbAh*w zCBURbr&rwX1umh*B#{=44j)8&S$p)$Xq2)#X030SlP&B;QJ1G^N^BLpy`)<3d}R?j z8G}mgLNV>oOG0m;-Ka-aY`gmz4BxX$oyZb9nV<@TF-fC=3pc0LsNyGdhBnz)h3%H; zgcHYGosFwJjS9N0;^4IGdB1^NhoIZN2|h#8x5BQ8ZLewQajjW~sz2lBN`FsPsUWen zuI_YS&L5(#@P*Amr2?Y^1F%EK>=J4gsaT;+zUOo@8fTo}*0~DIqqZ6<Tykmh&cq1J z=<%*kQ%<}s9-l~d3tUdFH>;vos%Vz8s7^mquXc}iqlRc1jK*-PaN?j_R|MSm?kCW< zM~*2Ap<nqp;)I@R(*tP)J0~P&IFDNTYMY&FM_w$K5-f%#d{qC$1tBLCWu2VunRQio z@yLD3p!_h2)GqVXeNZ32iEC$wpytD1XxMW0jDy5`Sr^9h@&c{#uFVWH+kogKvf=!P z;|xQ1hdCV&$UCIIm#%OS19qRD{N>+-Oul*c<%)ye3s<Di1JM(7XXycaA8==O8UJGU z;W4{)RD-#)6+`gQS?_h6>;w34uhmVQU4Sa|@TU?12H@i*Yta@a!?T!#B<s|s0iN@y z4s;*2*)MV~X(|u>ee|dwXt=Jmw_@_~TbcETc?XS2dMoC5IiJ>9;@WzX9wD|INl_u? zT9C<ij)Y1Ioja6$ejLdFCg-n^mqpKDlD|l&x8pZm3EK!5p3lM+&_kP=S7)p0vPp9( z^MH1K&M#fx+opvXWOnQt>Nf^bxmDBN&qrJ8>aPN4Hqqui)BEGlXSQOE!@yCjSw6H$ zgVhgPYK@*W{PA|^d*pJvq7Egd2{%8R)Ry&UXO=G*IH=c{2XBimt*%e1w1G3}#Yh-{ zWs05@?(<-U<{I5=j^UHI0;PC`VhkLJoKaPgl>fD3vEWKC>%!g$Y!4ZM#FtbqZGL-! z#R7|9sG%>T@7R`Na}=rCf@U3xfgR(po>x6jpZs{y5JyNN`$m{w&1Iu2ilXA@*G78x zlb^h{hgNg1{nsUabnxYXr4(P5VLaeihDl*q0*9=9Oav@}lBOQSmB=dyFnLS&<IAot zTp8qMi%exdF<km0jtn026{*xTMdzoSQqhJFkr&4zmY5Iu($iz8aCg{%`b^(Sq5+_O z7pD=<u$I2!#H#Xzmw*JW{5NCal6j5Fo-}L#y@@JUDgb5l1I_>|Yk@*=nOI=yh~cD; zPjfr4UP=GDZ_BypdC1lHs~Y-^An0*RofORGUGm@dV>)S#EJYmZweJ`&7l=Tj4an3= z+l1E;eEEr!D()dh34UPAUr$LO?hHMVtXXvyAc_t}DPqbJxb|ER^=2bZtNR7aXK@KV zfsYhF<4H?wzyq<Yo^hP6RDPY8DG_4D!5oCe&_pq|<hrmWMPYo;2fW0$qIe=t;>w{^ zPhf?PlQ%aCPJi>hT1f+>2F&c<B`L>c3-7D?*)Hw?S}v$sOgd@1Hsj<T)N)nQ*UYN{ zs)5CxVdl9$0D(j4qqW~uL7V~ib_d`cL0?dl58*cd=(Y?4EdA+g87Y9u6{ri9p^`?U z9uD?<Dl*m<3gKgdQ9LF6`a$o~gB9i<z~g924Z#Q%p)P!@2N2oKZI%PrJgKe%WI$LG zOrLCamQ$E7EGmJFp9X)zJbM2gf#H2Q%czjSXaogKfST5Af1+xGLuA90y+AIk#?VH9 zEdzR79O@5xlY?3c1!QeT#+$^I?5$|;cAGIEw>Xd*QKI#!`~$+#-t;4)3VgMGqs8yY z3fqV&!$Ne=U(^FTQ3Wzo@O1k5URonq2#qEwOW0Q|A50~_@qJbp+aOKagYKA$+r<T^ zRPxgC_NF-|!RNz_)*A9m23W_%XguIAi_hjbdTH@oQ4pxFXM#l5jFMQkEv6bUvgN5D z($Kdp!v!lZh0@gHxlZ(pa=y1<a~Z_Bc;Mmg{t6jevZ0+0ehu`;;^^Q0abxy$c6Bh9 z5n|n<it~gL1d@4SP6+=>a|uk$gbj1FjC_A7!_2>10Nb`sMLv7ZXG?*Zj0IvF;bDMH z$%veMX2HxGJIUn+S{Lv+fSLxk3q<dfpMsiBZr@YOmxQ#7{ak0%|HBI`oJ*ya0g{r! zg?-H1{KqoEFBwA`!*y-NS2tk*{7pOfE*T}{ZLMH+Or5r^LGnnP4(l67*+&*ACp&W4 zy;N0zx1ZOt6O3Rf<lGb=(*h+j_?;l{@y*r%qhp%K8wVjcg^Dx*VGcYf?6}HS5m*QT zUngQAoF+j&+ThMLeQ6JYBmjtQ9yf~(MwvG3M^v%${UzXzQ1XWrn8~#A8ybYL1C(YZ z?y)U?)-`8L4*|uWousbT0GwL`Bc)+*3-gUCUw`vpGL~~7E-P$w8sjB9i65i~5rntM zdGOTi4h@Ex*8<!U!Fv(7i%|GbVXh8X5)%o3<wE5B{6-|R5UKYWRE*3M+IR5L0uJHr zuAu5agUBjm7US}#!1g9_?X53%Vvzg>=hcw-;>{c^gsn30slR2DmMfUM9T(^e_ml;( zIl|n4LfQppsM_1xFDXjXFmy+B>i9GzAQ0@6-h`|MY%$COy>34c-?t6+mZR9w_}15X z6&eB3nh$p4q^1t1Vi@`nO_u%&B=E0ifP8YN)^cIXmZMfkMw=Q#YH;V}M{TsyE;Pa@ z`j2sW0DOYnQwLVl)z})!H$a+?0><~y(j}`$52t(SYVf%2{tvrYdGLEbkT4CGj~2_Q zEU@rbJm9j-+=8^4!IuVVCK2TWN^^gB`ZKG?91D`+K>(dxzJN(>W)=}JKn4N~I6D+8 zskWUT<}8*SL*w2M%lce#J$(j?jvgyK!^yzKb?Uec0Z~Yw+1#gkgP-#`Gk|`CSTA6> z%J<NrG>EXwZPNLH=ld(RkEWN4>dt`uC`EDItGq6-|7&&@$KM{<(fi<V2k7tx1M>vp zQjP%^1!&idy}l5_kvsIhDgs1d+F}cjYM>_kY%M#X1{h??@CO^hr}DJzP3~Ggt7S=U zE!J$p_DO19F2K17IiZfcnAxG6*m9s@>JzzoHu~e&YY=8Kj$OUj-!l+Z*dvYlhrZV^ z_16FgkLAWb13;s#by*q~nrpV*OkXd1w?fntOho8oJfLTFIe%MG@>U)gGt<@t(89dn zl_d+26`<CXIsH*(kL_$enJP+{YUL4b2rBI~&qZ&NLJ4jJ_WY#R(9JsJ(*|K09ON^a z10j9gmHII3+;wX$sC%wr05wHU-qQC_0DhIZETBMwfBd8>`le2p2bOTF0Gcg9MgVGe zl^}d((4Jh>Yv52Fay2X3D@<AosxWi@2YH%uiMI>B{tX3ThEGB_zv;bQY-<1~PXFZW zlyfMc>BX{eyU*=VzWy_v8xN>d%15u?%{Wvoq@0IHuD)><>HjthzfONWXbED|pE|`t zM68?B-Xxgsv&W%Au`K)-1fRhsGgWhPQMknLPtePYuLzM%|M3u=d5zO*QK%SpU%(Bd zx;}5br#5d<y@I;4dZqbE!ySNw1sHQCt_~pR*}n99po|9SgvSH61WXD-78T%<d*;wb z9x-|K1s>MB15J2AsSLi&8?(_@sI;7B0)$t;0)TwWt2Hn%;+s*NyaVs9!g>dmMdvvd z125EJK<YJl{adP&_aax~D+L$~uNyE7<ba`#wU}2RN8gU5=RlX-)k!$liaaO>F9H`_ zcJKoSVHAr}{EsL#aS(~UG8jOU!6PWmpZGLDE&O8+S~eEqs!kf_2r(+Jy8YnVQ`+_e zS)VFIbN6{5o>bPSS(*y-#p!t#$i8-%S>{F^0IUug@05+z=7qgq<peG=4dNiCRn-CQ zA9IfKu^3!xK5+s_u6##DGlZ8NdChFl2%VJ}SRH&j+_4cP$1$MUS6|=VF~c}lH2{r$ zsQ~rFWsG>{ek=<qKA?3wI<c$~l#(o%a5*!Qb0NB*8qi^EDdA8Lgkm9-eU*WdyJpGe zpeC|bh-<VzRvz0sRS>2cmIZe*pi+v+L-V)fyHRlIuW(p$wloVD?AWUo@F$Sq@eev= z#PIB#Sb5pSXDi#MA9z-x^L0kot^+H~Zl`(KV4j}bP_mLcY5&DKuG9sUi#LYaUhPYg zO-ps%qgKAxJL_(bt8JT;$fRwQV*nUqPaFsW&?BFUq>zZv`<)M`W^r$Rz6`oc{p(@1 zXi0NmSTCceSN_Z%hJ}>m+MoR-gn#NjHSaTrGxE-6L+S)QNq(OvHUHiYhv5OQB~H6k zMB1sTq+IB!@Bz_^WLjwYZz7WLd=A*v_g__%G9^4~#_mtxr`#6)5;_P@E<lg*%Psfd zSJ%^1YwsqHhE77%8jN{@F{OzNg?udsr#IxCfQlXWViwjv0fCFbRGTa1x4qDlb$95G z)Pio4E+S4CpBZ-bO9g<jlSLHPkHNOv4onjCFTl(}!p@ylB9|z9vTrK$4rfjR$o4en zsC$cI+D*&g*D1eD=#2rUe9}qJ(0<O9VR=a>lu;I);oD6;L3?HytV2oW4-OA)=hZ?z zzv(?bxHn95z*UT8$uAeVfHaz*5pLrC*t49?pcJZi?)N8+$cWDX-9FzWRwD18Kj$11 zQLWl6Bw4CeU@*iZ%`K4${9S0#!6x*A{|_1=f4rs7U55bhpkkmBSRfT0-i(L`?W}+5 zk>rKx5b*W#xj%t`IEZftLMY}d5JY!lEyB`PFQ`fhn0$n@73xa>wRQ#^?=X$>2Ve7Y ziwIcPDaz6yZ}84zPArSsgy-}<EA>Q@7Ohn5F8mFIC4-!|B4-;-FfwVjcG)a~9aWJv zj;bG>*ouF7bk7V8bvz7t@#zDO9@{?}^Fwe~zF{*wFrqF$F@t;r`QmC^SgVvGtn>kl ze95p09%MI0>cl*#FHY{K9i+N*^GXcp7`Y!_R{}V-o2gsCVNg~cws!(<*nM#z$FX=n zAVEell)v~F5}zfpdC3Huf|T#S1-6WcNSUNM0dU+>rGrrQeC?yj8@irAMM(#5f8fEc zuxzdk+<*z66SgIv4}%fI5kd0vo@jh~!4&6jfd{w@qnLTrLDsI1V+uNY82@KXJJc`G z4sLwfrz;t(Ji|R_<Vr!cAkdLHe#aEk;yq09H!les5m}v%K<CQ=+{lfbClk&|S+wN? zIQqt<F4#nk>(F-^lMEPRIbma!OHqO^gWWi>EjYhI9LTXOUH}|{8~dPqZQy;T7mWD) z610QPrts?m!pFiUFV+eWSGb5K313bE^bo1X|J?*#JEwRhlfgOvc)goNC<L8QL^O>! zUR=h1w3Gtk#;>wKI2c|5WN?!iM;~!)@*Zg407&+~jgZUkf5n}vV21I_v9t5cg3n&6 z0ixGw*4bh9!!IK|sB!WxaDk<au#I8u%{zM5{^ztRVHbgr>ZF&dJ~aSzf=_+kl>js5 zm$(6)+oMlm(1#!CM7(oDw1a&6SO`1;!yxoNQn(9Ulvp&3SC+;E&~L@?l))c3^)l8} z1+WCzRBf~%_#O-B2_o5IhqXnb4`5|t=b_^}&;oJcvS$#&hq#f~tkIy^Xh7}a!xzvj zs|8_(*nAeii_X3$3@_*5T9S&(GB|^`|DqfVQC|r_NAO*`ux2uKjiV8Q6fkE&r9mO4 z3IGW#8tYzSxTq{M&?E6lu>@MY%)2}hQdvC79z%0L0f-E|jx2FKz<Zz5Aq^QjHS);@ zv=YGO-8o>{;lvZV!24HdipH6a=g&0>C_5TJ6rr+c8<=_Mbe#^cOvbJ_85I)Qqo3qv zcqFfD0PieTn{PorXK=jWMhty5z&d~{>=nT6esDY!D-WH=$`cp^=1ZtF1Xz_#x^4A0 zOJO4Ld_oPd5X}%4D{~WIOUiTP;f*<Vg+KsV@GN*)7oY>A1W_<NjwK-%v5uAZg72$M zgQbT9HCbApp)tS(-s7DmRc!<k1toc4GsHs0{GI|2VA|ifU|JyRU3D~9Na%-OABcn{ z2xWWQ92SiLH^}fP9YY}3ig-f64_trZh|woxP-=@l@ogi}>}Dfu96$-WUJd%}D=`OQ z#Ak;svVOB!cF{l^^F;{glfg&8^ea!WEs|eq0EDX@2zVcdq5mBTP{zW+^n}D$qE7G# z=`2NQIhzl+Kt4Z(%nkXx1)MoyJ5c`@UjbCk$I;%wXU2bL?)X=iNtk+{$RbO=%Yv;j z^Fk%J)#6j1cFCa~3mnG=mA)X!V0>ya{|&VBj7#p90PywWfV9U6Cp)0)?B5d8pGna{ zd@*$?wz-yk4I2}*U-Ju8)wWqK)LV4uuH*Y?=a%@bw)jYoQ@uIYhV!qJK9fMyya)gd z4E@dAz-xfc*RMdfs?aB^NB~#PduDR6M|^#@dr>Ap^7fs);r&N4mj9#ZsvoLoo9N!6 z>qWY|8>Hicq=a-xN~5GA9hXiC=@3v*x<MK)jUX*8-JQ~KKfXU;_lMbM=b3Y6=A1`` zON2iO@S|Mh2e=ANEBC7OZ}B-eq0u)&kBUtPWEevCmk5DTE%`0No&^)}+z|~9mA%~n z9A%R|Z$LY#&XU&7lKj9X$J(;^!L;iMi7PDFuT}<tU`*lw3I@rA(Usrz8oJ)I454E3 zn1Or6$ogwR;Ml-hXRilvOt+#tu-+yEYD!(~`x1<D6Cnwt!Q*-#DBqRL7tkt(&eSdA zAcRX`f?N4XBgG(lS;GN*Bw=K0amy~_emG%}<17w>q-QDuJVpgZdXp0b;f3(C$XC`# z0M8@u{!$>?uXA;&=Ri67z%MC_BuoT`d>eom1gAVb|MtNyW`^Yi#QyOupiTK=IGQtk z0Sw%TRh0*{8ww`FZGJ-X)URs`E1&}XqX-&d2;GFnp|j2xg}eznw7eGyKWZTfWLY!A z=QabU>-vB@##u8X2;&F<M!<FOe`J}KCEpH@vc7;s5t0Wmz*>bJznGPHN>{dW@J1RN zK&Lx<OAL^*72j>sQ_!8wsH~V2S3kW`46Ta>qyo)@yvN%()pJ6F7liY*4sWOT{YOU# zNr-r+f6vyP0Wy>mqTTwa!*MJCsfv~|7_9w1kAc8XU#CT^xNsTbK&p#){oVWlz@_0k z3|Ye-uqh<A=MTCL`i+jGTHpJQ7?lBwG$2{C_nYqs<Pr^^6vqZ|2{MQPj*lIJ{ZZ9A zl}5DQ@2*O5Zd(A+$I^%yzgwpLEP3Gd-s7tb@IoN!MpuP9@xP|tn0!^!mn9xhI&Gbi zeKy)ZbfefvJcf+<;dj3i)t=;09JZsyX(=-&U%@Q>G9$^Dt^nv4GYDKz=v*Xw$ndh) z_Bl|l!tS#c6O=%^RJTEjH}m?TC3U5qr9>a>+Rjt|%P@A>H&%X%0-#txDVO(9`68K{ z_FotAkJD69AyT*AgMki0jU>O)t0God_wkE<QZ$j5rT`R4xh`^vp}Ys<bHFR3SI__5 z#hhYw-;Y5c2-duu2A|fPgHP~%?J5g|381SLa(b777zBEugYNPp7*wAV33PhzI)V>v zHf%K;JU{JI@n4n$!;i<+%l|Nh{2Xvkh7|pIfSghq=?PzZ9f^iYf4EP1e_?=kabd`V zRZupGjFfwYUYxRm3jqzAS-Rj#&V2y!`8(OoGj0%b|DqFj2QgN<D$L^X-olVr{-s}C zsm({=qE}$x{Y%4b7B72r<f?={slm$T=0d0J>0*i}rfi`@+IxxtRpw$~#XvkY&@W*3 zqu*VcjvIFr8q#iwEcN`6Qh^4)UrX0eqkl9Dgqjo01WnnjS!r(HJTcRPs}k5RDPG;^ z%UZ=H|L2EpWlMsYlQ$~hd1g5+8mY~IO$E^0Z(-}Bw@p!V16z-@3V%O^bRPvK>Gk}Y z0TGDHH`?*e5$3ZvP2P(@Rt(YnslA^AKW+@Tsn22;D)p#l`zV0$!aW&LM;hSa*3O#- zNYt+Y!OP}nQ2O|fJpWQ$pGKURNB}X^K|2W9`Z&cDxg6MZUwGn;37l@H+tVh=E?nt@ ze?L_Aze{}k^E;aSD*lL4-k)eTeJ~waqiHN$_rwd0vO7?Ww{LUfGXtY3D9I2Czc`Qq z@>Z)>A+J0HpS4Z`hVdk?+9d@{RV2om-<MF;<TmY{>67sYJdg6;`Ru$U0NZ1(*;_;X zf;Ppl8g)IHPSo2N^%w2`Vi_M)y4a_A-zIzVyak^zDi>!YXtTXfivReNQ1g#xVC<Am zNqAqAe2TlWSgQI)9}8$Kk~Km%yfEs#L<^KnaeU@Ymcp1LZVmA1z|hBoezT-uvQGXN zJzfUH?eBX09c>erUt)n(UCk7&R<|YPh>T4^@{Anb6mcNj|H~Nr27IdkzWG1kUrLWO zrB6K`NN*uZz(o-QoQMD%UqM&P8}r-*QX1g2?lGf$#`k75F&^`cS=fslfspoc^O0Jk z*quI7td~BnSad~Kpvc0EDcjQ#_YdY80(a_MP<gT5F7lAgX4h+7;NBi3oI0eJR0%@* zB(kVQ+wSMYg+#M?KKYb6Io@%=k>QA_CtTaL)gOnd@Vv)<g^dt=Baa)*_?=ysD#xL+ zg>lCi({DI3V2%wsLT|xEfd4nNhmyOdl7(=?Kp`%Bi(6`4)0trLn&Mm;xZvs)Xyg0& z0Z4D*5Z;mgn3LVcZ_FEWhLy)c8Hj87{<}ov*Wy=A^??N)!)EJ==x~ksr6Jy?qVA%; zHWg3-L>JW*Km~05tJE9@ugLus1`Lf+eu?GD6oR;y0c6YCs4w6~GQOv3-Rbq?QPlU| z6-;}zA@Yx~1m@_ds-ftg)gwhbs85E5A)7Mee>&AP;3h<+#7IQX8gZ*=$4i#j*~1Oz z)l?<pmHmD{tn?rq>^FAu|E~Q2lNsH%=JRR5f|{Lq&0Kareak8$Y`5jO0vnaMW1X_h z{V??c`6j-4@wn}dk=2l^^+{E(nP{<12$I+Q`b6FY^Mo^^0ebyeAzOw(+AH^**a2&V z2`;F_tu?kHk2De0`6_Gqotc0<Vu+^-Xr$=fC)Zm1S$hRU&if07s<C$-{a$jjmjtk2 zV+mtC@g75kT1Xrk)W||v?ir>KVB7zN!bXyt&fd+&GEEZj0ZCB$VWjhX%NB+upp^pe zaW)NWZtMSxn`ImM8z6*bpbnDHM0r^PRSax#<QD77pHph%2UY>Tlw{k2G{em;r*&qy zaHrW`W}dM*ZU0ryJ<C4KZfr6P5#|3!Fa%(H7`D1hU6Hu$Gt40_FW<5>Y`lKw`1^Kw zF9{gKMm&pE!F9C<A!)ia^3M+#{(It4fa-Baba#xZknpB=n&wRG6bGOL(uZMr&54)? zHSJf0U08TPOm{Y0nH0Ir%h+vCpJdwT7#H#r-cLzqlgg)56B)1|F1+2`R(6aip1WkF zxo2})hou*KmWIwh^a<a*Ksxc8*c&;T4&g;dV7(x!kF%|{7V*QFRbpwt1=ES(UFy~4 z{{S>>_x7;znnWK4C*HpiX(`S>qbDe^3H^i8jap3XtuJ%xnoO-V(*kxw{|0jXSznqQ zI2--;O9b1)t^7G(Hz%UZpR;rbzR!Bl=d+KMXX0zzZJvE>5&{j4+VAB-`S1O=Ya$_H zULvuf8G!?;4DQb{4e|n)cpRm!Olo71u2ab|O@xJUxZZRs^V1&4KmgA`(om^?gQ!^m zL5h(yAlzld+E~f-rtQetKr(on@A0M-_|HKKN+sKofC6J`w5-O|e+2WC0wYxa6$41q zwCJySA{aAlJSWzQ9SvyLSBjQf(BAt(Wxg{sQvAh_fsx7hyZP26J{-YTXhAV`Cg`cp zzp8^Iuj$P11z+}0Lo9wBX>F=SsEu1*i50~CkwZ4gUzh$6E=y4g|Ms@#N6h1+#j#1M zj>tE*c|CW%Gh9MKJ)DgH6n~Tb{c4wEt(Jmfqs@kj7&(kAiNjr&`ezVsrBmF$EEoN0 zq{37~#*JYK4>6|4zEyTT=%3h4M53@oA4eHV`Y8a$)(^#JU4TWaKpS>rgbwK&i6++o z>Rt(6wPG%Igu=js8Hnj<Y&R>+!}`nMWG=&04+!f&I;$>oM-|5)*qQBR&5jhyNnwhv zcTM5v5l3al_vZPvr9-4g?>gxHUoI11-LbQ-zjgM1_Mwq5ZtTfr%fLJzz-uy0n_Luk z=~DiHSq1l<y7t}lUET<&yy-BgGhQ~ws^S8zTpvyg-~Bf?BY&mm)oBYd7X^=b>1;N* zz&GYs`Mdno%y<0L{sj`K;f!n%d@Hi-p#G~dwquUwrKKJdOIaX$axr0i!W6SKTh7G( zdlKN7&2H?m_*PX9LOO>s3yTSGwX1~w`Aj^*06~N;@m(dly*}GIp0u<NM``?4WNR`5 zZN%cASq$p5)|ylo040BzZZ~u{ROl4G8L1E<Pa0~Vh=<Tv2`_iM7)Y9IU)+%njPIkn z%k2+zY*j5WDfNb*xHRaP2o+p1THgN(-sUI1S91M!_)DJn;A|gJj)snyNqCZ;|H-@g z(m`z|Z`fMA@-E5E;huh?H}HA(kW<>ru!RKHvwFZw0EAeIW9Qe1FFWSv)$1(o{AGNz z)z8Thr=9J1(V+H0u$2{b!tCKV0Mir&4)QOOM{f_WpmLM6kQ6hY{mTWJ7x{({%Q6Sp zNFkrC=RSMSaSNbCA?-evC;xf*x9nJ9zcp9h_{i{S0@gX$81?~V46_N8$S%LlRwagS z*Us+)%7*+e_0`z50EPY%9ABjs$-BQ1Vxkd#e=~kvo?_84iWIkn1w2G-`o^;@!Kif7 zypqbF!8B2U5uUB!$lG;dIK_pMR`yaShY3AP93KRqNd?COUrdFZOcmZOFY_i_SZ&r0 zR7$D#BfiEeplYCTSEG3^NyCo?_9;VfwE1+*{M&o4Pe+-PY26=*_c7*c?@6D;Dk{S) zh@|{lXlXk2B?B>mBX#B6hKA$N2Md6Yl=y1e7-w~EFt908^!}$<fazhcbK^`7RummC z1i9Le&3<BI1yJe;;Uqz5gge9nL9&wL*Wr+<DdYB1)YoRdwgAuV9@(~Db>sno;>@Z+ zqXYI8_A!(Rhp8WXsW88hIpOO|Ea7$Peelm*&GW-q#SlWYr}p;Wco7dmo{y}F>Zc!O zI1#U@7VRT3+XUFip#!3y*}QIB3_#YUr_<kDrh`Y3ACnpp3cBpaDP?$h$_Z=y>`>|o zR2jj=#6K4Y|BYsRFU~0I(LZfqGNuPL5Le`OXZQoVV#B2|$$wMt0KUDY@D4u;{FeDF z#zC}_pi=a@)3)Bb{l2a>OGQ%B^Du}vk8Wranb8VQb=wN}Bbm!VKK!#N;yJ(507(#8 z2N<wp1$-!L3let|i@GKW0ev%}jTrbqJf&3CbSPDPedzVR%Q%)fE+PxfbmhxO(AC;f z(J`psg^ng3sS+P%4XPzBrzCikkm`D_czT+e*#VY6NesbIxr#z6{MfpMu3YDD@QH^p zP@4@Z*T6OEh|4llgH*^yzCuO(DuLSryXH8u>Je3R3DT;vP(z$H=dPv1w$`E^nu#vP zuLGXi7?~9z)neT#@-ly&w~3Ji&4oHCgs?G11uqz1!Z#RFlg&ual3(l#El$Zh=OM*h zhdhwxPM{g6;5n=5th01L_6}cm1hB*W3w&f_V$SHMiv82l6a9O)wGonbHL&OKX5(K^ zfU`!~@t2kf7>;U{FBb5#K`M5X3OnPC(yLEw3)q+9-27x@*TjHVp<&wTV*2yzEtmp6 zB4N2k55HDM*s@GC_*aSEcR)1>pyX>O0Qyr|umEw=6V``Bu2%C_bfF_UH?l;|BH#PW znz7m3&(sWPx{&&LrwQApO=?$buM)(PUjM&`l$r^0n(tRfr{VWh#3KU5z!KVv$$Cpk zJx1;>l8|8{WI1PhN&$lCReY4OYA{!u!!U-iE|CAE&;d?u9lm`LBb9zIEBfj;a}ZMj z6-x>SLg<$ju=w5m6%O4ALCyRJ7~e4XM@GkE%(Dx;@y@|9rsZooS&eeTx_-c-jC03{ z=4vvCTh2^z+eVof{=0U*g~<!=XNcnK8_RiSckhHxHeaW{QQ(IG@GR&A24{59#@lNK zuj5c7lkaV0hm$_v@GNBcg|I9lzVmv5w)wW5uH*|{dgtgc2KZ^dBEAvUtu>R+RZ0CZ zR|~!uf%c7(;zG?~h;C!Uh+*a+U5d}JwZn&2)FlgX#mi5G|AJdB3c4c0|KX48`3GZ| z%X&6>Qp&}hzk+FXp>{%Fkn87mS(cLq8Dr5lCHAwL-Jl-PnbqWS6)HOa+|@eJ{q8J8 zaDnK$@nbm;VOV+M?elNy9Yc7Zf$C;Yn&V2-15%;de~SMp4RIp8$<t|`*Fkqok^d1{ z$-3hsR&Re|(Ls`CI!Hg-66*~8IL0D(aP0u6vj!n`=2(%p>vfHT1fx8d1x1M8N#!6i zh3=YUw6nx}1#0Jam^Pnca6Zo61gm2|iN$+jy<SwmC@=>qC||H2;Bhg1OUNyvY%BsQ zYM2OKOXDHVOj3IKpeHPPbG`E|Amn^IAp|lLB|_P1{4S{C8`010@K2jCK=eNPW3r{Q z<(E>(tONt3`X#^XgWKCIxGNk7j$w7GSqUGw<g;{1&~{d1F#P_Zwk-U3()g(R9~Ik{ z`S{y~RC%LJjdxGnSfkFaPhdJ7_3H~uhELv=J027JRC#;_nOv+v%DnSAcz#%sdVrX< z27yGj@ZGvs&h{#)Z|?TFmx;@dBml`qA_^2_R4UxHQj<qc>zN3M%5bv;c||Yh2=A|p zjvjaO3Zs0=rtjh%N{Qj^N%aV)I@t=qQo&mBI~H9;?^6<_!<QJpJxVN3A(MKoUc$wJ zP!m%DfBmA5*(PEC)aB-z4i*9I*L9`a&)1~-3Jx5WS)&#b6JpSXQK(&64uti2MZro> zF9<9ggqLhA_Q?5_Y1pQA$E?XZ(eGf!>dwk9cl+kN{d*Gpgs6}+sBC~Z00)TA*=%+C z{1P$w!khWS4`3Q^nUC&2_VKF=$}zp9{u*Da&c^Ah?a?PglSNDqI>c|LAh=AqQG1|C zm&>Oz!#p`jIbkWHvm6$_+ZFQi-&rQ{8a0&E(i~@}W1}*R$})lQ4B65fbf@|<lD|w8 z(@Yqt33%VKyTq?wIdHiXCkj=NBUKB5HKKE^YaUnu6PAVbtnDOC6n8zl_-G6iIr}>> zZ+lCZ4xd{o#kliDHID^PfwEFW{QB=-DvpigpFWepvtEt7!$%0U^nMXSOTfiZ_ri@| zoCpdfuC_&g`b^_LYaAOZ-5S!Na+K(6OSa(?C>0&9v4{gjGZTqwjg9A%og<r5hFUI- zyY!w?c{1}j_^szA*}bemL^MI7YyYTFQOrhUW^Y1mvi>$WlBfTm_5phI<2ev^r{?3I zGh&DQD1cEjP|qo4F)trO!iMJpoi^v&yH~gGB;b#}Vc1@tC7O^hz#qrrP{*6x)%pCf zFmBJQ=N<WO@m2W5gpbF&9Ra?afM({_<zy}Sem<1$!#?@g@7&a@kq&9Tz<@H-gLbS6 zo>s!#Fp5q~DUl_ixj=m@c6xdvQYcpw*wC74D#~GjC}9m4vm2)Rp#}QXz`jTKl-x}j z6l}IKdXzdTQBvKWz|Kn%n#*%5>1Z152`-D*Bp_F5%!21XC}lalaV(6~z?gwjaRzv3 zg5<d8wNEQ12u1H#`rt&3&A(gtwXsp2pne--|Im%NIW(oJ_)MwELJ%?sPMR^&GquxK z<ddfLl9aE5S+K#$GfdQ<a@Gl(R3`IH>#kb=M*F5$^gnnz6WYn*i=?MHL9Ev+6Jj6O z?wAO2h%aSB@}*<71^xzZufd8@a@6TyK>85XR`T+b$9w|_Zjxv~adTQdWVdwji!K2> zp*LQGTqgyzAhYy*0i%myHRNXBOLFTK?H9|a#BstOXQU&`%C0P5(<0b5BXJaC^$D^O zzX9JM^5^eQ2bUwB<(;w9IgnJP&w6~hCvTK`<4wY6;>?iwUNxhKq|2rmXPXAbr{9wG z{`TAm83iVjhbKB|6T<kk>9Tx)SmF^u7=ca{nf7l{n3(Ag{T`XFIy?)cEevr{!8y7i z>}Z|x`}0$kVqHZT*&NQ0-Q(CBGzkoB=J<SkGezmy;MCgM^?Z(U2O+}#ku`tc@AB_J ze9%Q;dqn@SaTX;>n-zQGlpr7~saeImJP292CIW)J?=510fHa?4c68-+Gw*kU%Xayf zw=kZS8{DVy#*7QBl}|2DhU8LtPSKfY@|}ugPLTk#=i9$I{MNOokveTV+K>9K=}@bL zYQMmd`_gQg-1QmW3{m!@H(d|gs@J>3>vE)8=GKm<&qRTu4*UjnQwrt%)z-S?B6Ccp zo*t+EW10r`>*%8)FrJNQ`)iOSoSMb|iNz7}r$L>R)uZDgPWh@~t|^=d{*|x3EnNS< zDac-j{azShNvke_ULjcJ244}w&6bM+TW`80!Cxzau(6bs+$3zV?Du4XK9(O~nre$J zh`aNrvUhK{sUAV;cp3?wMZRsR75}2ayO!W}KST`YAdogjvrPf=rq1327my4y2m-eQ zF5{Ad>~XIot;qVwj(nGH@txo|ttaUCvfwYF_0MFeoIJ(z?YL#BFhqJ%8^c?WNtbDK zvRy#f)b;0^^Kg%IeQOcLo~k{<bXmW^uKUvTz}UoNdes=$_Zh;<6W2p)O3!qa?COqo z$4PTkRBv#F78me|bHQuGaO(C^rwcJyg34<cUk;^pL!~|-v9#iNx(R5S=r8ypIs-Ab zjgSXA5skL|2!(RXPMlUSnJm8%CKE~NQT@cPszxG$xzfu10#Jgw_v%x%c13|ju>JFh zrPJ*aiw>ww5AxHOCB8T&-LR-N0sSVSPb0>|1!}UhLeh9H@TKA@5Y}qNx69?e)D&96 zkL@WCx*@a{0lwE)F9_}lf$R<{;LFqz=986Bv)lSXnJOst<pu41#E+ZUFM-ypi`dt) z)I4aKTkMh*fmF-EKDIH7oq}8*eWQfO$l40ejDT2hiTE4NV9=&({oufprfTUH{^-yR zX^fuKGerZJD?FFIWC=Y57Hf|Ie6I2ED+ULi9$;RGU_!DiZle|f!FRdA@Ouf`uwZ?P z!PyiR-47Yi*AQ32T5*Iw(b77t7)}tU4usD4O)HP*P6ZP|O7{jR!Bz!_j-;rMDbz~n zKzO^e;3SO+EkT-;7@i4^4k~`{yFA#dgacSI7ek~bO52%#?YrlB{{SYkp8X3(dGcd7 zs&4bW1Br}<K*TP-&%#y=r}ED)$A(L&`dCRn*>9xTf_#sUYk@{@EMVIJV$l{*pXL=} zqqY*i+=gsly|6(A;sDz&xj})E-W_BDDMR3JMR?MfaQ*6p4i~7r#=q2%G2Kf{`?*iW zH_o5mWOqK_a9ZQk4OIDmAvO9HzZy7ozfduMb$H(tc*+s!{CS}3PWCwgv3c0K!79tE zX#VJy$~!I#VbNPl(-1ri-YD?wlavK-{2bGJ7m`{;2<d$<mc?f@@)3HwMNR}&@z~xe zQ-VCm@rU$7_ZsCBCjMO9n0d~%($$~?bdNR9#7+WqxVx-qP{mpv3!gofC#djsf%WLo znE2Jo3>c500v)c><6aeD20Fmk&4hL}`mZ~SEpC2a!Q%Hb;n=L|2`urVU(U0#oNtPC zx$Tr2E0qBdcOB>e8U*zx7}<YpX3@`Ss_!=zXw96kcw^iq2QGtqZ|wn9^p?x_cMnGy zQxRcny&nqrig;WpLZ=`p?pJ5~hS$CCJQJt~UsLfz4hV&GPgXg%&693w;U<_|Gg0mw z(LV<6E#_5(BctU}nBA(GjZ=NWDE@NDfHKJhoW-0-SO>`HakyGc5)Nal4;yFM$Ch!z zV10a6W#^W`_7zkm6E+&3cAmk8C~5qDQZFzHrMqC=ng24}G7ToV(LLbO0TM@a>3`68 z`CiuZSZrDh6T>~OlF=RmFy7Dxu#0M8Jf#_%djb6QiO;Q99Ee{G8&sOYH9Qt??^T|Z zv(VWW)$lMfY6f+%g8NE@PL?OJEX>ixpHQYXGO!B!IwE+gilGWq)i3C9>Gclnw{_Y9 zaL!eS2Ri5{lLb*pCKoKC;16)te_Fh$1kEH*D^BRL&kxj_yOy?kOx%Z82AJ1JaN&FW z#`b;x_&h^~;f=f53s;6JoCjI`2!bVMvxSon^UV~t?lj5rIr8;gbrd1SxEr;l<E*FL zYzXStk{-1G*n7Q6stl*r^o)@GD?f#36JA~oC&A4KYDBBL)j-Ll&XFMk<jpT_aeGR) z-I_P!3JBcCj=|#DPc1OA=%8wryEzVo+=+=cz{u|$9#PrEr;UpU-*uGc&5zb=8`dud zvmGgWpYZoP$bzid@zP*82c|9gSU3p6k=<VLG?)HBIq{1vY+0RkF)hG61$OVL0+wCY zsRt@MY>VSaXWKvAo6+Q|g30@aQyvYq8ppw<pXRtMlsEcvZ~0|jex<j5)lDezrvH#y zRxRa`jB&B*;~2&+Ll?2&a}1@Ks<a(GzT=tET*HCazrZ}5R>TO-KLohYz7C|Pkmm8p zz>ybsFf>K7@q*SZ^I9mO{OYF<EI~F;TG~N}x8BwW+11kSJzgFbBubmkbfOEA_g?Xm zDF<ryCDF#pMwNIk=z3|IzwvJI=GWnOp6(IDwe*$7wx%mohyGg1zV|t%U*te2hz|La zxVkky!Q^;^P2R89$WBuYt9=A6#+G}x*poPU>Y9cL5;PP8Le{Jq+kpZpwhQY5+kp!z z=!xb#z}*iIxMM?3Eq(;ZZ=V4>M%<IQ=GcgrmmgHS^~7)Tg=_6G-htAT09TRh_&XPu zDKcy&u5#%6sV)p&M%SZ-5S_&#s%YQ^O@p(4V_qCb4B;Fn;>{=aCi;8`6SOUIK!?Xt zu@BTulO4G1RH;#NBe*pW@X05fQ!`2I=IS*oppg3nj73xfa_f^R1-s&<)uV<7Z|Nu* ztC;u^bqL>YhfDqIx0e0qvH6N<M3&4tfUo^ELBrUtn^mtDI^tB`P5TM{T5YaMR>(L> zj<zVk_}HPtW5H$qSpy8CQCNCxzbPXlNeq|3dBuS=Br9Tm+wtrlTBIfNT_6Z4u`SM^ zo{8rM$mML$covUZgJZC*-@$Yxl+dVAqAGwn!31PIFMU0$x9bK((zX(zBZQg^_oq*n z??CoMMt%b=bN=tLmp#outvJwcE!coT;P6YjJKktenHlIp0eB*-=BwfX<l2+*C~ba9 zCdI6!DbfO`(jra78~?=lP|<MbwJbwzdUEAZsab7?-?EWjj(!IgH5p5PMEZ5~P1axA zfN_y#x5(3S*UHe(9Smx({0>Uw0tH_1x)T~EE!H^Yz|^*-ajg@0kOg=a9p~lk75&2= zQwtvAzHW>$pgLGw!(0AP*4Q9xtUP#}<qX&s!fSxHzgx9;D0H|+AbPG3`lGU=(Lw&5 zc4nlSsfqg7ZP>9C=z~Rc|8$<KslWZOQfX}xE943c7(u=K$;o;}cAm<fwo?Qxea@dg zKp>=mY&6P)q4YQWpU+J)E=O(;8f{Cc=wf4ii$D$(xvc-V$!`RZiLT=$KGP=ij>WnA z>f|wXji>W>h=tG;58;>q$t$ch{iRkx@USK~R^)#O!w$rN{`I?JFiv~fWbbjO-oUL+ zcQM`kwz4Xm4h^u9b}Dc4{8vZ2?OXUKVtJguE#hOy;b$sGs3MHFME^z;^*7k<rIz&u zSxA>0=e%F%y+k9?xq(A-$pB@gJ)?gW2>ogJ8ss?8(Dj0%bWrG~JVlyXSKVy&1ZxG? zBo+%bEj2#7^n=W>pr69w$8~&dNZE)}sPSsSD{a>plRdFfFwdEwU5@@B8Y}6h5%Osk zx{I6pO?CXnY%V?7BFhztA_rx2>t&wEt<BKwyUsMl)yC`ZD`earN+eYTy!6+@QbUrX z{1-j_K<LYPphi!}Aw{X;J3PE%yqWy)$fxss<=h(P}1VY)Lp3WoU|5pTIvSdHHX3 z;_PfVfW;hxqCgdJ{ilnwVpfy=JlD<lwuEbi!+fS4XXSn|#&Jg6PN7kbP&u@>)Qm0i zxcTPl`~BI&&E7mc)eaHTtYXidCFd2b=OV<JiDOn_$~-8}tLN=(V~K#@SdPx5A4kOi zirBHw*o|<|`1cQU@j!r24t}y29>L?R0*8*)6`Fc6erCuDzb0nZK3h5Nc9h`kw5@|3 z*N7uxR3}D^FMs<90Y4Rs!G_=|ugX0p|5Juo-odgVLgl~!mnGF2#42XcPb~$SR&Zl7 z%%m$QxIcaOGo6?q;j{UbzmH1wUFF*}?VkV){HTlXJBvbi8fVI1z@jl$>!bE;!~_$a zdC2&GM*K1!7&(rWK(sHCeyD1fm%Q>WXUBc@?D$oR0pO6@57gw$KZT%yj0(H!OX^J} z(O0?DuubD|op+curotIz*rMeSvaE6%5OOy|XvHXaavxNcSrS;u3y$>PzZ`4F0fZm$ zHCwBeu9W?A4?Mk(Cu|tIrt5C&5h@W1VZpNEtY`7d(|z=C&nAh6J=c!Q-WGaJORdh3 zWv>d%+f&Ff_i#ru_2kafd1W?jxB+Kkke<Q7rs-Gm(~#5OuW#aMOq}ZZs8TM5;?JJV zu?Af`&N;kWd}D+|*PT@+uVeoNOPCD+G4Nui<h%dmIhsM|62gN-@JDuB4g+9QGem9o zsYXbf0mZKU07!2@%@TZ>uEeB@q=$^jPen&xkQzf-P2{I9-($O_nAL>XXX=xIdimzW z)ow?k1HN**V~Fj16PV313jO1wBiH-tg*tN<EQ|EF6p-2TZ=6uvsSE+WYwI9|2H>yr zP#42NTbm`Du}(&{x%1I^y&mwSU}X*Jm{Y@WktuRA!$&mfHG5@Hw>(w`=b*!d+R%K` zB2r(z_bzon%ox;T2Ipq))F8ClPD;*r*rsWg+Yjw>H4F4t%T4LqxXwwSL$W0<+coSv zLKI;8WvBGOzxd}RAiVRNJ1!e~dHwGg)1!jDZSVo!)g_t$OV!;b4!5ka=iDhguw%@Z z2YsFm;Gzk590#mGhG_o}{l^RJF9MS8j2*unXdrA=)be9PZ`>+$Ii6Qu*I>~tsQl{h z2i`|T=@4}GKOw%ef5M9W{L-tir5ob9x{aRT<}Qu8AtK}m(KYeq23StPNJCe>MZ9kV zEIC#3r&iYIa?Z*K_1h0GYi<Jw=j&4g9UK<jx)UqM{978m3PlP;nt)>K8BN!*|I!~D z!K$JH5;BYcYftd6OlReLx}}wGlgqCK|8|VP{+<v{0$!;`NOr*Tc;w5z`YriWAc<Kk zze&r1ng7U`V`;knKu2mX5b&CyA3onaK4ZN0^e;-|ZUfI=!*+S0xk0>4DUH6!M0KOj zac15tBGl^x=@JPMEK*2WrM?o*t^@3A{%v^7H%_KO(vjS)6EjMJmL1&=xMlF*b=)s6 zZGtoL9P^@R4@DYG?j0HK{_h)wiwXMn=U91_3N2ADvkZV2Mc?Bm3t!|oaLbpamn9iL zO*;C$+5>79k~BHx({uh)McZ0Uf`60dmMo(hFxvhxq3~NILVE&~8b$^$C>!vPT@Vw? zP$kRDoM5>>?h><X3EH8uzRaK~H%USA%lpMj!+GH)eTznwkbx#as!Y~S!NCH8x+eO; zK8wUruyZCAzi0dHj;+FHjKPm;LgJaeI9$<x8}on(Ws=~4Kn+k&T>K4!+F6|!9d3_L zfP<kt@oUIYC)fyZ6zgjA#m?>@I+h_nc^O$}><;oQ>L08VPpweVmC^A$8Yo|f6Dbgr z|E7@lFCpTXWYgYS2^+~4^+>tu2)iLW%g|pQO0^%+RhN-@zzGI0law09aoa4W(HBC! zV);vbzbbRcW?7C)2E}pM$Vp>qbzISQd$pr1GBB7P-B2OtcP=qwk@5?<#)JQ*^8Ma0 zjYKGWw?sw;OYBl^slVs^1-E!fYTl#ZA>|S=CSM5ze`j@*(4)_^#mkE{8Z)zTi<Mx= zcBEvy9ZNn>KIr1^Vn@u!|91Wz%RlJ9ch@oCU}uWFp8=G!dLCrrzt*OH4~@v4Nhr&e zLb5z@d#8nwyR%UQA&Q#UWuw;$Gw2B<Vl4QO7U`qoL&g@$I|*Da+9(^=5IbFKR?r0X zx_JXOyn`sF!Lf&lI5&`EdmyLd<#Vb^aeO+(5&NXx8!DToyCej(@yc8g;wU18PGHY$ zv2i7C?de4Vc!pu=V<CjB9>frm-ju~}h!<qg5$nopWbW;hkEdjTGcs3zGDqu-e~s_O z+F~i8`?8PvFFQ<wH~fu3PU-LM9P4@~{&aSkgkp5r?a5H<OqI{0)P7zk_h;?Gq^^1S zh8rW#sVLKA4HCZ1I+St31~@8C&3j6B=M~y{kHJ4+-M!I~45|zDI5Vzh&CmMjhPUSK zj3Nb^>-0)`v%%>h{v5D`VB-(((&{`utCtbO#l9gwYfCAlwQd`8{G7+$++$lIc~pUJ z<YB_9e`PD{q{yDK<r@ek9+~oT2YflZ<=i~zxh6h-YttoEI)a(wlF?>A5HacJ5XwtX zY43biZM5>2^vJ}T7zv7Z=Cvh7P|^vqkdz!$P*NMqs}PRVg;2oa)!SRRjIr=^YK4Am z9oC#umQ@0SIAg8Um>{EJGg@?{a9qKsM@n$Z2x}-Z{IP%1-T?dtE6bjK$mPOdS^hkL zkky25s>on#1=P69mb??w#e$z(AocI7v1(&ZylA+s1dVAes?BWra=#;X8tn_O`u6}W z13@Vernozmp>ixH&#}-;nyQze0-OeI7ad6!3INhYQ*TK6-@<aa2WH|SxJvzIH#_!Y zhSgEX-t{_`#g55$peBq2?qKfMoX$dom1jGd+DHF!N3uCUA4=si4f^^XA0h4~*JsR6 z)(XXFr+N?{y+yu1jz>A#pvy!M;tD&UJYaFjNB*3dX&Q_k(`3o+|E6j2U!Ro|!JnOP zDYQ||{qcL_SF8;>Af#kIO;&xRRGy%!_>DuVU`ClpQet3t2H+vTz>tvM91vXGEoHq! zg>$H~gAk)q0KFm`Iwpbir$H*_DWk5!d=?pFH)i0nkz;5<799^ifBE<rUNW$JYJUkp z2jp<%i5enjKB=0q5$&f~H?Xr?tQ2csQ~qmaJm|EE4VN{2-g<9@GU|(80ta<sfD@Uf zd)mI76;oxgIwr+gk#i7=*kMz{<uW+vWd<y4g>LlVuldo(N<Zqj)B?LY6<qXnS=YIO z;Y3kR?7beE@y<%+K;Ove*QcwyI%$?$;*-~<LzX57Lfy6|)@d5+>Cil)-G(fO=l|x! zMci0Z^f}nJHOH8JO@DFls^W*ZjY|caEQ5l2wkBnTJb$2~$BocZ01xY&l}M!_`UnlJ zHekb%E(YUMcc|7vzCGfcHo{=|u<nsn6#H;)&308D<~Lq`8h8v!D17=m4Zi_L1%+Ez zwz2$Mr6w}?bQ`Q^vL7z=tB1P*Wx(|n<#jnE?y8w~99d=Iivjfe`HlQHD2E8iTb1G< zvB8WaldWT8TN3=N(PTNC49gWDukj?~dsE2RrEJ&sq1C(=#pqhB&SdStK%c7*;G$^; zFLRQ=3U#%Quq=>#d7{aNNKR<r479R+)<GDF@0cz>A!aeIDkj;HzQH6A-jMO9gZ(`v zXsln#3#R!A&ukhiPoG;F{cY?koy6!9OjyF`8s@5|ov(y;!k)<ryT7+4sBEU|&4!DV z6U$hNjR;6sN>i>}cWcCc`C&@MhreXO<(KGQZZ7XvqE4_^{{I9(T~Q=Kt!!1FOZs za5jaxEvyia7n2SaYbEajKr0TBrycY-*V0|e^-Sp1Eh}-|xGIn(y|t&IW1;)HCLsr2 zkJaWta}ZUh%mkDI|D;l%tZ>f7xCLPhxqtl%^ZFtA`OpZ$s<5L;h;P=2VA21Ala2Xx zGk!Fwix<J;Xcab8sY$vjzivabd^gd_iJnNRH|PRW&!zV<o}GfS7Awy%`A<oJTWzGn zf4{EFLq)Kj*YAOeUFZIFvQq%Ni3Zd6&@25isO5HYN7MnMbU!!s8jt^lQMew5blPli zcfXdkKtTBPYY>i9VaOB}{P>lOjunoZbdK7yJu!rsN%-i?N=aV{`PoLjlVGl`_r-wF zQ&$B++@(WRW!w@gO|3RQc7-q4+?DrDARo}~8~Su=uMGTU$(X*j&q~UPzjbWwAxZ!G zDv8}8Jn+n07t113`YkaMt!hpTL1ypFLV}F>Lbfgs$~t&>7vkQwnVkN8C4BCBp<yNr z*l`<vhiK6;@_3LRb0y-)Th5W5zI<H(<^mEFWR&m!YQpXW<!6^I@IiUASCv^_DG)CC z-O}hXvafXH!Kxel$?}B6Fbf4D#kZhJg&n^AvhP3V9%fC#_AdetaxZd?LN=lOz3&{~ z(`@MSnr-J3Wo@eUz3rl>y;BG+tw(&2>(n6z6E4UcgMXI(GcO9KH5|WAvI0WQ1t5%J z(ZQcNzr=eh2rr@QkAifI%;iC~znV<KQg1SIs=~#wMDg>X?;X@d+T=}Rs_q1cPuCqV z(l~?0-kJ5f6<yu9VKzRQ2cw5|eyC5zfPgH|Gmm=F8jCC{Yd@>ExJ5pgQ9d_|3pw@a zj&;Zj$SFG-oh{i0wEsr>@VFI&kXe6hVXwjOJ6=-%?$Nt1Yda_>c2ldS4dli$sQiQu z_nJiep_X=69E=ZM$*cNo*)oB`#w^D{{Kux0<K1aYo2<Rit_(8-=-lTQk<0w_5B~1| zDe*a;1Cde3dzGC!n2IW-CrxR)1dtd1AptI}y%vmw-LtLg;(>yG7q2flXYYhP=;;qe z)4qP;yyysoJ`O|Z-aTBf)`Wdd%c%_7NNtu{67H0ByNhhq<8~bLA<w3{*UWx<Bd3Zr z*%zSv{)u{e$yh9rr5rSMd-SufP!{!y;n|D)RiTz&p)^!Jx7G~z(s7?0q|(j<-ZrfJ zRW)3gF@w7|hfU+rMxq=4YNM~ST;rg7d*S<U|HvpD`Z8IP8<gLB-bYFdLwwr$I~)IX z?#ZLIvU+x_`5gpu;`O7<eY02N!gNO$?|$_nVm1(u^QyG2e{`v~{g>OZ(#Lm=_%iC% z>oX%AgJu;Z?%fntzU-Q=Jz{KIq2DnrNk2T35gysH@!iXaG{S#z-_&y&D1S@j9mdZ7 z6^hRF<vq^=?zT*}u>o1FH!xS9))R+obze(`QBE#YW@@~CW_+E6pPBMjx8>mS4hwiz z>5z34CWU522pAXu+~7E?`|C9F_f{`rA$_;o4@p|j4!6rM%JJ|K4%`+bP<=GC%ksak zktdU)8f&g`8rQd)KJ<N0yLvtkUr}^k|E|Q-8)}nI596#h=WRHfuy5mE7M6??V3JyP z*?qClo>u1fL5IGF8X>G5oe0R~Ab(C}<pqWEs>nyL1f!73KZ7v`L<soVe0hNmJoeMa z3V^TjjY()w>qXzBJ|?2=W^w6vXo;fI8x<|`#Vs>_F|EB;3A6dr>!ycyiHA`q?VniT z%`#O!h(_EGUc-9iA0y96=6OH_Yo99)omAK3sqBg|-)w7xtVeV%{9e5DL!kWnmZU2d zWDdpK#BS2#@ceYXQjZw*BO|*dCTL?Gir?ZnH75QVd7IQvmSl~>5MEe!s!Zel@-dSq zGe)LR%UhhXD!M&l%R`FVqu)cUovOmcyuT&wYW#sVce&eQ){Wp_18rXi)ni-AK<)=X zRMS-LeG>ujASB{AFKwACB2F}uK6%XT_sdhZj0PAzinqU;`M7_zEHg?QDEw#yTr7{X zjGg_nQ2nydfq}q-^>;xeyIcz1Ih$9m$R0wLmW7=~&?{l^t=oUM^l++<*?b<0iBI=^ zBl*S#L-v+@U%(=(1FimJv;entWtF3G7riL{!J0^Nec9FwfiMY2%EOFrl0X4pt$npw z^mQs3OF^V8h^mqM2V5x+Pm^+mI~}hVz;L|MKriBfGhPh^3Rt?+=`dh~I8LcH?YkPv z2x5{1sOZwog6QVE^kM^;2=TC=M-jCWZRi`#hvsa`2R+^e#C#CBjM|ag2jd;yv&Kvs zpd-3XG%6QGZo3_o@?P`VFT){pjG>8YAISN$Df@Z^tg5OFsz%rlZ~W>4e+tB-6n9FB zc73F&@MiGMEwLVQXC#*^7P?pcz9T^JKGY^t{S$Y~rh>jKI>PmV2wQ@WfS1yE@8arI z?1Nvy`#$)oYG?21D4!sv@_6pu=uAiqxDO2zL1am@4rWvcaz8yLXK8q*)C30TOS?#b zm6*!?i)ty9l(n6P>s=*~;Ek|YX_23BD-fz&5WU=9*xGXWmx2>$9>xuP+q3z~TO5M< z%x(tElKQV8?p8vUWR_R!B7|XG;DhsPlhVwuiuohEB}~@A>C8)0t&z2!OjvVY3-)c7 z31P{02YvG6G9Zj-y$j!6(XU*XByi)g|873sYyWmeNM^MPxw2O~J;sPqYq1};wGh%& z^Q?f#S61p8#!xX$<%jkRq&HkCb9o(<3=&t%cIzg`756t{GTCWx3MB2>AfE*j5Psfa zpqh61C^+bmm4Ph=F@Vr@$AEtE!D`$}f(&ACnA3PUZ3r*`(5JO<ob_CYar%1Jl2P2^ z<+iF8|6_xqTI=0ie_`2)%r)C+{PXp2D`roNsR?rbB4xn({`?>#ZlBgN@1G77xR9me zv`%>nr^dX9X}D;mi)P_4#6qGR7r_}jyNr?m%4V3~_!_!|Qb6+Rr(^NG7GOxTF^~nT z{M*f@x^|l{Xj`uY`FD@KXgjL5{&7SQ0(D~vpciF_mVDTB3g2~0X9+XwnozTfXA8r7 zX#Mh`$TxX1mR18@-J!3XwZ_jEmd}yL^t@PTw61Knslc0-MyKrOO1<9sUi3L1Dm1Z| z?d>Ww3sre>qTd1=w$Jl}a9%)6wU)|&z8;|;rli7)k+{+P6a%doHMpv3O{D@D#7uN} z6*>TVqnX{|Cmp&|n-O4TWgD2#pdjxzrz4BB%J_OyvyX6nBq6iDTFJ$eR?!zR{QZtU zYT(ok7+99+HuwM6DAb3Njc>=DA-G1JSqO@TtLqFt6A;G_z*{+Fy)=hdG(LhFYg};( z*wTXU=z;e}^Fu4U>6Nc|R}Mg~uZe-`2Q2R-1AK6`fJF+_H82ekHYJZSeqCbgC#CY8 zrtoM6I}N?+QxLm%8nKMo=>*6krs!<Xc9rJQ5{Va7nfp3$Hlf>V<gIveU6^+j#djYz z{z}UsE?jLBVFKswi{D*Z>h!InxYU!1>o^+P$EHXbe%c@<v`?PgaQb%6<DE_F1!l}d z(d3nv&*&(CKN1F(30QDD$8Y7!kt-qa1l`LcwAr8)uXM5-Fu?EpX94}#G*U4lmzCs! z<(n+YAf7+off@k@wR>IF8;()c_}Y^?q~}xPw6OpZ#ec0?UF>vo7*=Mz<A@5)um$)| z+4?_32C|5DlFTJvc9x@9<yCwiH2oJ|1&-$2&k}){4aX(h$hZ^Lzu$~u_63T;Qai2F zd3^N9@MJK-<oenG`5R-w$_p>Zse69~$n}aBv~nDycN;(b=nEr&`lX?}FiAJiLp>^a zRXx3EPb#bble7d?L71~=(oqH)sCR<0bXT(<FH2rFqwSQ9@28*boXIAZ&soy&85ro5 zdY-(73dfG#zUYxXNg(CVwNUCXgVcY>gpk0Pt5$v2mT1WFX1m|OS~bC5q0frL(V1TS zv=gT<il8O)J5aAZ{n$q83Y-N)B+~1j(?aD?{zRR&s6yguh(sLp=tHJaK-RVpx1Ov< z|4xX2bXBE5HrDOv7R!I`FF+ZRT=x#yFYpn%g`fm8_vrZZ|E^*1p1P9W2)+Yil|Rjh zD2asa+~$)Qo(;-A8LGyolO*t%R+^-2Q;ee1;3rNz4gAX`^Wg;r>fBsf4z<lINsl%% z!Jvna27s<<A#|s^l}iqq<_&o23l1>AL^<^%^yC(cPL8G&6~3%am^VOyar1A+?HAsf zZ@y_i3D#>^g5fF}`WcjLPt&3jP;_(dxTioMl_XMM%hgnoWDU)0M}_5E4P;{UpIE6C zWktU!#_*577ZV|*!n{0mnKFv&xpH#gHmmSV-kuKaAv`ndscdijq~p%W!Pw9}#`Pz= zaXnR+<~T}rt|vot>C;Y!9x$ZxuRamub}2l<kNw%(zxeK7MjNdp&<SE5wsjqVP}{f1 zR=9|Cec%pBI4CUKbAtxe=XuWZ+VY_d1h+nqrG-E5cV=JiD0oD9_0X){pfEMsBOmjh z%}@<qU)fbjfAGGSfieqXS&RjXMW6xuTEmx|09`G0gA4_1{^ve~ZqaNcaGw)l6?=&h z&zS2{z$O-c#x*%^fu#KFpeEBsD0nc5^h*hPR^1NChkWLBH3L~MTurV82~{{duoJYr zuABt@63IU_S{>~!tRAyRI768+Luhtw-09avMxXYDv68VSV0FEBW@PyMq0gDvFfvAF zGxyf(IufE|%$FI_m_a&$)LWl5?4<W8DSr^dd%ovlb4Xm0BlfO~zyr2J_ZG$;>Zs@b zZjZY%$GCv@p=I1xOx-Pjpy%1L0$GmRJxVl|Zu(aoIwdR?=a=jTb;n~y?rlOJYSL$r zeTIA##qja79{kU>jLd93EuW;n%KEZw6glds>sfV6L9aN5wIMq2>%>#693OEqI0(%C z#J)L}c@L_sdmXs{!I@@7QZCT@z}}-9S$YYI;9D67(<OVAiZg(6tfQXxaM<U>-tQJ= z_c?yrw)><TWVKoJDcevqoc$T0%DLGy3wr9*Aa3P^1KP4fQRRg<y?HoTSQ|euT28lk zURd0#!+9%Q|GV_zr=gSBQoQmHH|<vmGNQyuAE4YfS+$+dz1KUQ<-2{ls2{Ih=jLUy z8<333efW5LF)SkCoO+VGW=4*t(m5LlD1zxOtwp;Xw6W-v23dnOD42~;y{S+Uxo5O5 za!|uyivodp@>(Yy3-V=`tUZ(tRr}(GAV(jH&^KS}!o17*!M~gV>4UYV36G*iJ$60g z{1||BYYq)66GC^T`#5<Mb%+HNcymJNews$1rqSV*Kn=IYw;lD}cl^8-N-Ap-fB11$ zY(WI+(XT{9%b&79*PdH#f#Jf|RKqt#q_`v0aENPXm<3T0XfT77n(~JC#AIoktPtpG za|~e_h(ZU(*9j4CPB09~z%p~`XZJn?F?^b)&D_020)N6mMUtP1XPGm;&j}_?vsMv) z&_aIQIq7M;Sx*PXE>IxtOnbE0s&v#&2Y$%!?c5J<O_8Sy-A8U!^Mh(ak|Ro>LPrjv z97fz)?iF;O$j#H^bHF2C{r!FS$eo=rOMhT(#?S8!Z=dGDUdwP}XYZ!=bx4u7lXssl z2Wm*Pck(?-B^RAmg80l)wjjQVV=e%X6R_yM5s-@0h5?qePeJi891Prt<mEsx?*GLv zE!t4t#Qhol0-3unP0rB2x$TW@%Lc(S6@--0;Y>=(0!`7>fHskF!B{)%TEW`K*s#gy zY@c2%n)V`*l;4$s*26Iff>qL;C+{;b7S1zlW6DV)WGmcwKwb{Y)9?d$Qhuml>)MT0 zQ*LS<djdX1$ybfXY_BM@S;N;*j<Yf6y>if2zNkDa%BHJ7B5gQ`M5t)dN1tYPG;GOS z{B?I_igI)~MaG@XwHRXeK9|;Z5{j7ptL=KBn1iCDIq&Qe4IWlSu1bD<TJNMl4Sx#M z82u;KUj4wUL^?Et1*mNYCW@a=_r8{T9L0wP^_;$XJQ*DLoYUX&47qvz#2fGXeEG@u zfT|>dpW(N)OZk=2pdn|52Z6}8(vk;fo<nm8yy2GjNmINcK;iJD&zql_PGs-&ZAbG9 z=ci_T{3V?$6gQqUncD;jp7@D<U1=ee`Eyot-cY>f*}{t*yS)=<v-dSzRb~+4Zcl!F zGMj3YYM;^1im!brO#m-8mh}f_$?>013iKDfsyTF*DcSGpqFyb2Oka~cDrNc*R#d;P zzlSl_Z_NVydDs@vgIMfr-CW?bTC=8faC|8GfQn(WFbvmzj>4q?=JE{yNKB`ZL#q=0 zEtvXUU_rE@ISAo>kMDCoN#CsyQ1<=QMd}C51QZxN4-6oY=Q$5VEo`%X#eL<rQ!hfN zuO)}M9!LTdbH)bhq(7qWqVkO2DJPA+X8*1iL3`(4D-3A=tFy3m{%KyyfuO$jf`XQR zfBDg3F&Daxba2W(2j#DL1JJFyp@v_cHbhliexg#|r+#5_+fEAVRizH57B3{MI&sEI zWazJM^mZ%<?SlpA8WmXH_k~I^5GdZ3#wshffBV+CIGWIG)gT){83!l(A7}3!)kM_2 z3nz4>gVL*X>AeU9q)HV~L3$HJkX{9nAYFRz(nLf+kRk|3B3<bqy@T}LTax*r?|bk4 zeQVvnZq8cFFe_`$K6^j=IXh?O>}|AN{?j*YBbge9(w@spS7o!hqn<WWq2-3y%rTjO z=Zp=fvMq(x_xb1^Ch&D<zJ%J*eiLk+wl}f}h`@>V)Mf^~UC3uU(c{Gz)hIRxsSPD; zKnUJ@zoW{e7?P(N`Y!D2@LD2P4(|H08`iJXBPw9vhLdzZ9L8U9-Ng4C%|3{@!965Q zP|7(%VE5LaTrR$1bwxTYDks3CP<xk+!Zx?;Ar>S7reKhL@y#S!^|}CG*#M56rn1Xj zf`Xi<u5a0Bw%U;wUVs)w*m|U2z!$+kn)?uKXJ4cV;T&6R_2d3bC}e9{o*_soTd{2T z9a(O`!pY^muSF(+cgr%%G#V$!FQscbe-sllSu>wBTFgz%`S}@$tBXFVDdT&tCtJMs z6`|UwbUaJ_pa{Mu80tXZYbZGgwsAM!1)kqK_*umd3YXy&o3#2Fv#$t?pF5BR%E{lB zKY!u?SW;?@60?Oyj3wB+yR}c?lWs(0!%?F5jDf^4?_=vt7;9%b4*lj}0zhw2Gq2#| z6?R~Hj!+<R8sH@K$2dO=S(1%+Lj?)thL&~%83V1aL{?CfD(q=Rz(PeIk3www1v(Sd z)Qr=cG_Q21XhVM+^cpAF93PY!3+MfVfZZo@Af2%$#mUeM+2Vfi@KaMV!%%nQYZjnZ zKhBT8Neqv+jE^#J;8vOf==_!O_GZWGB>ZAQwi@ea4&IL^eafd`R+pF?#Tm@v__u^2 z&6MkiXD~Fc<9GfW199s=@}Vwa<c_hUKa(7iL~j_RMEBs$U6QYVq=+qrZ=>lvza>@x zH*DBv^TV+H%9AiyoeMR{>Dn4h9G@^3ogx(l6I=A#!|m|F?mF(h>JgqCg%BAK5R~4f zfX8XD#1Z@dIdV^Ef`p&4k?<@3`RaUJAP=<Kq98csxvFnPeoEiIz@YnVK*R5VRkh_n z`O=4{Qa|M2+u$U!cZYurLejF1tW_UwBJh+yIiLVjF!KbCvZNPYi(Cd{&Qs9$S0*52 z7g4}Wo6+^6F-UAdy?B<MLiDb68pRRnjk+}5Y56?SPaO8>^eUBz!lx;*l{iES-9n3v ziI%v~z_f}p$ilef3le2@l6VZ=jcFL(2>KeklBIbbeQk0IDfgTsG-UBeMq0@+;moSt z=28n=X<eszNh`)7YU8C)r?X>GWKo~*yR(i7qVw2>%;er^%kzPla~f^COM-F|zJG5g zcjfttn#$%huEftXOvOwJFVRa&4B$Q}0?`J7RJlYCE*%_EY-MAd{4f2=fyXNe+?H{x zkgqk&K+nmC6@ize(uWctoewFt!=TL}*nQT8Pq_Z`r3fI;w;mHTW3@T-on>0z?IAFJ z>5tPCzIm6z<`dL-5@pQ?Hx0>aND494cfOey2X!t0><4kabCI^9g39>U<gf1FGSrp6 zYkuAvFe{Xe`v+}Z<Yur>m;q6pJ)HOQ32G2BTyY;ghCP0zc%!#^=lbF^gR-f4JzuZt z#&`=WD$(*}RQT~R<p4NyLm*UHk$ERZXUe?xc3$rSS(Fdy<|ze;snntCx#~~Bnw9?W zOS64G!PAk8fYr(1!_osTzU+`3e+5MCDwjgy2H(nQpk&te>2$87yFoLi#9&bQA&D_^ zI*7!_LrbZrzyTyMu+|y7^)w%R`|{Noo!kt|wO~fcjsiXf*q1>=lj7w70#|ExX-H&3 zS?t3D``ap;<5FO3I#AHlB->9Crri+nxzt7Lp^~?-bX*GJ!g!!r9Ai##%a&mhyA=_` z=i)Cfc}<5W`W6V5+W7gcTZI<ZkPxHj84&k9pAYj*=}jXY13=6lvjWlIeo{hh(ml?m z2O_TOc2~Tc^R^%K;r1gQB-BECS{Ib8DPyWee~eXC%Su!L2fccQ_8m<2!)&?xsnIXS z*WFZgK}Edp68k^B$4!>R&GN{dtrJ_iIg7|CjaGOf{Ma&|>^C>}C)PV@>Q8@FG(VJ| z%{2}Vri*+IVGosq4$1A3Rt)8PuuIA+4}Fn*r=d$~A0AAR5f$bV<!ftL^TD<THRBt% zSSB9wL~;rvR0Nf!Iw+g8PNYibzMReva&y=7qR3CQ80Nj4y;9;Y_6MXaHsRzEK>W|I zZJmi6X^c4C>UMzaTu}GNS${XX?G(RD<BX;1B4CFG7_thEC#bR2T}7C+&;VgxdMN8b z^vd!)|0ro)W$a-io1#{<$sP>Psv{EUk<Rw3?XNJWTix)(cig)eGx3Iyt=<K!BM(xk z`1Jh;@N@m6Va5)t1#cY<R?F0v`g#k=c=IftX;|+eJS%#>#V+0%9rT-PHej7Hm<h(E zWPtQV-I9Fd2x^FK*XPd~+uKca??j$y$q(MoNI&Fv{#pIH?mUm*ld=9wq^aeGQ(_2a z?wB?ir?wK2ceV9K@qOibuKE2a0{wP!;Kse2JI<6;=vqikjP=Way1wPQ&1SNna{|MQ zZn?LcIV>>NWYx?nM+#Dq|0us1>b4Kc-X+nNerD}O@~5NORgopat$?f9KlU%F^+JbN z6|Bs11?3nO$obUsG({~|6H{ZPE@J|#&_Bi#N)8eB+oM2qM>V>j6P9^?=?MwGKY+J# zhS{0ZxF%j%9D(5BJA1GK`WI6(CDw{mlX4hg#pVx=b1oe_D{N*GQdf?%!_A(N!=0BE z7qG<SA}Z9AP6QFmO^uK<0jRx;F;vU{kaAf@dx7AaF`@TBkLtT6anOJLHRo`1Wwf9I zZBy?)d$1E^+}fn<1fo$iE+`-xLsYKlDAm+F5@B-v;$kXZMr{2omrT|5rP${yuNEsH z0i3wA_cfGyqz_XYa+S=C$jHZtu#?;w>S1<#lj?H#leo^T?dRQIUP^W7T?V~P0#1I_ zhCEKSk?%iyAL>4;rZGtcrx5E&#}qs$+m!E-K4cn1p$1;`(I~q6H84Xj;19@9=Wrd` zYqs9URF_8(JvpF`;=B|1IO@>*T7X-b54)ubW&ioM$M$8<OPd#{3H3l!p9`UMyN3`o ze5vS5*OB#OR7n^&2UIw8hrB@Z0<z7lgKFu4-8rH#f*{&I&{`COAD;+`c$iSnzO2_8 zE6Wsl8Mdb~A-f(oZd1_8b}xTw;MsSpT!Ll-ylR~ykR48HZf;RDOO8r2856xu%#`^| zhO~Y1r(d4y*W3eS`-w1_EgEFqskh7FMUd4%tVUwE&EblQy;&HIXu`;wcTc~BeT8=W z6$P3#J}9f#msmQe#Y0R>Ts|p|i1PEzmyM=|Jm1bPI~@D=P{8J^ZF9xZBqikKZyHSX z+ns6Qlt11pFl}m}It~_c^ut;LrM}vzWKIhZU0N=`Mm<lw^zS6NM~2zNXx{(TwraKq z`~B*I8fZJR&geuSJH##^D{q*JM1(LO>h?9V%^GlUV!T7oXlObIf6CevNa$<wvkxn5 zryQ*sk4PeU?QH}<>Jc9Pg7z+cPB|`W(*9y8P?XE^zKP~rmuX`Z3*(~ZvZmJMm)%^N zsDhJ7t6r`&ON}S}soLC%UE9Y7>7Oly+p%>a$V+ItwFm1)_+cZp21i|&KTL$6^5@a7 zMA-X|j8&^dc@B57yFazI62oGS?x%$U6-WdBP{3scM!|Ik{JNy0TfF!DvI2=v#EcMr z6tYM}>{9{JT|<c|Oc^N(Ui@7Y6Ou8wx~PcRDZZ?}#!Mnqf(o|3g?V_Fv}(2s_dcec zu!b9Sp`fY$ku9&nl_{-aP@6XpPoX<oV}4F}jW+x-$tfP9${$*MyE{Dn2-xp<MAyYQ z$m+9I!9;ImCm6E_Z^V9;^PI4$i_DFt1ktRuq*D~hXOjnmuLHjwmY6PU$Ivb$HuoSb zlc2FixI=w!rgDt@+IK%{Ueed^fQrMAl?x*Y;&bz<dg&q=INbt=(y$Cc)DLmoIlLA< zjOd3{?PY_o$2zFGhD+R|qYH8^oV7xWScn?!(c&P?1+jmU`6uDnx*H~W!sb<lYBa;c zXj4DK2y*QS-%c9<KHU-kcCHMb$-{514y?(3nK6<Vw=o%Np3hk*@k9JdE(LaE4?srA zQtz&*weFw$l=aA{SfBKS7a2VY`ngAv5G*RcN2*cjn}{&?zz<LfE|8BKr8Kno^dp{i zXt5;Ws!_XuTjY(K%NwSc7ld~W1V%SSjfB@615a+Px`d^atr;);*crMvy=u_VI!Q;) zp2V@acXAQ%gN+4Ad1s9lI7CTEFrtX>mOiIgHRuq>BpxHEi<|$ra~Vkh-vm)5nSipv z2`sSey-DAvJDBlO^DY?tckRJzOkx#^upu3gW5BD-qZaYl7$ryapzqzZ^s^NM_4rz) zQ^9t!cLWtF>%|<-3vr!(OyG*&|6rtRlvwbwBVDA+h_Ibg+kY}^XYf#n2eJ-BpnwmV zB;G^$ejxixns%E@VV?JLxLy&<+cNjUkWOZ*@}OB%$1!1#k@D@(9@LLI*JbO$(D%#T z;8sT1divM+6$9=BY@oG@C;+feZ7F_YkEq(`<-&k$FAR7@-@?Crsj7WK_-EC6xAPb9 z8q;$R081YuBx^IidslNw54@}N5`au^n7?>%fL&oX_~giS$IP0$GEugxK>kjOe$tYZ zI&Jt*gE^lzdKc|SEv4QQE?orSvH1Dc18jw_EAf1NRg)zOjmG6bPk#mwhV#H&eWgWt zCY3Ma3j*2;(o3y7T=Bj8ty37%VSJbs+EZG95~Y{rfcoKt@nP<Puy(`pdKOPE$fq7+ zyoq5NjaP|dDC@j*)*w`@v=EA43A6uA;SiAxEYV(3oEl=rGA4?OS(JCgwZoW;b)5@x zR981XK6pMqK)XE-{$nN&@6{Rv;dbonI4L!uqBKU1k`QvN@tQ*Bq03!5t^vWzH@n<t zC)9+|O<#T)EEm90z}NevDn%5!iiPWqf{sz>pjG!!Tf5ieXr(n%cuEBE&_TmM!OtS? zKmpi^m~A?QNX&6~VFkhrqx#Sf8QN~>@kI6bp)O7e&V^wUt!coj5r+4-Cx!xBP$LLM zQ~rxBEfL{A@U1BaC=5nzBt6B%TH#4~R(~${V_258$p}9#Rwg*zxQtk{75Tj-c%9Jf z3yMhTcY;5=dSV(;g1C&j$`0Mdd|17^Bwx@;HT#BFf4({?q=E6qEOv1x#Ar9@Xo#cF zQG1oeW7^WF3*he>w6R;`2l0O&6)ZFge6Ha2aTx^G|0r`@?W4+;@Dw%P5q%#ED4C&< z^SXA(h*AQ~wFd?PoZU~@a<Io$4UE$R_kI{i0Amrc#+Cqc0~H_opA?Yi0t^^YVC#-2 z^>)4Et9WZa6_rEP^rM!wdF$grj~-7-g7%Ey1EBw6^=`<6cXEW<qxE{F^Pp~@iTw!% zq$J(%{5g=gIQ%g-4(9)YI-s=I5w_*TWzl7G!KDz|y`dzzNq~Ooj_cyt7j$Lwt9EU> z<WxADiM2MH5-MAXlvPm#XLdUBTZcX^a3i(#ehi+M5(u-(xh?z&+!Sq%rua?*(Y^&8 z0%aI}UD5n08!+<ekg#237Q(y7rU-ppwYuS$32d+eWn#j|u$dqf=e-?(7U2XsxI_ir zNOa}^oHBmOh`0Jrg5BS&QB#IlT1R8<;cCrkUP^aI{thBY!lC0PU^GD`)0uG>e8~1l z5@io&rC(9I1r23ywhO#slrrfbcH3-wQ?QeK8K%enp0!GX>@7>t2k7LCK-F8DfU~4H zxLc|0hnG5$CltS;2bapULnar(*|?t_MRgWHpWHN~Ri_Yfs|`3JGBXgK-NhTkqja51 z<5lYmU}_cUE%8GDV+*TgUCT}$POvVJHfk2dW_tki4qU_hNNwJ<EtUC`5!H#pKdK#P z(q2ntemJ#D0W)*s<432l<(`C1^H>yXI|hgC-OW4x;hs;%<JQ|j$MhyQTB8l}RVcX3 zz0-QA!N&z|^h!Fd>~R*IVyFDYjIko{;e|TWOF>*C4eNH1NpsF%I%s6<TZj-8eW%t9 zrt<TV^<fJ#Vukf)L*vHe$<C$fUwKiYKw{FRZ_gm02<1A*zF>c7ev>K2i$>UkBp3;5 z;yxeKKKdNWv9j?Y{N!dD$*H|MApyU=W&T#!Of8;_G<)QEGTSsRf6OzJ909Owc9F>M zkOHk56S}G#fs4@Ho}mU3SVe{3vTT?)sj{#r+_{Y#g&;s%<~B(2i~Tko>%TL!azLLn zdsxCn0!0uAk1Gt5Vu%TbS2Jzr^ly6ZoU^_6TfvHXDG3avo|jGBAbJwRj{skk5bo4$ z!AcqsL{Q^4tvC~#rcbcCIaydP_jYhD44C!C*Ct3ZS0PUrxb1RRrKnqnoY?X%(K->h z<o#P`T+aGRPb<UQyMy7>W-nKzlMa5c&8bV1CJnmj-0_E!vA;B&V0Cx!em$P!X!SYn z!73MY2wu1&jau@65f?Q@vJ0GFT=Em6BsXQ<O3>bQXwC4Jqv{(NGz};zT)H4F#753+ z#H)bK!Y(j^m9i2mrKr&?8~-Ei$q`ub)*kDXP9KQ#f&0=&^KjxDF5Jr=arlcW)vV|w zG0c35riab-n}UvO=}a<tC83{3x_FoI58n;7#*14UO=g_t?s@6L4L{#qm$1C#wW&M5 zp|3B<yp`YWLXA)vuv`dZOB04gwI9$Uxo=!hr=FLJxb<V0FXI@k!eCyEFOX<(3|mlf zhrRp`1fjG7075_m7+S!PN^AhFt{QH)oR4A$aog_gj_%~@Xf(#58kP&~K;KGjv%&90 zwY)kao<0T0mf3=T8<JD)UEQ~4%cVZsGpJ(zD*hoogaUJEEe!BgBr*N^Sq<K1s3|G8 zSAMCD4-tuKy5Hl157SV>g+=xh+=B3xfB%KSw;F=))x+HC5O$!(e_-SS-06`?^Q~CX z>`e}UBXXcjb`Mx#c_CciZZKE<QhL|=lZr2S&*-WB{V<*P1h`uEJ)p?vpz=KiWdiON zHXLAv!YD~CbS~|%cgWI+PG;>Xv1L#X*zG9Xe#>vQdV{pLWIfl%oLkfLn=X$~&V7RL zlsw^F8|CiqK-6iF3}*N;h=8()WUqi6vNqZ}^jB~EQm#J`Zm>uU7ceCI9LCqs4Ul1q zDlUNFFu*wP0yri|E{ekqk8Zq|sEx(Cf2X;Bnv`+pp7;ieI;^$@0Yn7DE}8@nTNJZF z2iU#Bg^tx4PerZb6N(k(tE%%-ve{nkkp;^_l}?nB=_Dw_(zs8JzyD@@DW^GH6)HCb zGD^)rWVa`Nby_k=EXZ*q8;0I+Iv*Oh4r0DdV@e`7R$-rjo@c1u%4^7-1z?p0W8l1i z`Dp;fItDU%AvBWuz=&3M!x(p+!Xb_0O+~g1DejB)<RNjmUnqMVC$UZMcF&>aW92@2 zf=Ice5@HF~2BZCk^6A6)BL)Hd4srNf<58336lb>6=NUE?<#8_b%Gc!aCzm50G8kWi zq(%HcZZ{=U%VhyYX!|lWS=6}$D)9`6^S&Hu*#`KIFa_?%fCUD1lDcS2%5LPvi2ifr z^8rgC%Kh%|2zdmJV{DjlwQ_aNAAFg`*DcBM<LQrO{~AUqxJ+KU8u>bxl~PJ`nK-qq z>@#8Oc+gRc>WFb$lGjk#2ayMgxU`S?tvV#wrya_kkoMlf#03*zBrkng6$U{2jR+^@ zPsE?=U{*G;*9vTaDjKpX2zAInvvE%VtA2o{>(<Eu1G1Nn9w|v$m_>R1(~5@ySxpMI zwgDx#hGUbUDcpsedNSw9j^jKX=510Pl$@tx?8+I9Y5O90NQ%;;+|v|<fKTF)XgLBw zCW8L8xiive7|uR@Qa}tiPylmynf!tti9UByNGw=+a-OKOYfX>rS%FmoPI(=P3(tYF zT?VAg6-+n;bKn4~lY5iE6#vBugpk2Q^(`=LHWPO|`kitEEGm>gmyhkwj}=SG$k|iW zV(ztku)#`Qn+xg|O{}G7)oA8|XT1(OOi~f#+X!CzAuRWWSyvQRHcwl(6Oc2$$Q=*? zrNI+r!0m{n)5vZl&f>>e@6==f|KKRVFC*4cN8}C_EjB<;x$tTbXm6Z1ER&cvET!Sd zA}|kcAfw{wrt}*~PY%dx9EiPuNF7RagCbQeGJOClqz2B%9uUExOhEVZ2U--*;04bi zZS?Y$cjCnCvn=Miwtp=DX?LCAEv>q<J!yThW@UvY*qxTKNvL)Ad9C=-BaM=h0eQ%* zMwKKRB{ot(leBZXT35Unj$yBD!3xmsXLDoje&d28TaIzQ0a_$1Ju1#&3O>Mvzzu8< zqscDkUXQTg?KUB1U&!h)^(g)2&x+fW30i5204d>tJ9($5k;`>zp=$oHRBJ02FpGmo zcF$YY`JBxe3slY6F?+6ViEX#JoKi_|8(2btixc#YQgnT$6fazSWlWPYDgDSFADz~s z4Z&}K(p^8~`u+`lbo=_t>da#93@jfsSPdJ+B-ZOf+!`=!#D&qM)muR6O@Yt*^}T8W zyA*07o6DGqPA>4N6wYO7YN!mgGBq1N__saiVH%!CCF3atrpG6#7iXHIKuTSUJM2+- ztcMtBHF;g?Q~V#Zj4yqQp&dF3Y-wQw9kpixQPeNR;sWGEC@Cp!ze$k;bA0(w;9~ue zt)S4Frsm8RwJ?dXFxYasUxTh{Kudx1o&fde#i$$?w8i^jKnm<r1iZyqHv`ipJnjuZ zPNP+iNWGx2c(3<&9=_|I@_X3)uZLn7yA2|o=mH)%#S_T8-Q%RpCHKp|LuF$c{@zYm zaGQlk+qc>YR@q|6vM>5t4@Haj=uL2|sF;$as|Arixu-z?oA)zeI0i&mNDUPhoHm}V z#+e7*q`p73FPOv(4IEU!oPZv2w5-VuaLma8H#vu8UX#?pQ_tB3OBh~?QrjmC4$m`} zE~449KylWdpAMQ@LP0rP@H{SRWj}ktt9la*9GED>k7D5PN&%N~i^k~>ICjeNl9l(x zW(fyGW59nHC)t>iv4IzgKq0uel3H2j)nM6|YuO=Uq9?;UE_nQJ;Ay9`q}LCJ&NlFB zD$oyostRC7i7r6qya}+6IdI?Z33LZ8WQYn2=7F6H1g_y7aNJ%sF}t~J_h?2b<y)V- z>icS-q7WMd^4trjw}=OB+NwY+e3!G88~<K3=N4Daa-D2Aj?`rr!->Ov%vQy?fo~~v z)&CN%)dpVubIo*kACI*07T7il?K4H2t&m*4bs={eqyqc+hiVTI0oPl=?Cb@wp~=vQ zbkI5)rVzsp-$Z-_GWP*7OG;#VEDzo=HcJ|vs`Sq%IG0_0IE0j1tvp0a_OpUEdLYdO zx*BWj(^mmT__ZzaKK;9FXVMqAXwD}hv5s=Y?}^%OGJZA8%$W<Q^zmINqiJ7OwyN>n zMSxRvi!RAbr_3acIN5hA$C>(wwH_k$usTqKVJo1#gvnGD?2XL;&o9OibwH00FMJ%6 z*bUGib*^Do7+ysVxTyw6k4D*=Ogf}=eNdxWOOg|CaeCu`cj&1Iet#;ee*{@d*c`^_ zGc41iG;QAq;ymcr#@%wwx%&1M#UDqlpa*+FY@hWZow(5W1uj@r56S0`1O8F2rR+HD zPDhPYgS^v*NC)0jH~tAZZ9InwP9_GG0>J!<59yLR+IkU4rB)vOZJ6TejnlM?g|K%B zDLa%|3r8!W>3UPS(6*u)%9Fqj36EQCdq}R%x-lKS-kDC4=x#tRN-<%OrJc)hhQJ%{ zqR;xnRFkg-nm*uVH^Yt}Xy~)b$L5JcxU<_7OkhM3Z?;8F0ZwA}pm{7+0v9yO;%3C` zOiq(ut%lW%Ie#N=e1{`dHmGs<s0w-DdGBLDj8xgv*L9jq%D1CXc1djak93){m)_Vu zw(AKd&37y>tHdQ#-x*>1F8C)W`2*Gt?HXR$pnzAT%!r?)Y+2VOn$$_f&;k{RQaL6Z zD+ugOF~LpEIV^C_1X#dWv*Ko5z~l&qX>bD6f$9aolP5qQ^j#Sg_U(|L201H8)EEPL z3)-&|gHs*9;(^l}c=gmIQX012ex$^OPur%x^-?6CR_>MKnojRFTeWq%s1!jGmmpUr z5lGlV5FqGx7e5huWEum1ftC(~TDse&U`2pDloPnDKZm`ZGyUr@=4U})fVxb|zfeG9 z{Au>8a}gS#HApCl9=4oDCM9K1C2IXe+=P5LXod%;_bVRJ`OJ?DJUb<x$j<WiSo1tu z=0h8}Rr=<~@!cjkHTp;MX)295(a<haO<e=EvUZI?BpX6UxiS|kgi;vP@W2XZ)0L14 zNr83Qo{+Nw4bac|-+&z1ePVFydjJsvSnGpSXA(gfVQq6}*m>#|6S$Kd7Vhy?Svih9 zX)#GPB_6dL$nCt`&-^TKS*9=6vs5}R%lgT8-ZDePh_EWb!~4YSHw!9AEd~Si&qM0U zgOd<Cu)|_3-Y(Vwu$czXEjI{6c>yEKI56E(2+$n>C&cXIPi^Y}qNNx=MsQ~~Wg@q# z7H7^9p-PD<UFYGKL!UI#xG^JXLR*bnKl4=8VxQ?oIHkRdaw>XTbv>ec$uczXd#_DQ z^lL6WnOa}nO#+PN1cp-?D&*I55$c-%j`Uz3$k<N&3%k`2LqKF6prKMOi)<lYYOE%j zbmOH`p38W_(Rxo*sm=14nQ&9`o#Jed$AQAGysc8*AI&>jh?veaJ703)H&4kD3?FEL zfN_{6&Sf<wh_a@vC;n^)^Phj+dEiR-dRNhGV97YjJyl|M49C~k&uN?~!zpoUZ$D#Z z>4kPLA0l(ZlLGq>RaDlDoK)@(jT+z+5nInn;SJvbm+VebC&`?_uv1QlQYG4&^<vz5 z6TnAf?P7QrTl$h{$>ZC)7<>^ezggnS1m-#k!D(85E+7*4<?!x(R<tmLL69go1)Q~( z__D<FiD007h0*bg1j}Uc-{F;Nkco0gTVJx)Ar8*wLGV7Cs`4Dl3um;jAp8v!kp_zV zaPuEz!breFXKW8Zo^2_h1nqDP199OEUpTHiJ#Ii)<^wkS6-9V^qZ){l+@T=ZL4kff zZ*^6J{Ota&<o!A^%}lAQfG2dBJ>3+XBOfya9gpgrwl-uxkCMJs%m;bls~~qxaqK2( zlHOpHa9&K(Ag6rT{=*HBG?39H<=<<{(8>kigjzW>3=PFQeBDRH9@y~+sfRbL5gi>d zR#Pd;vG^cQwiY>jx?_kJoJy6PZi11b0?p8yY>mE-)O<Q!a_Vh_q5h$-0h%Z0WDn5S z1<faL!TY4ez<CIC0jtaej1T<Y2Lk?ci-tzIF^0G>;S42162KUgCH+P@rFR5Rlv2YY zq9vj%nOT&57>_)a!=Sgf&Q)k=;@7s6j%1UnWeDTXXV)Vy9yg+yY7F#2nzm1H#KSP+ z;4hrSg$DY_dwbY~^CqwvV1pO#hYew>x#0m)G|G5a6u8o+L|wA&?O%A{Z$urXkGttz zJ+~zjYSRlzVLH6D=Gxa&jjmf#oML!!Jhbxza1Q=g*GKU{J8AzeHp&+t)T9=Ds65rv zPWVR{M?3~9u<7K&3+FOUPJ@S--9QxGDiL$Q4qi<QuyK8J1A~B18HftmR6(#+uz_SZ zXo8r1Hj8!f9*Y&>z}-7n^+fgtc)oq=+z1K3Cvj?AWw;`Crg%1;HWoiG1B;>J1UxR4 z$6kWqdVQRAVYo`A|M{N33^-c_kxO@4a)F2Q@y!0e#uK>!<iWcKfD@JS4i{W_X>l5l z@?=2_4#5k^$Cejl<axQN<q&EdAk;ryaX1mH>vLRneNyxmo4n_IhnW5eRe@0LdJu6h z>9w5gXUMach!*t2U&K>o4ZzrH_qCzF7`XBRSPFRvRMQ~&)&+6snNDR0{+2svTIC_d zTTVVqYy)UaZasy>)B2Fn5p~!3)5Sewau54G;#3_NiQWnXBa2e551W;x2p8_u%Cplk zeJf+pQS2rl$0rXAAY<<#A<lABRn9Q)1k`bQk1p_BYYu>M=*s2Vk+l}cmlJzU9_OPo zkxl244A~c6sor<hEG`!I;%NruZMXER+w0Pn3O%ETI`EQKf8x1*699j^hiRc!M!{5( z8aLRPhNj2507xZXD$d1TG~kB!eJ5Ia&_apa$Qqb=aMbLxvt|Vcf1Ob5+lM*$6Akdr zHmfC0m|l!!ZCj`GAzEmV>Z7Sdy~&qOJn%;B{q}ehfR{KQzY#Cp-HOEXkV^zb;z0+L zeW(MQ^B0I2h$!&Rs(84Tb`lwNuG7!c2Q1IV3N^HA!napTJviY1X^mJZUQL1`u}x{X zc~jyEA4ZL!QmwswIPnEZ&_$je(S@qoOgT@M0Dn91XqVmv1Y>7k!zS~-%WlT13R;X| zGshk%pg<xAyhIToBwE70+i6d<<P{EjDW?WHl8*op03nlRRnEs1f4ta<AF@(y;{;EI z4~pJ%(!t$ZQ9;VA)gSwziYqbc935Ty3KZ;cc@5=yg)mSX3q(;d48)4TU`dED7(+pc zq<IHKQ6lw{n_MIrLqQq@R<C$yTzQpim2$#V%vPd1>mr;US7gd+ks*qoZ>Ci+ul2{` zuD?R>hQ#ol3{%<iLw2PUS%G(n9<Ld}!RZJ3%1X3=el-mZ(j?FhN4E0>1_!mBp$v3E z{0Ep}K0MK<@d^oNCBTd`MD!}wRkAz$G>LJeiGxJOC$7bNWMQp4#M%D&zF9h3W9G*| z+}qng@bhaoUW}g|YTJoxRrDTN0loq|m>A5Q4UhPz<9GU?lb1~*^fQBtTebx9Z}y3k z%0A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 HCV~GC${HPc literal 0 HcmV?d00001 diff --git a/docs/public/static/images/templates/checkout.png b/docs/public/static/images/templates/checkout.png index 577daf8192714413b1a858184b46db085d5be2b2..e5cfba2cd01c553f71515bd7fe82925a8d2f7878 100644 GIT binary patch literal 22378 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 E0v^-M>;M1& literal 21469 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 diff --git a/docs/public/static/images/templates/landing-page.png b/docs/public/static/images/templates/landing-page.png index 8b5de5ce86bd3c2b3c9b9608f8023c820734760d..a6dfeac3242598eea570de8b8e5554b09672e162 100644 GIT binary patch literal 34799 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 literal 30575 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�HN!r?k|a#l+&_~pLzIm<xZrUTG&Kf|LQ_Psdk z3c17A>fa`l437qw6r4i@Ec&Hh2(d~1A;{Y^=<xnwV;1>I0B!+l0LyDf;{;$x;fY@W zL$^V=AE5|}e;~J>UOCWRGi<5b2CrzGN(S)2zr9yfWb(%MLLP(<h0xssXILaOk`|Km zjHC{`IN0OBd!Pk{KZoR8OwUlav|&fvQicD*Y9}&di$B}6LmW-tZ$vbb6x7jBUvlIH zs+UoGNGa#MX2NbuzA(W)XTUP+I$FYt4MQ+a>eHP4MyN+oK|_4_jLiz24v!Y?wbLF7 zJpAp0V;L+6mRX$YNr}tc-=l4w)IY6#XEiInRPpKgHMY-QpKCqtEJJ`oEK{f*M@mhS z*XWJ%A@%p>pcaa_K0ffeAR2iYFP5z^LT5WGBtglN8<d4g2QRc*_eGN5&c9$RFr0b` zK2F=h|Hp;{BedzKMhrvwfS<eWu_h+v9mwzbIWz=YN>Yw#)tE_@$UfzIAJwYiNGwh9 z;5^Sk^(jh=Z7xR*yiCSs0Gk7EK=Y8aeDO8493>^&Z7-bp=%EHG@tO>K*V9DMHrd1C zEhBLNn==FcM~=%BH9l+>-l1TKe-tH>6&2(wRkGn^**Cu#h$mL)WCC)c4Iml}uo%bO zyd~G!!jxj5Out5rn_-UwZ8N}M-rs2<)<6a3la7+VYCWDq-PM{#AT3k7(Do4Jad#MV zpe)D4TLRojzWZOo@O(7{0e3H0t@nXHWLXqmC-14tPxV$XQgcDyQ$ZR-ZFXKZr(CaY zLGK(s*^6rbncm96)U{WA5aLPL^&ZLHt=Lotj|X&Xhmnax-B;>gj@re?{1g>nMEyYY ztaTm@3g?B}h9wg7cmgVY8M`?U1X<2Tc}3`PPMmiF2?8iP3lS#j3$89HYEsv3?cJsO zEv&4(V3NIKe#^V#xH2er=KeSZr>$$3C)lE3oC2xf{?!Vk|MVBJa<aF#5))JObM&%c zARaZMLLA`#=Mtts8(k18&VmStj-(THR*fWMf69|npa^R|-bths2y*Ch0&Qo{fj(7} zz{#IImBwL8Xr+|Y$uxs7vQ3x`xHM{xk&OC3hzFQA2)We$KS&CWQko4o*#kaaxcMI+ zN1zIK!@XJ38nQ2rq8ERQNn9-&X`l}w|B!_>Y4=_xvMtp=>5pX|o^-UV^Aedxv2Ju7 z(r!A>qKY6h4}UZf%A{ofu7{}Ca6<pTtc*-NHWe>+3rHqD1l^_yI=8RKeYQK3b9cwI z6jrKah~1bHy4t}e2ZisSD4(H*5nqun8U?}_H7SdyoDWNl#6Wpie>C$#GWg)(_5BB0 zyRF#`khUrLthhDp-pTgLgRU+<^0p}~ZLF#Tv<6xnRQ}kPYAEr{y%TjIU5j7etw9V| zA_vCjf7$p)fZ@9ky&ElB%`u(u&1Z!F5IbE>zi968;oks?KWz*N>#7v%_`?1MV(N^Z z;odV(N3f&|B7wq59!4$CkR<V;HLGLVU6vW;7!iNIA~UG_26PYq$^ikG&fZf;>~k>b z=ofsIbL`cJG1g&M+C5BuH`!0XNqOIz29d$+HIStS*lKnDHcw7}oMXhlw$KM>lj;?L zltTSid#x*c7;X;wrYdRsjA3Zw5$qKyR?@a;*heM4ssDtZ4}#D9Rbf>*sQ9YWDaCIR z7URW7u9yzAotEm<1ekq((&-C3vfm)%gEcd5F+w|VE4`yiGoTQ193iad1|E^lmcO2G z2Jkr6L}E<2PG}N3b2Cx=Ax4XwS~du2=ge$+nYu~YhL|1C7S_E_25RW6gJiAH1dX=^ zC5%@Xs6_J|8NR(vf|>@OFh?HA{we>l_l8T}+Kk=84%UqfYUnnO3A^O-Z)XMznDQA% zoYonauS7GH=EXrbe=`$5ixK`{Ou82rPJ$wdq{?H3X4$}eZ(sYD7vDCSponXbCQbN# zWwtk-7ym-nb!y3N%t<*ccw>hnX`3@$5Q9jUO+AXS&kHYzfGlt3$@45P(O3A>csBT7 zHR&gdOG4K#0HD|5ndjOL^brKA*Xw=CI|3^;90%w?dQUuEZJZd<Xdb#~+z3eHVq0;@ zLIHze8uIHGvf6|+sQX-hVUs(?uVo!XXC7uvu&BX}L5t@I5C1on2!4GgFo}M-#S@o* zMS6%!tLPi7C|bvT|BW7QeCr6I1OTavlx1)?bBZ47hZ<JAu~vRV(&E0;G!O>}JJc2^ zu}+7)_-gLlFZHQfAVC=6KG4&EtwXQM4<(ZMPq_;V2fLV_@Y;srtl~WDzoMi+_k6=P zBrV~)rHAe80BD%7wt_<8*vnnNxwepuffgE&8X^;Xe0D87@O2`KhV7>pjklWLxUk`{ zyy5&hz76Szvxv41G=Z6RLY;+^ma-wo!WjKf-;PNJ0*vsQ!s2yV4Asw?pJHOo7Hc?} zdHbfbSSoFU!79thHGRpMt<!$u^2#u$;!h3)LSZ|T<yxZGj=t0G6%}TGuTvyry+V7j zq~;|9D?V#xihMn|PW0EPXLv2nN;>zMsSb7ghz7I4NSa!2?a4d%+enb=r!hZ^xt!rE zW$)*Crm#>qO26U%ul+@lXH$4={tPs-BX4Xi*>MPS{Cbd1^q{v1oH;+0jp#RP>8nAw zFEj5PgUAV4I79tFJNR83Cpx0T*<WZU*<8eJvG3f?-MVGJ1&i+r)jpP#vj4Zyc^i?p z&GludBo#~2{kv$)(j+bI7bVfhCk`-0b{xFxX>am{uK-166j~gN-X4^x@VNTg$LRvH zF{4i*=xTX}B}iGzOzyvjf}^!cj2?*ptJBrtX@=l4^U0-D#cfN|I4;utGf88ToIgC^ zJZMO`b}gQ8*^NWlZ_MKsxtH(%lyu#Ja6V68@4fflqj#sB-U(5H=)DW4_f8Tmg6MYy z5nYrxEuuy}y#>+Y(St)=g4g%={`c%NJG1l5GrP0T>}U6hRwXr%_mHxNtopj`a=TnS zx#~#fgd3drhAM)W{*+9$$1QELLlUEL;BUv?PN&UWXpA3ZxZR>f9TIj2_HjY%BP$z2 zg)I;#H_=Mc;xWT>T7WLQ;~kcbzfy&$=Z%D|(WM6eU2J0au8i{zugiBFz>}vpLR{yX z<x$sZa9ImE8q7;G`7T{XEbZH;8&k~L_CLOzuT@x^$;lY_1gwh)_|9r4A%A9SF*L+% z=n8NE7EH7fF`AFn%#93R_Vqr`qNQ4jVf-{_B$lj-Z^pNOVa8zC$c~J$p{L#bhJFk; zA5OoZn@YR{@b1cBnR6ujiw(qZPmA9jkDX(r?X>Y&2pl7vn1LWiJy8Rcmph%P5*_%^ z2K+8@I^)T3hlGVBlUzT^x={K(J^ayJs@fe_YXR}%#t!rKxP7=wXVqa-fMS-!k{}Z? z;UFj`Gk^tw@-iV7(3t@+FBDU8E4G0I0ONul%lNpEATVw?k>Ygzg*+8-_{fA8ZX2|p zq>cL@t73d_bN`BCoqi}O;s5wQ%fM@XSm67q{*N4mV{`568Jxb!RJf%?%=G!iJs#kN z2h>z2_=052^-M!cg)<$B`NBC-vAwTiOT$5cV>PBtc_gd)SiV$26k}lE(#B|mN6#N0 z_ez%Hd{*=*YV^B-jk||d<Kf4l2gz_baqGfx{$290G*O)hA|w<Yrr<8KYG$A7^OepH z3x}i<FSV=wXSuf=%?YSau<)&%4raga^5D=QDkqgs^cyWV*&Xgb%zvzrr>hwv-@>6! zLN3GH<Q4-U?0aqdr=?wLXC8^d;W<-l22Bm|;zY3wm{oROQ~Oeqz1B+!pKCD;Y^sVG zqc)B&+D~n<ibi0Ylpb-y?;o;-cm07{)m|=S%xSEAS8i_KiNtzSTn9<T6w8lgo>#;h z#XBVa%pp7SdOL`-q**mumZ*T~yQ9@h^*l6rmXzL43T_z&HFyK4$hKE^hdgs&b}Tr$ zx8j$7o@NWgZidV_%@NSr_)Op?(gQ|lbiCG-5@HKV>9q<>NzLhp>+h|gg(*@KAZNwf zJxLopMr2uQ5zIVie!j~JBzL8J%#f|Es3IgHLvJ5pp%xO%71)pZG`<!^ETtV+YPS&< zds6W?gLdA+<q4vPxO0P%!<!`~+UOlg_+sxQJcia=j^wUEh1O@~yLXe);Zgm`VC<S` zY<*YbPX(80q&Zto4u)@|PisH(HsUO8RwX8Mvq}-iIzz3`3t@1jVQRT%ZifdtZ2*f% zqMzBR#@Gh}nSLdtDbcht>k9||At`g%|D!IR%k_mH*nwjLjv{4|T{t}(2SaHGNDRbF zCX_2XyHBdxKH_CB<2$BX_`N|Q6AB04Qp8{#6n>s7Y4f8@2bku22{3Gjyx&4daVdz0 zwGW-$Fv<V%gkL*Tpu9yX@B@L6H;*Euf;ZbLgi0+$Z2N-U&l+0s3li^tkg=;Vp_q3N zau6Yy7H?*#m9U{JJHp%*YGlAa{yl3DR-N`AE0=!zz5j0%C*JHkhevEHu-hBen6CZV zfke@O36xSQyfyTemBi>IS<;0GWonwI%4<ug<20=x-w)fABPmhq3j~rn?r^J56C_<& zP^M2aM)v3e?<LD@^=MP3VAU&feY<kk5#hfIQl^R@I{oZk<ZVZ?C`LVsj;;<iY7ZAa zrDd~uB(|@aZJnRH$s&<20VdQhe39ZS*610cgK-7qOS!4(H*2boEWJ8Fw)Kcr!<hyY zHlY?1{HhG2D@hjlzyh+mB&Vl_-wqAjci5qwoWHh`g>beT0P_pGm^7b0BH6#5<VvH3 zSbuev1X7lpub0T&|FheJ8$$jsfoBQY&rdZUpJ*7QQ;&Q$w(y83SbK_K?cS3M_7ZVV z|F51O!baM?5eMu-?GK+Yw#D0j;v<FUNkD|Pu5kP5;g}G^T_gul_JavU*>v?dSC5ML z3m`+KtxZcXO-nbyu<~JAG<N~)<KQkIoLJvd&%3SPQdk8B^2ibClzTpJ#Ya1C(m<M* zYTQLcToJFkME`h3<)WpRWQA;fOTt)JlQCks?l5#!shT5+x+$G3R<=*BAAtn|YjK7} zpZH^AJ#DhAlgFo3*uD-2Sv@07t-LX~SzppsXf#xO#kS*-lEKmwlT-9{19$B|hwH%X z@qvo}j6d9t-&ruD{_SfMWTEuw<fMU`xGB>&x_L0>H_CQ2bQM<XqS&;R{l!N9A!iuI z*yy9G{PonLEd7YUxZx55?W(N|(?6o8{Jwf=;^s4b+a_yViLN|^>@?zcbCBZ4l+DpW z7-N%<s)d9ZXGIBp3W<%ggeU3Y&&lmR1GBT2Kn<DHGJg|LqsKB`Sq&zA34~{=$pD?F zW0ohSdVf_||NjOLa(&7ApWoTc<k}$YNHHDpGDCzBbMk8M_h0xCoAk?5{;-+4GFAl= zo5x|UbO!?Z+&`Qr*Q8`+XL(?A#Oi>s8isKSzFI~glDypR>Upo@Gqw>|rJ8`SW;Yta z@+|hzlOC&e^M!PKGmRc-o4(rg!|#N$zRm!N=_bSsr`Ae(Ib^6<Z7If@ZpMc(69_ZA z(hpu3Ur~z9$Y-yH&uSL=nmQeRBHC4Z8R6#StK!0s)BL&FRDC0U?1h%cGu|X;`sC<~ z$Im&ru!|j15n6gG)W#96qSnpPiB^W%QUt@})^|H$Y|k}&)}Hb=7*N&2V@!8|tg*dq zfu>jf>8ld6%5)pYA-}k<=^Im}CiBmK)p>mgGV>W)a5-@i*3NSPe951E4Vzad#$W$8 zOFo^QAN&vLPqOY`nb5H3cse+S4}{6sR{1)6fm;P=-PN=-<Wyw>*brPKyJ^+91mmmd zhZrQd$&Xr+oQm{O^o5>@OYc@iEE>m*zq@}Q2%wB8ACo=7Y`h(##G}Dqe`z-H*b!rc zd5U96eD=&XO>k_wU!gOs3Bb{C0=Cwx9=EhSPja#4sjjO&cG1rB0Q~MbqOY7ba9J72 z;J1S+4>Kjo>$z$&j#kju)F+0O*Gz)JvTIM6nm{2&!UO(oAA6F^KaoUc{33N;gOdp< zqkS2VV1&y)O&xR`gH{BVSG34ni8#}ttjqcyh9Wj2&o4uUG!rsNn9o_Gbx3(r{1{Q+ z$N*6iMfw1_XVhRtYdBwB!=>l5AS1Mkn3IOPwgGAXUib*4q1r_7g#Y9y;74)>>Ja4R zX$H7NmF(@an+<Cr&y<x<L^o1<kKp^u^TG%ZD=6XO`A6)E`HG%t8Eerxe9MzbbAs=D z5zeqCigv4duR3F|&ry5yTmp@wFh54niGS5aP0FVHqN<iR%X$)eWUlbp_~?>p3Gq|A zgQ|_;_Q0pd*np&puVQLt`aWUy>l%I{$)HzRf^&!=yM}JV&D$XdLBKsGt<O2rFeT6j zVI-u*%|F{`kIj7)s-4HF7D#2@lF_^$KRyX{waaMMnyYwHR+=`HT<0$@$v`p|z*ibc zFrr2-M_2mT+lki~vOi%2tjubtR9B`5JA%>Z(w4Omlh1@oH-qGhz2Y)(61ZksKJu}! zCu$#0W5t_J-%z%F^ju@nt0V6ek`Zp6wq&C&%uib-GcQR=vvMYR39KcMK)Au)FzbD* z>zT$M3V~Z+3o<uE5egs>WYMn0u5X}|2AI}LlcdZL#7U#Vg{n>L{{YJMIm%SU;riO{ z9vNZjmgoqw>?+)SH*ShS=%Qeb6yik1B|V9b-H8hP{GB$$(ch9IKq6&I#1lRIBJ;^v zU(DXo-0%uSYspHn1F7(+c(1zl^EV^*rW~9Y&pEjUM+<533u2uI$SIsP>(vqk(Vjbt zb6})S-0^W~9os{skS|rM#GnoZ>c@vMTo(#xrQFUmkHH3hF6>FpliBuS?(g4h8juq` zE)KK7AT1VIeqM5%l+h>$mlHrsjz*+AHvGq*Pa3@rTQ!gEHh-vK0imXDZs%lW<D|kd zr%ivu$>qBD7;Ohs`p(|=?W!OmJYW*l&CiHXg})`Uf3aICXBWn~q`ie=V+|9cAq2Bc zr}EeiIvSFF^L6leJE&dBCwMxDn=Zs31D&rVVCi<J74mOt{{bDM_a;kQa@kv>qjE|p zk(;6_3lBT3PGFd`Hu!T<0#{XR^_)6`RAaJ|KsLRKO;Q%UL?V8H(aFJdAixBA{R&Xc zR)MmDj&E%;tGj*aK}4BkmF@}}Qd2D7UlM)K(vM%<MF^9sag5_-<HSkv=D4)tpt{wo z%z$83R7n5RfL*Q6iGq)I0->Rfe;sW`h0Ky6?82{>y^=$p%&OrYnaFBCzYBh9jW^`X zA!5Cxru76Nbf<1*Drv1Y)-V5GXv2Vv>~-7q!f&(Dp%s6vtr`x(%?%_VQVgUSVn+^G zx)|*wq~zi}-tYrohj6#%mP5MiUue=B5LVl+1EoosbvcL>YRApO&@S5M0g*&GVr3^f z%9u?85_cjX%VdvGKQC5w%GDWc2@pLsy=bz28b-2Ga4AMqXSCw!R7aOKkx`^FNx?2A z7WUqc#$GT?)R2i?T$yeJ_N3(BLC$s=kw3xlyXq@3QSAfhSs^~LB^`uJRXv>LSAIU_ zj$fAcbuRuF4Q>6#N&72xxuGymWN*nE72<R<57S#Z^Nxl5w<A}EVh`^hBWo^6<hJnQ zUBB#B;zR0v^+KyE?J}UiE(#1#q&;`Y$|5I3E6kr~4s3HMz@iUtF3U<9yqB}w!1k;z zSCLB7RvBIf$rvq!-FQU)9Tzm_$mF<!{uNY~$WU{G6`jR|{MyHSbdBCUWv^Hdsd|LN zQy!C;7%stDvU)L9Ri~GGGaPfBRArb8?vTj%+l;M2hJa8jk2sTBhR>{RXBywcvuV(d zjX8)7-r(ZX)1oEff0I7rb>*UH{?AYJA}fFk>>k!PYf<Bu?Ng@@f6kzUuf>gXT<g$8 z8T6D2hYq(o_6D0}+7SFMWck{kJP6W66MWI3cZqNK8_C%{9Re?Q8QXrc^?giIv-sv$ z+#RX1kGm6W_irnYylT2l%}sbftN`mTt^c9;f;|^Vf;ScvZuD_Fedyh|snAzX7W_&p zDmHRklB7rAH7%ou`I527GaD#0`H{@I<q%yUkWvav<laQz&!RIX&uav9Iw3ym{)6}O z1z98F1Y}A@Ikgj;hTcXxlj>GLxt^Z{>wOa{f1)CwyC4Ft`D^raypYkzKH@Vr=c7kj zl#`4cNcim;KcDOLrA7M*_G`m@#TX&aKYw#g11*V*22%Iou;D-hgk|7(lW~H{1c7zN zrtT}XK1$?LpRqh<?Rf1ILNw@X)gye*>RI9DQ=`oN-Qa>?EL5ra^anA+oD~5`>v>0; zc2x)JE&z!693s<bkburK662GtrSViHKDC1dhkq8aeZAZ|Q)QmB;N9+F@N7+YDBn=N z8dxOp2_aLTParVa*C9*6<4Cm)CNm8;Ar*hXoW94leRE}VG~T*c71b-+o|LW<s<eCZ zT>|Hh7BP@gc>CK0{rprO;2||K*T*3nJ2!(0-r)ibE2<7;!AFheaZ#vk5Dk686!hWZ ze6`d%vq5n+p)mrt<d7xngQy|7(FjjmH-;q{R&Nf%ri<fm@^Qz_O2G8G?Ssnxlnp%U zr>P!bg+RSG3`niq31hQ}Uv>b-8HXJH>gPBhPM$rekRIQ>D3J@V!iI<&tG4Q287$J0 zq*qjR%~hX1X`d{!#bg|YhD6-qme?xB9L$^${(-Nmm{3cnSGTbZx3&s!`hCJ*bpX`l zUj=uqPeIo{+$WH1ik(l{zG~dTP?CxTBXGeqvazAxsF;&_+sRkNup<7G7lm80v6(<f z>PHq?B&&J9KOqZ}H2Kl~C|Bj|8byLSe^zu;42o$PH-FDw%aHwTa<9pYD7@q5FG4Ox za0=G-mlz<u*<K7Q?wdD2lvpz+jRN6}nVdL1Yt~rE0oXu2lv3BD<2Y_JHu~4c{cCpA zh2avIS1-RrNWFc{dGces3NLct&)<tK8q5PPo83DjgorY^E<KV2oPW7@@nMHI22Htk z2a&nn^)I%|Uu>Bh@%>fOmg9LU@&4#PZb*!3vRUi5{#Vm-=>9clF}$cfp3??7c8t<) zOH9!$X(zO_PnyXp4>ySFBPSD2-C<IJz29>nV&)66czPq5I#IPn($aM?JEqRgu_;bq z8p?~5!$sTboa*;HPR9a=*Z*AmWam4Ue>*0-H~&J@0Q~;-aJ3`fL4muCJz@=`BvUg% zIkDmPYXLpcyYZLR&yC;c(C_-g$DC01+-HW1PZAsRxZ}$f+#bI6cS43*cq{<xM@2 zrC&z|^lAffud;vJ&-K5u+O{_<T4Y8Oqg;&u8uc;WMpb>ymN5}GF4s)Xn~x-0FkMK^ z&P1$3gR++}em4I3L(s2IK<`y_;P$9P6dTpWJr;;uDjIs4x!~5_$g{qHZ#723)L_(Q zx6mD+8uZ#~!Y|d`vP>leo>#}%EeC3;l(iFlBRJ8}ez|Ok1u+qx*5{FXkoc}lJ<_!s zf~dfGK!=CBFl>cevUet&rYQ({MTgDqo_V%@I#1bGkJM$T=1u@dvfz-te}zu>@jwaZ zZc{BLkE!o1;ch^Yj0JDcGzOkNcFxNz&&iTqG#3oV9vY&jt#vWboP{SLCF{M7Wc)OQ z0~Ip3GIgg(YPWu>Hxo(y+_?XEvYsiakS=21-~tEguc}Psu#2<4*R(QefXmRS-P<sP z=?6!CShzmbqx@4!2xcq!iMHEXHwx{bg|q3L`oWIGJbZDI8$NlCT316sjhA47t*Kca z$e`Ie9@HfYz2wUxsdQaMs91~3AV=@Z+;NY6u}fS;LZG<2`2{1m{F)q8pzX)f^(}`7 z>Azk}D3`RGlRT4nT2y%NpKdsk@F&Y6lU&_^V5Atg)e#3xkIh2ye{IW9jkSK!G@|3m zt)3T(u#-!*2>D3xG3<{bQsDgDRGPcB?glm|^*KE%J4EZ;M(s%+{8-`nJrmqJEZBXc z5jOYE!P#!_3lHRdm2LpR&4fxW59E_h7BvbT^teVaCGhU;?|0yG1}$C#Qtr_aJOgez zWCoZP8Ry@k0Q0#}vbbEe5znftGHAdrY2;=49w0@pIUddg_quDClYb@OAwtw}2BW+h zQqyve<R~gP0LqNk>y=92oYw-$#j2k*g;x?ubQ)c24S*Fkp$G@5dwnxvTmuge#f%9r z1G6VthaPH#;=ltFC?EZ2X|j_({x|udrEN4U?lJZzdbIEAI$umt*_Wlsy}!=0OD?l1 zjgA%e>kSau#^O|tWO(7fg%5cR&We;zou<wbmoV+z1&^yFIM5m?cD~AkLk0SjGc$GG zI{WNwSF-)guIr8^do^|}hw6*p01<+Q$`368=b?s^?Cp%1m9}6OmUVG#^HGyX+9Hg# zV8Ed?<f+VA6~{E;A05Z<X&MVfh>}_szb8~7#U*Wf0u(lQB}K!uV!-i9n{VuCp<+*9 z^giHiWS9Fn-|Cn;VZ#;BOPR6?+0?|fSVnPE<CLhJ{koTdEDa}Rk4{l`wO`Nl7prt= z5QVQn<t-GOefx+-kk<*4IP0Ziz)75;Iccdd)s0j#>Zx3UZvq`s8XVHlEIh$!-F{;0 zId?Y<l>pig`@nX+&CQK;EBB%41|bZO!DFp{N-f0Kn`m0AU3q2ILS}^?vNi;Lz=puD ze)-S9?-uPcXXzmF!_WtQ$Pyg;=uOnFYZ>eKW=n{&5dOeRrjb4YC!YA#GIk0U3UNwf zX-OL^X+jLj*YJ<jSj+@T!ok4L?^-jT@gmXUwkk_aGbmJlMOWhVu|{v~i}W@3A4&Ey zLSK_Zjj-2){wyjnN8@^GQ6P25(I3;fa3M(b<_`;gl38EXSYgfBdo4h?vF2ZJ)-@jL zjpqMqNI_QGHa>2=GlbtIEh}O}!27LXsunFy2$!3{g&(@(Sor90dvsKy6>YBhH7>+x z_=n<&>);*v_VZX0Al5LP+xJCPFRZAmdZ=s5+zPF67R_b?Z5?#->f_wM_<JQ&ThE)j zL^PXuv~_roR{4?Bm{+0sJ#b}fH`SUnjSg(%0E-@^>Lr{ere+43rhfw_;(Ug|rBYe1 zra=Br-G~OI7Qs}AA!0=R-1OzS)qRN9CnLEQky2-3^-(3<BjI4nI-*5<fg`La9-w7+ z&E4O4`3MUFW?iUTAix4H<4=i(BA?g_>w5c!p{?V<>nP7AZxun%Y2t8>c7d03Rs+}o zCG5%Ho1mmW8R$`PNXhZD_m~1l6o+-Zd^nehmbfj$NCf7%N?Ju;q&U<l*7qG@lBy+l z!{_9TdpT*OzxH;G(e-Ir+e?WI%s)%mmyj>27+W<d-pN&?IDe0cjJVeNG2-6*wzpI@ z5r{ZvOY|YaUKz7zoeT*54q;crBj(`>9s4QvPH-ff=j7!L1L#K4G~izLz~-QBDVG19 zWR~-wdb0vcDf?|I*Y$cnA;6ks*j{uiI>~X{gssH?W{cAj{9w0x291%?B0Y8JjcSj* z7X#h|Udnh3KO9SyoBZHn-)?N_hc5lj7KA>CT!bL{GNvqgZ#~iPI1vZt(Ro6s{yu~I z_+}#XBOM}WKOSB?JzWJ=;yya8&{a<O$0XV?9qS^(zcco}ZxjR7y{{bCF#or3YS#`P zX3{K~8ULj1j^#(;UaIGs`SX(y`Mt5xD@lp;JSTTd)yL7i9|QHkK?q!y8bv~kr2GoH z&P9HsBNuh@);4vY_kNj~96nK~ACo(i30>E9p42ykr~T2%J7o4e;P2C0xTtC=Tm6yQ zxDYz=1X=#V;cJzGVv+g<Hbx8k<Ecg1-ho*fbYddpS*<U^o+1Q9v&!qTJo---7mw%i z-KqKLWR}(6Yuh@qttANd*Oh-I^U$GE39ZRRrwE91%q#z<&0S{UOQ;SGebH1eig)`9 zLThG)4~7(x5^e|N#k~}%!hXwy--vXGdmrNQ1%4o)Z#0FyuYirrUGVR0?VkNJ+Q0mH zIicQ-5HP|l@z#5`KP&CT8n?kIdMV01Y7T=Io(l|J<9CyM4bVNxPoId!2<Nrg>p$Bw zr8ceYUS{n6Db|Rf^qHp1GMMc6O!oYnd4#~W%cjYxvgeuvuKEm9MH&&Mu4jEFWJTu1 z56vcIsjl>@?R3H@mY&pKF0(7|gVLuuQb+2aP~5Z})RW<jS^;Jw0WWEXl(sa~P9SU- zb-tf)_7Vus=}%2O%ADOhf3;=7)MbKRSDW`VDhvFqssE%n{Ok{RkI--P`ndw?UGL4T zgP$L8N-2~9wu{Q}`eBwA&Xa8R37fO2WE;oxhl1r{l?q`PfL6u2JLGn9s6X^(AYnR~ zfmC{fNeHTp4(a_S)=MF45jxEOF9q@UOVX)nkd2?OC)PGgHv{7MadU*hIyf_*{(1hh zD>BepU4D9JPsMoL)F;Ha;~d*Uy?KfHr$~eT<d<6D8?qJJFFt(C&%Lk{oZ<1Csxws~ z%!i4Mt_o>JeC~}>2F{-xg)(UuZBimr5H0rgab6Pg)&Tj2?niMGpWtc9rM=hU;@svp zlxC&51WBM!d9lC=Gl|xspXlr4wF+XD-ngwcgFdNLjTdPGl{V-4)27(-mn#%bt2n3S zR9O;#;(x?d5Sk@hv0HQ0h1)fqeAdrr_g(gIwUE|JY=4&VMmsGA1Nfe=tWl9ZQ0rFR zZ0{sRV`E9+`&*_KINUn$3>_(B(ata$=+_OEEqIfhQaxU|u#ACn16RN3{q|WZzXClZ z<DMIm&*!7rF6$7J-q~jWD-xdV`yd`Mga+zk)jG5|#|bw?e>i$Amt=T|Eh^NY@im`( z|BC11kfn#S_j&tQ)A)EMam<Q%$si`t1c@E!wY0B^U);M_uIo**A3lQ6#AG?KVa)vo z@iw9+dFljxEyRa%V4v}ySsrfww5xk+^+8;T-kJS3LHKx{r#T;86E}5z@U!tO9JB7w z*e3)}De?Q1=Dh^SUlnW2n7Xiva!;s!J8}<V^KQ(It>ToAHSQODh3@KFFZunvUIJSE zp2?oJY&!xuUXlw8xwkdvyy#@g5Xr&y%PY%lsK^=TM~-j(2DLK@+BM=Joc8<e6>oX? zB5AFaSTi<7MjEsRET(A@4~h`BC2vhUqjqi?+<(-N{SQO=k~@iNUVT=e0@dlWtJS^_ z?-vXXn2D|@73pOVI}uerlSj-Y^VDQQ8#Lf?m=6BL_A$qw_TOmX5t#Iy5S*{d1GK*i z$yG%*RHhT?Jl1%r8Nmi5uzYC@AWa?+N9esY8wWYa9D7-EUa=#kgovTDeJQ=ycpwU{ zS!%SY_o{yAu6yJVV@AMyxmumQ2DTGT`B@vMNENqT6}bNz_JiG}n!RR17c&bNY$qoB zHJk#d7GZo+(b4aDhx*biUo1JSb=Dh0xmKVL>$8fvxq#dlI7LM=gEylVo-^CkvG&<1 zv+`~-mnpH@vZj;1ZL}4E#*jc#v1JT*d{zE6C1~@J{-*lC7h5SK@ikae@Ra?B0flw> zD0og`8uObg79K7=LFu^I1V4mG;rDv#cXl(5a9=3r+$6FitNnoanesCCpvLZ)f52NE zcPW@|oD+*owpmjPUGX!)f$8s2s$JQJ==>)QYPC4!_~+$KE)=|b!E~QrN$~?CEGqjL z?pc-Pnhv^eNj!O3RHnPx>PiA~wF|UH%nlRV=Q5#sL8^7HIPhK2OJ9iL&uxFn^h$lN znqP>Yu98ocpb*(UookEcQj2`e9^UC))-1i>NhWpA4;4A-Rzy7>1ATW}J9B&c`)W2b zXX$Jt-C^g~j3PWBlDsSY@4Y|8#*7i~ncaP>QZK<+h@D-s@?a5@UFn1D3-E&=@bmLz zeAm#<vu~)V&iQ^`XIohH*jp<n>}LWxgHNj3x>wm(O!_Bwq>FiiOt*sw+Casv2;VTA z*BzA^M*cai3-gmuHP}u+zvgkbXH%Wf71NI@_fB~K)I@v@7PERS^}9xoq~6RF1m{A= zlNr~f<qU6Lj*}`|r%Whe;SpWyQ<d)?9va4hJuDGeiY|I#LN>;Tn@cO<FD4`Jsq0Su zHmIrsx^eFf$1~Kc?oOvgZq|9q^eR`W551jRyPXn=<(0w+fSK<=09BFd!%Ojke@w)W zr_4q(2(7DTGTSjRmDy#g0bBOSIg~%aof(>9rzCxEiGBkuLYWlQn<}$IR7Nv%#m!Ro zRxT;yoxmj<wvFRlFNzJ54?gbttVSF=Xn`h^jS*FVXF5+)6okO#&^GVm9}SxmTB!V@ zXUK}jX3+Dkin;r<y_eqPG{M&wdH#{g#f;lu^-?ZJF<iHbi1??=PREMF<S3>n`voQW zenOOGm`S^3RlWtXQ2F<sbzw2Cc*u<Cu?xO8Ab-U6&C!0to(-2>at+Nk!ngx|`%@6J zu8Sm3FHLW1kVez-h>&6qu_JGea8oS&Lh=|!*pWJ-bek%DEHb5MFUm3+^Mr+I5XzLG zuhVz9@KA?etj;8M?=7RR0;FP<Uf4xPDcrx5frH?0Y?)c~VXSr}cD*($%o#CEwp?1( zT@~k3?k&B6)QSve!hO+U|9)8>pPYw6nqqtZ3{bxQ;id%B%Bh{Bb#t8A({w_8?WMA@ zB%Mea$Q|}=vQoA(5FRnN@WuU}dk`T#-j%-hb8vn5aC^y%5O_TyUX6GVcx_?-V1Jfj z#*kbt`)1!Xo?xL|=x}>`=+i+~mLNY4Y_@*RhUqQu_uX2;j9(<w0S9use>d20qn*2A zpqMr);3XCWJ^+@XL1^*eAcYqejwk?d^$e`B@tIi=KO)YsLd~wXvT(K0?Gc`lP7>8_ z%NLFl9No9w=>~n4m_77)r26hqMXmPMC1fBL6Ty#zbPm4yS6Q((gq@=gu6F<YeP7qn znH(5Y0wJz&krkMrN10$;_;zeG<Wb^d+jFtDtAdy)4^LvX{NQhhwJ~Q_HK42AkT0NZ z^ZO=hY;>#0`x_!X`fZ^2N0dHaz?p6Gt$`y9E)eTmPlo(4RyO1t!v177lrm-74hvW$ zP|2?opnUcGppX0AC5qIp>k}@#Galg*@ta;_{IBXa>O;~^mLHn2z73*~6)HK6+S^LU z%in9ZSip6FVd_04LI?+f0Yaq3l0Y8+F@X0UQibvnmZA=pA^*=%ypObll%BtC#*A+; zW$(S@6=pjV8R6?s-E|*!K?(*W>IaPOBf@eRAZHWRln9DKqO=6#x36Dg<!5QAr@RWl zAKmIn3SdV0GMq5`$XQoPR%UO8q;d)CL@Fr=Y$vcIRTB|nk3btSA{ZxxNDWBBgE>7S ztDNXmADjTIP`QdGm;`ln4i_4l>d5^F8S2Dz&3~2-`~fV7CLBSAhelqb7`M<1PW=)c zKi&@hgryz))Cx}Jp34R~ejtQx`Z^Yw7Nj|do(40Q3h%VgzR+!5=yIlqe0OKeHMfVP zg-9`bLFs#%1Ttx1@=eqW_^bbvW2gE+gH6PW@_Xc{i~_}|4cM>!W$VJa3huoF4w9+) z!4e{IKtTc&WY>uZ>sb=RQ2IC&t@l~gEQ5yHP4396Q*EC#bF5L+TP;QF%#a-AJL9^p z$B^qFbeJ|=Zd3a_d0*s5>a06)56Q{A1czlG#!z9UKx@8LIFVToc0-T1PMF$0Yab>0 zfV5^=Pk&6@dc6*x|IRJKLL~eJXNx>V(R-WD(hG&IWELD3IU1x~F?YZ!y4#1!Tq%g< zvt_dlL>V*n?N$H9=`FeHCcn@LcCg#q>noO}R-v@SXdx4Unfc}h*m$o5vkv^ENw3*Y zJ~#y1hUvDXw^y4{PHjKaUuh(oGAiXs+i~8C`@1*+;!eF95GS~cGX2|2wGt`xFTU8& zm8ThLEQ|6SspslA>@?_q!o%9s0+a9mF~=!zV3Rble)_rU6(cd*6>DeK<dXAU35!m+ zs2CTLWw88w2AXqpN+bOt195Z4+Z00|K9jwfI8XivN(@pK6X6I|&H4|yXT6%aqTt1v zEc|%g0p0)kZzJgD^=wbk)BlKFa?_^^R-w{V1oIuACk^jdbhQAysM^cibCbBPm>3_$ zu0lsr+efdGQL9!ENo&&k5HQhy^J#(g%rv-&q;&Vk9FzR>XVb#im7GY`c4D}PR`v#p zIH*A>!E8iI-e#Dpp~4VP859#9aKW)@IQWm^v{*?921)GgL+oYV>6~<9KHWPAU!}3I zPU8iu8)l&u9ykVRL+S{Z+ywB7O|dg*F-h_2Q$x-IOb%L+ySsY>#S!RXnteKtYBCe7 z1Oo)7by;xfBI$l63F+NWckjX?&B~6<yWp}NztG4?578p6C^1J^3RT1x6-`R3vh}DV z8u~z;doA{exZixturmo{o-~uBYn>EhCKj*B)LY901IIogF`F7<Ng?`Nbf`-KTGO#k z^eSKGF>3JQqgYAX^30mb-+}K(*21rGnB6N)$8f|R4XNQR4|yWTia(&@Bj(}>!fFtd z^=|$hm6k+C3W{7!mO}ENy^#RQ;&`ISB1%Q^R;6n17eNZkqA*$#&~MzzS;gX6@^JPw zlJyd9wJ%LmFFG7r*t48o?z+jp8f<_@U@CHMqJ0!KLhgg>^}b=3LTvwD?2T_(A1&n2 zcG%F6!kg|-nIDm)9PP|-)$YMJ^?6Lm<>W)Yvq6*^B^|!dwPbv>1+l;*r>e&k_rPi0 z23#cHb3F{X7~al`7$OQkSgS^=lG^|GqQORvStn1<-!y+D++))b=96&~L~!+CTdh_O z3?|f7Ia!A1r}lG$EsfrI6n#|jj0s06g*Wpe$`oY5j~Kuw@*y{~rgXUe|2p8Zvj*7b zh@rp^6$I4zw7XT}X1MC)3vIwcU0xg$O}%SI!NQpI==M`hbM2Dl^g&fkoE&gc>JQnF zpf;?tuG1KxAkf#h$C%5oR3xQD2AelhHZ#)=ceQ0xMaz&d_SpztNiZ+w&lX;?*eq>E z`e8l{wW;z%YHep9nYX!o=C-rKIitq`D_6|SS{WysDhPRCxkS}(<bF-4>8P)AOz0(U zFah$88h~s(-X6G@K(12*%3sPatJf${=maIIDecEGYMbjbFQL>R)^`D4XvVjd(}EtP zAQf|IQ1ksP%0nq{Lc^^N#DO<c5{b`BOk!_sxQ6l<dU$6yt&`!~+``)~6R^Zi_%Eil zR|+ZTK&Nt&(z`_6ZKk>#o*XMw?4q1Vl_T*~zT<%<n_?lR+u|R203w~C>NnTXx`zlW z)3h}swg|jIe)?qUD)+fa!l`$3yS#CCSYX9oF5sQu?rU8V5<QKK5xT%)#>?(8rxyE5 zxhq_RlTo&mN6L!J^Rq==@7)tY(eFBhg%T6&;}sToES2ne`hr~fgqebD8-_if87Ow? zo|lVlE1e6(@lc|{Vb7USN{`R}p9<878!7On{Bf4q3!~hy_?u#Z=3n6R)(@k_zX&f1 z=P7^6$?;tg>8Z#_KOw=^8yE<fsef~wQJ4$PG@`$vUMVfLt)vDiOg1nOt-OiCL=nd3 zy8Y(363h+t{^K4cEg{DFA#ZC#2;=VHP?7feGs1E6fsr&-P<G*TH&shV9>Jwv?e+5Z z-24R(z)S^7+Mseel<b%GdkynyNst`du_~T$G@*t|kJSi1sde3yi<BAt9y#MCj6Jd2 zO6tU)lAvpBZf0o~?*Ci3w{{OhVBe_-Rx#p7>{UrYj8Og{EU2C%!8(gmLDnT|tvwIb zqK!q`6VxShjVW1{>S_|E9p6nQ$<28p`%N#t3zQ@Hk#;H}wx_-G_-kE4$+mf^ftxlo zOWRMDZXAtsuPFf6dJAC+pe|u#Oq92|_|0oZ#OvpTd6BQmKfa28^4dJ_`^IAm*KRbo zKvbmL&PIFz)>jbIff6fYWk*of6y(O2fTv|tjJ^&Y?|3Vfg}7=BPLNTR-x94zm;h8L zBWQ)1bkU?!mq(ZWVhWk}T00%+R|@mn!{Mv=3g<!F)hGJ_0nMh{-08V`t~GTyW5kFs zT#KZJvEon7Qos<}h{q`pF#Kk#{~1QqmP5a2C&^tf(uLh~;zwrB7ckVw)$xY#+U;3R z4FU4IDg}D?i&tha<rn%SLWEMlXUgx{v=cl)1RsPTs={eKSHXO36-D=`0|UsMxrk>+ zv{pN=h0zP$=gY@yobb>(tWYU3#1BuA+G|JOci<3Q%+N2X;|xj$Q}*@ncQ1v>Mz?r? zoaFGZ=SRWP5|l3Xd~NSTcv?V9BSUMH*Up}YlP81fvsd2V3H%*gHwuEj{iAGsYsnN> z!+yR<_JOA$cVZUr>HloPdg6P`C&^9cNAO!jj0a`_>k1ScF5Z3mgHh{8C}-fMrclV7 z`CUX2tXk_((GykhbW%_<d2tG#v1_3o#gb$Qd%!^9JGC{K15=kyuvC`l>?@1qVl5Rs zR^MkpTTE%b=b8`1jp>f@u{$JfaGQH22`7$dER}e?jz!D*`aFw|?E|KNC!Kwky*k^E zbE{AJl;m+~OgNel^jHl<cFxGObz+u{WSa<N;+JUBM!PW(BMmVs<9ao7ZaB>bUalb2 z_~y~KIZl*u+t@W2x~yEUtZ;V)#|F#NUuRy8RQtusk}=4Oq&D`z4xCU+W#3c*_`_Ar zby9{{(nhZ_P?UT+<;iOWjc6czS`)#<7I5%uX#o20Aw`Xxa7WSi-;bf<J`#&>L}b8R z{`>Q$ERuc=kpK;ExL>%dKN%cX1@DT`#xChPw%19iO84wd1;QFu7G&nd47X$0#EQ~K z>tR*ti}4p#vPW9~Z0W$c>tqqWPMDe={EOD`=17VCH<2lf72}+8brDg({J1Q8WC-g} zJ?k~C4+5ekPdQSJg3O^KGSD*k>e5oabZKjBHFfwPj{iSTPDn%qc(W%ETI5+~>-6YV zJtRHTx}f0BE@}|v0j-clX%R|Q=Bi8v8d5?vQ_>Iwtdq~<F{5y^z8_hpM6bMF#X+s^ z&zxt(o|coALJ={NM;Y3}Mud>JvcGSGx+$OTUKXTk(b9fB)Mz@IKWdgJ8E(Qx;d}*d z8VrV+lXjNCZ_pT685z~L1JS)LREUO*cSS9IVt}sey2q4l>WHHJ&5V|gF|fJP<@5u` zf$z;u7Iw1CvbZWyG4udjPC`=x3OOYHqA4;XadYW~dnj;1!D)MOSINhwN6!%*;7lHQ zQ2jTF(e==2zGIn{!QUrbF(OUY^HXZ`ZL!i*BZwr+(ZCYA?WKgKfBU}K4QuVo>T0oR zCgkmUNaThDY`^1c0|s)vofu*nt<!;{Xqe&kGO<+cY}0(oZr`)R2Tf6jH9vErQV5s~ zqX;qt5`A0Z5f;zF_xD<<PGQ2!NWKkHwQ(C7{2p;je&F`qX-Q(NGK|ifh&}ZZ1PeTq zQf<^XrwA9e;rgJdhs||y@`4xn>$HRt-rpYEFiRQ4%M+L;R~#4vQRPKGeBeDCEg0t% ztqy!FDZ^?ZPg8<+(-&YW1v?bIapU<2e8q%lje|esR+8=|Fr0dg!(vJhET-M-rp{2! z0d1sgjf7ko>>on*#=S;Q|8*s{ivR3PKbVcYLQU1b3BO%!mR)4m2Oox)BFx>Yr`;4s zQF!(=<*$#vb;IIPgilX-d(4;ooL27Yl*<Z!Bx{sjA4W;tGkIMR<$1kB<)iD9t4-E3 z^OLdMd?Skl`Le2j+T+@qcXN%WN5#hwZP~ubOpfz~K7)GaBbWF5-+UL==;}D>{!nGG z!dPK3HpFrPh`1pAi`TQ`ZB|#Dr>Nb@+~(*#(i@)i9b_VN{y@rgfsdkQ^gopTpQ?q8 z^*K3h2}|?AGK>I=+S(wE*s#TTGXvVhp*&6f(r?0Burm>9+744$!XgpAKgP@*FB7A) z5JjG|g;ZdFBKF~IZ5~z9u8+u;!>ZcN^pniS<l}`1qtdb$Vducg`d=eo^O_W+2D_el zTiB7$nH*zpbj06{%QX=eg)pnq?mM1fqlWd!i|cFGIbZ41jVV2Q#|T)?tm(IU9-b>M zTW$9^UL75%nANr>)shW=DUl7^LiFq1YK7BaN_J^E$DTv5*qhIc$h^X*tiyYiTrG)) z1**hjg?u@&O@c6w0AhE4{e*X`&sAHwT-C)6-zlgn>jMh@A9;>m>ue7qmkT)0<Hy@< zt|FQV_oLmHjAds$N88<w)f;e|Psta?kCUww8q9q<pOys%Q1bJ`p9#G-s+B4EN?4m) zf4$L3<7r<wDHk>zg_kOLt{5z<^H))gm~7frh3C!REduy<(0jTzSt>RjfbpAth|)Z> zfiVcq+Pz5}Zrv_KK*xI)@VLv})dd5s%DDgSguk#G{PXcBYq`#x1ml#ph?^7L-EX`* zdsvZxgW)sNB?8?=Y^^Up!%K?l_s<|jj)s=iJeT!#`OZSTw5GKIHJ0tu6xS`~M7n^X z$xXop7k!!eFaviDubtQ?CCb0~cV)Z%$IgQ{h_q?)!;kCvG_0~QUt6GiiftY7N;g77 zU?Ghy#}W&b)bqHJ5P2VGIig?s4%NWCg_Tr4xU(`CHGVLG@s-BOkh-9b^Uojf=Xp&h zcOU3yXRB1hqtK)1kBH`G-mwG(KWr(xL?ldB*?YYcfp65Nu}kxjXHG^!>|j=VI=_C* zWjNE{nEJSzf>^dXjcaVj9MLnU+ixB&vF-J%$;jOG9P10q{=TWqtXP>B+w1o_w(mrQ zf_t$QO--#TcQksP%@MTR5JKa`0q9J_=1c^Ujy`07@*f3Atax9KA@+=O|L&dfQG7~Q z;la<V`%x!p#J(1gLa0cXpRsZ%(QakL>&Q?93+?A#Lp}p?XAr;Kh-w#^;2rTB^BfYg zH7{H_uihOMx*-<6jRKETVn;a)#x3j7o6R;FPc$%@#0tePHT)?kZF9k#7vSh>&y9Rn zdQiT~Fg{_j?KaB*$|erzWA>QvmH}FGBrcxcd=YFl{r&uzAEqF$V?<4{%9+b4nv~Qc z;0KkN`T1zIEQEYb9uJ*T301hwD^16bySo~+C*5!OHHlx$rmL3VMM&MH%bp=IhPa!@ ziD)H<*oa9O-<vFJ2JTTk;T$o))gF{?7KvB%!VW?MDviakNw&x?OtnLPagYmWIC;j7 zKTW{HrPDuu!STw~tq$u%Q#3yy;9Wwc2L<xEyuN*Y)9!$?PFf8O0|c$SfsV_*6Cd$J z6MHCScXX&udnYc#q=c3zU~KC&5u~cD4!AiKxhy~a?<P1~z>e7_ICnZ<R%Q13YV;&w z6CQ&GeEG9IAEH^dNVf)xWp=wbIcmNvGa<?BIK?HtQzoWe{hh+l$^0kfuQfCVt74-D V>&i*9@V{dNbTkar8&vJ%{}1P$FxUV9 diff --git a/docs/scripts/formattedTSDemos.js b/docs/scripts/formattedTSDemos.js index f1a4a46b9f4dbd..abdf96b6263d12 100644 --- a/docs/scripts/formattedTSDemos.js +++ b/docs/scripts/formattedTSDemos.js @@ -23,7 +23,7 @@ const { createTypeScriptProjectBuilder, } = require('@mui-internal/api-docs-builder/utils/createTypeScriptProject'); const yargs = require('yargs'); -const { fixBabelGeneratorIssues, fixLineEndings } = require('@mui-internal/docs-utils'); +const { fixBabelGeneratorIssues, fixLineEndings } = require('@mui/internal-docs-utils'); const { default: CORE_TYPESCRIPT_PROJECTS } = require('../../scripts/coreTypeScriptProjects'); const babelConfig = { @@ -84,7 +84,9 @@ async function transpileFile(tsxPath, project) { const source = await fse.readFile(tsxPath, 'utf8'); const transformOptions = { ...babelConfig, filename: tsxPath }; - const enableJSXPreview = !tsxPath.includes(path.join('pages', 'premium-themes')); + const enableJSXPreview = + !tsxPath.includes(path.join('pages', 'premium-themes')) && + !tsxPath.includes(path.join('getting-started', 'templates')); if (enableJSXPreview) { transformOptions.plugins = transformOptions.plugins.concat([ [ diff --git a/docs/src/MuiPage.ts b/docs/src/MuiPage.ts index fdaa4a820a9d5b..934782c099a824 100644 --- a/docs/src/MuiPage.ts +++ b/docs/src/MuiPage.ts @@ -17,7 +17,7 @@ export interface MuiPage { plan?: 'community' | 'pro' | 'premium'; /** * In case the children have pathnames out of pathname value, use this field to scope other pathnames. - * Pathname can be partial, e.g. '/components/' will cover '/components/button/' and '/components/link/'. + * Pathname can be partial, for example '/components/' will cover '/components/button/' and '/components/link/'. * @deprecated Dead code, to remove. */ scopePathnames?: string[]; diff --git a/docs/src/components/header/HeaderNavBar.tsx b/docs/src/components/header/HeaderNavBar.tsx index a5577aa05ab62a..5d53a9b03d7100 100644 --- a/docs/src/components/header/HeaderNavBar.tsx +++ b/docs/src/components/header/HeaderNavBar.tsx @@ -27,8 +27,8 @@ const Navigation = styled('nav')(({ theme }) => [ }, '& li': { ...theme.typography.body2, - color: (theme.vars || theme).palette.text.primary, - fontWeight: theme.typography.fontWeightBold, + color: (theme.vars || theme).palette.text.secondary, + fontWeight: theme.typography.fontWeightSemiBold, '& > a, & > button': { display: 'inline-block', color: 'inherit', @@ -38,7 +38,7 @@ const Navigation = styled('nav')(({ theme }) => [ borderRadius: (theme.vars || theme).shape.borderRadius, border: '1px solid transparent', '&:hover': { - color: (theme.vars || theme).palette.grey[900], + color: (theme.vars || theme).palette.text.primary, backgroundColor: (theme.vars || theme).palette.grey[50], borderColor: (theme.vars || theme).palette.grey[100], '@media (hover: none)': { @@ -302,7 +302,7 @@ export default function HeaderNavBar() { id={PRODUCT_IDS[4]} href={ROUTES.productToolpad} icon={<IconImage name="product-toolpad" />} - name="MUI Toolpad" + name="Toolpad" chip={<Chip label="Beta" size="small" color="primary" variant="outlined" />} description="Low-code admin builder." /> diff --git a/docs/src/components/header/HeaderNavDropdown.tsx b/docs/src/components/header/HeaderNavDropdown.tsx index 95fbe4c39c0c70..b48ed4d0a64b44 100644 --- a/docs/src/components/header/HeaderNavDropdown.tsx +++ b/docs/src/components/header/HeaderNavDropdown.tsx @@ -76,7 +76,7 @@ const PRODUCTS = [ href: ROUTES.productDesignKits, }, { - name: 'MUI Toolpad', + name: 'Toolpad', description: 'Low-code admin builder.', href: ROUTES.productToolpad, chip: 'Beta', @@ -110,9 +110,9 @@ const DOCS = [ href: ROUTES.xIntro, }, { - name: 'MUI Toolpad', - description: 'Low-code admin builder.', - href: ROUTES.toolpadDocs, + name: 'Toolpad', + description: 'Low-code admin builder', + href: ROUTES.toolpadStudioDocs, chip: 'Beta', }, ]; diff --git a/docs/src/components/header/ThemeModeToggle.tsx b/docs/src/components/header/ThemeModeToggle.tsx index ff22e076ae3797..fda2d61676d99d 100644 --- a/docs/src/components/header/ThemeModeToggle.tsx +++ b/docs/src/components/header/ThemeModeToggle.tsx @@ -4,16 +4,12 @@ import IconButton from '@mui/material/IconButton'; import Tooltip from '@mui/material/Tooltip'; import DarkModeOutlined from '@mui/icons-material/DarkModeOutlined'; import LightModeOutlined from '@mui/icons-material/LightModeOutlined'; -import useMediaQuery from '@mui/material/useMediaQuery'; -import { useChangeTheme } from 'docs/src/modules/components/ThemeContext'; +import { useColorSchemeShim } from 'docs/src/modules/components/ThemeContext'; -function CssVarsModeToggle(props: { onChange: (checked: boolean) => void }) { - const [mounted, setMounted] = React.useState(false); +function CssVarsModeToggle(props: { onChange: (newMode: string) => void }) { const { mode, systemMode, setMode } = useColorScheme(); - React.useEffect(() => { - setMounted(true); - }, []); const calculatedMode = mode === 'system' ? systemMode : mode; + return ( <Tooltip title={calculatedMode === 'dark' ? 'Turn on the light' : 'Turn off the light'}> <IconButton @@ -21,11 +17,12 @@ function CssVarsModeToggle(props: { onChange: (checked: boolean) => void }) { disableTouchRipple disabled={!calculatedMode} onClick={() => { - props.onChange(calculatedMode === 'light'); - setMode(calculatedMode === 'dark' ? 'light' : 'dark'); + const newMode = calculatedMode === 'dark' ? 'light' : 'dark'; + props.onChange(newMode); + setMode(newMode); }} > - {!calculatedMode || !mounted + {!calculatedMode ? null : { light: <DarkModeOutlined fontSize="small" />, @@ -37,55 +34,37 @@ function CssVarsModeToggle(props: { onChange: (checked: boolean) => void }) { } export default function ThemeModeToggle() { - const theme = useTheme(); - const changeTheme = useChangeTheme(); - const [mode, setMode] = React.useState<string | null>(null); - const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); - - React.useEffect(() => { - let initialMode = 'system'; - try { - initialMode = localStorage.getItem('mui-mode') || initialMode; - } catch (error) { - // do nothing - } - setMode(initialMode); - }, []); - - const handleChangeThemeMode = (checked: boolean) => { - const paletteMode = checked ? 'dark' : 'light'; - setMode(paletteMode); + // TODO replace with useColorScheme once all pages support css vars + const { mode, systemMode, setMode } = useColorSchemeShim(); + const calculatedMode = mode === 'system' ? systemMode : mode; - try { - localStorage.setItem('mui-mode', paletteMode); // syncing with homepage, can be removed once all pages are migrated to CSS variables - } catch (error) { - // do nothing - } - changeTheme({ paletteMode }); - }; + const theme = useTheme(); + // Server-side hydration if (mode === null) { return <IconButton color="primary" disableTouchRipple />; } - if (theme.vars) { - // Temporarily renders conditionally because `useColorScheme` could not be used in the pages that haven't migrated to CSS theme variables. - return <CssVarsModeToggle onChange={handleChangeThemeMode} />; + // TODO remove this code branch, all pages should be migrated to use CssVarsProvider + if (!theme.vars) { + return ( + <Tooltip title={calculatedMode === 'dark' ? 'Turn on the light' : 'Turn off the light'}> + <IconButton + color="primary" + disableTouchRipple + onClick={() => { + setMode(calculatedMode === 'dark' ? 'light' : 'dark'); + }} + > + {calculatedMode === 'dark' ? ( + <LightModeOutlined fontSize="small" /> + ) : ( + <DarkModeOutlined fontSize="small" /> + )} + </IconButton> + </Tooltip> + ); } - const checked = mode === 'system' ? prefersDarkMode : mode === 'dark'; - - return ( - <Tooltip title={checked ? 'Turn on the light' : 'Turn off the light'}> - <IconButton - color="primary" - disableTouchRipple - onClick={() => { - handleChangeThemeMode(!checked); - }} - > - {checked ? <LightModeOutlined fontSize="small" /> : <DarkModeOutlined fontSize="small" />} - </IconButton> - </Tooltip> - ); + return <CssVarsModeToggle onChange={setMode} />; } diff --git a/docs/src/components/home/XGridGlobalStyles.tsx b/docs/src/components/home/XGridGlobalStyles.tsx index f0f7796bdddda7..a8925787b9d43e 100644 --- a/docs/src/components/home/XGridGlobalStyles.tsx +++ b/docs/src/components/home/XGridGlobalStyles.tsx @@ -18,6 +18,7 @@ export default function XGridGlobalStyles({ border: 'none', fontSize: '0.75rem', borderRadius: '0px', + '--DataGrid-rowBorderColor': (theme.vars || theme).palette.grey[200], // toolbar // style GridToolbar '& .MuiDataGrid-toolbarContainer': { @@ -51,9 +52,6 @@ export default function XGridGlobalStyles({ '& .MuiDataGrid-menuIcon svg': { fontSize: '1rem', }, - '& .MuiDataGrid-columnHeaders': { - borderColor: (theme.vars || theme).palette.grey[200], - }, '& .MuiDataGrid-columnSeparator': { color: (theme.vars || theme).palette.grey[200], '&:hover': { @@ -66,10 +64,6 @@ export default function XGridGlobalStyles({ '& .MuiDataGrid-virtualScroller': { backgroundColor: (theme.vars || theme).palette.grey[50], }, - '& .MuiDataGrid-cell': { - borderBottom: '1px solid', - borderColor: (theme.vars || theme).palette.grey[200], - }, '& .MuiDataGrid-editInputCell': { fontSize: '0.75rem', '& > input': { @@ -118,6 +112,7 @@ export default function XGridGlobalStyles({ theme.applyDarkStyles({ [selector]: { '& .MuiDataGrid-root': { + '--DataGrid-rowBorderColor': alpha(theme.palette.primaryDark[500], 0.5), '& .MuiDataGrid-toolbarContainer': { '& > button': { borderColor: (theme.vars || theme).palette.divider, @@ -129,9 +124,6 @@ export default function XGridGlobalStyles({ '& .MuiIconButton-root:not(.Mui-disabled)': { color: (theme.vars || theme).palette.primary[300], }, - '& .MuiDataGrid-columnHeaders': { - borderColor: (theme.vars || theme).palette.divider, - }, '& .MuiDataGrid-columnSeparator': { color: (theme.vars || theme).palette.primaryDark[400], '&:hover': { @@ -143,9 +135,6 @@ export default function XGridGlobalStyles({ '& .MuiDataGrid-virtualScroller': { backgroundColor: (theme.vars || theme).palette.primaryDark[900], }, - '& .MuiDataGrid-cell': { - borderColor: alpha(theme.palette.primaryDark[500], 0.5), - }, '& .MuiTablePagination-root': { '& .MuiIconButton-root': { '&:not([disabled])': { diff --git a/docs/src/components/pricing/PricingTable.tsx b/docs/src/components/pricing/PricingTable.tsx index 031c3fa68005cd..e9b9dc71280ea1 100644 --- a/docs/src/components/pricing/PricingTable.tsx +++ b/docs/src/components/pricing/PricingTable.tsx @@ -607,7 +607,7 @@ const rowHeaders: Record<string, React.ReactNode> = { {...{ label: 'Technical support for MUI Core', tooltip: - 'Support for MUI Core (e.g. Material UI) is provided by the community. MUI Core maintainers focus on solving root issues to support the community at large.', + 'Support for MUI Core (for example Material UI) is provided by the community. MUI Core maintainers focus on solving root issues to support the community at large.', }} /> ), diff --git a/docs/src/components/productX/XGridFullDemo.tsx b/docs/src/components/productX/XGridFullDemo.tsx index 23a226eb81832b..194cdd8d9136c7 100644 --- a/docs/src/components/productX/XGridFullDemo.tsx +++ b/docs/src/components/productX/XGridFullDemo.tsx @@ -269,8 +269,8 @@ export default function XGridFullDemo() { <DataGridPro {...data} density="compact" - components={{ - Toolbar: GridToolbar, + slots={{ + toolbar: GridToolbar, }} loading={loading} checkboxSelection diff --git a/docs/src/components/productX/XHero.tsx b/docs/src/components/productX/XHero.tsx index 8585a7a8ca1409..95d9d956cefd6b 100644 --- a/docs/src/components/productX/XHero.tsx +++ b/docs/src/components/productX/XHero.tsx @@ -163,16 +163,13 @@ export default function XHero() { '& .MuiDataGrid-root': { border: 0, color: 'text.secondary', + '--DataGrid-rowBorderColor': (theme) => theme.palette.grey[200], '& .MuiCheckbox-root': { p: 0.5, '& > svg': { fontSize: '1.25rem', }, }, - '& .MuiDataGrid-columnHeaders': { - borderBottom: '1px solid', - borderColor: 'grey.200', - }, [`& .MuiDataGrid-columnHeader:focus, & .MuiDataGrid-columnHeader:focus-within`]: { outline: 'none', @@ -188,22 +185,6 @@ export default function XHero() { '& button, & button > svg': { fontSize: 16, }, - '& .MuiDataGrid-cell': { - fontSize: '0.875rem', - color: 'text.secondary', - borderBottom: '1px solid', - borderColor: 'grey.200', - }, - '& .MuiDataGrid-viewport': { - '& .MuiDataGrid-cell': { - fontSize: '0.875rem', - color: 'text.secondary', - }, - '& .MuiInputBase-input': { - fontSize: '0.875rem', - px: 0.5, - }, - }, '& .MuiChip-root.Rejected': { color: red[800], backgroundColor: red[50], @@ -232,12 +213,7 @@ export default function XHero() { (theme) => theme.applyDarkStyles({ '& .MuiDataGrid-root': { - '& .MuiDataGrid-columnHeaders': { - borderColor: 'divider', - }, - '& .MuiDataGrid-cell': { - borderColor: alpha(theme.palette.primaryDark[600], 0.5), - }, + '--DataGrid-rowBorderColor': alpha(theme.palette.primaryDark[600], 0.5), '& .MuiChip-root.Rejected': { color: red[200], backgroundColor: alpha(red[900], 0.2), diff --git a/docs/src/components/productX/XTheming.tsx b/docs/src/components/productX/XTheming.tsx index a2571519f4a0c1..42bd5aee4f5dc8 100644 --- a/docs/src/components/productX/XTheming.tsx +++ b/docs/src/components/productX/XTheming.tsx @@ -40,7 +40,7 @@ export default function XTheming() { const columns: Array<GridColDef> = [ { field: 'desk', - headerName: 'desk', + headerName: 'Desk', width: customized ? 72 : 100, sortable: false, editable: true, @@ -65,6 +65,7 @@ export default function XTheming() { sortable: false, editable: true, ...(customized && { + display: 'flex', renderCell: (params: GridCellParams) => { return <ProgressBar value={Number(params.value)!} />; }, @@ -81,6 +82,7 @@ export default function XTheming() { sortable: false, editable: true, ...(customized && { + display: 'flex', renderCell: (params: GridCellParams) => { return <Status status={(params.value || '').toString()} />; }, diff --git a/docs/src/components/showcase/FolderTable.tsx b/docs/src/components/showcase/FolderTable.tsx index d6eecf4d4e610f..dcf0005bfdf3cf 100644 --- a/docs/src/components/showcase/FolderTable.tsx +++ b/docs/src/components/showcase/FolderTable.tsx @@ -150,10 +150,10 @@ export default function BasicTable() { <TableCell align="right"> <Typography fontSize={13} - fontWeight={600} + fontWeight="bold" sx={(theme: Theme) => ({ mr: 1, - color: 'success.700', + color: 'success.800', ...theme.applyDarkStyles({ color: 'success.500', }), diff --git a/docs/src/components/showcase/NotificationCard.tsx b/docs/src/components/showcase/NotificationCard.tsx index 545b6b32d86d09..8f08369d6b3a4f 100644 --- a/docs/src/components/showcase/NotificationCard.tsx +++ b/docs/src/components/showcase/NotificationCard.tsx @@ -71,9 +71,10 @@ export default function NotificationCard() { </div> <Chip label="3" - color="success" size="small" - sx={{ ml: 'auto', color: '#fff', fontSize: '0.75rem', height: 18 }} + variant="outlined" + color="success" + sx={{ ml: 'auto', fontSize: '0.75rem', height: 18 }} /> </Box> </Box> diff --git a/docs/src/components/showcase/ThemeDatePicker.tsx b/docs/src/components/showcase/ThemeDatePicker.tsx index 3d8ea6e347b4a2..2695dba9efbc2a 100644 --- a/docs/src/components/showcase/ThemeDatePicker.tsx +++ b/docs/src/components/showcase/ThemeDatePicker.tsx @@ -43,7 +43,7 @@ export default function ThemeDatePicker() { paddingLeft: '18px', }, '& .MuiTypography-caption': { - color: 'grey.500', + color: 'text.tertiary', height: 24, }, '[role="presentation"]': { diff --git a/docs/src/layouts/AppFooter.tsx b/docs/src/layouts/AppFooter.tsx index 6ca7ef50d6a7bd..d8655f2d9cc87d 100644 --- a/docs/src/layouts/AppFooter.tsx +++ b/docs/src/layouts/AppFooter.tsx @@ -83,7 +83,7 @@ export default function AppFooter(props: AppFooterProps) { MUI X </Link> <Link prefetch={false} href={ROUTES.productToolpad}> - MUI Toolpad + Toolpad </Link> <Link prefetch={false} href={ROUTES.productTemplates}> Templates diff --git a/docs/src/layouts/AppHeader.tsx b/docs/src/layouts/AppHeader.tsx index 6e467d01819e7d..7c8d9de1f13614 100644 --- a/docs/src/layouts/AppHeader.tsx +++ b/docs/src/layouts/AppHeader.tsx @@ -39,7 +39,6 @@ interface AppHeaderProps { export default function AppHeader(props: AppHeaderProps) { const { gitHubRepository = 'https://github.com/mui' } = props; - const t = useTranslate(); return ( diff --git a/docs/src/modules/components/Ad.js b/docs/src/modules/components/Ad.js index 0e910aa6614d4d..9750bfeed8b6ab 100644 --- a/docs/src/modules/components/Ad.js +++ b/docs/src/modules/components/Ad.js @@ -88,7 +88,7 @@ class AdErrorBoundary extends React.Component { componentDidCatch() { // send explicit `'null'` const eventLabel = String(this.props.eventLabel); - // TODO: Use proper error monitoring service (e.g. Sentry) instead + // TODO: Use proper error monitoring service (for example Sentry) instead window.gtag('event', 'ad', { eventAction: 'crash', diff --git a/docs/src/modules/components/AdCarbon.js b/docs/src/modules/components/AdCarbon.js index c161f25405eb3d..64963da1702729 100644 --- a/docs/src/modules/components/AdCarbon.js +++ b/docs/src/modules/components/AdCarbon.js @@ -37,7 +37,7 @@ function AdCarbonImage() { // Once the script starts loading, it will asynchronous resolve, with no way to stop it. // This leads to duplication of the ad. // - // To solve the issue, e.g. StrictModel double effect execution, we debounce the load action. + // To solve the issue, for example StrictModel double effect execution, we debounce the load action. const load = setTimeout(() => { const script = loadScript( 'https://cdn.carbonads.com/carbon.js?serve=CKYIL27L&placement=material-uicom', diff --git a/docs/src/modules/components/ApiPage/list/ClassesList.tsx b/docs/src/modules/components/ApiPage/list/ClassesList.tsx index fdf4497f465f4b..d7366af1b60239 100644 --- a/docs/src/modules/components/ApiPage/list/ClassesList.tsx +++ b/docs/src/modules/components/ApiPage/list/ClassesList.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { styled } from '@mui/material/styles'; import kebabCase from 'lodash/kebabCase'; -import { ComponentClassDefinition } from '@mui-internal/docs-utils'; +import { ComponentClassDefinition } from '@mui/internal-docs-utils'; import { useTranslate } from '@mui/docs/i18n'; import ExpandableApiItem, { ApiItemContaier, diff --git a/docs/src/modules/components/ApiPage/sections/ClassesSection.tsx b/docs/src/modules/components/ApiPage/sections/ClassesSection.tsx index 5f6badcd5150c8..bc09404849b40c 100644 --- a/docs/src/modules/components/ApiPage/sections/ClassesSection.tsx +++ b/docs/src/modules/components/ApiPage/sections/ClassesSection.tsx @@ -1,7 +1,7 @@ /* eslint-disable react/no-danger */ import * as React from 'react'; import { useTranslate } from '@mui/docs/i18n'; -import { ComponentClassDefinition } from '@mui-internal/docs-utils'; +import { ComponentClassDefinition } from '@mui/internal-docs-utils'; import Box from '@mui/material/Box'; import ToggleDisplayOption, { ApiDisplayOptions, diff --git a/docs/src/modules/components/ApiPage/table/ClassesTable.tsx b/docs/src/modules/components/ApiPage/table/ClassesTable.tsx index b2c98cf1674cf9..f2546202208eb1 100644 --- a/docs/src/modules/components/ApiPage/table/ClassesTable.tsx +++ b/docs/src/modules/components/ApiPage/table/ClassesTable.tsx @@ -1,6 +1,6 @@ /* eslint-disable react/no-danger */ import * as React from 'react'; -import { ComponentClassDefinition } from '@mui-internal/docs-utils'; +import { ComponentClassDefinition } from '@mui/internal-docs-utils'; import { styled, alpha } from '@mui/material/styles'; import { brandingDarkTheme as darkTheme, diff --git a/docs/src/modules/components/AppNavDrawer.js b/docs/src/modules/components/AppNavDrawer.js index 1bdf1b4a0ddd4e..f56d19f285935b 100644 --- a/docs/src/modules/components/AppNavDrawer.js +++ b/docs/src/modules/components/AppNavDrawer.js @@ -165,7 +165,7 @@ function PersistScroll(props) { const activeBox = activeDrawerLink.getBoundingClientRect(); if (activeBox.top < 0 || activeBox.bottom + browserUrlPreviewMarge > window.innerHeight) { - // Scroll the least possible from the initial render, e.g. server-side, scrollTop = 0. + // Scroll the least possible from the initial render, for example server-side, scrollTop = 0. activeDrawerLink.scrollIntoView({ block: 'nearest' }); } diff --git a/docs/src/modules/components/AppNavDrawerItem.js b/docs/src/modules/components/AppNavDrawerItem.js index 1379f1c1740c00..e626a11fe7d7e0 100644 --- a/docs/src/modules/components/AppNavDrawerItem.js +++ b/docs/src/modules/components/AppNavDrawerItem.js @@ -262,7 +262,7 @@ export default function AppNavDrawerItem(props) { } = props; const [open, setOpen] = React.useState(initiallyExpanded); const handleClick = (event) => { - // Ignore click events meant for native link handling, e.g. open in new tab + // Ignore click events meant for native link handling, for example open in new tab if (samePageLinkNavigation(event)) { return; } diff --git a/docs/src/modules/components/AppSearch.js b/docs/src/modules/components/AppSearch.js index 5cb970959fe432..00e9cc24183998 100644 --- a/docs/src/modules/components/AppSearch.js +++ b/docs/src/modules/components/AppSearch.js @@ -273,6 +273,8 @@ const productNameProductId = { x: 'MUI X', system: 'MUI System', toolpad: 'Toolpad', + 'toolpad-studio': 'Toolpad Studio', + 'toolpad-core': 'Toolpad Core', }; export function convertProductIdToName(productInfo) { @@ -616,6 +618,10 @@ export default function AppSearch(props) { width: '18px', height: '18px', }, + '& .DocSearch-VisuallyHiddenForAccessibility': { + width: 0, + visibility: 'hidden', + }, }, '& .DocSearch-Cancel': { display: 'block', diff --git a/docs/src/modules/components/AppSettingsDrawer.js b/docs/src/modules/components/AppSettingsDrawer.js index d79c5d4ef97018..8b04f7d36e3dd6 100644 --- a/docs/src/modules/components/AppSettingsDrawer.js +++ b/docs/src/modules/components/AppSettingsDrawer.js @@ -1,7 +1,6 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { styled, useTheme } from '@mui/material/styles'; -import useMediaQuery from '@mui/material/useMediaQuery'; import Drawer from '@mui/material/Drawer'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; @@ -16,7 +15,7 @@ import DarkModeOutlinedIcon from '@mui/icons-material/DarkModeOutlined'; import SettingsBrightnessIcon from '@mui/icons-material/SettingsBrightness'; import FormatTextdirectionLToRIcon from '@mui/icons-material/FormatTextdirectionLToR'; import FormatTextdirectionRToLIcon from '@mui/icons-material/FormatTextdirectionRToL'; -import { useChangeTheme } from 'docs/src/modules/components/ThemeContext'; +import { useColorSchemeShim, useChangeTheme } from 'docs/src/modules/components/ThemeContext'; import { useTranslate } from '@mui/docs/i18n'; const Heading = styled(Typography)(({ theme }) => ({ @@ -37,55 +36,26 @@ const IconToggleButton = styled(ToggleButton)({ }, }); -function AppSettingsDrawer(props) { +export default function AppSettingsDrawer(props) { const { onClose, open = false, ...other } = props; const t = useTranslate(); const upperTheme = useTheme(); const changeTheme = useChangeTheme(); - const [mode, setMode] = React.useState(null); - const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); - const preferredMode = prefersDarkMode ? 'dark' : 'light'; - React.useEffect(() => { - // syncing with homepage, can be removed once all pages are migrated to CSS variables - let initialMode = 'system'; - try { - initialMode = localStorage.getItem('mui-mode') || initialMode; - } catch (error) { - // do nothing - } - setMode(initialMode); - }, [preferredMode]); + // TODO replace with useColorScheme once all pages support css vars + const { mode, setMode } = useColorSchemeShim(); const handleChangeThemeMode = (event, paletteMode) => { if (paletteMode === null) { return; } - setMode(paletteMode); - - if (paletteMode === 'system') { - try { - localStorage.setItem('mui-mode', 'system'); // syncing with homepage, can be removed once all pages are migrated to CSS variables - } catch (error) { - // thrown when cookies are disabled. - } - changeTheme({ paletteMode: preferredMode }); - } else { - try { - localStorage.setItem('mui-mode', paletteMode); // syncing with homepage, can be removed once all pages are migrated to CSS variables - } catch (error) { - // thrown when cookies are disabled. - } - changeTheme({ paletteMode }); - } }; const handleChangeDirection = (event, direction) => { if (direction === null) { direction = upperTheme.direction; } - changeTheme({ direction }); }; @@ -202,5 +172,3 @@ AppSettingsDrawer.propTypes = { onClose: PropTypes.func.isRequired, open: PropTypes.bool, }; - -export default AppSettingsDrawer; diff --git a/docs/src/modules/components/AppTableOfContents.js b/docs/src/modules/components/AppTableOfContents.js index 3f68c7033b29b6..c093a609b9342b 100644 --- a/docs/src/modules/components/AppTableOfContents.js +++ b/docs/src/modules/components/AppTableOfContents.js @@ -205,7 +205,7 @@ export default function AppTableOfContents(props) { useThrottledOnScroll(items.length > 0 ? findActiveIndex : null, 166); const handleClick = (hash) => (event) => { - // Ignore click events meant for native link handling, e.g. open in new tab + // Ignore click events meant for native link handling, for example open in new tab if (samePageLinkNavigation(event)) { return; } diff --git a/docs/src/modules/components/HighlightedCodeWithTabs.tsx b/docs/src/modules/components/HighlightedCodeWithTabs.tsx index f31a70348e42df..7372011ff47f06 100644 --- a/docs/src/modules/components/HighlightedCodeWithTabs.tsx +++ b/docs/src/modules/components/HighlightedCodeWithTabs.tsx @@ -4,6 +4,7 @@ import { Tabs, TabsOwnProps } from '@mui/base/Tabs'; import { TabsList as TabsListBase } from '@mui/base/TabsList'; import { TabPanel as TabPanelBase } from '@mui/base/TabPanel'; import { Tab as TabBase } from '@mui/base/Tab'; +import useLocalStorageState from '@mui/utils/useLocalStorageState'; import HighlightedCode from './HighlightedCode'; const TabList = styled(TabsListBase)(({ theme }) => ({ @@ -77,44 +78,25 @@ type TabsConfig = { language: string; tab: string; }; -export default function HighlightedCodeWithTabs({ - tabs, - storageKey, -}: { - tabs: Array<TabsConfig>; - storageKey?: string; -} & Record<string, any>) { + +export default function HighlightedCodeWithTabs( + props: { + tabs: Array<TabsConfig>; + storageKey?: string; + } & Record<string, any>, +) { + const { tabs, storageKey } = props; const availableTabs = React.useMemo(() => tabs.map(({ tab }) => tab), [tabs]); - const [activeTab, setActiveTab] = React.useState(availableTabs[0]); + const [activeTab, setActiveTab] = useLocalStorageState(storageKey ?? null, availableTabs[0]); const [mounted, setMounted] = React.useState(false); React.useEffect(() => { - try { - setActiveTab((prev) => { - if (storageKey === undefined) { - return prev; - } - const storedValues = localStorage.getItem(storageKey); - - return storedValues && availableTabs.includes(storedValues) ? storedValues : prev; - }); - } catch (error) { - // ignore error - } setMounted(true); - }, [availableTabs, storageKey]); + }, []); const handleChange: TabsOwnProps['onChange'] = (event, newValue) => { setActiveTab(newValue as string); - if (storageKey === undefined) { - return; - } - try { - localStorage.setItem(storageKey, newValue as string); - } catch (error) { - // ignore error - } }; const ownerState = { mounted }; diff --git a/docs/src/modules/components/JoyUsageDemo.tsx b/docs/src/modules/components/JoyUsageDemo.tsx index 00bde782ba070a..3816f8cb40912c 100644 --- a/docs/src/modules/components/JoyUsageDemo.tsx +++ b/docs/src/modules/components/JoyUsageDemo.tsx @@ -109,7 +109,7 @@ interface JoyUsageDemoProps<ComponentProps> { */ data: Array<{ /** - * Name of the prop, e.g. 'children' + * Name of the prop, for example 'children' */ propName: Extract<keyof ComponentProps, string>; /** diff --git a/docs/src/modules/components/MarkdownDocs.js b/docs/src/modules/components/MarkdownDocs.js index 68fce71531bd04..d97703653a0fd9 100644 --- a/docs/src/modules/components/MarkdownDocs.js +++ b/docs/src/modules/components/MarkdownDocs.js @@ -32,7 +32,7 @@ export default function MarkdownDocs(props) { disableAd = false, disableToc = false, /** - * Some pages, e.g. Joy theme builder, should not be a nested CssVarsProvider to control its own state. + * Some pages, for example Joy theme builder, should not be a nested CssVarsProvider to control its own state. * This config will skip the CssVarsProvider at the root of the page. */ disableCssVarsProvider = false, diff --git a/docs/src/modules/components/MarkdownDocsV2.js b/docs/src/modules/components/MarkdownDocsV2.js index 59be4f176c2d91..37cb3f4c931048 100644 --- a/docs/src/modules/components/MarkdownDocsV2.js +++ b/docs/src/modules/components/MarkdownDocsV2.js @@ -271,7 +271,7 @@ export default function MarkdownDocsV2(props) { {commonElements} {activeTab === '' && localizedDoc.rendered - // for the "hook only" edge case, e.g. Base UI autocomplete + // for the "hook only" edge case, for example Base UI autocomplete .slice( i, localizedDoc.rendered.length - (localizedDoc.headers.components.length > 0 ? 1 : 0), diff --git a/docs/src/modules/components/MarkdownElement.js b/docs/src/modules/components/MarkdownElement.js index 24f18dd51a749b..e18b2ed63aefa8 100644 --- a/docs/src/modules/components/MarkdownElement.js +++ b/docs/src/modules/components/MarkdownElement.js @@ -509,6 +509,15 @@ const Root = styled('div')( position: 'relative', // Font size reset to fix a bug with Safari 16.0 when letterSpacing is set fontSize: 10, + '&:has(.MuiCode-title)': { + margin: theme.spacing(2, 'auto'), + border: `1px solid var(--muidocs-palette-primaryDark-700, ${lightTheme.palette.primaryDark[700]})`, + borderRadius: theme.shape.borderRadius, + overflow: 'clip', + '& .MuiCode-copy': { + top: '56px', + }, + }, }, '& .MuiCode-copy-container': { // This container is only used in demo and highlight code @@ -601,6 +610,43 @@ const Root = styled('div')( marginBottom: theme.spacing(1), }, }, + '& .feature-list': { + padding: 0, + listStyle: 'none', + '& li': { + marginBottom: 6, + display: 'flex', + alignItems: 'center', + gap: 12, + '::before': { + content: `url('/static/branding/pricing/yes-light.svg')`, + width: '18px', + height: '18px', + }, + }, + }, + '& .MuiCode-title': { + padding: theme.spacing(1.5), + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1.5), + borderBottom: `1px solid var(--muidocs-palette-primaryDark-700, ${lightTheme.palette.primaryDark[700]})`, + backgroundColor: `var(--muidocs-palette-primaryDark-900, ${lightTheme.palette.primaryDark[900]})`, + fontFamily: theme.typography.fontFamilyCode, + fontSize: theme.typography.pxToRem(12), + fontWeight: theme.typography.fontWeightBold, + color: `var(--muidocs-palette-grey-200, ${lightTheme.palette.grey[200]})`, + '::before': { + content: `url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M13.3333 3.99996H8L7.06 3.05996C6.80667 2.80663 6.46667 2.66663 6.11334 2.66663H2.66667C1.93334 2.66663 1.34 3.26663 1.34 3.99996L1.33334 12C1.33334 12.7333 1.93334 13.3333 2.66667 13.3333H13.3333C14.0667 13.3333 14.6667 12.7333 14.6667 12V5.33329C14.6667 4.59996 14.0667 3.99996 13.3333 3.99996ZM12.6667 12H3.33334C2.96667 12 2.66667 11.7 2.66667 11.3333V5.99996C2.66667 5.63329 2.96667 5.33329 3.33334 5.33329H12.6667C13.0333 5.33329 13.3333 5.63329 13.3333 5.99996V11.3333C13.3333 11.7 13.0333 12 12.6667 12Z' fill='%2399CCF3'/%3E%3C/svg%3E%0A");`, + width: '16px', + height: '16px', + }, + '& + pre': { + margin: 0, + border: 'none', + borderRadius: 0, + }, + }, }), ({ theme }) => ({ [`:where(${theme.vars ? '[data-mui-color-scheme="dark"]' : '.mode-dark'}) &`]: { @@ -747,6 +793,13 @@ const Root = styled('div')( backgroundColor: `var(--muidocs-palette-primaryDark-800, ${darkTheme.palette.primaryDark[800]})`, }, }, + '& .feature-list': { + '& li': { + '::before': { + content: `url('/static/branding/pricing/yes-dark.svg')`, + }, + }, + }, }, }), ); diff --git a/docs/src/modules/components/MarkdownLinks.js b/docs/src/modules/components/MarkdownLinks.js index 8f71b7320a9650..0ae9a7232fff27 100644 --- a/docs/src/modules/components/MarkdownLinks.js +++ b/docs/src/modules/components/MarkdownLinks.js @@ -40,7 +40,7 @@ function isLink(event) { * @param {MouseEvent} event */ function handleClick(event) { - // Ignore click events meant for native link handling, e.g. open in new tab + // Ignore click events meant for native link handling, for example open in new tab if (samePageLinkNavigation(event)) { return; } diff --git a/docs/src/modules/components/MaterialFreeTemplatesCollection.js b/docs/src/modules/components/MaterialFreeTemplatesCollection.js index 21f2434fa55110..c548c841bd70fb 100644 --- a/docs/src/modules/components/MaterialFreeTemplatesCollection.js +++ b/docs/src/modules/components/MaterialFreeTemplatesCollection.js @@ -1,14 +1,11 @@ /* eslint-disable material-ui/no-hardcoded-labels */ import * as React from 'react'; -import NextLink from 'next/link'; -import { alpha } from '@mui/material/styles'; import Box from '@mui/material/Box'; import Card from '@mui/material/Card'; import CardMedia from '@mui/material/CardMedia'; import Button from '@mui/material/Button'; import Grid from '@mui/material/Grid'; import Typography from '@mui/material/Typography'; -import Link from '@mui/material/Link'; import Visibility from '@mui/icons-material/Visibility'; import CodeRoundedIcon from '@mui/icons-material/CodeRounded'; import { useTranslate } from '@mui/docs/i18n'; @@ -80,105 +77,70 @@ export default function Templates() { const t = useTranslate(); return ( - <Grid container spacing={2} sx={{ pt: 2, pb: 4 }}> + <Grid container spacing={2} sx={{ py: 2 }}> {layouts(t).map((layout) => ( - <Grid item xs={12} sm={4} key={layout.title}> + <Grid item xs={12} sm={6} key={layout.title}> <Card variant="outlined" sx={{ height: '100%', - background: 'background.paper', - borderColor: 'divider', display: 'flex', flexDirection: 'column', + borderColor: 'divider', }} > - <Box + <CardMedia + component="img" + image={layout.src} + title={layout.title} sx={{ - overflow: 'auto', - position: 'relative', + aspectRatio: '16 / 9', + objectPosition: 'top', borderBottom: '1px solid', borderColor: 'divider', }} - > - <CardMedia - component="a" - href={layout.href} - image={layout.src} - title={layout.title} - rel="nofollow" - target="_blank" - sx={(theme) => ({ - height: 0, - pt: '65%', - '&:focus-visible': { - borderRadius: 1, - outline: `3px solid ${alpha(theme.palette.primary[500], 0.5)}`, - outlineOffset: '-8px', - }, - })} - /> - <NextLink href={layout.href} passHref legacyBehavior> - {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} - <Link - tabIndex={-1} - aria-hidden - data-ga-event-category="material-ui-template" - data-ga-event-label={layout.title} - data-ga-event-action="preview-img" - sx={(theme) => ({ - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'column', - gap: 1, - transition: '0.15s', - position: 'absolute', - width: '100%', - height: '100%', - top: 0, - left: 0, - bgcolor: alpha(theme.palette.primary[50], 0.5), - backdropFilter: 'blur(4px)', - opacity: 0, - '&:hover, &:focus-visible': { - opacity: 1, - }, - ...theme.applyDarkStyles({ - bgcolor: alpha(theme.palette.common.black, 0.8), - }), - })} - > - <Visibility /> - <Typography - fontWeight="bold" - color="text.primary" - sx={{ textDecorationLine: 'underline' }} - > - View live preview - </Typography> - </Link> - </NextLink> - </Box> - <Box sx={{ p: 2, flexGrow: 1, display: 'flex', flexDirection: 'column' }}> - <Typography component="h3" variant="subtitle1" fontWeight="bold" gutterBottom> + /> + <Box sx={{ p: 2, pt: 1.5 }}> + <Typography component="h3" variant="body1" fontWeight="semiBold"> {layout.title} </Typography> <Typography variant="body2" color="text.secondary" mb={2}> {layout.description} </Typography> - <Button - component="a" - href={layout.source} - size="small" - fullWidth - variant="outlined" - color="secondary" - startIcon={<CodeRoundedIcon sx={{ mr: 0.5 }} />} - sx={{ mt: 'auto' }} + <Box + sx={{ + display: 'flex', + flexDirection: { xs: 'column', sm: 'row' }, + gap: 1, + mt: 'auto', + }} > - {t('sourceCode')} - </Button> + <Button + component="a" + href={layout.href} + size="small" + fullWidth + variant="outlined" + color="secondary" + startIcon={<Visibility sx={{ mr: 0.5 }} />} + data-ga-event-category="material-ui-template" + data-ga-event-label={layout.title} + data-ga-event-action="preview-img" + > + Live preview + </Button> + <Button + component="a" + href={layout.source} + size="small" + fullWidth + variant="outlined" + color="secondary" + startIcon={<CodeRoundedIcon sx={{ mr: 0.5 }} />} + > + Source code + </Button> + </Box> </Box> </Card> </Grid> diff --git a/docs/src/modules/components/MaterialUIComponents/MaterialDataDisplayComponents.js b/docs/src/modules/components/MaterialUIComponents/MaterialDataDisplayComponents.js index 0cc2d5f800e61a..6e14e91093bcbc 100644 --- a/docs/src/modules/components/MaterialUIComponents/MaterialDataDisplayComponents.js +++ b/docs/src/modules/components/MaterialUIComponents/MaterialDataDisplayComponents.js @@ -20,7 +20,7 @@ const dataDisplayComponents = [ link: '/material-ui/react-badge/', md1: false, md2: false, - md3: true, + md3: false, noGuidelines: false, }, { @@ -30,7 +30,7 @@ const dataDisplayComponents = [ link: '/material-ui/react-chip/', md1: false, md2: true, - md3: true, + md3: false, noGuidelines: false, }, { @@ -40,7 +40,7 @@ const dataDisplayComponents = [ link: '/material-ui/react-divider/', md1: false, md2: true, - md3: true, + md3: false, noGuidelines: false, }, { diff --git a/docs/src/modules/components/MaterialUIComponents/MaterialFeedbackComponents.js b/docs/src/modules/components/MaterialUIComponents/MaterialFeedbackComponents.js index 35e112ad076503..c3cc1c11a174fa 100644 --- a/docs/src/modules/components/MaterialUIComponents/MaterialFeedbackComponents.js +++ b/docs/src/modules/components/MaterialUIComponents/MaterialFeedbackComponents.js @@ -40,7 +40,7 @@ const feedbackComponents = [ link: '/material-ui/react-progress/', md1: false, md2: true, - md3: true, + md3: false, noGuidelines: false, }, { diff --git a/docs/src/modules/components/MaterialUIComponents/MaterialInputComponents.js b/docs/src/modules/components/MaterialUIComponents/MaterialInputComponents.js index 4037c016012df8..1d67aa2ffacb36 100644 --- a/docs/src/modules/components/MaterialUIComponents/MaterialInputComponents.js +++ b/docs/src/modules/components/MaterialUIComponents/MaterialInputComponents.js @@ -20,7 +20,7 @@ const inputComponents = [ link: '/material-ui/react-button/', md1: false, md2: true, - md3: true, + md3: false, noGuidelines: false, }, { @@ -90,7 +90,7 @@ const inputComponents = [ link: '/material-ui/react-slider/', md1: false, md2: true, - md3: true, + md3: false, noGuidelines: false, }, { diff --git a/docs/src/modules/components/MaterialUIExampleCollection.js b/docs/src/modules/components/MaterialUIExampleCollection.js index 15fb508ee72374..d53eddf8147e59 100644 --- a/docs/src/modules/components/MaterialUIExampleCollection.js +++ b/docs/src/modules/components/MaterialUIExampleCollection.js @@ -13,78 +13,78 @@ const examples = [ name: 'Next.js App Router', label: 'View JS example', tsLabel: 'View TS example', - link: 'https://github.com/mui/material-ui/tree/master/examples/material-ui-nextjs', - tsLink: 'https://github.com/mui/material-ui/tree/master/examples/material-ui-nextjs-ts', + link: 'https://github.com/mui/material-ui/tree/next/examples/material-ui-nextjs', + tsLink: 'https://github.com/mui/material-ui/tree/next/examples/material-ui-nextjs-ts', src: '/static/images/examples/next.svg', }, { name: 'Vite.js', label: 'View JS example', tsLabel: 'View TS example', - link: 'https://github.com/mui/material-ui/tree/master/examples/material-ui-vite', - tsLink: 'https://github.com/mui/material-ui/tree/master/examples/material-ui-vite-ts', + link: 'https://github.com/mui/material-ui/tree/next/examples/material-ui-vite', + tsLink: 'https://github.com/mui/material-ui/tree/next/examples/material-ui-vite-ts', src: '/static/images/examples/vite.svg', }, { name: 'Next.js Pages Router', label: 'View JS example', tsLabel: 'View TS example', - link: 'https://github.com/mui/material-ui/tree/master/examples/material-ui-nextjs-pages-router', + link: 'https://github.com/mui/material-ui/tree/next/examples/material-ui-nextjs-pages-router', tsLink: - 'https://github.com/mui/material-ui/tree/master/examples/material-ui-nextjs-pages-router-ts', + 'https://github.com/mui/material-ui/tree/next/examples/material-ui-nextjs-pages-router-ts', src: '/static/images/examples/next.svg', }, { name: 'Remix', label: 'View TS example', - link: 'https://github.com/mui/material-ui/tree/master/examples/material-ui-remix-ts', + link: 'https://github.com/mui/material-ui/tree/next/examples/material-ui-remix-ts', src: '/static/images/examples/remix.svg', }, { name: 'Tailwind CSS + CRA', label: 'View TS example', - link: 'https://github.com/mui/material-ui/tree/master/examples/material-ui-cra-tailwind-ts', + link: 'https://github.com/mui/material-ui/tree/next/examples/material-ui-cra-tailwind-ts', src: '/static/images/examples/tailwindcss.svg', }, { name: 'Create React App', label: 'View JS example', tsLabel: 'View TS example', - link: 'https://github.com/mui/material-ui/tree/master/examples/material-ui-cra', - tsLink: 'https://github.com/mui/material-ui/tree/master/examples/material-ui-cra-ts', + link: 'https://github.com/mui/material-ui/tree/next/examples/material-ui-cra', + tsLink: 'https://github.com/mui/material-ui/tree/next/examples/material-ui-cra-ts', src: '/static/images/examples/cra.svg', }, { name: 'styled-components', label: 'View JS example', tsLabel: 'View TS example', - link: 'https://github.com/mui/material-ui/tree/master/examples/material-ui-cra-styled-components', + link: 'https://github.com/mui/material-ui/tree/next/examples/material-ui-cra-styled-components', tsLink: - 'https://github.com/mui/material-ui/tree/master/examples/material-ui-cra-styled-components-ts', + 'https://github.com/mui/material-ui/tree/next/examples/material-ui-cra-styled-components-ts', src: '/static/images/examples/styled.png', }, { name: 'Preact', label: 'View JS example', - link: 'https://github.com/mui/material-ui/tree/master/examples/material-ui-preact', + link: 'https://github.com/mui/material-ui/tree/next/examples/material-ui-preact', src: '/static/images/examples/preact.svg', }, { name: 'CDN', label: 'View JS example', - link: 'https://github.com/mui/material-ui/tree/master/examples/material-ui-via-cdn', + link: 'https://github.com/mui/material-ui/tree/next/examples/material-ui-via-cdn', src: <CloudRoundedIcon />, }, { name: 'Express.js (server-rendered)', label: 'View JS example', - link: 'https://github.com/mui/material-ui/tree/master/examples/material-ui-express-ssr', + link: 'https://github.com/mui/material-ui/tree/next/examples/material-ui-express-ssr', src: '/static/images/examples/express.png', }, { name: 'Gatsby', label: 'View JS example', - link: 'https://github.com/mui/material-ui/tree/master/examples/material-ui-gatsby', + link: 'https://github.com/mui/material-ui/tree/next/examples/material-ui-gatsby', src: '/static/images/examples/gatsby.svg', }, { diff --git a/docs/src/modules/components/MaterialYouUsageDemo.tsx b/docs/src/modules/components/MaterialYouUsageDemo.tsx deleted file mode 100644 index 6f42e405dfcc02..00000000000000 --- a/docs/src/modules/components/MaterialYouUsageDemo.tsx +++ /dev/null @@ -1,377 +0,0 @@ -import * as React from 'react'; -import { useTheme as md2UseTheme, alpha } from '@mui/material/styles'; -import ReplayRoundedIcon from '@mui/icons-material/ReplayRounded'; -import Box from '@mui/material/Box'; -import Divider from '@mui/material/Divider'; -import FormControl from '@mui/material/FormControl'; -import FormLabel, { formLabelClasses } from '@mui/material/FormLabel'; -import IconButton from '@mui/material/IconButton'; -import MenuItem from '@mui/material/MenuItem'; -import Select from '@mui/material/Select'; -import Switch from '@mui/material/Switch'; -import Typography from '@mui/material/Typography'; -import { - extendTheme, - CssVarsProvider as MaterialYouCssVarsProvider, - useColorScheme, -} from '@mui/material-next/styles'; -import BrandingProvider from 'docs/src/BrandingProvider'; -import HighlightedCode from 'docs/src/modules/components/HighlightedCode'; - -type Mode = 'light' | 'dark' | 'system'; - -const materialYouTheme = extendTheme(); -const shallowEqual = (item1: { [k: string]: any }, item2: { [k: string]: any }) => { - let equal = true; - Object.entries(item1).forEach(([key, value]: [string, any]) => { - if (item2[key] !== value) { - equal = false; - } - }); - return equal; -}; - -const defaultGetCodeBlock = (code: string) => code; - -function createCode( - data: { - name: string; - props: Record<string, string | number | boolean>; - childrenAccepted?: boolean; - }, - getCodeBlock = defaultGetCodeBlock, -) { - const { props: inProps, name, childrenAccepted } = data; - const closedJsx = childrenAccepted ? '>' : '/>'; - let code = `<${name}`; - const props = Object.entries(inProps).sort((a, b) => a[0].localeCompare(b[0])); - - if (!Object.keys(props).length) { - code = `${code} ${closedJsx}`; - } else { - let children = ''; - props.forEach((prop) => { - if (prop[0] !== 'children' && prop[1] !== undefined) { - if (props.length <= 2) { - if (typeof prop[1] === 'boolean') { - code = `${code} ${prop[0]}${prop[1] ? '' : '={false}'}`; - } else if (typeof prop[1] === 'function') { - code = `${code} ${prop[0]}={${(prop[1] as Function).toString()}}`; - } else { - code = `${code} ${prop[0]}=${ - typeof prop[1] === 'number' ? `{${prop[1]}}` : `"${prop[1]}"` - }`; - } - } else if (typeof prop[1] === 'function') { - code = `${code}\n ${prop[0]}={${(prop[1] as Function).toString()}}`; - } else if (typeof prop[1] === 'boolean') { - code = `${code}\n ${prop[0]}${prop[1] ? '' : '={false}'}`; - } else { - code = `${code}\n ${prop[0]}=${ - typeof prop[1] === 'number' ? `{${prop[1]}}` : `"${prop[1]}"` - }`; - } - } else { - children = prop[1] as string; - } - }); - if (children) { - code = `${code}${props.length > 2 ? `\n>` : '>'}\n ${children}\n</${name}>`; - } else { - code = `${code}${props.length > 2 ? `\n${closedJsx}` : `${childrenAccepted ? '>' : ' />'}`}`; - } - } - - return getCodeBlock(code); -} - -export const prependLinesSpace = (code: string, size: number = 2) => { - const newCode: string[] = []; - code.split('\n').forEach((line) => { - newCode.push(`${Array(size).fill(' ').join('')}${line}`); - }); - return newCode.join('\n'); -}; - -function ModeSwitcher({ md2Mode }: { md2Mode: Mode }) { - const { setMode } = useColorScheme(); - React.useEffect(() => { - setMode(md2Mode); - }, [md2Mode, setMode]); - return null; -} - -interface MaterialYouUsageDemoProps<ComponentProps> { - /** - * Name of the component to show in the code block. - */ - componentName: string; - /** - * For displaying the close bracket of the component in the code block. - * if `true`, shows '>' otherwise shows '/>' - */ - childrenAccepted?: boolean; - /** - * Configuration - */ - data: Array<{ - /** - * Name of the prop, e.g. 'children' - */ - propName: Extract<keyof ComponentProps, string>; - /** - * The controller to be used: - * - `switch`: render the switch component for boolean - * - `color`: render the built-in color selector - * - `select`: render <select> with the specified options - * - `input`: render <input /> - * - `radio`: render group of radios - */ - knob?: - | 'switch' - | 'color' - | 'select' - | 'input' - | 'radio' - | 'controlled' - | 'number' - | 'placement'; - /** - * The options for these knobs: `select` and `radio` - */ - options?: Array<string>; - /** - * The labels for these knobs: `radio` - */ - labels?: Array<string>; - /** - * The default value to be used by the components. - * If exists, it will be injected to the `renderDemo` callback but it will not show - * in the code block. - * - * To make it appears in the code block, specified `codeBlockDisplay: true` - */ - defaultValue?: string | number | boolean; - /** - * If not specify (`undefined`), the prop displays when user change the value - * If `true`, the prop with defaultValue will always display in the code block. - * If `false`, the prop does not display in the code block. - */ - codeBlockDisplay?: boolean; - onChange?: (event: React.SyntheticEvent) => void; - }>; - /** - * A function to override the code block result. - */ - getCodeBlock?: (code: string, props: ComponentProps) => string; - renderDemo: (props: ComponentProps) => React.ReactElement; -} - -export default function MaterialYouUsageDemo<T extends { [k: string]: any } = {}>({ - componentName, - childrenAccepted = false, - data, - renderDemo, - getCodeBlock = defaultGetCodeBlock, -}: MaterialYouUsageDemoProps<T>) { - const initialProps = {} as { [k in keyof T]: any }; - let demoProps = {} as { [k in keyof T]: any }; - let codeBlockProps = {} as { [k in keyof T]: any }; - data.forEach((p) => { - demoProps[p.propName] = p.defaultValue; - if (p.codeBlockDisplay) { - initialProps[p.propName] = p.defaultValue; - } - if (!p.knob) { - codeBlockProps[p.propName] = p.defaultValue; - } - }); - const [props, setProps] = React.useState<T>(initialProps as T); - demoProps = { ...demoProps, ...props }; - codeBlockProps = { ...props, ...codeBlockProps }; - data.forEach((p) => { - if (p.codeBlockDisplay === false) { - delete codeBlockProps[p.propName]; - } - }); - - const md2Theme = md2UseTheme(); - return ( - <Box - sx={{ - flexGrow: 1, - maxWidth: '100%', - display: 'flex', - flexDirection: { xs: 'column', md: 'row' }, - '& .markdown-body pre': { - margin: 0, - borderRadius: 'md', - }, - }} - > - <Box - sx={{ display: 'flex', flexDirection: 'column', flexGrow: 999, minWidth: 0, p: 3, gap: 3 }} - > - <Box - sx={{ - flexGrow: 1, - m: 'auto', - display: 'flex', - alignItems: 'center', - }} - > - <MaterialYouCssVarsProvider theme={materialYouTheme}> - <ModeSwitcher md2Mode={md2Theme.palette.mode} /> - {renderDemo(demoProps)} - </MaterialYouCssVarsProvider> - </Box> - <BrandingProvider mode="dark"> - <HighlightedCode - code={createCode( - { - name: componentName, - props: codeBlockProps, - childrenAccepted, - }, - (code) => getCodeBlock(code, demoProps), - )} - language="jsx" - sx={{ display: { xs: 'none', md: 'block' } }} - /> - </BrandingProvider> - </Box> - <Box - sx={(theme) => ({ - flexShrink: 0, - gap: 2, - borderLeft: '1px solid', - borderColor: theme.palette.grey[200], - background: alpha(theme.palette.grey[50], 0.5), - minWidth: '250px', - [`:where(${theme.vars ? '[data-mui-color-scheme="dark"]' : '.mode-dark'}) &`]: { - borderColor: alpha(theme.palette.grey[900], 0.8), - backgroundColor: alpha(theme.palette.grey[900], 0.3), - }, - })} - > - <Box - sx={{ - px: 3, - py: 2, - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - }} - > - <Typography - id="usage-props" - component="h3" - fontWeight="600" - sx={{ scrollMarginTop: 160, fontFamily: 'General Sans' }} - > - Playground - </Typography> - <IconButton - aria-label="Reset all" - size="small" - onClick={() => setProps(initialProps as T)} - sx={{ - visibility: !shallowEqual(props, initialProps) ? 'visible' : 'hidden', - }} - > - <ReplayRoundedIcon /> - </IconButton> - </Box> - <Divider sx={{ opacity: 0.5 }} /> - <Box - sx={{ - p: 3, - display: 'flex', - flexDirection: 'column', - gap: 2, - [`& .${formLabelClasses.root}`]: { - fontWeight: 'lg', - }, - }} - > - {data.map(({ propName, knob, options = [], defaultValue, onChange }) => { - const resolvedValue = props[propName] ?? defaultValue; - if (!knob) { - return null; - } - if (knob === 'switch') { - return ( - <FormControl - key={propName} - size="small" - sx={{ - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - }} - > - <FormLabel - sx={{ - textTransform: 'capitalize', - fontWeight: 'medium', - fontSize: '0.875rem', - color: 'text.secondary', - }} - > - {propName} - </FormLabel> - <Switch - size="small" - checked={Boolean(resolvedValue)} - onChange={(event) => { - setProps((latestProps) => ({ - ...latestProps, - [propName]: event.target.checked, - })); - onChange?.(event); - }} - /> - </FormControl> - ); - } - if (knob === 'select') { - return ( - <FormControl key={propName} size="small"> - <FormLabel - sx={{ - textTransform: 'capitalize', - fontWeight: 'medium', - fontSize: '0.875rem', - mb: 0.5, - }} - > - {propName} - </FormLabel> - <Select - placeholder="Select a variant..." - value={(resolvedValue || 'none') as string} - onChange={(event) => { - setProps((latestProps) => ({ - ...latestProps, - [propName]: event.target.value, - })); - onChange?.(event as React.SyntheticEvent); - }} - > - {options.map((value) => ( - <MenuItem key={value} value={value}> - {value} - </MenuItem> - ))} - </Select> - </FormControl> - ); - } - return null; - })} - </Box> - </Box> - </Box> - ); -} diff --git a/docs/src/modules/components/MuiProductSelector.tsx b/docs/src/modules/components/MuiProductSelector.tsx index abd7ae14f094bb..27fd949782cdc8 100644 --- a/docs/src/modules/components/MuiProductSelector.tsx +++ b/docs/src/modules/components/MuiProductSelector.tsx @@ -186,7 +186,7 @@ export default function MuiProductSelector() { </Box> <li role="none"> <Link - href={ROUTES.toolpadDocs} + href={ROUTES.toolpadStudioDocs} sx={(theme) => ({ p: 2, pr: 3, @@ -204,7 +204,7 @@ export default function MuiProductSelector() { <ProductSubMenu role="menuitem" icon={<IconImage name="product-toolpad" />} - name="MUI Toolpad" + name="Toolpad" description="Low-code admin builder." chip={<Chip size="small" label="Beta" color="primary" variant="outlined" />} /> diff --git a/docs/src/modules/components/Notifications.js b/docs/src/modules/components/Notifications.js index 63c3e0f82e009f..265312ae688114 100644 --- a/docs/src/modules/components/Notifications.js +++ b/docs/src/modules/components/Notifications.js @@ -116,7 +116,7 @@ export default function Notifications() { // and create some distraction. const timeout = setTimeout(async () => { const notifications = await fetchNotifications().catch(() => { - // Swallow the exceptions, e.g. rate limit + // Swallow the exceptions, for example rate limit return []; }); diff --git a/docs/src/modules/components/ThemeContext.js b/docs/src/modules/components/ThemeContext.js index 50fe41c5a7adf5..46713fcf715e8b 100644 --- a/docs/src/modules/components/ThemeContext.js +++ b/docs/src/modules/components/ThemeContext.js @@ -5,7 +5,6 @@ import { createTheme as createMdTheme, } from '@mui/material/styles'; import { deepmerge } from '@mui/utils'; -import useMediaQuery from '@mui/material/useMediaQuery'; import { enUS, zhCN, ptBR } from '@mui/material/locale'; import { unstable_useEnhancedEffect as useEnhancedEffect } from '@mui/material/utils'; import { getCookie } from 'docs/src/modules/utils/helpers'; @@ -16,6 +15,8 @@ import { getThemedComponents, getMetaThemeColor, } from 'docs/src/modules/brandingTheme'; +import useMediaQuery from '@mui/material/useMediaQuery'; +import useLocalStorageState from '@mui/utils/useLocalStorageState'; const languageMap = { en: enUS, @@ -111,82 +112,87 @@ if (process.env.NODE_ENV !== 'production') { export function ThemeProvider(props) { const { children } = props; - const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)', { noSsr: true }); - const preferredMode = prefersDarkMode ? 'dark' : 'light'; - const [themeOptions, dispatch] = React.useReducer( - (state, action) => { - switch (action.type) { - case 'SET_SPACING': - return { - ...state, - spacing: action.payload, - }; - case 'INCREASE_SPACING': { - return { - ...state, - spacing: state.spacing + 1, - }; - } - case 'DECREASE_SPACING': { - return { - ...state, - spacing: state.spacing - 1, - }; - } - case 'SET_DENSE': - return { - ...state, - dense: action.payload, - }; - case 'RESET_DENSITY': - return { - ...state, - dense: themeInitialOptions.dense, - spacing: themeInitialOptions.spacing, - }; - case 'RESET_COLORS': - return { - ...state, - paletteColors: themeInitialOptions.paletteColors, - }; - case 'CHANGE': - return { - ...state, - paletteMode: action.payload.paletteMode || state.paletteMode, - direction: action.payload.direction || state.direction, - paletteColors: action.payload.paletteColors || state.paletteColors, - }; - default: - throw new Error(`Unrecognized type ${action.type}`); + const [themeOptions, dispatch] = React.useReducer((state, action) => { + switch (action.type) { + case 'SET_SPACING': + return { + ...state, + spacing: action.payload, + }; + case 'INCREASE_SPACING': { + return { + ...state, + spacing: state.spacing + 1, + }; } - }, - { ...themeInitialOptions, paletteMode: 'light' }, - ); + case 'DECREASE_SPACING': { + return { + ...state, + spacing: state.spacing - 1, + }; + } + case 'SET_DENSE': + return { + ...state, + dense: action.payload, + }; + case 'RESET_DENSITY': + return { + ...state, + dense: themeInitialOptions.dense, + spacing: themeInitialOptions.spacing, + }; + case 'RESET_COLORS': + return { + ...state, + paletteColors: themeInitialOptions.paletteColors, + }; + case 'CHANGE': + // No value changed + if ( + (!action.payload.paletteMode || action.payload.paletteMode === state.paletteMode) && + (!action.payload.direction || action.payload.direction === state.direction) && + (!action.payload.paletteColors || action.payload.paletteColors === state.paletteColors) + ) { + return state; + } + + return { + ...state, + paletteMode: action.payload.paletteMode || state.paletteMode, + direction: action.payload.direction || state.direction, + paletteColors: action.payload.paletteColors || state.paletteColors, + }; + default: + throw new Error(`Unrecognized type ${action.type}`); + } + }, themeInitialOptions); const userLanguage = useUserLanguage(); const { dense, direction, paletteColors, paletteMode, spacing } = themeOptions; useLazyCSS('/static/styles/prism-okaidia.css', '#prismjs'); - useEnhancedEffect(() => { - const nextPaletteColors = JSON.parse(getCookie('paletteColors') || 'null'); - let nextPaletteMode = preferredMode; // syncing with homepage, can be removed once all pages are migrated to CSS variables - try { - nextPaletteMode = localStorage.getItem('mui-mode') ?? preferredMode; - } catch (error) { - // mainly thrown when cookies are disabled. - } + // TODO replace with useColorScheme once all pages support css vars + const { mode, systemMode } = useColorSchemeShim(); + const calculatedMode = mode === 'system' ? systemMode : mode; - if (nextPaletteMode === 'system') { - nextPaletteMode = preferredMode; + useEnhancedEffect(() => { + let nextPaletteColors = JSON.parse(getCookie('paletteColors') || 'null'); + // Set default value if no value is found in cookie + if (nextPaletteColors === null) { + nextPaletteColors = themeInitialOptions.paletteColors; } dispatch({ type: 'CHANGE', - payload: { paletteColors: nextPaletteColors, paletteMode: nextPaletteMode }, + payload: { + paletteColors: nextPaletteColors, + paletteMode: calculatedMode, + }, }); - }, [preferredMode]); + }, [calculatedMode]); useEnhancedEffect(() => { document.body.dir = direction; @@ -271,3 +277,16 @@ export function useChangeTheme() { const dispatch = React.useContext(DispatchContext); return React.useCallback((options) => dispatch({ type: 'CHANGE', payload: options }), [dispatch]); } + +// TODO: remove once all pages support css vars and replace call sites with useColorScheme() +export function useColorSchemeShim() { + const [mode, setMode] = useLocalStorageState('mui-mode', 'system'); + const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)', { noSsr: true }); + const systemMode = prefersDarkMode ? 'dark' : 'light'; + + return { + mode, + systemMode, + setMode, + }; +} diff --git a/docs/src/modules/components/TopLayoutBlog.js b/docs/src/modules/components/TopLayoutBlog.js index df796c63a6ef08..be14672647dbc1 100644 --- a/docs/src/modules/components/TopLayoutBlog.js +++ b/docs/src/modules/components/TopLayoutBlog.js @@ -111,6 +111,11 @@ export const authors = { avatar: 'https://avatars.githubusercontent.com/u/805073', github: 'colmtuite', }, + diegoandai: { + name: 'Diego Andai', + avatar: 'https://avatars.githubusercontent.com/u/16889233', + github: 'DiegoAndai', + }, }; const classes = { diff --git a/docs/src/modules/sandbox/CodeSandbox.test.js b/docs/src/modules/sandbox/CodeSandbox.test.js index 16f745b352da2a..16249c8dc4500c 100644 --- a/docs/src/modules/sandbox/CodeSandbox.test.js +++ b/docs/src/modules/sandbox/CodeSandbox.test.js @@ -33,7 +33,7 @@ describe('CodeSandbox', () => { 'https://github.com/mui/material-ui/blob/v5.7.0/docs/data/material/components/buttons/BasicButtons.js', dependencies: { react: 'latest', - '@mui/material': 'latest', + '@mui/material': 'next', 'react-dom': 'latest', '@emotion/react': 'latest', '@emotion/styled': 'latest', @@ -123,7 +123,7 @@ ReactDOM.createRoot(document.querySelector("#root")).render( 'https://github.com/mui/material-ui/blob/v5.7.0/docs/data/material/components/buttons/BasicButtons.tsx', dependencies: { react: 'latest', - '@mui/material': 'latest', + '@mui/material': 'next', 'react-dom': 'latest', '@emotion/react': 'latest', '@emotion/styled': 'latest', @@ -230,7 +230,7 @@ ReactDOM.createRoot(document.querySelector("#root")!).render( expect(result.dependencies).to.deep.equal({ '@emotion/react': 'latest', '@emotion/styled': 'latest', - '@mui/material': 'latest', + '@mui/material': 'next', '@types/react': 'latest', '@types/react-dom': 'latest', react: 'latest', diff --git a/docs/src/modules/sandbox/CodeSandbox.ts b/docs/src/modules/sandbox/CodeSandbox.ts index 0df110fbc68cac..13e93beaccfa61 100644 --- a/docs/src/modules/sandbox/CodeSandbox.ts +++ b/docs/src/modules/sandbox/CodeSandbox.ts @@ -83,7 +83,7 @@ function createReactApp(demoData: DemoData) { devDependencies, /** * @param {string} initialFile - * @description should start with `/`, e.g. `/Demo.tsx`. If the extension is not provided, + * @description should start with `/`, for example `/Demo.tsx`. If the extension is not provided, * it will be appended based on the code variant. */ openSandbox: (initialFile: string = `/src/Demo.${ext}`) => diff --git a/docs/src/modules/sandbox/Dependencies.test.js b/docs/src/modules/sandbox/Dependencies.test.js index 71b4f6b1cba69b..94eca04aab7ac6 100644 --- a/docs/src/modules/sandbox/Dependencies.test.js +++ b/docs/src/modules/sandbox/Dependencies.test.js @@ -40,8 +40,8 @@ const styles = theme => ({ '@emotion/react': 'latest', '@emotion/styled': 'latest', '@foo-bar/bip': 'latest', - '@mui/material': 'latest', - '@mui/base': 'latest', + '@mui/material': 'next', + '@mui/base': 'next', 'prop-types': 'latest', }); }); @@ -71,7 +71,7 @@ const suggestions = [ 'react-dom': 'latest', '@emotion/react': 'latest', '@emotion/styled': 'latest', - '@mui/material': 'latest', + '@mui/material': 'next', '@unexisting/thing': 'latest', 'autosuggest-highlight': 'latest', 'prop-types': 'latest', @@ -100,8 +100,8 @@ import { LocalizationProvider as MuiPickersLocalizationProvider, KeyboardTimePic 'prop-types': 'latest', '@emotion/react': 'latest', '@emotion/styled': 'latest', - '@mui/material': 'latest', - '@mui/lab': 'latest', + '@mui/material': 'next', + '@mui/lab': 'next', }); }); @@ -127,8 +127,8 @@ import 'exceljs'; 'prop-types': 'latest', '@emotion/react': 'latest', '@emotion/styled': 'latest', - '@mui/material': 'latest', - '@mui/lab': 'latest', + '@mui/material': 'next', + '@mui/lab': 'next', exceljs: 'latest', }); }); @@ -146,8 +146,8 @@ import 'exceljs'; '@emotion/react': 'latest', '@emotion/styled': 'latest', '@foo-bar/bip': 'latest', - '@mui/material': 'latest', - '@mui/base': 'latest', + '@mui/material': 'next', + '@mui/base': 'next', '@types/foo-bar__bip': 'latest', '@types/prop-types': 'latest', '@types/react-dom': 'latest', @@ -167,7 +167,7 @@ import 'exceljs'; 'react-dom': 'latest', '@emotion/react': 'latest', '@emotion/styled': 'latest', - '@mui/material': 'latest', + '@mui/material': 'next', '@types/react-dom': 'latest', '@types/react': 'latest', typescript: 'latest', @@ -195,8 +195,8 @@ import { 'react-dom': 'latest', '@emotion/react': 'latest', '@emotion/styled': 'latest', - '@mui/material': 'latest', - '@mui/lab': 'latest', + '@mui/material': 'next', + '@mui/lab': 'next', }); }); @@ -215,8 +215,8 @@ import lab from '@mui/lab'; 'react-dom': 'latest', '@emotion/react': 'latest', '@emotion/styled': 'latest', - '@mui/material': 'latest', - '@mui/lab': 'latest', + '@mui/material': 'next', + '@mui/lab': 'next', }); }); @@ -517,10 +517,10 @@ export default function EmailExample() { 'react-dom': 'latest', '@emotion/react': 'latest', '@emotion/styled': 'latest', - '@mui/icons-material': 'latest', - '@mui/joy': 'latest', - '@mui/material': 'latest', - '@mui/system': 'latest', + '@mui/icons-material': 'next', + '@mui/joy': 'next', + '@mui/material': 'next', + '@mui/system': 'next', '@types/react': 'latest', '@types/react-dom': 'latest', typescript: 'latest', diff --git a/docs/src/modules/sandbox/Dependencies.ts b/docs/src/modules/sandbox/Dependencies.ts index 1f0f69ae774749..c943c19e0729f4 100644 --- a/docs/src/modules/sandbox/Dependencies.ts +++ b/docs/src/modules/sandbox/Dependencies.ts @@ -49,7 +49,7 @@ export default function SandboxDependencies( process.env.SOURCE_CODE_REPO !== 'https://github.com/mui/material-ui' ) { // #default-branch-switch - return 'latest'; + return 'next'; } const shortSha = commitRef.slice(0, 8); return `https://pkg.csb.dev/mui/material-ui/commit/${shortSha}/@mui/${packageName}`; @@ -103,7 +103,6 @@ export default function SandboxDependencies( '@mui/private-classnames': getMuiPackageVersion('classnames'), '@mui/base': getMuiPackageVersion('base'), '@mui/utils': getMuiPackageVersion('utils'), - '@mui/material-next': getMuiPackageVersion('material-next'), '@mui/material-nextjs': getMuiPackageVersion('material-nextjs'), '@mui/joy': getMuiPackageVersion('joy'), }; diff --git a/docs/src/modules/sandbox/StackBlitz.test.js b/docs/src/modules/sandbox/StackBlitz.test.js index 0d25c2a95c77b7..67bc0625b4cf80 100644 --- a/docs/src/modules/sandbox/StackBlitz.test.js +++ b/docs/src/modules/sandbox/StackBlitz.test.js @@ -83,7 +83,7 @@ ReactDOM.createRoot(document.querySelector("#root")).render( }, dependencies: { react: 'latest', - '@mui/material': 'latest', + '@mui/material': 'next', 'react-dom': 'latest', '@emotion/react': 'latest', '@emotion/styled': 'latest', @@ -186,7 +186,7 @@ ReactDOM.createRoot(document.querySelector("#root")!).render( }, dependencies: { react: 'latest', - '@mui/material': 'latest', + '@mui/material': 'next', 'react-dom': 'latest', '@emotion/react': 'latest', '@emotion/styled': 'latest', diff --git a/docs/src/modules/utils/getProductInfoFromUrl.test.js b/docs/src/modules/utils/getProductInfoFromUrl.test.js index 61061f7e2af56d..90b14261522b73 100644 --- a/docs/src/modules/utils/getProductInfoFromUrl.test.js +++ b/docs/src/modules/utils/getProductInfoFromUrl.test.js @@ -90,10 +90,17 @@ describe('getProductInfoFromUrl', () => { }); }); - it('should handle MUI Toolpad', () => { - expect(getProductInfoFromUrl('/toolpad/getting-started/first-app/')).to.deep.equal({ - productCategoryId: 'null', - productId: 'toolpad', + it('should handle Toolpad Core', () => { + expect(getProductInfoFromUrl('/toolpad/getting-started/')).to.deep.equal({ + productCategoryId: 'toolpad', + productId: 'toolpad-core', + }); + }); + + it('should handle Toolpad Studio', () => { + expect(getProductInfoFromUrl('/toolpad/studio/getting-started/first-app/')).to.deep.equal({ + productCategoryId: 'toolpad', + productId: 'toolpad-studio', }); }); }); diff --git a/docs/src/modules/utils/getProductInfoFromUrl.ts b/docs/src/modules/utils/getProductInfoFromUrl.ts index 763141fb4ed569..f2577ae8c50b13 100644 --- a/docs/src/modules/utils/getProductInfoFromUrl.ts +++ b/docs/src/modules/utils/getProductInfoFromUrl.ts @@ -51,7 +51,17 @@ export default function getProductInfoFromUrl(asPath: string): MuiProductInfo { } } - if (firstFolder === 'toolpad' || firstFolder === 'docs') { + if (firstFolder === 'toolpad') { + productCategoryId = 'toolpad'; + const secondFolder = asPathWithoutLang.replace(/^\/+[^/]+\/([^/]+)\/.*/, '$1'); + if (secondFolder === 'studio') { + productId = 'toolpad-studio'; + } else { + productId = 'toolpad-core'; + } + } + + if (firstFolder === 'docs') { productId = firstFolder; } diff --git a/docs/src/pages/premium-themes/onepirate/modules/views/privacy.md b/docs/src/pages/premium-themes/onepirate/modules/views/privacy.md index d01e183f530ba8..aa909a70f293e0 100644 --- a/docs/src/pages/premium-themes/onepirate/modules/views/privacy.md +++ b/docs/src/pages/premium-themes/onepirate/modules/views/privacy.md @@ -13,10 +13,10 @@ Please read this policy carefully to understand how we handle and treat your per We may collect and process the following personal information from you: -- **Information you provide to us:** We collect personal information when you voluntarily provide us with such information in the course of using our website or Services. For example, when you register to use our Services, we will collect your name, email address and organization information. We also collect personal information from you when you subscribe to our newsletter, or respond to a survey. If you make an enquiry through our website, or contact us in any other way, we will keep a copy of your communications with us. +- **Information you provide to us:** We collect personal information when you voluntarily provide us with such information in the course of using our website or Services. For example, when you register to use our Services, we will collect your name, email address and organization information. We also collect personal information from you when you subscribe to our newsletter, or respond to a survey. If you make an inquiry through our website, or contact us in any other way, we will keep a copy of your communications with us. - **Information we collect when you do business with us:** We may process your personal information when you do business with us – for example, as a customer or prospective customer, or as a vendor, supplier, consultant or other third party. For example, we may hold your business contact information and financial account information (if any) and other communications you have with us for the purposes of maintaining our business relations with you. - **Information we automatically collect:** We may also collect certain technical information by automatic means when you visit our website, such as IP address, browser type and operating system, referring URLs, your use of our website, and other clickstream data. We collect this information automatically through the use of various technologies, such as cookies. -- **Personal information where we act as a data processor:** We also process personal information on behalf of our customers in the context of supporting our products and services. Where a customer subscribes to our Services for their website, game or app, they will be the ones who control what event data is collected and stored on our systems. For example, they may ask us to log basic user data (e.g. email address or username), device identifiers, IP addresses, event type, and related source code. In such cases, we are data processors acting in accordance with the instructions of our customers. You will need to refer to the privacy policies of our customers to find out more about how such information is handled by them. +- **Personal information where we act as a data processor:** We also process personal information on behalf of our customers in the context of supporting our products and services. Where a customer subscribes to our Services for their website, game or app, they will be the ones who control what event data is collected and stored on our systems. For example, they may ask us to log basic user data (for example email address or username), device identifiers, IP addresses, event type, and related source code. In such cases, we are data processors acting in accordance with the instructions of our customers. You will need to refer to the privacy policies of our customers to find out more about how such information is handled by them. ## What do we use your information for? @@ -84,7 +84,7 @@ You can also unsubscribe from our marketing communications at any time by follow ## Data Retention -We may retain your personal information as long as you continue to use the Services, have an account with us or for as long as is necessary to fulfil the purposes outlined in the policy. You can ask to close your account by contacting us at the details below and we will delete your personal information on request. +We may retain your personal information as long as you continue to use the Services, have an account with us or for as long as is necessary to fulfill the purposes outlined in the policy. You can ask to close your account by contacting us at the details below and we will delete your personal information on request. We may however retain personal information for an additional period as is permitted or required under applicable laws, for legal, tax or regulatory reasons, or for legitimate and lawful business purposes. diff --git a/docs/src/pages/versions/versions.md b/docs/src/pages/versions/versions.md index b008b18f544117..0d46a8bafb2f8a 100644 --- a/docs/src/pages/versions/versions.md +++ b/docs/src/pages/versions/versions.md @@ -80,7 +80,7 @@ You can follow the [milestones](https://github.com/mui/material-ui/milestones) f Sometimes "breaking changes", such as the removal of support for select APIs and features, are necessary. To make these transitions as easy as possible: -- The number of breaking changes is minimized, and migration tools are provided when possible (e.g. codemods). +- The number of breaking changes is minimized, and migration tools are provided when possible (for example codemods). - The deprecation policy described below is followed so that you have time to update your apps to the latest APIs and best practices. ### Deprecation policy diff --git a/docs/src/route.ts b/docs/src/route.ts index 792ff14c1c056c..b4a0c5828de3a9 100644 --- a/docs/src/route.ts +++ b/docs/src/route.ts @@ -50,6 +50,7 @@ const ROUTES = { treeViewOverview: '/x/react-tree-view/', // Toolpad pages toolpadDocs: '/toolpad/getting-started/', + toolpadStudioDocs: '/toolpad/studio/getting-started', // External pages rssFeed: '/feed/blog/rss.xml', handbook: 'https://mui-org.notion.site/Handbook-f086d47e10794d5e839aef9dc67f324b', diff --git a/docs/translations/api-docs-joy/autocomplete/autocomplete.json b/docs/translations/api-docs-joy/autocomplete/autocomplete.json index a03b4c15f42bdc..1472da2b7fab9f 100644 --- a/docs/translations/api-docs-joy/autocomplete/autocomplete.json +++ b/docs/translations/api-docs-joy/autocomplete/autocomplete.json @@ -108,7 +108,7 @@ "description": "The maximum number of tags that will be visible when not focused. Set <code>-1</code> to disable the limit." }, "loading": { - "description": "If <code>true</code>, the component is in a loading state. This shows the <code>loadingText</code> in place of suggestions (only if there are no suggestions to show, e.g. <code>options</code> are empty)." + "description": "If <code>true</code>, the component is in a loading state. This shows the <code>loadingText</code> in place of suggestions (only if there are no suggestions to show, for example <code>options</code> are empty)." }, "loadingText": { "description": "Text to display when in a loading state.<br>For localization purposes, you can use the provided <a href=\"/material-ui/guides/localization/\">translations</a>." diff --git a/docs/translations/api-docs/autocomplete/autocomplete.json b/docs/translations/api-docs/autocomplete/autocomplete.json index d68f8fc3fb6965..b257a40f8d3e01 100644 --- a/docs/translations/api-docs/autocomplete/autocomplete.json +++ b/docs/translations/api-docs/autocomplete/autocomplete.json @@ -104,7 +104,7 @@ "ListboxComponent": { "description": "The component used to render the listbox." }, "ListboxProps": { "description": "Props applied to the Listbox element." }, "loading": { - "description": "If <code>true</code>, the component is in a loading state. This shows the <code>loadingText</code> in place of suggestions (only if there are no suggestions to show, e.g. <code>options</code> are empty)." + "description": "If <code>true</code>, the component is in a loading state. This shows the <code>loadingText</code> in place of suggestions (only if there are no suggestions to show, for example <code>options</code> are empty)." }, "loadingText": { "description": "Text to display when in a loading state.<br>For localization purposes, you can use the provided <a href=\"/material-ui/guides/localization/\">translations</a>." @@ -287,17 +287,17 @@ "tag": { "description": "Styles applied to {{nodeName}}, {{conditions}}.", "nodeName": "the tag elements", - "conditions": "e.g. the chips" + "conditions": "for example the chips" }, "tagSizeMedium": { "description": "Styles applied to {{nodeName}}, {{conditions}}.", "nodeName": "the tag elements", - "conditions": "e.g. the chips if <code>size=\"medium\"</code>" + "conditions": "for example the chips if <code>size=\"medium\"</code>" }, "tagSizeSmall": { "description": "Styles applied to {{nodeName}}, {{conditions}}.", "nodeName": "the tag elements", - "conditions": "e.g. the chips if <code>size=\"small\"</code>" + "conditions": "for example the chips if <code>size=\"small\"</code>" } } } diff --git a/docs/translations/api-docs/circular-progress/circular-progress.json b/docs/translations/api-docs/circular-progress/circular-progress.json index d1223babd066ce..396bcdc6e441a5 100644 --- a/docs/translations/api-docs/circular-progress/circular-progress.json +++ b/docs/translations/api-docs/circular-progress/circular-progress.json @@ -9,7 +9,7 @@ "description": "If <code>true</code>, the shrink animation is disabled. This only works if variant is <code>indeterminate</code>." }, "size": { - "description": "The size of the component. If using a number, the pixel unit is assumed. If using a string, you need to provide the CSS unit, e.g. '3rem'." + "description": "The size of the component. If using a number, the pixel unit is assumed. If using a string, you need to provide the CSS unit, for example '3rem'." }, "sx": { "description": "The system prop that allows defining system overrides as well as additional CSS styles." diff --git a/docs/translations/api-docs/icon/icon.json b/docs/translations/api-docs/icon/icon.json index 27699639adaa32..fa866f0daa2bf0 100644 --- a/docs/translations/api-docs/icon/icon.json +++ b/docs/translations/api-docs/icon/icon.json @@ -2,7 +2,7 @@ "componentDescription": "", "propDescriptions": { "baseClassName": { - "description": "The base class applied to the icon. Defaults to 'material-icons', but can be changed to any other base class that suits the icon font you're using (e.g. material-icons-rounded, fas, etc)." + "description": "The base class applied to the icon. Defaults to 'material-icons', but can be changed to any other base class that suits the icon font you're using (for example material-icons-rounded, fas, etc)." }, "children": { "description": "The name of the icon font ligature." }, "classes": { "description": "Override or extend the styles applied to the component." }, diff --git a/docs/writing-rules.zip b/docs/writing-rules.zip deleted file mode 100644 index a1decff750c50035c305dfb9a2bc8b45683936ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3538 zcma);2{_dGAIC>CZXx$gj#6Wsn<FGgqQNrGhBU^EdxqTO$`N5RjtL>wvBDB!NXmUg z?mOfNAx0#!3H?XA&woR^?fjna%y*u7=KcOX-`DT+`MeFGKoA{Z|HWEsZt->V*Bc{% z58&hO=;?^Hhq!yWpgp8aO=tnYR-sY!bMo@?r3X-eHYorA5F7a)hGd=nPnxef(>>QE zQRJVpjsXCIM|3n@F>bCNXq36DJIX^6hjID8u++&T!kB~xgC=Q)h}A$kDEm@)<<?|e zl~BknW{ptGVLuE}9*^_^W(X+r5~!5LKH7iL4Wd?~Y^vv9tG0Px7M*7((MHlHO(!D4 zdW{i1aB;Ua*$_P%+N;5pI5opM$t&sSrUW{u^-9Z_L`)Q{LYT%@sg_Ht&e*-0eMRv= zt6<GG^Hz<mDy%>H5^aFn2@X!~Ti#q2><Fl%On40Md9Em`hXZ^PV@0prYBi_{I&;OC zEo^H<o|$XgL&J^g(txfu4DLNrxfopUlJ5@}rVRYq`wic2Za~ykTrRDm{qHw-lkXRT z>A<!aZSiu|xm{YTuC4ON4fkeS@iysA0m2Vz@H3o(O{|iyGc?5JUq@zZYr-`%7L(>v z80A%KCT1EDZq32%wDZx6+v!0k6h)=r=6R<2nA*Tw4}Zl3yE@hE0R|}y45nvaE3ryH zx|ncjay3K1BPlI=FoSCbSr?_u?kCeSLwyil#mk&99x}Xg^5Q%K&jd$i_akVghL4<@ zr5Pr6*+X+sPG$aQ!Y%0_eEf7P^H5K`(ztAr``gWBu)nycUqZ&lhUgAQC%xtLN+%17 zaB)-=<t+f=qkTtI*GRVUou<&RQ-2J&=C_9uo|J~y!wXWU<xKDMFXs;)@1pSap-ccX zXqa~v(Ya-%rZ$d3I8^$w1L0x6HN8+6J159t7T@Tdij|ycTPF5)5)5hSB(@?|`1h1t zmy0MAZSN4rBZ$s;YgK#@>GdC+OwrOW@gfXMvpTrA`PQ6#biO6kRU@#zG>S<scW%93 zCziXAmBioDWgf^wIv4p-by$BTMlGvetodU!lj<>f?ZB2LP_4B=ddR6H-Ric5597`y z>kf?Pjoo#7i!0A=OtN)J9X#rslkcVF$;Z4)UPtT1z;(Y;Y_K>38w~nKM}93HYCt~n zh%KcSsTe~}2fyHV%r4qU(PrM})+-Y%`b%26=Sv3MI1D1jDLYGlS^4s1hh~)?n#Q{} zi4KIz0~hpE6?|=a<*PWNlnvk>{W$@$E?mOx6dJ&s>NeAu#QZckUn324-Ps6)q+4;I z*GQfCo!4m=*HPPmPY@rjverOCuYUHPlS^Zfmv|ym&oZ>_7#=%;M*F%dac~-dr>>U_ z>8`o;8by@r8GUq|*}lHR6&gF30o8h$RCgCYMW_<dp8!jgij_R(mJ{);M$ZIo+>H}? zd!p-@!ddX*U@N7VP>k&A6y<6YwmNt6hLB?(vZpbMu7^&{WrMmDTAYtf(e8P2p*pe~ zXSN}S%Q$bJXx#9530sT^)fJbUTX70hw)enrEJd+U6C38W)wfvtb`G^I^jo<-9Wn|b za@#t4<jMcqwy<cd=ROWiSFAUA>*$L8aWNR_Lz^!$)NGl9qBpWv0<qbdp`nYyi<ru; z%BF5kf@k5LAEEl`OhLPUJ9LV72XzO?or!~zeC?8v>~U>)2Lu7}OK+YI9u;SELq%86 zJ$g)=cWpgcu(V!J65nfO2$Xyg$}hAsT>DT(pqIBE1}k{QW!x~C@M>Dt))WYl*DOs5 z9uN?TI!1!g{$5%U79x|YJ`V~L>SzRGHg6x->rBxb;V*ig^Z=`)>UF=2!1O-(^3u{x zj?Mc8fK-2Ckq<8rekFJDfslX^BzDoc61{p&t189C=44M~zmK=Tm;4H4$Ef-gMcQGv zG-T6~gBHiQ;XI8>me6G0rSiM^VKHF|BH9v3H`3cGr#k2xV0;~~OjBBrpI2l__eT_` zL}_qLcNI^|{PBM6nQSIf=Kb0ApxuqC>_M{%(%e0ugS+-EYm-OY6n~cvxbCXOo1a&1 z6UeOU$*e?<up%QO&#mu4>T4NCuXm3ho?l)tb?mq_^Rs1kdaiJ(3eSZuoe+8T0Uj^I z%o?M?D@aH@$Oo-})1T^3_R1Hc<q<l~=wo-=it*E9_r>NoiHG?kmjW4vI)%Lvnm+OH zQ=DPGA%<^fyhQB@=E|R_EeBTxKjGydGffxAeagfHdlAafyVWIyQ9UZ$Gxu$acbJiO z_LTM;-YmuPqMT_CmAX2!QDOC=+xHfbePi9d%(m40PCw=F24syWbMcrG?mbW%k8<Wt zlRWSE)2Y}?8$n1%`7d1-csceHb}~dYE8x59nUkkEk^^BX$JTDN2)&yWHn9fBmeAFa zMlV!M0@PR)Tu&Gv{Jd0-S=ZD{-L}*`*+s-<@HD2|ts20dD%v8_yiU{H37h379^bwo z{51C*dY4EP=NqaX7?E2Dist-KwflLYAUJQ2xyh^}jHyT`e4Rz-vNk$26BRRH$6Kwz z$fs!e#xx!gE~u4EVz0ht9d~hensqbitmDng27gH1Azm)r_^@3nYtpGPI5hWs@8bt} zV)a<&>BKB(DV6qAwCyI--@LDZ;1=y{B4eCn*7ZODfcFS*4R;$X>Oh;!CNusHXN++J znKMJpXD|&&Qvrcrt@VOXfh-=`T&u(?j6T4eEEA9PoFF-`Lq3%mIi(Wo86)&Atj7o3 zHCyX=p;I1A@pG+<aBBJ{&`lZ=bn6t)REPqrEU{HHELstaP;HBp9xe8;C_)&el5Av* zMt`E;6gh55bAEh_tB+oBIesK{Ek$^mkP<o|JEG3Cs8iVHz<r8syc=)l{aLOerev(Z zhK<(Be5g#qnXo<r(H}yMML3xA@D`fPkI95E4%I}tK6iLRFV3ZucMc!nWS9Od0HT?K zo`&V~^@Yn;z~)b}6=jZaC*7!%T+{W07l$lFw7if=3dqWS#7!K6%--DW0wt$R?+z)~ z!W^%{ng%t?)YupW`-XyLU=pP1Ge~C(oLIN6zqMX)JBU5jdr=8;IVG>T_g-;CNd7>T z38o*_!s-GeaPkYrTxqi%GU`Y)EcGiT@w->}$(fxs=LGm@zbS8R@HYN6X4dAZ?n`eV zlh=9tZR!d&$2uj+-DlBHrk0Ba&a934s%t3+QuKdmzv*#f%IcX~q?JyN-83p)?%8v* zZrxEB$y)DeSV=6G|7GZ|iy)H}BReT+CsA~iifxEvW+!0T-<>CJIN!(q%CfQB#aJWL zwiDG8>BRy`4q_8IiiP;)I{AyPAN)TRI5l;CYUB-3OLOIJXL?RRB}|R@TrX2l<?5on zpcgl64DQ=%w%7q0LMe_30RJfnbNv;GnE@;6``g2@nDWn9d?XU@YyMLx{!^3OzyQC6 z=p51fPDK81CI`dYw<eV2;yHNZ-$c3pCVnukd@JrouF8Yr-w7}O&E((^{?_CR=&;GZ zkK`Zw9UUa!`V~?g_WOsiLN?kTEC0g(Xb1b&D2?W@(V>1uHrVfHM+_**cL)GbldlRg KU26LMZ~p_&tciXA diff --git a/docs/writing-rules/ComponentNameConventions.yml b/docs/writing-rules/ComponentNameConventions.yml deleted file mode 100644 index df3c31e4984702..00000000000000 --- a/docs/writing-rules/ComponentNameConventions.yml +++ /dev/null @@ -1,16 +0,0 @@ -extends: substitution -message: To be consistent with component name, consider using '%s' instead of '%s'. -level: error -ignorecase: true -# swap maps tokens in form of bad: good -# for more information: https://vale.sh/docs/topics/styles/#substitution -swap: - 'Heat map': Heatmap - 'Tree map': Treemap - 'Sparkline Chart': Sparkline - 'Gauge Chart': Gauge - 'Treemap Chart': Treemap -# Don't forget to run the following command to generate the package writing-rules.zip file -# Vale uses that ZIP file and not the YAML files. -# -# pnpm docs:zipRules diff --git a/docs/writing-rules/ComposedWords.yml b/docs/writing-rules/ComposedWords.yml deleted file mode 100644 index 0821b0025a92a3..00000000000000 --- a/docs/writing-rules/ComposedWords.yml +++ /dev/null @@ -1,19 +0,0 @@ -extends: substitution -message: To be consistent with the rest of the documentation, consider using '%s' instead of '%s'. -level: error -ignorecase: true -# swap maps tokens in form of bad: good -# for more information: https://vale.sh/docs/topics/styles/#substitution -swap: - sub-component: subcomponent - sub-components: subcomponents - 'sub component': subcomponent - 'sub components': subcomponents - 'use-case': 'use case' - 'usecase': 'use case' - 'client side': 'client-side' - 'server side': 'server-side' -# Don't forget to run the following command to generate the package writing-rules.zip file -# Vale uses that ZIP file and not the YAML files. -# -# pnpm docs:zipRules diff --git a/docs/writing-rules/NamingConventions.yml b/docs/writing-rules/NamingConventions.yml deleted file mode 100644 index 6fc6645f1862f4..00000000000000 --- a/docs/writing-rules/NamingConventions.yml +++ /dev/null @@ -1,24 +0,0 @@ -extends: substitution -message: To be consistent with capitalization, consider using '%s' instead of '%s'. -level: error -ignorecase: false -# swap maps tokens in form of bad: good -# for more information: https://vale.sh/docs/topics/styles/#substitution -swap: - api: API - Api: API - typescript: TypeScript - Typescript: TypeScript - ts: TypeScript - TS: TypeScript - javascript: JavaScript - Javascript: JavaScript - css: CSS - Css: CSS - NPM: npm # https://css-tricks.com/start-sentence-npm/ - Github: GitHub - StackOverflow: Stack Overflow -# Don't forget to run the following command to generate the package writing-rules.zip file -# Vale uses that ZIP file and not the YAML files. -# -# pnpm docs:zipRules diff --git a/docs/writing-rules/Typos.yml b/docs/writing-rules/Typos.yml deleted file mode 100644 index c0f85dc4bacf4a..00000000000000 --- a/docs/writing-rules/Typos.yml +++ /dev/null @@ -1,15 +0,0 @@ -extends: substitution -message: Do you mean '%s' instead of '%s'? -level: error -ignorecase: true -# swap maps tokens in form of bad: good -# for more information: https://vale.sh/docs/topics/styles/#substitution -swap: - bellow: below - eg: e.g. - eg.: e.g. - 'e.g ': 'e.g. ' -# Don't forget to run the following command to generate the package writing-rules.zip file -# Vale uses that ZIP file and not the YAML files. -# -# pnpm docs:zipRules diff --git a/examples/base-ui-cra-ts/README.md b/examples/base-ui-cra-ts/README.md index 7938f7fca126d0..2029b19cd297d5 100644 --- a/examples/base-ui-cra-ts/README.md +++ b/examples/base-ui-cra-ts/README.md @@ -11,7 +11,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/base-ui-cra-ts +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/base-ui-cra-ts cd base-ui-cra-ts ``` @@ -26,6 +26,6 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/base-ui-cra-ts) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/base-ui-cra-ts) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/base-ui-cra-ts) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/base-ui-cra-ts) diff --git a/examples/base-ui-cra/README.md b/examples/base-ui-cra/README.md index b30905698d8d41..ba507114f7029d 100644 --- a/examples/base-ui-cra/README.md +++ b/examples/base-ui-cra/README.md @@ -11,7 +11,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/base-ui-cra +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/base-ui-cra cd base-ui-cra ``` @@ -26,6 +26,6 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/base-ui-cra) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/base-ui-cra) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/base-ui-cra) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/base-ui-cra) diff --git a/examples/base-ui-nextjs-tailwind-ts/README.md b/examples/base-ui-nextjs-tailwind-ts/README.md index 61fec67e60e1bf..4810c38b4c01ea 100644 --- a/examples/base-ui-nextjs-tailwind-ts/README.md +++ b/examples/base-ui-nextjs-tailwind-ts/README.md @@ -1,6 +1,6 @@ -# Base UI - Next.js App Router with Tailwind CSS example in TypeScript +# Base UI - Next.js App Router with Tailwind CSS example in TypeScript -This is a [Next.js](https://nextjs.org/) project bootstrapped using [`create-next-app`](https://github.com/vercel/next.js/tree/HEAD/packages/create-next-app) with Base UI installed and Tailwind CSS for styles. +This is a [Next.js](https://nextjs.org/) project bootstrapped using [`create-next-app`](https://github.com/vercel/next.js/tree/HEAD/packages/create-next-app) with Base UI installed and Tailwind CSS for styles. ## How to use @@ -9,7 +9,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/base-ui-nextjs-tailwind-ts +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/base-ui-nextjs-tailwind-ts cd base-ui-nextjs-tailwind-ts ``` @@ -24,9 +24,9 @@ or <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/base-ui-nextjs-tailwind-ts) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/base-ui-nextjs-tailwind-ts) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/base-ui-nextjs-tailwind-ts) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/base-ui-nextjs-tailwind-ts) ## Learn more diff --git a/examples/base-ui-vite-tailwind-ts/README.md b/examples/base-ui-vite-tailwind-ts/README.md index 7eace09f2ee306..fb1f58b18a3714 100644 --- a/examples/base-ui-vite-tailwind-ts/README.md +++ b/examples/base-ui-vite-tailwind-ts/README.md @@ -1,17 +1,19 @@ -# Base UI - Vite.js example with Tailwind CSS in TypeScript +# Base UI - Vite.js example with Tailwind CSS in TypeScript [Base UI](https://mui.com/base-ui/) is a library of unstyled React UI components and hooks. [Vite](https://vitejs.dev/) is a build tool that aims to provide a faster and leaner development experience for modern web projects, consisting of a dev server and a build command -[Tailwind CSS](https://tailwindcss.com/) is a utility-first CSS framework that provides low-level CSS classes that can be composed to build custom UI designs. +[Tailwind CSS](https://tailwindcss.com/) is a utility-first CSS framework that provides low-level CSS classes that can be composed to build custom UI designs. ## How to use Download the example [or clone the repo](https://github.com/mui/material-ui): +<!-- #default-branch-switch --> + ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/base-ui-vite-tailwind-ts +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/base-ui-vite-tailwind-ts cd base-ui-vite-tailwind-ts ``` @@ -26,6 +28,6 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/base-ui-vite-tailwind-ts) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/base-ui-vite-tailwind-ts) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/base-ui-vite-tailwind-ts) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/next/master/examples/base-ui-vite-tailwind-ts) diff --git a/examples/base-ui-vite-tailwind/README.md b/examples/base-ui-vite-tailwind/README.md index 98dc76dab63606..c9eae3c65e2acc 100644 --- a/examples/base-ui-vite-tailwind/README.md +++ b/examples/base-ui-vite-tailwind/README.md @@ -1,17 +1,19 @@ -# Base UI - Vite.js example with Tailwind CSS +# Base UI - Vite.js example with Tailwind CSS [Base UI](https://mui.com/base-ui/) is a library of unstyled React UI components and hooks. [Vite](https://vitejs.dev/) is a build tool that aims to provide a faster and leaner development experience for modern web projects, consisting of a dev server and a build command -[Tailwind CSS](https://tailwindcss.com/) is a utility-first CSS framework that provides low-level CSS classes that can be composed to build custom UI designs. +[Tailwind CSS](https://tailwindcss.com/) is a utility-first CSS framework that provides low-level CSS classes that can be composed to build custom UI designs. ## How to use Download the example [or clone the repo](https://github.com/mui/material-ui): +<!-- #default-branch-switch --> + ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/base-ui-vite-tailwind +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/base-ui-vite-tailwind cd base-ui-vite-tailwind ``` @@ -26,6 +28,6 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/base-ui-vite-tailwind) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/base-ui-vite-tailwind) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/base-ui-vite-tailwind) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/base-ui-vite-tailwind) diff --git a/examples/joy-ui-cra-ts/README.md b/examples/joy-ui-cra-ts/README.md index 01765382b76618..e0de9c863fc936 100644 --- a/examples/joy-ui-cra-ts/README.md +++ b/examples/joy-ui-cra-ts/README.md @@ -9,7 +9,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/joy-ui-cra-ts +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/joy-ui-cra-ts cd joy-ui-cra-ts ``` @@ -24,13 +24,13 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/joy-ui-cra-ts) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/joy-ui-cra-ts) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/joy-ui-cra-ts) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/joy-ui-cra-ts) ## What's next? <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/joy-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/joy-ui/getting-started/templates/) section. diff --git a/examples/joy-ui-nextjs-ts/README.md b/examples/joy-ui-nextjs-ts/README.md index 796dc1137db89c..5be78719e54c3a 100644 --- a/examples/joy-ui-nextjs-ts/README.md +++ b/examples/joy-ui-nextjs-ts/README.md @@ -9,7 +9,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/joy-ui-nextjs-ts +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/joy-ui-nextjs-ts cd joy-ui-nextjs-ts ``` @@ -26,9 +26,9 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/joy-ui-nextjs-ts) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/joy-ui-nextjs-ts) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/joy-ui-nextjs-ts) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/joy-ui-nextjs-ts) ## Learn more @@ -42,4 +42,4 @@ To learn more about this example: <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/joy-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/joy-ui/getting-started/templates/) section. diff --git a/examples/joy-ui-vite-ts/README.md b/examples/joy-ui-vite-ts/README.md index d4132910765647..573ee7874108e2 100644 --- a/examples/joy-ui-vite-ts/README.md +++ b/examples/joy-ui-vite-ts/README.md @@ -8,8 +8,10 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): +<!-- #default-branch-switch --> + ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/joy-ui-vite-ts +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/joy-ui-vite-ts cd joy-ui-vite-ts ``` @@ -24,9 +26,9 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/joy-ui-vite-ts) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/joy-ui-vite-ts) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/joy-ui-vite-ts) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/joy-ui-vite-ts) ## The idea behind the example @@ -38,4 +40,4 @@ It includes `@mui/joy` and it's peer dependencies. <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/joy-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/joy-ui/getting-started/templates/) section. diff --git a/examples/material-ui-cra-styled-components-ts/README.md b/examples/material-ui-cra-styled-components-ts/README.md index 4fa59826c2ab3b..e4fe2372157f31 100644 --- a/examples/material-ui-cra-styled-components-ts/README.md +++ b/examples/material-ui-cra-styled-components-ts/README.md @@ -23,7 +23,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-cra-styled-components-ts +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/material-ui-cra-styled-components-ts cd material-ui-cra-styled-components-ts ``` @@ -40,7 +40,7 @@ npm start Note that CodeSandbox is not supporting react-app-rewired, yet you can [still see the code](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-cra-styled-components-ts). -The following link leverages this demo: https://mui.com/material-ui/integrations/interoperability/#change-the-default-styled-engine with Parcel's alias feature within the `package.json`. +The following link leverages this demo: https://next.mui.com/material-ui/integrations/interoperability/#change-the-default-styled-engine with Parcel's alias feature within the `package.json`. [![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/styled-components-interoperability-w9z9d) @@ -57,4 +57,4 @@ Note, the version 5 of `@mui/styled-engine-sc` is compatible with version 5 of ` <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/material-ui/getting-started/templates/) section. diff --git a/examples/material-ui-cra-styled-components/README.md b/examples/material-ui-cra-styled-components/README.md index e4a94251ed7bc8..b8f29298bc033a 100644 --- a/examples/material-ui-cra-styled-components/README.md +++ b/examples/material-ui-cra-styled-components/README.md @@ -7,7 +7,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-cra-styled-components +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/material-ui-cra-styled-components cd material-ui-cra-styled-components ``` @@ -24,7 +24,7 @@ npm start Note that CodeSandbox is not supporting react-app-rewired, yet you can [still see the code](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-cra-styled-components). -The following link leverages this demo: https://mui.com/material-ui/integrations/interoperability/#change-the-default-styled-engine with Parcel's alias feature within the `package.json`. +The following link leverages this demo: https://next.mui.com/material-ui/integrations/interoperability/#change-the-default-styled-engine with Parcel's alias feature within the `package.json`. [![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/styled-components-interoperability-w9z9d) @@ -41,4 +41,4 @@ Note, the version 5 of `@mui/styled-engine-sc` is compatible with version 5 of ` <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/material-ui/getting-started/templates/) section. diff --git a/examples/material-ui-cra-tailwind-ts/README.md b/examples/material-ui-cra-tailwind-ts/README.md index 38909723194c41..86a1d1a8421c0d 100644 --- a/examples/material-ui-cra-tailwind-ts/README.md +++ b/examples/material-ui-cra-tailwind-ts/README.md @@ -1,4 +1,4 @@ -# Material UI - CRA example with Tailwind CSS in TypeScript +# Material UI - CRA example with Tailwind CSS in TypeScript ## How to use @@ -7,7 +7,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-cra-tailwind-ts +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/material-ui-cra-tailwind-ts cd material-ui-cra-tailwind-ts ``` @@ -22,13 +22,11 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-cra-tailwind-ts) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/material-ui-cra-tailwind-ts) ## The idea behind the example -<!-- #default-branch-switch --> - -This example demonstrates how you can use [Tailwind CSS](https://tailwindcss.com/) and [Create React App](https://github.com/facebookincubator/create-react-app) together with Material UI. +This example demonstrates how you can use [Tailwind CSS](https://tailwindcss.com/) and [Create React App](https://github.com/facebookincubator/create-react-app) together with Material UI. It includes `@mui/material` and its peer dependencies, including [Emotion](https://emotion.sh/docs/introduction), the default style engine in Material UI v5. ## What's next? @@ -36,4 +34,4 @@ It includes `@mui/material` and its peer dependencies, including [Emotion](https <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/material-ui/getting-started/templates/) section. diff --git a/examples/material-ui-cra-ts/README.md b/examples/material-ui-cra-ts/README.md index 61123d67d1aa92..fcceed26f12f77 100644 --- a/examples/material-ui-cra-ts/README.md +++ b/examples/material-ui-cra-ts/README.md @@ -7,7 +7,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-cra-ts +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/material-ui-cra-ts cd material-ui-cra-ts ``` @@ -22,19 +22,19 @@ or: <!-- #default-branch-switch --> -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-cra-ts) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/material-ui-cra-ts) -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-cra-ts) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/material-ui-cra-ts) ## The idea behind the example This example demonstrates how you can use Material UI with [Create React App](https://github.com/facebookincubator/create-react-app) in [TypeScript](https://github.com/Microsoft/TypeScript). It includes `@mui/material` and its peer dependencies, including [Emotion](https://emotion.sh/docs/introduction), the default style engine in Material UI v5. -If you prefer, you can [use styled-components instead](https://mui.com/material-ui/integrations/interoperability/#styled-components). +If you prefer, you can [use styled-components instead](https://next.mui.com/material-ui/integrations/interoperability/#styled-components). ## What's next? <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/material-ui/getting-started/templates/) section. diff --git a/examples/material-ui-cra/README.md b/examples/material-ui-cra/README.md index 8fb09165686f79..20998ef50776f6 100644 --- a/examples/material-ui-cra/README.md +++ b/examples/material-ui-cra/README.md @@ -7,7 +7,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-cra +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/material-ui-cra cd material-ui-cra ``` @@ -22,9 +22,9 @@ or: <!-- #default-branch-switch --> -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-cra) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/material-ui-cra) -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-cra) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/material-ui-cra) ## The idea behind the example @@ -32,11 +32,11 @@ or: This example demonstrates how you can use [Create React App](https://github.com/facebookincubator/create-react-app) with Material UI. It includes `@mui/material` and its peer dependencies, including [Emotion](https://emotion.sh/docs/introduction), the default style engine in Material UI v5. -If you prefer, you can [use styled-components instead](https://mui.com/material-ui/integrations/interoperability/#styled-components). +If you prefer, you can [use styled-components instead](https://next.mui.com/material-ui/integrations/interoperability/#styled-components). ## What's next? <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/material-ui/getting-started/templates/) section. diff --git a/examples/material-ui-express-ssr/README.md b/examples/material-ui-express-ssr/README.md index f82396af5d50b3..5618dc2949c70e 100644 --- a/examples/material-ui-express-ssr/README.md +++ b/examples/material-ui-express-ssr/README.md @@ -7,7 +7,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-express-ssr +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/material-ui-express-ssr cd material-ui-express-ssr ``` @@ -22,20 +22,20 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-express-ssr) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/material-ui-express-ssr) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-express-ssr) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/material-ui-express-ssr) ## The idea behind the example -This is the reference implementation of the [Server Rendering tutorial](https://mui.com/material-ui/guides/server-rendering/). +This is the reference implementation of the [Server Rendering tutorial](https://next.mui.com/material-ui/guides/server-rendering/). The example project includes `@mui/material` and its peer dependencies, including [Emotion](https://emotion.sh/docs/introduction), the default style engine in Material UI v5. -If you prefer, you can [use styled-components instead](https://mui.com/material-ui/integrations/interoperability/#styled-components). +If you prefer, you can [use styled-components instead](https://next.mui.com/material-ui/integrations/interoperability/#styled-components). ## What's next? <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/material-ui/getting-started/templates/) section. diff --git a/examples/material-ui-gatsby/README.md b/examples/material-ui-gatsby/README.md index f08f8e84e91f87..35bd45b72b4750 100644 --- a/examples/material-ui-gatsby/README.md +++ b/examples/material-ui-gatsby/README.md @@ -7,7 +7,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-gatsby +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/material-ui-gatsby cd material-ui-gatsby ``` @@ -22,11 +22,11 @@ npm run develop The project uses [Gatsby](https://github.com/gatsbyjs/gatsby), which is a static site generator for React. It includes `@mui/material` and its peer dependencies, including [Emotion](https://emotion.sh/docs/introduction), the default style engine in Material UI v5. -If you prefer, you can [use styled-components instead](https://mui.com/material-ui/integrations/interoperability/#styled-components). +If you prefer, you can [use styled-components instead](https://next.mui.com/material-ui/integrations/interoperability/#styled-components). ## What's next? <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/material-ui/getting-started/templates/) section. diff --git a/examples/material-ui-nextjs-pages-router-ts/README.md b/examples/material-ui-nextjs-pages-router-ts/README.md index d3adb3fd4065f6..905124355f1d42 100644 --- a/examples/material-ui-nextjs-pages-router-ts/README.md +++ b/examples/material-ui-nextjs-pages-router-ts/README.md @@ -7,7 +7,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-nextjs-pages-router-ts +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/material-ui-nextjs-pages-router-ts cd material-ui-nextjs-pages-router-ts ``` @@ -22,27 +22,27 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-nextjs-pages-router-ts) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/material-ui-nextjs-pages-router-ts) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-nextjs-pages-router-ts) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/material-ui-nextjs-pages-router-ts) ## The idea behind the example **Note:** This example is set up to use the Next.js Pages Router. As of Next.js 13.4, the newer App Router pattern is stable. -We recommend starting new projects with the [Material UI with Next.js (App Router) example](https://github.com/mui/material-ui/tree/master/examples/material-ui-nextjs-ts) unless you need (or prefer) the Pages Router. +We recommend starting new projects with the [Material UI with Next.js (App Router) example](https://github.com/mui/material-ui/tree/next/examples/material-ui-nextjs-ts) unless you need (or prefer) the Pages Router. The project uses [Next.js](https://github.com/vercel/next.js), which is a framework for server-rendered React apps. -It includes `@mui/material` and its peer dependencies, including [Emotion](https://emotion.sh/docs/introduction), the default style engine in Material UI v5. If you prefer, you can [use styled-components instead](https://mui.com/material-ui/integrations/interoperability/#styled-components). +It includes `@mui/material` and its peer dependencies, including [Emotion](https://emotion.sh/docs/introduction), the default style engine in Material UI v5. If you prefer, you can [use styled-components instead](https://next.mui.com/material-ui/integrations/interoperability/#styled-components). ## The link component The [example folder](https://github.com/mui/material-ui/tree/HEAD/examples/material-ui-nextjs-pages-router-ts) provides an adapter for the use of [Next.js's Link component](https://nextjs.org/docs/pages/api-reference/components/link) with Material UI. -More information [in the documentation](https://mui.com/material-ui/integrations/routing/#next-js-pages-router). +More information [in the documentation](https://next.mui.com/material-ui/integrations/routing/#next-js-pages-router). ## What's next? <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/material-ui/getting-started/templates/) section. diff --git a/examples/material-ui-nextjs-pages-router/README.md b/examples/material-ui-nextjs-pages-router/README.md index 5aaa617e16a4fa..116dc4659fd8a6 100644 --- a/examples/material-ui-nextjs-pages-router/README.md +++ b/examples/material-ui-nextjs-pages-router/README.md @@ -7,7 +7,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-nextjs-pages-router +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/material-ui-nextjs-pages-router cd material-ui-nextjs-pages-router ``` @@ -22,28 +22,28 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-nextjs-pages-router) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/material-ui-nextjs-pages-router) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-nextjs-pages-router) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/material-ui-nextjs-pages-router) ## The idea behind the example **Note:** This example is set up to use the Next.js Pages Router. As of Next.js 13.4, the newer App Router pattern is stable. -We recommend starting new projects with the [Material UI with Next.js (App Router) example](https://github.com/mui/material-ui/tree/master/examples/material-ui-nextjs) unless you need (or prefer) the Pages Router. +We recommend starting new projects with the [Material UI with Next.js (App Router) example](https://github.com/mui/material-ui/tree/next/examples/material-ui-nextjs) unless you need (or prefer) the Pages Router. The project uses [Next.js](https://github.com/vercel/next.js), which is a framework for server-rendered React apps. It includes `@mui/material` and its peer dependencies, including [Emotion](https://emotion.sh/docs/introduction), the default style engine in Material UI v5. -If you prefer, you can [use styled-components instead](https://mui.com/material-ui/integrations/interoperability/#styled-components). +If you prefer, you can [use styled-components instead](https://next.mui.com/material-ui/integrations/interoperability/#styled-components). ## The Link component The [example folder](https://github.com/mui/material-ui/tree/HEAD/examples/material-ui-nextjs-pages-router) provides an adapter for the use of [Next.js's Link component](https://nextjs.org/docs/pages/api-reference/components/link) with Material UI. -More information [in the documentation](https://mui.com/material-ui/integrations/routing/#next-js-pages-router). +More information [in the documentation](https://next.mui.com/material-ui/integrations/routing/#next-js-pages-router). ## What's next? <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/material-ui/getting-started/templates/) section. diff --git a/examples/material-ui-nextjs-ts-v4-v5-migration/README.md b/examples/material-ui-nextjs-ts-v4-v5-migration/README.md index 45c5bd369f2ed0..7226e5db8251e2 100644 --- a/examples/material-ui-nextjs-ts-v4-v5-migration/README.md +++ b/examples/material-ui-nextjs-ts-v4-v5-migration/README.md @@ -7,7 +7,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-nextjs-ts-v4-v5-migration +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/material-ui-nextjs-ts-v4-v5-migration cd material-ui-nextjs-ts-v4-v5-migration ``` @@ -22,21 +22,21 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-nextjs-ts-v4-v5-migration) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/material-ui-nextjs-ts-v4-v5-migration) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-nextjs-ts-v4-v5-migration) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/material-ui-nextjs-ts-v4-v5-migration) ## The idea behind the example The project uses [Next.js](https://github.com/vercel/next.js), which is a framework for server-rendered React apps. -It includes `@mui/material` and its peer dependencies, including [Emotion](https://emotion.sh/docs/introduction), the default style engine in Material UI v5. If you prefer, you can [use styled-components instead](https://mui.com/material-ui/integrations/interoperability/#styled-components). +It includes `@mui/material` and its peer dependencies, including [Emotion](https://emotion.sh/docs/introduction), the default style engine in Material UI v5. If you prefer, you can [use styled-components instead](https://next.mui.com/material-ui/integrations/interoperability/#styled-components). It also includes `@mui/styles`, the legacy styling solution that uses JSS as an engine. It provides all the necessary config for working with both Emotion and JSS for server-side rendering. The project is intended as a basic starter for migrating your application from v4 to v5, as it lets the JSS style overrides take precedence over the default styles passed to the components by Emotion. -It demonstrates what results after handling v5's breaking changes to the [theme](https://mui.com/material-ui/migration/v5-style-changes/) and [components](https://mui.com/material-ui/migration/v5-component-changes/). +It demonstrates what results after handling v5's breaking changes to the [theme](https://next.mui.com/material-ui/migration/v5-style-changes/) and [components](https://next.mui.com/material-ui/migration/v5-component-changes/). ## The Link component Next.js Pages Router has [a custom Link component](https://nextjs.org/docs/pages/api-reference/components/link). The example folder provides adapters for usage with Material UI. -You can find more information [in the documentation](https://mui.com/material-ui/integrations/routing/#next-js-pages-router). +You can find more information [in the documentation](https://next.mui.com/material-ui/integrations/routing/#next-js-pages-router). diff --git a/examples/material-ui-nextjs-ts-v4-v5-migration/pages/_document.tsx b/examples/material-ui-nextjs-ts-v4-v5-migration/pages/_document.tsx index 3fa686412005d7..696c68d67145e1 100644 --- a/examples/material-ui-nextjs-ts-v4-v5-migration/pages/_document.tsx +++ b/examples/material-ui-nextjs-ts-v4-v5-migration/pages/_document.tsx @@ -67,7 +67,7 @@ MyDocument.getInitialProps = async (ctx: DocumentContext) => { resolveProps: async (initialProps: DocumentInitialProps) => { // Generate the css string for the styles coming from jss let css = jssSheets.toString(); - // It might be undefined, e.g. after an error. + // It might be undefined, for example after an error. if (css && process.env.NODE_ENV === 'production') { const result1 = await prefixer.process(css, { from: undefined }); css = result1.css; diff --git a/examples/material-ui-nextjs-ts/README.md b/examples/material-ui-nextjs-ts/README.md index df5d86c065a2a4..976964f5bf95b0 100644 --- a/examples/material-ui-nextjs-ts/README.md +++ b/examples/material-ui-nextjs-ts/README.md @@ -9,7 +9,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-nextjs-ts +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/material-ui-nextjs-ts cd material-ui-nextjs-ts ``` @@ -26,9 +26,9 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-nextjs-ts) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/material-ui-nextjs-ts) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-nextjs-ts) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/material-ui-nextjs-ts) ## Learn more @@ -42,4 +42,4 @@ To learn more about this example: <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/material-ui/getting-started/templates/) section. diff --git a/examples/material-ui-nextjs/README.md b/examples/material-ui-nextjs/README.md index 98ea0fabe4fc94..13eacf4492aac3 100644 --- a/examples/material-ui-nextjs/README.md +++ b/examples/material-ui-nextjs/README.md @@ -9,7 +9,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-nextjs +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/material-ui-nextjs cd material-ui-nextjs ``` @@ -26,20 +26,20 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-nextjs) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/material-ui-nextjs) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-nextjs) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/material-ui-nextjs) ## Learn more To learn more about this example: - [Next.js documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Customizing Material UI](https://mui.com/material-ui/customization/how-to-customize/) - approaches to customizing Material UI. +- [Customizing Material UI](https://next.mui.com/material-ui/customization/how-to-customize/) - approaches to customizing Material UI. ## What's next? <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/material-ui/getting-started/templates/) section. diff --git a/examples/material-ui-preact/README.md b/examples/material-ui-preact/README.md index 9bc17eb026f674..a712c3bf6be9db 100644 --- a/examples/material-ui-preact/README.md +++ b/examples/material-ui-preact/README.md @@ -7,7 +7,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-preact +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/material-ui-preact cd material-ui-preact ``` @@ -28,11 +28,11 @@ It includes `@mui/material` and its peer dependencies, including [Emotion](https <!-- #default-branch-switch --> -If you prefer, you can [use styled-components instead](https://mui.com/material-ui/integrations/interoperability/#styled-components). +If you prefer, you can [use styled-components instead](https://next.mui.com/material-ui/integrations/interoperability/#styled-components). ## What's next? <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/material-ui/getting-started/templates/) section. diff --git a/examples/material-ui-remix-ts/README.md b/examples/material-ui-remix-ts/README.md index 2f2fd82886d918..147b6bce1f7066 100644 --- a/examples/material-ui-remix-ts/README.md +++ b/examples/material-ui-remix-ts/README.md @@ -7,7 +7,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-remix-ts +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/material-ui-remix-ts cd material-ui-remix-ts ``` @@ -22,19 +22,19 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-remix-ts) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/material-ui-remix-ts) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-remix-ts) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/material-ui-remix-ts) ## The idea behind the example The project uses [Remix](https://remix.run/), which is a full stack web framework. It includes `@mui/material` and its peer dependencies, including [Emotion](https://emotion.sh/docs/introduction), the default style engine in Material UI v5. -If you prefer, you can [use styled-components instead](https://mui.com/material-ui/integrations/interoperability/#styled-components). +If you prefer, you can [use styled-components instead](https://next.mui.com/material-ui/integrations/interoperability/#styled-components). ## What's next? <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/material-ui/getting-started/templates/) section. diff --git a/examples/material-ui-via-cdn/README.md b/examples/material-ui-via-cdn/README.md index 5034856a72f0c7..fea6b75a3cce21 100644 --- a/examples/material-ui-via-cdn/README.md +++ b/examples/material-ui-via-cdn/README.md @@ -4,8 +4,10 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): +<!-- #default-branch-switch --> + ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-via-cdn +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/material-ui-via-cdn cd material-ui-via-cdn ``` @@ -23,7 +25,7 @@ The client has to download the entire library, regardless of which components ar <!-- #default-branch-switch --> -[The live preview.](https://rawcdn.githack.com/mui/material-ui/master/examples/material-ui-via-cdn/index.html) +[The live preview.](https://rawcdn.githack.com/mui/material-ui/next/examples/material-ui-via-cdn/index.html) ## UMD releases @@ -37,6 +39,6 @@ We are providing two Universal Module Definition (UMD) files: ## What's next? You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/material-ui/getting-started/templates/) section. <!-- #default-branch-switch --> diff --git a/examples/material-ui-vite-ts/README.md b/examples/material-ui-vite-ts/README.md index a0cf054adef4c5..65838939855f34 100644 --- a/examples/material-ui-vite-ts/README.md +++ b/examples/material-ui-vite-ts/README.md @@ -7,7 +7,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-vite-ts +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/material-ui-vite-ts cd material-ui-vite-ts ``` @@ -22,9 +22,9 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-vite-ts) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/material-ui-vite-ts) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-vite-ts) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/material-ui-vite-ts) ## The idea behind the example @@ -36,4 +36,4 @@ It includes `@mui/material` and its peer dependencies, including [Emotion](https <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/material-ui/getting-started/templates/) section. diff --git a/examples/material-ui-vite/README.md b/examples/material-ui-vite/README.md index 4ef312c42b8b84..5dea6e71b7c2b8 100644 --- a/examples/material-ui-vite/README.md +++ b/examples/material-ui-vite/README.md @@ -7,7 +7,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-vite +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/material-ui-vite cd material-ui-vite ``` @@ -22,9 +22,9 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-vite) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/material-ui-vite) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-vite) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/material-ui-vite) ## The idea behind the example @@ -36,4 +36,4 @@ It includes `@mui/material` and its peer dependencies, including [Emotion](https <!-- #default-branch-switch --> You now have a working example project. -You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section. +You can head back to the documentation and continue by browsing the [templates](https://next.mui.com/material-ui/getting-started/templates/) section. diff --git a/examples/pigment-css-nextjs-ts/README.md b/examples/pigment-css-nextjs-ts/README.md index 63159deba18aae..2f8b866e4d9fe8 100644 --- a/examples/pigment-css-nextjs-ts/README.md +++ b/examples/pigment-css-nextjs-ts/README.md @@ -9,7 +9,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/pigment-css-nextjs-ts +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/pigment-css-nextjs-ts cd pigment-css-nextjs-ts ``` @@ -26,13 +26,13 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/pigment-css-nextjs-ts) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/pigment-css-nextjs-ts) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/pigment-css-nextjs-ts) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/pigment-css-nextjs-ts) ## Learn more To learn more about this example: -- [Pigment CSS documentation](https://github.com/mui/material-ui/blob/master/packages/pigment-react/README.md) - learn more about Pigment CSS features and APIs. +- [Pigment CSS documentation](https://github.com/mui/material-ui/blob/next/packages/pigment-css-react/README.md) - learn more about Pigment CSS features and APIs. - [Next.js documentation](https://nextjs.org/docs) - learn about Next.js features and APIs. diff --git a/examples/pigment-css-nextjs-ts/src/app/page.tsx b/examples/pigment-css-nextjs-ts/src/app/page.tsx index c2290095110036..d900930fddd997 100644 --- a/examples/pigment-css-nextjs-ts/src/app/page.tsx +++ b/examples/pigment-css-nextjs-ts/src/app/page.tsx @@ -175,7 +175,7 @@ export default function Home() { })} > <Link - href="https://github.com/mui/material-ui/blob/master/packages/pigment-react/README.md" + href="https://github.com/mui/material-ui/blob/master/packages/pigment-css-react/README.md" target="_blank" rel="noopener noreferrer" > diff --git a/examples/pigment-css-vite-ts/README.md b/examples/pigment-css-vite-ts/README.md index f1154bd1e61d36..dc7a1f1f540190 100644 --- a/examples/pigment-css-vite-ts/README.md +++ b/examples/pigment-css-vite-ts/README.md @@ -7,7 +7,7 @@ Download the example [or clone the repo](https://github.com/mui/material-ui): <!-- #default-branch-switch --> ```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/pigment-css-vite-ts +curl https://codeload.github.com/mui/material-ui/tar.gz/next | tar -xz --strip=2 material-ui-next/examples/pigment-css-vite-ts cd pigment-css-vite-ts ``` @@ -22,13 +22,13 @@ or: <!-- #default-branch-switch --> -[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/pigment-css-vite-ts) +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/next/examples/pigment-css-vite-ts) -[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/pigment-css-vite-ts) +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/next/examples/pigment-css-vite-ts) ## Learn more To learn more about this example: -- [Pigment CSS documentation](https://github.com/mui/material-ui/blob/master/packages/pigment-react/README.md) - learn more about Pigment CSS features and APIs. +- [Pigment CSS documentation](https://github.com/mui/material-ui/blob/next/packages/pigment-css-react/README.md) - learn more about Pigment CSS features and APIs. - [Vite documentation](https://vitejs.dev/guide/) - learn about Vite features and APIs. diff --git a/examples/pigment-css-vite-ts/src/App.tsx b/examples/pigment-css-vite-ts/src/App.tsx index 10351d263a0286..011af95d7d5be0 100644 --- a/examples/pigment-css-vite-ts/src/App.tsx +++ b/examples/pigment-css-vite-ts/src/App.tsx @@ -177,7 +177,7 @@ export default function Home() { })} > <Link - href="https://github.com/mui/material-ui/blob/master/packages/pigment-react/README.md" + href="https://github.com/mui/material-ui/blob/master/packages/pigment-css-react/README.md" target="_blank" rel="noopener noreferrer" > diff --git a/netlify/functions/feedback-management.js b/netlify/functions/feedback-management.mts similarity index 82% rename from netlify/functions/feedback-management.js rename to netlify/functions/feedback-management.mts index 9bc88669932cec..051317beeafcd4 100644 --- a/netlify/functions/feedback-management.js +++ b/netlify/functions/feedback-management.mts @@ -1,7 +1,8 @@ -const querystring = require('node:querystring'); -const { App, AwsLambdaReceiver } = require('@slack/bolt'); -const { JWT } = require('google-auth-library'); -const { sheets } = require('@googleapis/sheets'); +import querystring from 'node:querystring'; +import { App, AwsLambdaReceiver, BlockAction, ButtonAction } from '@slack/bolt'; +import { JWT } from 'google-auth-library'; +import { sheets } from '@googleapis/sheets'; +import { Handler } from '@netlify/functions'; const X_FEEBACKS_CHANNEL_ID = 'C04U3R2V9UK'; const JOY_FEEBACKS_CHANNEL_ID = 'C050VE13HDL'; @@ -35,7 +36,7 @@ const spreadSheetsIds = { // Setup of the slack bot (taken from https://slack.dev/bolt-js/deployments/aws-lambda) const awsLambdaReceiver = new AwsLambdaReceiver({ - signingSecret: process.env.SLACK_SIGNING_SECRET, + signingSecret: process.env.SLACK_SIGNING_SECRET!, }); const app = new App({ @@ -45,27 +46,30 @@ const app = new App({ }); // Define slack actions to answer -app.action('delete_action', async ({ ack, body, client, logger }) => { +app.action<BlockAction<ButtonAction>>('delete_action', async ({ ack, body, client, logger }) => { try { await ack(); const { user: { username }, - channel: { id: channelId }, + channel, message, actions: [{ value }], } = body; + const channelId = channel?.id; + const { comment, currentLocationURL = '', commmentSectionURL = '' } = JSON.parse(value); const googleAuth = new JWT({ email: 'service-account-804@docs-feedbacks.iam.gserviceaccount.com', - key: process.env.G_SHEET_TOKEN.replace(/\\n/g, '\n'), + key: process.env.G_SHEET_TOKEN!.replace(/\\n/g, '\n'), scopes: ['https://www.googleapis.com/auth/spreadsheets'], }); const service = sheets({ version: 'v4', auth: googleAuth }); - await service.spreadsheets.values.append({ + // @ts-ignore + service.spreadsheets.values.append({ spreadsheetId: spreadSheetsIds.forLater, range: 'Deleted messages!A:D', valueInputOption: 'USER_ENTERED', @@ -73,9 +77,13 @@ app.action('delete_action', async ({ ack, body, client, logger }) => { values: [[username, comment, currentLocationURL, commmentSectionURL]], }, }); + + if (!channelId) { + throw Error('feedback-management: Unknonw channel Id'); + } await client.chat.delete({ channel: channelId, - ts: message.ts, + ts: message!.ts, as_user: true, token: process.env.SLACK_BOT_TOKEN, }); @@ -89,31 +97,37 @@ app.action('save_message', async ({ ack, body, client, logger }) => { await ack(); const { user: { username }, - channel: { id: channelId }, + channel, message, actions: [{ value }], - } = body; + } = body as BlockAction<ButtonAction>; + const channelId = channel?.id; const { comment, currentLocationURL = '', commmentSectionURL = '' } = JSON.parse(value); const googleAuth = new JWT({ email: 'service-account-804@docs-feedbacks.iam.gserviceaccount.com', - key: process.env.G_SHEET_TOKEN.replace(/\\n/g, '\n'), + key: process.env.G_SHEET_TOKEN!.replace(/\\n/g, '\n'), scopes: ['https://www.googleapis.com/auth/spreadsheets'], }); const service = sheets({ version: 'v4', auth: googleAuth }); - await service.spreadsheets.values.append({ + // @ts-ignore + service.spreadsheets.values.append({ spreadsheetId: spreadSheetsIds.forLater, range: 'Sheet1!A:D', valueInputOption: 'USER_ENTERED', - resource: { + updates: { values: [[username, comment, currentLocationURL, commmentSectionURL]], }, }); - await client.chat.postMessage({ + + if (!channelId) { + throw Error('feedback-management: Unknonw channel Id'); + } + client.chat.postMessage({ channel: channelId, - thread_ts: message.ts, + thread_ts: message!.ts, as_user: true, text: `Saved in <https://docs.google.com/spreadsheets/d/${spreadSheetsIds.forLater}/>`, }); @@ -122,11 +136,8 @@ app.action('save_message', async ({ ack, body, client, logger }) => { } }); -/** - * @param {object} event - * @param {object} context - */ -exports.handler = async (event, context, callback) => { +// eslint-disable-next-line import/prefer-default-export +export const handler: Handler = async (event, context, callback) => { if (event.httpMethod !== 'POST') { return { statusCode: 404 }; } @@ -227,8 +238,9 @@ from ${commmentSectionURL} unfurl_media: false, }); } else { - const handler = await awsLambdaReceiver.start(); - return handler(event, context, callback); + const awsHandler = await awsLambdaReceiver.start(); + // @ts-ignore + return awsHandler(event, context, callback); } } catch (error) { // eslint-disable-next-line no-console diff --git a/package.json b/package.json index a8bf59a37a6a95..f615b64c09c23e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mui/monorepo", - "version": "5.15.12", + "version": "5.15.14", "private": true, "scripts": { "preinstall": "npx only-allow pnpm", @@ -16,8 +16,8 @@ "release:version": "lerna version --no-changelog --no-push --no-git-tag-version --no-private --force-publish=@mui/core-downloads-tracker", "release:build": "lerna run --concurrency 8 --no-private build --skip-nx-cache", "release:changelog": "node scripts/releaseChangelog.mjs", - "release:publish": "pnpm publish --recursive --tag latest", - "release:publish:dry-run": "pnpm publish --recursive --tag latest --registry=\"http://localhost:4873/\"", + "release:publish": "pnpm publish --recursive --tag next", + "release:publish:dry-run": "pnpm publish --recursive --tag next --registry=\"http://localhost:4873/\"", "release:tag": "node scripts/releaseTag.mjs", "docs:api": "rimraf --glob ./docs/pages/**/api-docs ./docs/pages/**/api && pnpm docs:api:build", "docs:api:build": "tsx ./scripts/buidApiDocs/index.ts", @@ -36,7 +36,7 @@ "docs:typescript:check": "pnpm --filter docs typescript", "docs:typescript:formatted": "tsx ./docs/scripts/formattedTSDemos", "docs:mdicons:synonyms": "cross-env BABEL_ENV=development babel-node --extensions \".tsx,.ts,.js,.mjs\" ./docs/scripts/updateIconSynonyms && pnpm prettier", - "docs:zipRules": "cd docs && rm writing-rules.zip && zip -r writing-rules.zip writing-rules", + "docs:zipRules": "cd docs && rm mui-vale.zip && zip -r mui-vale.zip mui-vale && cd ../ && vale sync", "extract-error-codes": "cross-env MUI_EXTRACT_ERROR_CODES=true lerna run --concurrency 8 build:modern", "rsc:build": "tsx ./packages/rsc-builder/buildRsc.ts", "template:screenshot": "cross-env BABEL_ENV=development babel-node --extensions \".tsx,.ts,.js\" ./docs/scripts/generateTemplateScreenshots", @@ -46,6 +46,7 @@ "eslint:ci": "eslint . --report-unused-disable-directives --ext .js,.ts,.tsx --max-warnings 0", "stylelint": "stylelint --reportInvalidScopeDisables --reportNeedlessDisables \"docs/**/*.{js,ts,tsx}\"", "markdownlint": "markdownlint-cli2 \"**/*.md\"", + "valelint": "git ls-files | grep -h \".md$\" | xargs vale --filter='.Level==\"error\"'", "prettier": "pretty-quick --ignore-path .eslintignore", "prettier:all": "prettier --write . --ignore-path .eslintignore", "size:snapshot": "node --max-old-space-size=4096 ./scripts/sizeSnapshot/create", @@ -54,9 +55,9 @@ "test": "node scripts/test.mjs", "tc": "node test/cli.js", "test:extended": "pnpm eslint && pnpm typescript && pnpm test:coverage", - "test:pigment-react:ci": "pnpm nx run @pigment-css/react:test:ci", - "test:coverage": "cross-env NODE_ENV=test BABEL_ENV=coverage nyc --reporter=text mocha 'packages/**/*.test.{js,ts,tsx}' 'docs/**/*.test.{js,ts,tsx}' --exclude 'packages/pigment-react/**/*.test.{js,ts,tsx}' && pnpm test:pigment-react", - "test:coverage:ci": "cross-env NODE_ENV=test BABEL_ENV=coverage nyc --reporter=lcov mocha 'packages/**/*.test.{js,ts,tsx}' 'docs/**/*.test.{js,ts,tsx}' --exclude 'packages/pigment-react/**/*.test.{js,ts,tsx}' && pnpm test:pigment-react:ci", + "test:pigment-css-react:ci": "pnpm nx run @pigment-css/react:test:ci", + "test:coverage": "cross-env NODE_ENV=test BABEL_ENV=coverage nyc --reporter=text mocha 'packages/**/*.test.{js,ts,tsx}' 'docs/**/*.test.{js,ts,tsx}' --exclude 'packages/pigment-css-react/**/*.test.{js,ts,tsx}' && pnpm test:pigment-css-react", + "test:coverage:ci": "cross-env NODE_ENV=test BABEL_ENV=coverage nyc --reporter=lcov mocha 'packages/**/*.test.{js,ts,tsx}' 'docs/**/*.test.{js,ts,tsx}' --exclude 'packages/pigment-css-react/**/*.test.{js,ts,tsx}' && pnpm test:pigment-css-react:ci", "test:coverage:html": "cross-env NODE_ENV=test BABEL_ENV=coverage nyc --reporter=html mocha 'packages/**/*.test.{js,ts,tsx}' 'docs/**/*.test.{js,ts,tsx}'", "test:e2e": "cross-env NODE_ENV=production pnpm test:e2e:build && concurrently --success first --kill-others \"pnpm test:e2e:run\" \"pnpm test:e2e:server\"", "test:e2e:build": "webpack --config test/e2e/webpack.config.js", @@ -84,11 +85,12 @@ "dependencies": { "@googleapis/sheets": "^5.0.5", "@slack/bolt": "^3.17.1", + "@netlify/functions": "^2.6.0", "execa": "^8.0.1", - "google-auth-library": "^9.6.3" + "google-auth-library": "^9.7.0" }, "devDependencies": { - "@argos-ci/core": "^1.5.4", + "@argos-ci/core": "^1.5.5", "@babel/cli": "^7.23.9", "@babel/core": "^7.23.9", "@babel/node": "^7.23.9", @@ -104,23 +106,23 @@ "@babel/preset-typescript": "^7.23.3", "@babel/register": "^7.23.7", "@mnajdova/enzyme-adapter-react-18": "^0.2.0", + "@mui/internal-docs-utils": "workspace:^", "@mui/internal-scripts": "workspace:^", "@mui-internal/api-docs-builder": "workspace:^", "@mui-internal/api-docs-builder-core": "workspace:^", - "@mui-internal/docs-utils": "workspace:^", "@mui-internal/test-utils": "workspace:^", "@mui/joy": "workspace:*", "@mui/material": "workspace:^", "@mui/utils": "workspace:^", "@pigment-css/react": "workspace:^", - "@next/eslint-plugin-next": "^14.1.1", + "@next/eslint-plugin-next": "^14.1.3", "@octokit/rest": "^20.0.2", "@playwright/test": "1.42.1", "@types/enzyme": "^3.10.18", "@types/fs-extra": "^11.0.4", - "@types/lodash": "^4.14.202", + "@types/lodash": "^4.17.0", "@types/mocha": "^10.0.6", - "@types/node": "^18.19.21", + "@types/node": "^18.19.25", "@types/prettier": "^2.7.3", "@types/react": "^18.2.55", "@types/yargs": "^17.0.32", @@ -134,7 +136,7 @@ "babel-plugin-react-remove-properties": "^0.3.0", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "chalk": "^5.3.0", - "compression-webpack-plugin": "^11.0.0", + "compression-webpack-plugin": "^11.1.0", "concurrently": "^8.2.2", "cpy-cli": "^5.0.0", "cross-env": "^7.0.3", @@ -151,8 +153,8 @@ "eslint-plugin-import": "^2.29.1", "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-material-ui": "workspace:^", - "eslint-plugin-mocha": "^10.3.0", - "eslint-plugin-react": "^7.33.2", + "eslint-plugin-mocha": "^10.4.1", + "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.6.0", "fast-glob": "^3.3.2", "fs-extra": "^11.2.0", @@ -161,7 +163,7 @@ "karma-browserstack-launcher": "~1.6.0", "karma-chrome-launcher": "^3.2.0", "karma-coverage-istanbul-reporter": "^3.0.3", - "karma-firefox-launcher": "^2.1.2", + "karma-firefox-launcher": "^2.1.3", "karma-mocha": "^2.0.1", "karma-sourcemap-loader": "^0.4.0", "karma-webpack": "^5.0.0", @@ -169,7 +171,7 @@ "lodash": "^4.17.21", "markdownlint-cli2": "^0.12.1", "mocha": "^10.3.0", - "nx": "^17.2.8", + "nx": "^17.3.2", "nyc": "^15.1.0", "piscina": "^4.4.0", "postcss-styled-syntax": "^0.6.4", @@ -207,10 +209,10 @@ "@babel/preset-typescript": "^7.23.3", "@babel/runtime": "^7.23.9", "@babel/types": "^7.23.9", - "@definitelytyped/header-parser": "^0.2.6", - "@definitelytyped/typescript-versions": "^0.1.0", + "@definitelytyped/header-parser": "^0.2.8", + "@definitelytyped/typescript-versions": "^0.1.1", "@definitelytyped/utils": "^0.1.5", - "@types/node": "^18.19.21", + "@types/node": "^18.19.25", "@types/react": "^18.2.55", "@types/react-dom": "18.2.19", "cross-fetch": "^4.0.0" diff --git a/packages/docs-utils/.npmignore b/packages-internal/docs-utils/.npmignore similarity index 100% rename from packages/docs-utils/.npmignore rename to packages-internal/docs-utils/.npmignore diff --git a/packages/docs-utils/CHANGELOG.md b/packages-internal/docs-utils/CHANGELOG.md similarity index 64% rename from packages/docs-utils/CHANGELOG.md rename to packages-internal/docs-utils/CHANGELOG.md index 3255c1a72040cf..f466e1ffab289d 100644 --- a/packages/docs-utils/CHANGELOG.md +++ b/packages-internal/docs-utils/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 1.0.3 + +Renamed the package to @mui/internal-docs-utils + ## 1.0.2 Fixed incorrectly released package. diff --git a/packages/docs-utils/README.md b/packages-internal/docs-utils/README.md similarity index 89% rename from packages/docs-utils/README.md rename to packages-internal/docs-utils/README.md index 8a19bc3cbc5e1a..92be36e77819af 100644 --- a/packages/docs-utils/README.md +++ b/packages-internal/docs-utils/README.md @@ -1,4 +1,4 @@ -# @mui-internal/docs-utils +# @mui/internal-docs-utils This package contains utilities shared between MUI docs generation scripts. This is an internal package not meant for general use. diff --git a/packages/docs-utils/package.json b/packages-internal/docs-utils/package.json similarity index 87% rename from packages/docs-utils/package.json rename to packages-internal/docs-utils/package.json index a199a615938c69..7ddabfa3a217fa 100644 --- a/packages/docs-utils/package.json +++ b/packages-internal/docs-utils/package.json @@ -1,6 +1,6 @@ { - "name": "@mui-internal/docs-utils", - "version": "1.0.2", + "name": "@mui/internal-docs-utils", + "version": "1.0.4", "author": "MUI Team", "description": "Utilities for MUI docs. This is an internal package not meant for general use.", "main": "./build/index.js", @@ -11,7 +11,7 @@ "repository": { "type": "git", "url": "https://github.com/mui/material-ui.git", - "directory": "packages/docs-utils" + "directory": "packages-internal/docs-utils" }, "scripts": { "prebuild": "rimraf ./build", diff --git a/packages/docs-utils/src/ComponentClassDefinition.ts b/packages-internal/docs-utils/src/ComponentClassDefinition.ts similarity index 100% rename from packages/docs-utils/src/ComponentClassDefinition.ts rename to packages-internal/docs-utils/src/ComponentClassDefinition.ts diff --git a/packages/docs-utils/src/createTypeScriptProject.ts b/packages-internal/docs-utils/src/createTypeScriptProject.ts similarity index 100% rename from packages/docs-utils/src/createTypeScriptProject.ts rename to packages-internal/docs-utils/src/createTypeScriptProject.ts diff --git a/packages/docs-utils/src/getPropsFromComponentNode.ts b/packages-internal/docs-utils/src/getPropsFromComponentNode.ts similarity index 100% rename from packages/docs-utils/src/getPropsFromComponentNode.ts rename to packages-internal/docs-utils/src/getPropsFromComponentNode.ts diff --git a/packages/docs-utils/src/index.ts b/packages-internal/docs-utils/src/index.ts similarity index 100% rename from packages/docs-utils/src/index.ts rename to packages-internal/docs-utils/src/index.ts diff --git a/packages/docs-utils/tsconfig.build.json b/packages-internal/docs-utils/tsconfig.build.json similarity index 100% rename from packages/docs-utils/tsconfig.build.json rename to packages-internal/docs-utils/tsconfig.build.json diff --git a/packages/docs-utils/tsconfig.json b/packages-internal/docs-utils/tsconfig.json similarity index 100% rename from packages/docs-utils/tsconfig.json rename to packages-internal/docs-utils/tsconfig.json diff --git a/packages-internal/scripts/CHANGELOG.md b/packages-internal/scripts/CHANGELOG.md index dd86612b025856..92cc3433251004 100644 --- a/packages-internal/scripts/CHANGELOG.md +++ b/packages-internal/scripts/CHANGELOG.md @@ -2,7 +2,7 @@ ## 1.0.1 -- Unpinned version of the @mui-internal/docs-utils dependency. +- Unpinned version of the @mui/internal-docs-utils dependency. - Corrected the README file. ## 1.0.0 diff --git a/packages-internal/scripts/README.md b/packages-internal/scripts/README.md index 7d0ed6c75648a4..da7898bb3120d5 100644 --- a/packages-internal/scripts/README.md +++ b/packages-internal/scripts/README.md @@ -1,11 +1,11 @@ # @mui/internal-scripts -Code infra scripts for MUI repositories +This is that code infra scripts for the MUI organization repositories. It is not meant for general use. ## Scripts -- `build` - transpiles TS files into the build directory. +- `build` - transpiles TypeScript files into the build directory. - `release:publish` - builds the project and publishes it in the npm registry. - `release:publish:dry-run` - builds the project and publishes it in a local registry accessible on port 4873 (this is the default port of Verdaccio private npm server). - `test` - runs all the tests. diff --git a/packages-internal/scripts/package.json b/packages-internal/scripts/package.json index dfdea9913a2cfe..391eca25d51455 100644 --- a/packages-internal/scripts/package.json +++ b/packages-internal/scripts/package.json @@ -1,6 +1,6 @@ { "name": "@mui/internal-scripts", - "version": "1.0.1", + "version": "1.0.3", "author": "MUI Team", "description": "Utilities supporting MUI libraries build and docs generation. This is an internal package not meant for general use.", "main": "build/index.js", @@ -30,7 +30,7 @@ "@babel/plugin-syntax-jsx": "^7.23.3", "@babel/plugin-syntax-typescript": "^7.23.3", "@babel/types": "^7.23.9", - "@mui-internal/docs-utils": "workspace:^", + "@mui/internal-docs-utils": "workspace:^", "doctrine": "^3.0.0", "lodash": "^4.17.21", "typescript": "^5.3.3", @@ -41,8 +41,8 @@ "@types/babel__core": "^7.20.5", "@types/chai": "^4.3.12", "@types/doctrine": "^0.0.9", - "@types/lodash": "^4.14.202", - "@types/node": "^18.19.21", + "@types/lodash": "^4.17.0", + "@types/node": "^18.19.25", "@types/prettier": "^2.7.3", "@types/react": "^18.2.55", "@types/uuid": "^9.0.8", diff --git a/packages-internal/scripts/tsconfig.typecheck.json b/packages-internal/scripts/tsconfig.typecheck.json index ae133d71c10945..0d271133c6cee3 100644 --- a/packages-internal/scripts/tsconfig.typecheck.json +++ b/packages-internal/scripts/tsconfig.typecheck.json @@ -7,5 +7,5 @@ }, "include": ["./**/*.ts"], "exclude": ["./build", "./node_modules"], - "references": [{ "path": "../../packages/docs-utils/tsconfig.build.json" }] + "references": [{ "path": "../../packages-internal/docs-utils/tsconfig.build.json" }] } diff --git a/packages-internal/scripts/typescript-to-proptypes/CHANGELOG.old.md b/packages-internal/scripts/typescript-to-proptypes/CHANGELOG.old.md index 38d10c92ae4789..d755fbf8d08167 100644 --- a/packages-internal/scripts/typescript-to-proptypes/CHANGELOG.old.md +++ b/packages-internal/scripts/typescript-to-proptypes/CHANGELOG.old.md @@ -123,7 +123,7 @@ All notable changes to this project will be documented in this file. See [standa - **parser:** handle optional any ([30f56ec](https://github.com/merceyz/typescript-to-proptypes/commit/30f56ec)) - **parser:** handle optional React.ElementType ([c7a87fd](https://github.com/merceyz/typescript-to-proptypes/commit/c7a87fd)) - **parser:** treat ComponentType as elementType ([53f1e21](https://github.com/merceyz/typescript-to-proptypes/commit/53f1e21)) -- export typescript as ts ([ba90e22](https://github.com/merceyz/typescript-to-proptypes/commit/ba90e22)) +- export TypeScript as ts ([ba90e22](https://github.com/merceyz/typescript-to-proptypes/commit/ba90e22)) ### Features diff --git a/packages-internal/scripts/typescript-to-proptypes/src/getPropTypesFromFile.ts b/packages-internal/scripts/typescript-to-proptypes/src/getPropTypesFromFile.ts index 9b0d034cadcde6..f35dda6bd41a74 100644 --- a/packages-internal/scripts/typescript-to-proptypes/src/getPropTypesFromFile.ts +++ b/packages-internal/scripts/typescript-to-proptypes/src/getPropTypesFromFile.ts @@ -4,7 +4,7 @@ import { GetPropsFromComponentDeclarationOptions, getPropsFromComponentNode, TypeScriptProject, -} from '@mui-internal/docs-utils'; +} from '@mui/internal-docs-utils'; import { createUnionType, createUndefinedType, @@ -546,7 +546,7 @@ export function getPropTypesFromFile({ const shouldInclude: PropTypesProject['shouldInclude'] = (data): boolean => { // ref is a reserved prop name in React - // e.g. https://github.com/reactjs/rfcs/pull/107 + // for example https://github.com/reactjs/rfcs/pull/107 // no need to add a prop-type if (data.name === 'ref') { return false; diff --git a/packages-internal/scripts/typescript-to-proptypes/src/injectPropTypesInFile.ts b/packages-internal/scripts/typescript-to-proptypes/src/injectPropTypesInFile.ts index c292660b5334ce..48e7ca74f5b73e 100644 --- a/packages-internal/scripts/typescript-to-proptypes/src/injectPropTypesInFile.ts +++ b/packages-internal/scripts/typescript-to-proptypes/src/injectPropTypesInFile.ts @@ -154,7 +154,7 @@ function createBabelPlugin({ data, ) => { // key is a reserved prop name in React - // e.g. https://github.com/reactjs/rfcs/pull/107 + // for example https://github.com/reactjs/rfcs/pull/107 // no need to add a prop-type if we won't generate the docs for it. if (data.prop.name === 'key' && data.prop.jsDoc === '@ignore') { return false; @@ -389,7 +389,7 @@ function createBabelPlugin({ getFromProp(nodeInit.params[0]); } else if (babelTypes.isCallExpression(nodeInit)) { if ((nodeInit.callee as babel.types.Identifier)?.name?.match(/create[A-Z].*/)) { - // Any components that are created by a factory function, e.g. System Box | Container | Grid. + // Any components that are created by a factory function, for example System Box | Container | Grid. getFromProp(node); } else { // x = react.memo(props => <div/>) / react.forwardRef(props => <div />) diff --git a/packages-internal/scripts/typescript-to-proptypes/test/typescript-to-proptypes.test.ts b/packages-internal/scripts/typescript-to-proptypes/test/typescript-to-proptypes.test.ts index 769925840e26b8..6b29900311a4a5 100644 --- a/packages-internal/scripts/typescript-to-proptypes/test/typescript-to-proptypes.test.ts +++ b/packages-internal/scripts/typescript-to-proptypes/test/typescript-to-proptypes.test.ts @@ -4,7 +4,7 @@ import * as ts from 'typescript'; import { expect } from 'chai'; import glob from 'fast-glob'; import prettier from 'prettier'; -import { TypeScriptProject, createTypeScriptProjectBuilder } from '@mui-internal/docs-utils'; +import { TypeScriptProject, createTypeScriptProjectBuilder } from '@mui/internal-docs-utils'; import { generatePropTypes } from '../src/generatePropTypes'; import { injectPropTypesInFile } from '../src/injectPropTypesInFile'; import { getPropTypesFromFile } from '../src/getPropTypesFromFile'; diff --git a/packages/api-docs-builder-core/baseUi/generateBaseUiApiPages.ts b/packages/api-docs-builder-core/baseUi/generateBaseUiApiPages.ts index 2989159dd6fd24..fc5fc499e42cff 100644 --- a/packages/api-docs-builder-core/baseUi/generateBaseUiApiPages.ts +++ b/packages/api-docs-builder-core/baseUi/generateBaseUiApiPages.ts @@ -15,7 +15,7 @@ export async function generateBaseUIApiPages() { const componentName = pathnameTokens[3]; // TODO: fix `productName` should be called `productId` and include the full name, - // e.g. base-ui below. + // for example base-ui below. if ( productName === 'base' && (markdown.filename.indexOf('\\components\\') >= 0 || diff --git a/packages/api-docs-builder-core/package.json b/packages/api-docs-builder-core/package.json index 76d3feff4753e1..1d50e34a43e4d6 100644 --- a/packages/api-docs-builder-core/package.json +++ b/packages/api-docs-builder-core/package.json @@ -17,7 +17,7 @@ "devDependencies": { "@types/chai": "^4.3.12", "@types/mocha": "^10.0.6", - "@types/node": "^18.19.21", + "@types/node": "^18.19.25", "@types/sinon": "^10.0.20", "chai": "^4.4.1", "sinon": "^15.2.0", diff --git a/packages/api-docs-builder/ApiBuilders/ComponentApiBuilder.ts b/packages/api-docs-builder/ApiBuilders/ComponentApiBuilder.ts index 379532af1e7d0f..5c39c67af43796 100644 --- a/packages/api-docs-builder/ApiBuilders/ComponentApiBuilder.ts +++ b/packages/api-docs-builder/ApiBuilders/ComponentApiBuilder.ts @@ -10,7 +10,7 @@ import remarkVisit from 'unist-util-visit'; import type { Link } from 'mdast'; import { defaultHandlers, parse as docgenParse, ReactDocgenApi } from 'react-docgen'; import { renderMarkdown } from '@mui/internal-markdown'; -import { ComponentClassDefinition } from '@mui-internal/docs-utils'; +import { ComponentClassDefinition } from '@mui/internal-docs-utils'; import { ProjectSettings, SortingStrategiesType } from '../ProjectSettings'; import { ComponentInfo, toGitHubPath, writePrettifiedFile } from '../buildApiUtils'; import muiDefaultPropsHandler from '../utils/defaultPropsHandler'; @@ -55,7 +55,7 @@ export interface ReactApi extends ReactDocgenApi { /** * If `true`, the component supports theme default props customization. * If `null`, we couldn't infer this information. - * If `undefined`, it's not applicable in this context, e.g. Base UI components. + * If `undefined`, it's not applicable in this context, for example Base UI components. */ themeDefaultProps: boolean | undefined | null; /** @@ -734,6 +734,10 @@ export default async function generateComponentApi( }); } + if (!reactApi.props) { + reactApi.props = {}; + } + // Ignore what we might have generated in `annotateComponentDefinition` const annotatedDescriptionMatch = reactApi.description.match(/(Demos|API):\r?\n\r?\n/); if (annotatedDescriptionMatch !== null) { @@ -747,6 +751,8 @@ export default async function generateComponentApi( reactApi.muiName = componentInfo.muiName; reactApi.apiPathname = componentInfo.apiPathname; reactApi.EOL = EOL; + reactApi.slots = []; + reactApi.classes = []; reactApi.demos = componentInfo.getDemos(); if (reactApi.demos.length === 0) { throw new Error( @@ -770,16 +776,18 @@ export default async function generateComponentApi( } } - const { slots, classes } = parseSlotsAndClasses({ - typescriptProject: project, - projectSettings, - componentName: reactApi.name, - muiName: reactApi.muiName, - slotInterfaceName: componentInfo.slotInterfaceName, - }); + if (!projectSettings.skipSlotsAndClasses) { + const { slots, classes } = parseSlotsAndClasses({ + typescriptProject: project, + projectSettings, + componentName: reactApi.name, + muiName: reactApi.muiName, + slotInterfaceName: componentInfo.slotInterfaceName, + }); - reactApi.slots = slots; - reactApi.classes = classes; + reactApi.slots = slots; + reactApi.classes = classes; + } attachPropsTable(reactApi, projectSettings.propsSettings); attachTranslations(reactApi, projectSettings.propsSettings); diff --git a/packages/api-docs-builder/ProjectSettings.ts b/packages/api-docs-builder/ProjectSettings.ts index 3a848328893462..2ba937f0a4e9b2 100644 --- a/packages/api-docs-builder/ProjectSettings.ts +++ b/packages/api-docs-builder/ProjectSettings.ts @@ -1,4 +1,4 @@ -import { ComponentClassDefinition } from '@mui-internal/docs-utils'; +import { ComponentClassDefinition } from '@mui/internal-docs-utils'; import { ComponentInfo, HookInfo } from './buildApiUtils'; import { CreateTypeScriptProjectOptions } from './utils/createTypeScriptProject'; import { CreateDescribeablePropSettings } from './utils/createDescribeableProp'; @@ -60,6 +60,10 @@ export interface ProjectSettings { * Determine is the component definition should be updated. */ skipAnnotatingComponentDefinition?: boolean | ((filename: string) => boolean); + /** + * If `true`, skips extracting CSS class and slot information from the component. + */ + skipSlotsAndClasses?: boolean; /** * The path to the translation directory. */ diff --git a/packages/api-docs-builder/buildApiUtils.ts b/packages/api-docs-builder/buildApiUtils.ts index a62c632111ae4a..726e47069ff899 100644 --- a/packages/api-docs-builder/buildApiUtils.ts +++ b/packages/api-docs-builder/buildApiUtils.ts @@ -3,7 +3,7 @@ import path from 'path'; import * as ts from 'typescript'; import * as prettier from 'prettier'; import kebabCase from 'lodash/kebabCase'; -import { getLineFeed } from '@mui-internal/docs-utils'; +import { getLineFeed } from '@mui/internal-docs-utils'; import { replaceComponentLinks } from './utils/replaceUrl'; import { TypeScriptProject } from './utils/createTypeScriptProject'; diff --git a/packages/api-docs-builder/package.json b/packages/api-docs-builder/package.json index 6ed2ac331eccfe..6d50457c687aa7 100644 --- a/packages/api-docs-builder/package.json +++ b/packages/api-docs-builder/package.json @@ -11,7 +11,7 @@ "@babel/core": "^7.23.9", "@babel/preset-typescript": "^7.23.3", "@babel/traverse": "^7.23.9", - "@mui-internal/docs-utils": "workspace:^", + "@mui/internal-docs-utils": "workspace:^", "@mui/internal-markdown": "workspace:^", "ast-types": "^0.14.2", "doctrine": "^3.0.0", @@ -20,7 +20,7 @@ "lodash": "^4.17.21", "prettier": "^3.2.5", "react-docgen": "^5.4.3", - "recast": "^0.23.5", + "recast": "^0.23.6", "remark": "^13.0.0", "typescript": "^5.3.3", "unist-util-visit": "^2.0.3" @@ -32,7 +32,7 @@ "@types/doctrine": "^0.0.9", "@types/mdast": "4.0.3", "@types/mocha": "^10.0.6", - "@types/node": "^18.19.21", + "@types/node": "^18.19.25", "@types/react-docgen": "workspace:*", "@types/sinon": "^10.0.20", "chai": "^4.4.1", diff --git a/packages/api-docs-builder/tsconfig.json b/packages/api-docs-builder/tsconfig.json index 41e0c9ff506e27..4c8cb67b09768f 100644 --- a/packages/api-docs-builder/tsconfig.json +++ b/packages/api-docs-builder/tsconfig.json @@ -14,7 +14,7 @@ "strict": true, "baseUrl": "./", "paths": { - "@mui-internal/docs-utils": ["../docs-utils/src"] + "@mui/internal-docs-utils": ["../docs-utils/src"] } }, "include": ["./**/*.ts", "./**/*.js"], diff --git a/packages/api-docs-builder/utils/parseSlotsAndClasses.ts b/packages/api-docs-builder/utils/parseSlotsAndClasses.ts index 92582e71355370..64a27b69b8dc4a 100644 --- a/packages/api-docs-builder/utils/parseSlotsAndClasses.ts +++ b/packages/api-docs-builder/utils/parseSlotsAndClasses.ts @@ -1,5 +1,5 @@ import * as ts from 'typescript'; -import { ComponentClassDefinition } from '@mui-internal/docs-utils'; +import { ComponentClassDefinition } from '@mui/internal-docs-utils'; import { renderMarkdown } from '@mui/internal-markdown'; import { getSymbolDescription, getSymbolJSDocTags } from '../buildApiUtils'; import { TypeScriptProject } from './createTypeScriptProject'; diff --git a/packages/api-docs-builder/utils/parseTest.ts b/packages/api-docs-builder/utils/parseTest.ts index 1331f49afb5fa9..14d2c70c9939cb 100644 --- a/packages/api-docs-builder/utils/parseTest.ts +++ b/packages/api-docs-builder/utils/parseTest.ts @@ -73,7 +73,7 @@ function getRefInstance(valueNode: babel.Node): string | undefined { if (!babel.types.isMemberExpression(valueNode)) { throw new Error( - 'Expected a member expression (e.g. window.HTMLDivElement) or a global identifier (e.g. Object) in refInstanceof. ' + + 'Expected a member expression (for example window.HTMLDivElement) or a global identifier (for example Object) in refInstanceof. ' + 'If the ref will not be resolved use `refInstanceof: undefined`.', ); } diff --git a/packages/eslint-plugin-material-ui/README.md b/packages/eslint-plugin-material-ui/README.md index 8ee4945295b0a3..0688c6e2c38d38 100644 --- a/packages/eslint-plugin-material-ui/README.md +++ b/packages/eslint-plugin-material-ui/README.md @@ -14,7 +14,7 @@ Custom eslint rules for MUI. Prevent `fireEvent.keyDown(document.activeElement)`. The implementation we use already verifies that the passed target can be the target of a -`keydown` event. Passing the target explicitly (e.g. `fireEvent.keyDown(getByRole('tab', { selected: true }))`) makes the test more readable. +`keydown` event. Passing the target explicitly (for example `fireEvent.keyDown(getByRole('tab', { selected: true }))`) makes the test more readable. ### docgen-ignore-before-comment diff --git a/packages/eslint-plugin-material-ui/src/rules/mui-name-matches-component-name.js b/packages/eslint-plugin-material-ui/src/rules/mui-name-matches-component-name.js index db51e2b85a4023..008258d91b249c 100644 --- a/packages/eslint-plugin-material-ui/src/rules/mui-name-matches-component-name.js +++ b/packages/eslint-plugin-material-ui/src/rules/mui-name-matches-component-name.js @@ -116,7 +116,7 @@ const rule = { ? parent.init.expression.callee : parent.init.callee; if (callee.name.includes(parent.id.name)) { - // For component factory, e.g. const Container = createContainer({ ... }) + // For component factory, for example const Container = createContainer({ ... }) componentName = parent.id.name; } } diff --git a/packages/feedback/README.md b/packages/feedback/README.md index ce315d149cd545..23dcc510b3af00 100644 --- a/packages/feedback/README.md +++ b/packages/feedback/README.md @@ -45,7 +45,7 @@ The project includes an IAM access policy that will grant the lambda function ac To set this up, first [set up the credentials](https://claudiajs.com/tutorials/installing.html#configuring-access-credentials), then: 1. run `pnpm install` (from the root workspace) to install the dependencies -1. Navigate into the directory of this README, e.g. `cd docs/packages/feedback` +1. Navigate into the directory of this README, for example `cd docs/packages/feedback` 1. run `pnpm setup` to create the lambda function on AWS under the default name. This will also ask you for table names for development and production. If you used the above AWS command, they will be `feedback-dev` and `feedback-dev` respectively. diff --git a/packages/feedback/package.json b/packages/feedback/package.json index 9a409066be134a..3b4bb3be53e22c 100644 --- a/packages/feedback/package.json +++ b/packages/feedback/package.json @@ -25,6 +25,6 @@ "claudia": "^5.14.1" }, "optionalDependencies": { - "aws-sdk": "^2.1569.0" + "aws-sdk": "^2.1579.0" } } diff --git a/packages/markdown/package.json b/packages/markdown/package.json index e7067fa50d9507..5a7b0373e8311c 100644 --- a/packages/markdown/package.json +++ b/packages/markdown/package.json @@ -1,6 +1,6 @@ { "name": "@mui/internal-markdown", - "version": "1.0.0", + "version": "1.0.1", "author": "MUI Team", "description": "MUI markdown parser. This is an internal package not meant for general use.", "main": "./index.js", @@ -13,7 +13,7 @@ "repository": { "type": "git", "url": "https://github.com/mui/material-ui.git", - "directory": "packages/docs-utils" + "directory": "packages/markdown" }, "scripts": { "release:publish": "pnpm publish --tag latest", diff --git a/packages/markdown/parseMarkdown.js b/packages/markdown/parseMarkdown.js index 15feaa6c6372bc..a9848f583dce47 100644 --- a/packages/markdown/parseMarkdown.js +++ b/packages/markdown/parseMarkdown.js @@ -188,6 +188,7 @@ function getContents(markdown) { .replace(headerRegExp, '') // Remove header information .split(/^{{("(?:demo|component)":.*)}}$/gm) // Split markdown into an array, separating demos .flatMap((text) => text.split(/^(<codeblock.*?<\/codeblock>)$/gmsu)) + .flatMap((text) => text.split(/^(<featureList.*?<\/featureList>)$/gmsu)) .filter((content) => !emptyRegExp.test(content)); // Remove empty lines return rep; } @@ -212,23 +213,37 @@ function getDescription(markdown) { } function getCodeblock(content) { - if (content.startsWith('<codeblock')) { - const storageKey = content.match(/^<codeblock [^>]*storageKey=["|'](\S*)["|'].*>/m)?.[1]; - const blocks = [...content.matchAll(/^```(\S*) (\S*)\n(.*?)\n```/gmsu)].map( - ([, language, tab, code]) => ({ language, tab, code }), - ); - - const blocksData = blocks.filter( - (block) => block.tab !== undefined && !emptyRegExp.test(block.code), - ); + if (!content.startsWith('<codeblock')) { + return undefined; + } + const storageKey = content.match(/^<codeblock [^>]*storageKey=["|'](\S*)["|'].*>/m)?.[1]; + const blocks = [...content.matchAll(/^```(\S*) (\S*)\n(.*?)\n```/gmsu)].map( + ([, language, tab, code]) => ({ language, tab, code }), + ); + + const blocksData = blocks.filter( + (block) => block.tab !== undefined && !emptyRegExp.test(block.code), + ); + + return { + type: 'codeblock', + data: blocksData, + storageKey, + }; +} - return { - type: 'codeblock', - data: blocksData, - storageKey, - }; +function getFeatureList(content) { + if (!content.startsWith('<featureList')) { + return undefined; } - return undefined; + const lines = content + .split('\n') + .filter((line) => line.startsWith('- ')) + .map((line) => line.slice(2)); + + return ['<ul class="feature-list">', ...lines.map((line) => `<li>${line}</li>`), '</ul>'].join( + '', + ); } /** @@ -395,6 +410,7 @@ function createRender(context) { renderer.code = (code, infostring, escaped) => { // https://github.com/markedjs/marked/blob/30e90e5175700890e6feb1836c57b9404c854466/src/Renderer.js#L15 const lang = (infostring || '').match(/\S*/)[0]; + const title = (infostring || '').match(/title="([^"]*)"/)?.[1]; const out = prism(code, lang); if (out != null && out !== code) { escaped = true; @@ -407,7 +423,7 @@ function createRender(context) { return `<pre><code>${escaped ? code : escape(code, true)}</code></pre>\n`; } - return `<div class="MuiCode-root"><pre><code class="language-${escape(lang, true)}">${ + return `<div class="MuiCode-root">${title ? `<div class="MuiCode-title">${title}</div>` : ''}<pre><code class="language-${escape(lang, true)}">${ escaped ? code : escape(code, true) }</code></pre>${[ '<button data-ga-event-category="code" data-ga-event-action="copy-click" aria-label="Copy the code" class="MuiCode-copy">', @@ -475,6 +491,7 @@ module.exports = { getContents, getDescription, getCodeblock, + getFeatureList, getHeaders, getTitle, renderMarkdown, diff --git a/packages/markdown/prepareMarkdown.js b/packages/markdown/prepareMarkdown.js index c66fbfebfc21cc..caaa61d0fbbd97 100644 --- a/packages/markdown/prepareMarkdown.js +++ b/packages/markdown/prepareMarkdown.js @@ -7,6 +7,7 @@ const { getContents, getDescription, getCodeblock, + getFeatureList, getHeaders, getTitle, } = require('./parseMarkdown'); @@ -155,13 +156,18 @@ ${headers.hooks return null; } } - const codeblock = getCodeblock(content); if (codeblock) { return codeblock; } + const featureList = getFeatureList(content); + + if (featureList) { + return featureList; + } + return render(content); }); diff --git a/packages/mui-babel-macros/package.json b/packages/mui-babel-macros/package.json index 1f579c02d1e4e6..cb422a56c3ec48 100644 --- a/packages/mui-babel-macros/package.json +++ b/packages/mui-babel-macros/package.json @@ -1,6 +1,6 @@ { "name": "@mui/internal-babel-macros", - "version": "1.0.0", + "version": "1.0.1", "author": "MUI Team", "description": "MUI Babel macros. This is an internal package not meant for general use.", "main": "./MuiError.macro.js", @@ -32,7 +32,7 @@ "@types/babel-plugin-macros": "^3.1.3", "@types/chai": "^4.3.12", "@types/mocha": "^10.0.6", - "@types/node": "^18.19.21", + "@types/node": "^18.19.25", "babel-plugin-tester": "^11.0.4", "chai": "^4.4.1" }, diff --git a/packages/mui-base/README.md b/packages/mui-base/README.md index 35b605e1330406..e96bbd7afda60b 100644 --- a/packages/mui-base/README.md +++ b/packages/mui-base/README.md @@ -1,6 +1,6 @@ <!-- markdownlint-disable-next-line --> <p align="center"> - <a href="https://mui.com/base-ui/" rel="noopener" target="_blank"><img width="150" height="133" src="https://mui.com/static/logo.svg" alt="Base UI logo"></a> + <a href="https://next.mui.com/base-ui/" rel="noopener" target="_blank"><img width="150" height="133" src="https://next.mui.com/static/logo.svg" alt="Base UI logo"></a> </p> <h1 align="center">Base UI</h1> @@ -19,7 +19,7 @@ npm install @mui/base <!-- #default-branch-switch --> -Visit [https://mui.com/base-ui/](https://mui.com/base-ui/) to view the full documentation. +Visit [https://next.mui.com/base-ui/](https://next.mui.com/base-ui/) to view the full documentation. ## Questions @@ -28,14 +28,14 @@ Use the "base-ui" tag on Stack Overflow to make it easier for the community to ## Examples -Our documentation features [a collection of example projects using Base UI](https://github.com/mui/material-ui/tree/master/examples). +Our documentation features [a collection of example projects using Base UI](https://github.com/mui/material-ui/tree/next/examples). ## Contributing Read the [contributing guide](/CONTRIBUTING.md) to learn about our development process, how to propose bug fixes and improvements, and how to build and test your changes. Contributing to Base UI is about more than just issues and pull requests! -There are many other ways to [support MUI](https://mui.com/material-ui/getting-started/faq/#mui-is-awesome-how-can-i-support-the-project) beyond contributing to the code base. +There are many other ways to [support Base UI](https://next.mui.com/material-ui/getting-started/faq/#mui-is-awesome-how-can-i-support-the-project) beyond contributing to the code base. ## Changelog @@ -43,7 +43,7 @@ The [changelog](https://github.com/mui/material-ui/releases) is regularly update ## Roadmap -Future plans and high-priority features and enhancements can be found in our [roadmap](https://mui.com/material-ui/discover-more/roadmap/). +Future plans and high-priority features and enhancements can be found in the [roadmap](https://next.mui.com/material-ui/discover-more/roadmap/). ## License diff --git a/packages/mui-base/package.json b/packages/mui-base/package.json index b7e86fdb4d5a82..350c356217b117 100644 --- a/packages/mui-base/package.json +++ b/packages/mui-base/package.json @@ -1,6 +1,6 @@ { "name": "@mui/base", - "version": "5.0.0-beta.38", + "version": "5.0.0-beta.40", "private": false, "author": "MUI Team", "description": "Base UI is a library of headless ('unstyled') React components and low-level hooks. You gain complete control over your app's CSS and accessibility features.", diff --git a/packages/mui-base/src/FocusTrap/FocusTrap.tsx b/packages/mui-base/src/FocusTrap/FocusTrap.tsx index 68803e9080e62d..bd52658ac9b7df 100644 --- a/packages/mui-base/src/FocusTrap/FocusTrap.tsx +++ b/packages/mui-base/src/FocusTrap/FocusTrap.tsx @@ -313,7 +313,7 @@ function FocusTrap(props: FocusTrapProps): JSX.Element { doc.addEventListener('keydown', loopFocus, true); // With Edge, Safari and Firefox, no focus related events are fired when the focused area stops being a focused area. - // e.g. https://bugzilla.mozilla.org/show_bug.cgi?id=559561. + // for example https://bugzilla.mozilla.org/show_bug.cgi?id=559561. // Instead, we can look if the active element was restored on the BODY element. // // The whatwg spec defines how the browser should behave but does not explicitly mention any events: diff --git a/packages/mui-base/src/Unstable_Popup/Popup.tsx b/packages/mui-base/src/Unstable_Popup/Popup.tsx index 0b0a90cfde1dc9..4281b239ebbb6b 100644 --- a/packages/mui-base/src/Unstable_Popup/Popup.tsx +++ b/packages/mui-base/src/Unstable_Popup/Popup.tsx @@ -16,10 +16,10 @@ import { } from '@mui/utils'; import { unstable_composeClasses as composeClasses } from '../composeClasses'; import { Portal } from '../Portal'; -import { useSlotProps, WithOptionalOwnerState } from '../utils'; +import { useSlotProps, WithOptionalOwnerState, PolymorphicComponent } from '../utils'; import { useClassNamesOverride } from '../utils/ClassNameConfigurator'; import { getPopupUtilityClass } from './popupClasses'; -import { PopupOwnerState, PopupProps, PopupRootSlotProps } from './Popup.types'; +import { PopupOwnerState, PopupProps, PopupRootSlotProps, PopupTypeMap } from './Popup.types'; import { useTransitionTrigger, TransitionContext } from '../useTransition'; import { PopupContext, PopupContextValue } from './PopupContext'; @@ -154,7 +154,7 @@ const Popup = React.forwardRef(function Popup<RootComponentType extends React.El </PopupContext.Provider> </Portal> ); -}); +}) as PolymorphicComponent<PopupTypeMap>; Popup.propTypes /* remove-proptypes */ = { // ┌────────────────────────────── Warning ──────────────────────────────┐ diff --git a/packages/mui-base/src/useAutocomplete/useAutocomplete.js b/packages/mui-base/src/useAutocomplete/useAutocomplete.js index 13d846b468d9c5..344b2cce0efe0f 100644 --- a/packages/mui-base/src/useAutocomplete/useAutocomplete.js +++ b/packages/mui-base/src/useAutocomplete/useAutocomplete.js @@ -598,7 +598,7 @@ export function useAutocomplete(props) { [ `A textarea element was provided to ${componentName} where input was expected.`, `This is not a supported scenario but it may work under certain conditions.`, - `A textarea keyboard navigation may conflict with Autocomplete controls (e.g. enter and arrow keys).`, + `A textarea keyboard navigation may conflict with Autocomplete controls (for example enter and arrow keys).`, `Make sure to test keyboard navigation and add custom event handlers if necessary.`, ].join('\n'), ); diff --git a/packages/mui-base/src/useCompound/useCompoundItem.ts b/packages/mui-base/src/useCompound/useCompoundItem.ts index 05f28a0d16e9e4..1c65a8d8dab4a6 100644 --- a/packages/mui-base/src/useCompound/useCompoundItem.ts +++ b/packages/mui-base/src/useCompound/useCompoundItem.ts @@ -32,7 +32,7 @@ export interface UseCompoundItemReturnValue<Key> { * This can be either a value, or a function that generates a value based on already registered siblings' ids. * If a function, it's called with the set of the ids of all the items that have already been registered. * Return `existingKeys.size` if you want to use the index of the new item as the id. - * @param itemMetadata Arbitrary metadata to pass to the parent component. This should be a stable reference (e.g. a memoized object), to avoid unnecessary re-registrations. + * @param itemMetadata Arbitrary metadata to pass to the parent component. This should be a stable reference (for example a memoized object), to avoid unnecessary re-registrations. * * @ignore - internal hook. */ diff --git a/packages/mui-base/src/useCompound/useCompoundParent.ts b/packages/mui-base/src/useCompound/useCompoundParent.ts index b85b4d31496d48..8cc2dad24b6efe 100644 --- a/packages/mui-base/src/useCompound/useCompoundParent.ts +++ b/packages/mui-base/src/useCompound/useCompoundParent.ts @@ -18,7 +18,7 @@ export type CompoundComponentContextValue<Key, Subitem> = { /** * Registers an item with the parent. * This should be called during the effect phase of the child component. - * The `itemMetadata` should be a stable reference (e.g. a memoized object), to avoid unnecessary re-registrations. + * The `itemMetadata` should be a stable reference (for example a memoized object), to avoid unnecessary re-registrations. * * @param id Id of the item or A function that generates a unique id for the item. * It is called with the set of the ids of all the items that have already been registered. diff --git a/packages/mui-base/src/useList/useListItem.ts b/packages/mui-base/src/useList/useListItem.ts index 6a41a278d3a804..99997484501811 100644 --- a/packages/mui-base/src/useList/useListItem.ts +++ b/packages/mui-base/src/useList/useListItem.ts @@ -7,7 +7,7 @@ import { ListActionTypes } from './listActions.types'; import { ListContext } from './ListContext'; /** - * Contains the logic for an item of a list-like component (e.g. Select, Menu, etc.). + * Contains the logic for an item of a list-like component (for example Select, Menu, etc.). * It handles the item's mouse events and tab index. * * @template ItemValue The type of the item's value. This should be consistent with the type of useList's `items` parameter. diff --git a/packages/mui-base/src/useSelect/useSelect.test.tsx b/packages/mui-base/src/useSelect/useSelect.test.tsx index 014bbd7f26dd8f..05451e21f309ef 100644 --- a/packages/mui-base/src/useSelect/useSelect.test.tsx +++ b/packages/mui-base/src/useSelect/useSelect.test.tsx @@ -44,7 +44,7 @@ describe('useSelect', () => { border: 0, clip: 'rect(0 0 0 0)', height: '1px', - margin: -1, + margin: '-1px', overflow: 'hidden', padding: 0, position: 'absolute', diff --git a/packages/mui-base/src/utils/useRootElementName.ts b/packages/mui-base/src/utils/useRootElementName.ts index 71358471fdc70f..607a67fe496ec1 100644 --- a/packages/mui-base/src/utils/useRootElementName.ts +++ b/packages/mui-base/src/utils/useRootElementName.ts @@ -3,7 +3,7 @@ import * as React from 'react'; type UseRootElementNameParameters = { /** - * The HTML element expected to be rendered, e.g. 'div', 'button' etc + * The HTML element expected to be rendered, for example 'div', 'button' etc * @default '' */ rootElementName?: keyof HTMLElementTagNameMap; @@ -17,7 +17,7 @@ type UseRootElementNameParameters = { /** * @ignore - do not document. * - * Use this function determine the host element correctly on the server (in a SSR context, e.g. Next.js) + * Use this function determine the host element correctly on the server (in a SSR context, for example Next.js) */ export function useRootElementName( parameters: UseRootElementNameParameters, @@ -36,7 +36,7 @@ export function useRootElementName( `useRootElementName: the \`rootElementName\` prop of ${ componentName ? `the ${componentName} component` : 'a component' } expected the '${rootElementNameProp}' element, but a '${rootElementName.toLowerCase()}' was rendered instead`, - 'This may cause hydration issues in an SSR context, e.g. in a Next.js app', + 'This may cause hydration issues in an SSR context, for example in a Next.js app', ); } }, [rootElementNameProp, rootElementName, componentName]); diff --git a/packages/mui-codemod/CONTRIBUTING.md b/packages/mui-codemod/CONTRIBUTING.md index 7d536a0c2f8b4c..89af440e5fef79 100644 --- a/packages/mui-codemod/CONTRIBUTING.md +++ b/packages/mui-codemod/CONTRIBUTING.md @@ -4,7 +4,7 @@ The codemod is a tool that helps developers migrate their codebase when we introduce changes in a new version. The changes could be deprecations, enhancements, or breaking changes. -The codemods for JS files are based on [jscodeshift](https://github.com/facebook/jscodeshift) which is a wrapper of [recast](https://github.com/benjamn/recast). +The codemods for JavaScript files are based on [jscodeshift](https://github.com/facebook/jscodeshift) which is a wrapper of [recast](https://github.com/benjamn/recast). The codemods for CSS files are based on [postcss](https://github.com/postcss/postcss). @@ -22,7 +22,7 @@ The codemods for CSS files are based on [postcss](https://github.com/postcss/pos - `actual.css` - the input for the postcss plugin (optional) - `expected.css` - the expected output of the postcss plugin (optional) 3. Use [astexplorer](https://astexplorer.net/) to check the AST types and properties - - For JS codemods set </> to @babel/parser because we use [`tsx`](https://github.com/benjamn/recast/blob/master/parsers/babel.ts) as a default parser. + - For JavaScript codemods set </> to @babel/parser because we use [`tsx`](https://github.com/benjamn/recast/blob/master/parsers/babel.ts) as a default parser. - For CSS codemods set </> to postcss 4. [Test the codemod locally](#local) 5. Add the codemod to README.md diff --git a/packages/mui-codemod/README.md b/packages/mui-codemod/README.md index cd262fec93df9e..55e7f114c0eb14 100644 --- a/packages/mui-codemod/README.md +++ b/packages/mui-codemod/README.md @@ -14,7 +14,7 @@ Some of the codemods also run [postcss](https://github.com/postcss/postcss) plug <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest <codemod> <paths...> +npx @mui/codemod@next <codemod> <paths...> Applies a `@mui/codemod` to the specified paths @@ -34,10 +34,10 @@ Options: --jscodeshift [string] [default: false] Examples: - npx @mui/codemod@latest v4.0.0/theme-spacing-api src - npx @mui/codemod@latest v5.0.0/component-rename-prop src -- + npx @mui/codemod@next v4.0.0/theme-spacing-api src + npx @mui/codemod@next v5.0.0/component-rename-prop src -- --component=Grid --from=prop --to=newProp - npx @mui/codemod@latest v5.0.0/preset-safe src --parser=flow + npx @mui/codemod@next v5.0.0/preset-safe src --parser=flow ``` ### jscodeshift options @@ -45,7 +45,7 @@ Examples: To pass more options directly to jscodeshift, use `--jscodeshift="..."`. For example: ```bash -npx @mui/codemod@latest --jscodeshift="--run-in-band --verbose=2" +npx @mui/codemod@next --jscodeshift="--run-in-band --verbose=2" ``` See all available options [here](https://github.com/facebook/jscodeshift#usage-cli). @@ -56,7 +56,7 @@ Options to [recast](https://github.com/benjamn/recast)'s printer can be provided through jscodeshift's `printOptions` command line argument ```bash -npx @mui/codemod@latest <transform> <path> --jscodeshift="--printOptions='{\"quote\":\"double\"}'" +npx @mui/codemod@next <transform> <path> --jscodeshift="--printOptions='{\"quote\":\"double\"}'" ``` ## Included scripts @@ -70,7 +70,7 @@ npx @mui/codemod@latest <transform> <path> --jscodeshift="--printOptions='{\"quo ### Deprecations ```bash -npx @mui/codemod@latest deprecations/all <path> +npx @mui/codemod@next deprecations/all <path> ``` #### `all` @@ -89,7 +89,7 @@ A combination of all deprecations. ``` ```bash -npx @mui/codemod@latest deprecations/accordion-props <path> +npx @mui/codemod@next deprecations/accordion-props <path> ``` #### `accordion-summary-classes` @@ -133,7 +133,7 @@ CSS transforms: ``` ```bash -npx @mui/codemod@latest deprecations/accordion-summary-classes <path> +npx @mui/codemod@next deprecations/accordion-summary-classes <path> ``` #### `alert-classes` @@ -229,7 +229,7 @@ CSS transforms: ``` ```bash -npx @mui/codemod@latest deprecations/alert-classes <path> +npx @mui/codemod@next deprecations/alert-classes <path> ``` #### `alert-props` @@ -255,7 +255,7 @@ npx @mui/codemod@latest deprecations/alert-classes <path> ``` ```bash -npx @mui/codemod@latest deprecations/alert-props <path> +npx @mui/codemod@next deprecations/alert-props <path> ``` #### `avatar-props` @@ -494,7 +494,7 @@ CSS transforms: ``` ```bash -npx @mui/codemod@latest deprecations/button-classes <path> +npx @mui/codemod@next deprecations/button-classes <path> ``` #### `chip-classes` @@ -676,7 +676,7 @@ CSS transforms: ``` ```bash -npx @mui/codemod@latest deprecations/chip-classes <path> +npx @mui/codemod@next deprecations/chip-classes <path> ``` #### `divider-props` @@ -689,7 +689,7 @@ npx @mui/codemod@latest deprecations/chip-classes <path> ``` ```bash -npx @mui/codemod@latest deprecations/divider-props <path> +npx @mui/codemod@next deprecations/divider-props <path> ``` #### `pagination-item-classes` @@ -754,7 +754,7 @@ CSS transforms: ``` ```bash -npx @mui/codemod@latest deprecations/pagination-item-classes <path> +npx @mui/codemod@next deprecations/pagination-item-classes <path> ``` #### `slider-props` @@ -780,7 +780,7 @@ npx @mui/codemod@latest deprecations/pagination-item-classes <path> ``` ```bash -npx @mui/codemod@latest deprecations/step-label-props <path> +npx @mui/codemod@next deprecations/slider-props <path> ``` #### `step-label-props` @@ -823,7 +823,7 @@ This codemod updates the import and re-export statements. ``` ```bash -npx @mui/codemod@latest v5.0.0/base-use-named-exports <path> +npx @mui/codemod@next v5.0.0/base-use-named-exports <path> ``` #### `base-remove-unstyled-suffix` @@ -836,7 +836,7 @@ The `Unstyled` suffix has been removed from all Base UI component names, includ ``` ```bash -npx @mui/codemod@latest v5.0.0/base-remove-unstyled-suffix <path> +npx @mui/codemod@next v5.0.0/base-remove-unstyled-suffix <path> ``` #### `base-remove-component-prop` @@ -851,7 +851,7 @@ This change only affects Base UI components. ``` ```bash -npx @mui/codemod@latest v5.0.0/base-remove-component-prop <path> +npx @mui/codemod@next v5.0.0/base-remove-component-prop <path> ``` #### `rename-css-variables` @@ -866,7 +866,7 @@ Updates the names of the CSS variables of the Joy UI components to adapt to the ``` ```bash -npx @mui/codemod@latest v5.0.0/rename-css-variables <path> +npx @mui/codemod@next v5.0.0/rename-css-variables <path> ``` #### `base-hook-imports` @@ -879,7 +879,7 @@ Updates the sources of the imports of the Base UI hooks to adapt to the new dir ``` ```bash -npx @mui/codemod@latest v5.0.0/base-hook-imports <path> +npx @mui/codemod@next v5.0.0/base-hook-imports <path> ``` #### `joy-rename-classname-prefix` @@ -894,7 +894,7 @@ Renames the classname prefix from `'Joy'` to `'Mui'` for Joy UI components. ``` ```bash -npx @mui/codemod@latest v5.0.0/joy-rename-classname-prefix <path> +npx @mui/codemod@next v5.0.0/joy-rename-classname-prefix <path> ``` #### `joy-rename-row-prop` @@ -909,7 +909,7 @@ Transforms `row` prop to `orientation` prop across `Card`, `List` and `RadioGrou ``` ```bash -npx @mui/codemod@latest v5.0.0/joy-rename-row-prop <path> +npx @mui/codemod@next v5.0.0/joy-rename-row-prop <path> ``` #### `joy-avatar-remove-imgProps` @@ -927,7 +927,7 @@ This change only affects Joy UI Avatar component. ``` ```bash -npx @mui/codemod@latest v5.0.0/joy-avatar-remove-imgProps <path> +npx @mui/codemod@next v5.0.0/joy-avatar-remove-imgProps <path> ``` #### `joy-text-field-to-input` @@ -985,7 +985,7 @@ This change only affects Joy UI components. ``` ```bash -npx @mui/codemod@latest v5.0.0/joy-text-field-to-input <path> +npx @mui/codemod@next v5.0.0/joy-text-field-to-input <path> ``` #### `joy-rename-components-to-slots` @@ -1004,7 +1004,7 @@ This change only affects Joy UI components. ``` ```bash -npx @mui/codemod@latest v5.0.0/joy-rename-components-to-slots <path> +npx @mui/codemod@next v5.0.0/joy-rename-components-to-slots <path> ``` The associated breaking change was done in [#34997](https://github.com/mui/material-ui/pull/34997). @@ -1014,7 +1014,7 @@ The associated breaking change was done in [#34997](https://github.com/mui/mater Rename the imports of Date and Time Pickers from `@mui/lab` to `@mui/x-date-pickers` and `@mui/x-date-pickers-pro`. ```bash -npx @mui/codemod@latest v5.0.0/date-pickers-moved-to-x <path> +npx @mui/codemod@next v5.0.0/date-pickers-moved-to-x <path> ``` #### `tree-view-moved-to-x` @@ -1022,7 +1022,7 @@ npx @mui/codemod@latest v5.0.0/date-pickers-moved-to-x <path> Rename the imports of Tree View from `@mui/lab` to `@mui/x-tree-view`. ```bash -npx @mui/codemod@latest v5.0.0/tree-view-moved-to-x <path> +npx @mui/codemod@next v5.0.0/tree-view-moved-to-x <path> ``` #### 🚀 `preset-safe` @@ -1030,7 +1030,7 @@ npx @mui/codemod@latest v5.0.0/tree-view-moved-to-x <path> A combination of all important transformers for migrating v4 to v5. ⚠️ This codemod should be run only once. ```bash -npx @mui/codemod@latest v5.0.0/preset-safe <path|folder> +npx @mui/codemod@next v5.0.0/preset-safe <path|folder> ``` The list includes these transformers @@ -1097,7 +1097,7 @@ Imports and inserts `adaptV4Theme` into `createTheme` (or `createMuiTheme`) ``` ```bash -npx @mui/codemod@latest v5.0.0/adapter-v4 <path> +npx @mui/codemod@next v5.0.0/adapter-v4 <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#theme). @@ -1114,7 +1114,7 @@ Renames `Autocomplete`'s `closeIcon` prop to `clearIcon`. <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/autocomplete-rename-closeicon <path> +npx @mui/codemod@next v5.0.0/autocomplete-rename-closeicon <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#autocomplete). @@ -1133,7 +1133,7 @@ Renames `Autocomplete`'s `getOptionSelected` to `isOptionEqualToValue`. <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/autocomplete-rename-option <path> +npx @mui/codemod@next v5.0.0/autocomplete-rename-option <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#autocomplete). @@ -1152,7 +1152,7 @@ Updates the `Avatar`'s `variant` value and `classes` key from 'circle' to 'circu <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/avatar-circle-circular <path> +npx @mui/codemod@next v5.0.0/avatar-circle-circular <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#avatar). @@ -1187,7 +1187,7 @@ Renames the badge's props. <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/badge-overlap-value <path> +npx @mui/codemod@next v5.0.0/badge-overlap-value <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#badge). @@ -1211,7 +1211,7 @@ This change only affects Base UI components. <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/base-rename-components-to-slots <path> +npx @mui/codemod@next v5.0.0/base-rename-components-to-slots <path> ``` The associated breaking change was done in [#34693](https://github.com/mui/material-ui/pull/34693). @@ -1230,7 +1230,7 @@ Updates the Box API from separate system props to `sx`. <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/box-borderradius-values <path> +npx @mui/codemod@next v5.0.0/box-borderradius-values <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#box). @@ -1245,7 +1245,7 @@ Renames the Box `css` prop to `sx` ``` ```bash -npx @mui/codemod@latest v5.0.0/box-rename-css <path> +npx @mui/codemod@next v5.0.0/box-rename-css <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#box). @@ -1266,7 +1266,7 @@ Renames the Box `grid*Gap` props. <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/box-rename-gap <path> +npx @mui/codemod@next v5.0.0/box-rename-gap <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#box). @@ -1283,7 +1283,7 @@ Removes the outdated `color` prop values. <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/button-color-prop <path> +npx @mui/codemod@next v5.0.0/button-color-prop <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#button). @@ -1300,7 +1300,7 @@ Removes the Chip `variant` prop if the value is `"default"`. <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/chip-variant-prop <path> +npx @mui/codemod@next v5.0.0/chip-variant-prop <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#chip). @@ -1317,7 +1317,7 @@ Renames the CircularProgress `static` variant to `determinate`. <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/circularprogress-variant <path> +npx @mui/codemod@next v5.0.0/circularprogress-variant <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#circularprogress). @@ -1336,7 +1336,7 @@ Renames `Collapse`'s `collapsedHeight` prop to `collapsedSize`. <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/collapse-rename-collapsedheight <path> +npx @mui/codemod@next v5.0.0/collapse-rename-collapsedheight <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#collapse). @@ -1355,7 +1355,7 @@ A generic codemod to rename any component prop. <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/component-rename-prop <path> -- --component=Grid --from=prop --to=newProp +npx @mui/codemod@next v5.0.0/component-rename-prop <path> -- --component=Grid --from=prop --to=newProp ``` #### `core-styles-import` @@ -1368,7 +1368,7 @@ Renames private import from `core/styles/*` to `core/styles` ``` ```bash -npx @mui/codemod@latest v5.0.0/core-styles-import <path> +npx @mui/codemod@next v5.0.0/core-styles-import <path> ``` #### `create-theme` @@ -1381,7 +1381,7 @@ Renames the function `createMuiTheme` to `createTheme` ``` ```bash -npx @mui/codemod@latest v5.0.0/create-theme <path> +npx @mui/codemod@next v5.0.0/create-theme <path> ``` #### `dialog-props` @@ -1394,7 +1394,7 @@ Remove `disableBackdropClick` prop from `<Dialog>` ``` ```bash -npx @mui/codemod@latest v5.0.0/dialog-props <path> +npx @mui/codemod@next v5.0.0/dialog-props <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#dialog). @@ -1409,7 +1409,7 @@ Remove `disableTypography` prop from `<DialogTitle>` ``` ```bash -npx @mui/codemod@latest v5.0.0/dialog-title-props <path> +npx @mui/codemod@next v5.0.0/dialog-title-props <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#dialog). @@ -1426,7 +1426,7 @@ Adds `prepend: true` to Emotion `createCache` ``` ```bash -npx @mui/codemod@latest v5.0.0/create-theme <path> +npx @mui/codemod@next v5.0.0/create-theme <path> ``` #### `expansion-panel-component` @@ -1434,7 +1434,7 @@ npx @mui/codemod@latest v5.0.0/create-theme <path> Renames `ExpansionPanel*` to `Accordion*` ```bash -npx @mui/codemod@latest v5.0.0/expansion-panel-component <path> +npx @mui/codemod@next v5.0.0/expansion-panel-component <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#expansionpanel). @@ -1447,7 +1447,7 @@ You can find more details about this breaking change in [the migration guide](ht ``` ```bash -npx @mui/codemod@latest v5.0.0/fab-variant <path> +npx @mui/codemod@next v5.0.0/fab-variant <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#fab). @@ -1467,7 +1467,7 @@ Renames the `fade` style utility import and calls to `alpha`. <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/fade-rename-alpha <path> +npx @mui/codemod@next v5.0.0/fade-rename-alpha <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#styles). @@ -1484,7 +1484,7 @@ Renames `Grid`'s `justify` prop to `justifyContent`. <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/grid-justify-justifycontent <path> +npx @mui/codemod@next v5.0.0/grid-justify-justifycontent <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#grid). @@ -1494,7 +1494,7 @@ You can find more details about this breaking change in [the migration guide](ht Renames `GridList*` to `ImageList*` ```bash -npx @mui/codemod@latest v5.0.0/grid-list-component <path> +npx @mui/codemod@next v5.0.0/grid-list-component <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#gridlist). @@ -1511,7 +1511,7 @@ Adds `size="large"` if `size` is not defined to get the same appearance as v4. ``` ```bash -npx @mui/codemod@latest v5.0.0/icon-button-size <path> +npx @mui/codemod@next v5.0.0/icon-button-size <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#iconbutton). @@ -1576,7 +1576,7 @@ Replace JSS styling with `makeStyles` or `withStyles` to `styled` API. ``` ```bash -npx @mui/codemod@latest v5.0.0/jss-to-styled <path> +npx @mui/codemod@next v5.0.0/jss-to-styled <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#1-use-styled-or-sx-api). @@ -1648,20 +1648,20 @@ Migrate JSS styling with `makeStyles` or `withStyles` to the corresponding `tss- ``` ```bash -npx @mui/codemod@latest v5.0.0/jss-to-tss-react <path> +npx @mui/codemod@next v5.0.0/jss-to-tss-react <path> ``` The following scenarios are not currently handled by this codemod and will be marked with a "TODO jss-to-tss-react codemod" comment: -- If the hook returned by `makeStyles` (e.g. `useStyles`) is exported and used in another file, +- If the hook returned by `makeStyles` (for example `useStyles`) is exported and used in another file, the usages in other files will not be converted. - Arrow functions as the value for a CSS prop will not be converted. Arrow functions **are** supported at the rule level, though with some caveats listed below. - In order for arrow functions at the rule level to be converted, the parameter must use object - destructuring (e.g. `root: ({color, padding}) => (...)`). If the parameter is not destructured - (e.g. `root: (props) => (...)`), it will not be converted. -- If an arrow function at the rule level contains a code block (i.e. contains an explicit `return` + destructuring (for example `root: ({color, padding}) => (...)`). If the parameter is not destructured + (for example `root: (props) => (...)`), it will not be converted. +- If an arrow function at the rule level contains a code block (that is contains an explicit `return` statement) rather than just an object expression, it will not be converted. You can find more details about migrating from JSS to tss-react in [the migration guide](https://mui.com/material-ui/migration/migrating-from-jss/#2-use-tss-react). @@ -1676,7 +1676,7 @@ Apply `underline="hover"` to `<Link />` that does not define `underline` prop (t ``` ```bash -npx @mui/codemod@latest v5.0.0/link-underline-hover <path> +npx @mui/codemod@next v5.0.0/link-underline-hover <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#link). @@ -1714,7 +1714,7 @@ Moves JSS imports to `@material-ui/styles` ``` ```bash -npx @mui/codemod@latest v5.0.0/material-ui-styles <path> +npx @mui/codemod@next v5.0.0/material-ui-styles <path> ``` #### `material-ui-types` @@ -1727,7 +1727,7 @@ Renames `Omit` import from `@material-ui/types` to `DistributiveOmit` ``` ```bash -npx @mui/codemod@latest v5.0.0/material-ui-types <path> +npx @mui/codemod@next v5.0.0/material-ui-types <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#material-ui-types). @@ -1744,7 +1744,7 @@ Removes `disableBackdropClick` and `onEscapeKeyDown` from `<Modal>` ``` ```bash -npx @mui/codemod@latest v5.0.0/modal-props <path> +npx @mui/codemod@next v5.0.0/modal-props <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#modal). @@ -1768,7 +1768,7 @@ or <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/moved-lab-modules <path> +npx @mui/codemod@next v5.0.0/moved-lab-modules <path> ``` You can find more details about this breaking change in the migration guide. @@ -1793,7 +1793,7 @@ Renames `Pagination*`'s `shape` values from 'round' to 'circular'. ``` ```bash -npx @mui/codemod@latest v5.0.0/pagination-round-circular <path> +npx @mui/codemod@next v5.0.0/pagination-round-circular <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#pagination). @@ -1812,7 +1812,7 @@ Fix private import paths. <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/optimal-imports <path> +npx @mui/codemod@next v5.0.0/optimal-imports <path> ``` #### `root-ref` @@ -1820,7 +1820,7 @@ npx @mui/codemod@latest v5.0.0/optimal-imports <path> Removes `RootRef` from the codebase. ```bash -npx @mui/codemod@latest v5.0.0/root-ref <path> +npx @mui/codemod@next v5.0.0/root-ref <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#rootref). @@ -1835,7 +1835,7 @@ You can find more details about this breaking change in [the migration guide](ht ``` ```bash -npx @mui/codemod@latest v5.0.0/skeleton-variant <path> +npx @mui/codemod@next v5.0.0/skeleton-variant <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#skeleton). @@ -1845,7 +1845,7 @@ You can find more details about this breaking change in [the migration guide](ht Applies `StyledEngineProvider` to the files that contains `ThemeProvider`. ```bash -npx @mui/codemod@latest v5.0.0/styled-engine-provider <path> +npx @mui/codemod@next v5.0.0/styled-engine-provider <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-style-changes/#style-library). @@ -1871,7 +1871,7 @@ Renames props in `Table*` components. ``` ```bash -npx @mui/codemod@latest v5.0.0/table-props <path> +npx @mui/codemod@next v5.0.0/table-props <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#table). @@ -1890,7 +1890,7 @@ Renames the `Tabs`'s `scrollButtons` prop values. ``` ```bash -npx @mui/codemod@latest v5.0.0/tabs-scroll-buttons <path> +npx @mui/codemod@next v5.0.0/tabs-scroll-buttons <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#tabs). @@ -1909,7 +1909,7 @@ Renames `TextField`'s rows props. ``` ```bash -npx @mui/codemod@latest v5.0.0/textarea-minmax-rows <path> +npx @mui/codemod@next v5.0.0/textarea-minmax-rows <path> ``` You can find more details about this breaking change in the migration guide. @@ -1922,7 +1922,7 @@ You can find more details about this breaking change in the migration guide. Adds `DefaultTheme` module augmentation to TypeScript projects. ```bash -npx @mui/codemod@latest v5.0.0/theme-augment <path> +npx @mui/codemod@next v5.0.0/theme-augment <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#material-ui-styles). @@ -1941,7 +1941,7 @@ Updates breakpoint values to match new logic. ⚠️ This mod is not idempotent, <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/theme-breakpoints <path> +npx @mui/codemod@next v5.0.0/theme-breakpoints <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#theme). @@ -1951,7 +1951,7 @@ You can find more details about this breaking change in [the migration guide](ht Renames `theme.breakpoints.width('md')` to `theme.breakpoints.values.md`. ```bash -npx @mui/codemod@latest v5.0.0/theme-breakpoints-width <path> +npx @mui/codemod@next v5.0.0/theme-breakpoints-width <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#theme). @@ -1964,7 +1964,7 @@ You can find more details about this breaking change in [the migration guide](ht ``` ```bash -npx @mui/codemod@latest v5.0.0/theme-options <path> +npx @mui/codemod@next v5.0.0/theme-options <path> ``` #### `theme-palette-mode` @@ -1986,7 +1986,7 @@ Renames `type` to `mode`. ``` ```bash -npx @mui/codemod@latest v5.0.0/theme-palette-mode <path> +npx @mui/codemod@next v5.0.0/theme-palette-mode <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#theme). @@ -1996,7 +1996,7 @@ You can find more details about this breaking change in [the migration guide](ht Renames `MuiThemeProvider` to `ThemeProvider`. ```bash -npx @mui/codemod@latest v5.0.0/theme-provider <path> +npx @mui/codemod@next v5.0.0/theme-provider <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#material-ui-core-styles). @@ -2015,7 +2015,7 @@ Removes the 'px' suffix from some template strings. <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/theme-spacing <path> +npx @mui/codemod@next v5.0.0/theme-spacing <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#theme). @@ -2030,7 +2030,7 @@ Renames `theme.typography.round($number)` to `Math.round($number * 1e5) / 1e5`. ``` ```bash -npx @mui/codemod@latest v5.0.0/theme-typography-round <path> +npx @mui/codemod@next v5.0.0/theme-typography-round <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#theme). @@ -2048,7 +2048,7 @@ Converts all `@mui/material` submodule imports to the root module: <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/top-level-imports <path> +npx @mui/codemod@next v5.0.0/top-level-imports <path> ``` Head to https://mui.com/guides/minimizing-bundle-size/ to understand when it's useful. @@ -2058,7 +2058,7 @@ Head to https://mui.com/guides/minimizing-bundle-size/ to understand when it's u Renames import `transitions` to `createTransitions` ```bash -npx @mui/codemod@latest v5.0.0/transitions <path> +npx @mui/codemod@next v5.0.0/transitions <path> ``` #### `use-autocomplete` @@ -2071,7 +2071,7 @@ Renames `useAutocomplete` related import from lab to core ``` ```bash -npx @mui/codemod@latest v5.0.0/use-autocomplete <path> +npx @mui/codemod@next v5.0.0/use-autocomplete <path> ``` #### `use-transitionprops` @@ -2100,7 +2100,7 @@ Updates Dialog, Menu, Popover, and Snackbar to use the `TransitionProps` prop to <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/use-transitionprops <path> +npx @mui/codemod@next v5.0.0/use-transitionprops <path> ``` You can find more details about this breaking change in [the migration guide](/material-ui/migration/v5-component-changes/#dialog). @@ -2130,7 +2130,7 @@ The diff should look like this: <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v5.0.0/variant-prop <path> +npx @mui/codemod@next v5.0.0/variant-prop <path> ``` #### `with-mobile-dialog` @@ -2144,7 +2144,7 @@ Removes imported `withMobileDialog`, and inserts hardcoded version to prevent ap ``` ```bash -npx @mui/codemod@latest v5.0.0/with-mobile-dialog <path> +npx @mui/codemod@next v5.0.0/with-mobile-dialog <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-component-changes/#dialog). @@ -2160,7 +2160,7 @@ Removes `withWidth` import, and inserts hardcoded version to prevent application ``` ```bash -npx @mui/codemod@latest v5.0.0/with-width <path> +npx @mui/codemod@next v5.0.0/with-width <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/v5-style-changes/#material-ui-core-styles). @@ -2221,7 +2221,7 @@ Replace every occurrence of `material-ui` related package with the new package n ``` ```bash -npx @mui/codemod@latest v5.0.0/mui-replace <path> +npx @mui/codemod@next v5.0.0/mui-replace <path> ``` You can find more details about this breaking change in [the migration guide](https://mui.com/material-ui/migration/migration-v4/#update-material-ui-version). @@ -2241,7 +2241,7 @@ The diff should look like this: <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v4.0.0/theme-spacing-api <path> +npx @mui/codemod@next v4.0.0/theme-spacing-api <path> ``` This codemod tries to perform a basic expression simplification which can be improved for expressions that use more than one operation. @@ -2268,7 +2268,7 @@ Converts all `@material-ui/core` imports more than 1 level deep to the optimal f <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v4.0.0/optimal-imports <path> +npx @mui/codemod@next v4.0.0/optimal-imports <path> ``` Head to https://mui.com/guides/minimizing-bundle-size/ to understand when it's useful. @@ -2286,7 +2286,7 @@ Converts all `@material-ui/core` submodule imports to the root module: <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v4.0.0/top-level-imports <path> +npx @mui/codemod@next v4.0.0/top-level-imports <path> ``` Head to https://mui.com/guides/minimizing-bundle-size/ to understand when it's useful. @@ -2307,7 +2307,7 @@ The diff should look like this: <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v1.0.0/import-path <path> +npx @mui/codemod@next v1.0.0/import-path <path> ``` **Notice**: if you are migrating from pre-v1.0, and your imports use `material-ui`, you will need to manually find and replace all references to `material-ui` in your code to `@material-ui/core`. E.g.: @@ -2334,7 +2334,7 @@ The diff should look like this: <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v1.0.0/color-imports <path> +npx @mui/codemod@next v1.0.0/color-imports <path> ``` **additional options** @@ -2342,7 +2342,7 @@ npx @mui/codemod@latest v1.0.0/color-imports <path> <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v1.0.0/color-imports <path> -- --importPath='mui/styles/colors' --targetPath='mui/colors' +npx @mui/codemod@next v1.0.0/color-imports <path> -- --importPath='mui/styles/colors' --targetPath='mui/colors' ``` #### `svg-icon-imports` @@ -2360,7 +2360,7 @@ The diff should look like this: <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v1.0.0/svg-icon-imports <path> +npx @mui/codemod@next v1.0.0/svg-icon-imports <path> ``` #### `menu-item-primary-text` @@ -2378,7 +2378,7 @@ The diff should look like this: <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v1.0.0/menu-item-primary-text <path> +npx @mui/codemod@next v1.0.0/menu-item-primary-text <path> ``` ### v0.15.0 @@ -2402,5 +2402,5 @@ The diff should look like this: <!-- #default-branch-switch --> ```bash -npx @mui/codemod@latest v0.15.0/import-path <path> +npx @mui/codemod@next v0.15.0/import-path <path> ``` diff --git a/packages/mui-codemod/package.json b/packages/mui-codemod/package.json index 1bbfcdfc94ac7d..0b8512b7042c33 100644 --- a/packages/mui-codemod/package.json +++ b/packages/mui-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@mui/codemod", - "version": "5.15.12", + "version": "5.15.14", "bin": "./codemod.js", "private": false, "author": "MUI Team", diff --git a/packages/mui-codemod/src/v5.0.0/jss-to-tss-react.js b/packages/mui-codemod/src/v5.0.0/jss-to-tss-react.js index bba9d5d01d1201..e49c244f619c60 100644 --- a/packages/mui-codemod/src/v5.0.0/jss-to-tss-react.js +++ b/packages/mui-codemod/src/v5.0.0/jss-to-tss-react.js @@ -111,7 +111,7 @@ function transformStylesExpression(j, comments, stylesExpression, nestedKeys, se if (prop.value.body.type === 'ObjectExpression') { let example = ''; if (prop.value.params[0].type === 'Identifier') { - example = ' (e.g. `(props) => ({...})` instead of `({color}) => ({...})`)'; + example = ' (for example `(props) => ({...})` instead of `({color}) => ({...})`)'; } extraComment = ` Arrow function has parameter type of ${prop.value.params[0].type} instead of ObjectPattern${example}.`; } else { @@ -367,7 +367,7 @@ export default function transformer(file, api, options) { }); }); /** - * Convert classes assignment syntax in calls to the hook (e.g. useStyles) and + * Convert classes assignment syntax in calls to the hook (for example useStyles) and * convert usages of clsx or classnames to cx. */ styleHooks.forEach((hookName) => { diff --git a/packages/mui-codemod/src/v5.0.0/jss-to-tss-react.test/expected-todo-comments.js b/packages/mui-codemod/src/v5.0.0/jss-to-tss-react.test/expected-todo-comments.js index e190d8f9e008cb..7c03ca65738aff 100644 --- a/packages/mui-codemod/src/v5.0.0/jss-to-tss-react.test/expected-todo-comments.js +++ b/packages/mui-codemod/src/v5.0.0/jss-to-tss-react.test/expected-todo-comments.js @@ -29,7 +29,7 @@ export const useExportedStyles = makeStyles()({ }); // TODO jss-to-tss-react codemod: Unable to handle style definition reliably. Unsupported arrow function syntax. -// Arrow function has parameter type of Identifier instead of ObjectPattern (e.g. `(props) => ({...})` instead of `({color}) => ({...})`). +// Arrow function has parameter type of Identifier instead of ObjectPattern (for example `(props) => ({...})` instead of `({color}) => ({...})`). const useStyles2 = makeStyles()({ test2: props => ({ backgroundColor: "blue", diff --git a/packages/mui-core-downloads-tracker/package.json b/packages/mui-core-downloads-tracker/package.json index 9d778304278211..8c2ff52575ebfe 100644 --- a/packages/mui-core-downloads-tracker/package.json +++ b/packages/mui-core-downloads-tracker/package.json @@ -1,6 +1,6 @@ { "name": "@mui/core-downloads-tracker", - "version": "5.15.12", + "version": "5.15.14", "private": false, "author": "MUI Team", "description": "Internal package to track number of downloads of our design system libraries", diff --git a/packages/mui-docs/package.json b/packages/mui-docs/package.json index cf0c7855e2caae..773cad1039afdb 100644 --- a/packages/mui-docs/package.json +++ b/packages/mui-docs/package.json @@ -1,6 +1,6 @@ { "name": "@mui/docs", - "version": "5.15.12", + "version": "5.15.14", "private": false, "author": "MUI Team", "description": "MUI Docs - Documentation building blocks.", @@ -29,7 +29,7 @@ "build:node": "node ../../scripts/build.mjs node", "build:stable": "node ../../scripts/build.mjs stable", "build:types": "node ../../scripts/buildTypes.mjs", - "build:copy-files": "node ../../scripts/copyFiles.mjs", + "build:copy-files": "node ../../scripts/copyFiles.mjs ./src/translations/translations.json:./translations/translations.json", "prebuild": "rimraf build", "release": "pnpm build && pnpm publish", "test": "exit 0" @@ -43,7 +43,7 @@ "prop-types": "^15.8.1" }, "devDependencies": { - "@types/node": "^18.19.21", + "@types/node": "^18.19.25", "@types/prop-types": "^15.7.11", "@types/react": "^18.2.55", "next": "^13.5.1", diff --git a/packages/mui-docs/src/translations/translations.json b/packages/mui-docs/src/translations/translations.json index 2cfd55015973b3..555c2b6f9306ee 100644 --- a/packages/mui-docs/src/translations/translations.json +++ b/packages/mui-docs/src/translations/translations.json @@ -62,7 +62,7 @@ "joy-variant": "To learn how to add your own variants, check out <a href='/joy-ui/customization/themed-components/#extend-variants'>Themed components—Extend variants</a>." } }, - "landingPageDescr": "A responsive landing page layout with many common sections.", + "landingPageDescr": "A responsive landing page layout with common sections found in marketing pages.", "landingPageTitle": "Landing page", "searchButton": "Search…", "algoliaSearch": "What are you looking for?", @@ -75,13 +75,13 @@ "toggleSettings": "Toggle settings drawer" }, "backToTop": "Scroll back to top", - "blogDescr": "A sophisticated blog page layout. Markdown support is courtesy of markdown-to-jsx.", + "blogDescr": "A polished blog page layout. Markdown support is courtesy of markdown-to-jsx.", "blogTitle": "Blog", "bundleSize": "Bundle size", "bundleSizeTooltip": "Scroll down to 'Exports Analysis' for a more detailed report.", "cancel": "Cancel", "cdn": "or use a CDN.", - "checkoutDescr": "A step-by-step checkout page layout. Adapt the number of steps to suit your needs, or make steps optional.", + "checkoutDescr": "A step-by-step checkout flow with an adaptable (or optional) number of steps.", "checkoutTitle": "Checkout", "clickToCopy": "Click to copy", "close": "Close", @@ -176,7 +176,7 @@ "showSource": "Show code", "showTSSource": "Show TypeScript source", "signInDescr": "A simple sign-in page using text fields, buttons, checkboxes, links, and more.", - "signInSideDescr": "A simple sign-in page with a two-column layout using text fields, buttons, and more.", + "signInSideDescr": "A sign-in page with a two-column layout using text fields, buttons, and more.", "signInSideTitle": "Sign-in side", "signInTitle": "Sign-in", "signUpDescr": "A simple sign-up page using text fields, buttons, checkboxes, links, and more.", diff --git a/packages/mui-envinfo/envinfo.js b/packages/mui-envinfo/envinfo.js index eb364007f7d53e..aca2a91e4226e4 100755 --- a/packages/mui-envinfo/envinfo.js +++ b/packages/mui-envinfo/envinfo.js @@ -26,7 +26,7 @@ envinfo // `markdown: true` uses level 2 headings. It doesn't necessarily fit our issue template json, duplicates: true, - // include transitive dependencies and important packages that are transitive dependencies (e.g. `jsdom` is usually a transitive dependency inside jest) + // include transitive dependencies and important packages that are transitive dependencies (for example `jsdom` is usually a transitive dependency inside jest) fullTree: true, showNotFound: true, }, diff --git a/packages/mui-envinfo/package.json b/packages/mui-envinfo/package.json index e5ce3a88cc3bb9..32ddd5668876cf 100644 --- a/packages/mui-envinfo/package.json +++ b/packages/mui-envinfo/package.json @@ -1,6 +1,6 @@ { "name": "@mui/envinfo", - "version": "2.0.18", + "version": "2.0.19", "private": false, "author": "MUI Team", "description": "Logs infos about the environment relevant to @mui/*", diff --git a/packages/mui-icons-material/README.md b/packages/mui-icons-material/README.md index cf532bffeb018a..cf7d2d539d0db7 100644 --- a/packages/mui-icons-material/README.md +++ b/packages/mui-icons-material/README.md @@ -9,17 +9,19 @@ Install the package in your project directory with: <!-- #default-branch-switch --> ```bash -npm install @mui/icons-material +npm install @mui/icons-material@next ``` <!-- #default-branch-switch --> -These components use the Material UI's [SvgIcon](https://mui.com/material-ui/api/svg-icon) component to render the SVG path for each icon. +These components use the Material UI's [SvgIcon](https://next.mui.com/material-ui/api/svg-icon) component to render the SVG path for each icon. If you are not already using Material UI in your project, you can add it with: +<!-- #default-branch-switch --> + ```bash -npm install @mui/material +npm install @mui/material@next ``` ## Documentation diff --git a/packages/mui-icons-material/builder.md b/packages/mui-icons-material/builder.md index c3cfee1fef8f7b..d13b2d1c4ff210 100644 --- a/packages/mui-icons-material/builder.md +++ b/packages/mui-icons-material/builder.md @@ -28,7 +28,7 @@ through command line options. - `--output-dir` - Directory to output generated components. - `--svg-dir` - Directory containing the source SVG icons. - `--inner-path` - "Reach into" subdirs, since libraries like material-design-icons - use arbitrary build directories to organize icons, e.g. "action/svg/production/". + use arbitrary build directories to organize icons, for example "action/svg/production/". - `--file-suffix` - Process only files ending with the specified suffix/ - `--rename-filter` - Apply a custom filter to rename the generated icons. The default and Material Design filters can be found in `filters/rename`. diff --git a/packages/mui-icons-material/package.json b/packages/mui-icons-material/package.json index 7adc8f786fe252..7e444974b82cec 100644 --- a/packages/mui-icons-material/package.json +++ b/packages/mui-icons-material/package.json @@ -1,6 +1,6 @@ { "name": "@mui/icons-material", - "version": "5.15.12", + "version": "5.15.14", "private": false, "author": "MUI Team", "description": "Material Design icons distributed as SVG React components.", diff --git a/packages/mui-icons-material/scripts/download.mjs b/packages/mui-icons-material/scripts/download.mjs index 88dd8f5d5732ed..43f4244d6db771 100644 --- a/packages/mui-icons-material/scripts/download.mjs +++ b/packages/mui-icons-material/scripts/download.mjs @@ -10,7 +10,7 @@ const currentDirectory = fileURLToPath(new URL('.', import.meta.url)); // Icons we don't publish. // This is just a list of new icons. -// In the future we might change what icons we want to exclude (e.g. by popularity) +// In the future we might change what icons we want to exclude (for example by popularity) const ignoredIconNames = new Set([ // TODO v6: Whatsapp duplicates with WhatsApp // We don't need it https://fonts.google.com/icons?icon.set=Material+Icons&icon.query=whatsapp diff --git a/packages/mui-icons-material/test/generated-types/tsconfig.json b/packages/mui-icons-material/test/generated-types/tsconfig.json index 1099b243812bac..7d97d6b4ec2a58 100644 --- a/packages/mui-icons-material/test/generated-types/tsconfig.json +++ b/packages/mui-icons-material/test/generated-types/tsconfig.json @@ -23,8 +23,6 @@ "@mui/lab/*": ["./mui-lab/src/*"], "@mui/internal-markdown": ["./markdown"], "@mui/internal-markdown/*": ["./markdown/*"], - "@mui/material-next": ["./mui-material-next/src"], - "@mui/material-next/*": ["./mui-material-next/src/*"], "@mui/material-nextjs": ["./mui-material-nextjs/src"], "@mui/material-nextjs/*": ["./mui-material-nextjs/src/*"], "@mui/material": ["./mui-material/src"], @@ -42,12 +40,12 @@ "@mui/types": ["./mui-types"], "@mui/utils": ["./mui-utils/src"], "@mui/utils/*": ["./mui-utils/src/*"], - "@pigment-css/nextjs-plugin": ["./pigment-nextjs-plugin/src"], - "@pigment-css/nextjs-plugin/*": ["./pigment-nextjs-plugin/src/*"], - "@pigment-css/react": ["./pigment-react/src"], - "@pigment-css/react/*": ["./pigment-react/src/*"], - "@pigment-css/vite-plugin": ["./pigment-vite-plugin/src"], - "@pigment-css/vite-plugin/*": ["./pigment-vite-plugin/src/*"], + "@pigment-css/nextjs-plugin": ["./pigment-css-nextjs-plugin/src"], + "@pigment-css/nextjs-plugin/*": ["./pigment-css-nextjs-plugin/src/*"], + "@pigment-css/react": ["./pigment-css-react/src"], + "@pigment-css/react/*": ["./pigment-css-react/src/*"], + "@pigment-css/vite-plugin": ["./pigment-css-vite-plugin/src"], + "@pigment-css/vite-plugin/*": ["./pigment-css-vite-plugin/src/*"], "@mui/internal-scripts/typescript-to-proptypes": [ "../packages-internal/scripts/typescript-to-proptypes/src" ] diff --git a/packages/mui-joy/README.md b/packages/mui-joy/README.md index a69c37d192cbba..4921f8a288f6af 100644 --- a/packages/mui-joy/README.md +++ b/packages/mui-joy/README.md @@ -26,14 +26,14 @@ Use the "joy-ui" tag on Stack Overflow to make it easier for the community to f ## Examples -Our documentation features [a collection of example projects using Joy UI](https://github.com/mui/material-ui/tree/master/examples). +The documentation features [a collection of example projects using Joy UI](https://github.com/mui/material-ui/tree/master/examples). ## Contributing -Read the [contributing guide](/CONTRIBUTING.md) to learn about our development process, how to propose bug fixes and improvements, and how to build and test your changes. +Read the [contributing guide](/CONTRIBUTING.md) to learn about the development process, how to propose bug fixes and improvements, and how to build and test your changes. Contributing to Joy UI is about more than just issues and pull requests! -There are many other ways to [support MUI](https://mui.com/material-ui/getting-started/faq/#mui-is-awesome-how-can-i-support-the-project) beyond contributing to the code base. +There are many other ways to [support Joy UI](https://mui.com/material-ui/getting-started/faq/#mui-is-awesome-how-can-i-support-the-project) beyond contributing to the code base. ## Changelog @@ -41,7 +41,7 @@ The [changelog](https://github.com/mui/material-ui/releases) is regularly update ## Roadmap -Future plans and high-priority features and enhancements can be found in our [roadmap](https://mui.com/material-ui/discover-more/roadmap/). +Future plans and high-priority features and enhancements can be found in the [roadmap](https://mui.com/material-ui/discover-more/roadmap/). ## License diff --git a/packages/mui-joy/package.json b/packages/mui-joy/package.json index 170fb702806512..9501e9774a071a 100644 --- a/packages/mui-joy/package.json +++ b/packages/mui-joy/package.json @@ -1,6 +1,6 @@ { "name": "@mui/joy", - "version": "5.0.0-beta.30", + "version": "5.0.0-beta.32", "private": false, "author": "MUI Team", "description": "Joy UI is an open-source React component library that implements MUI's own design principles. It's comprehensive and can be used in production out of the box.", diff --git a/packages/mui-joy/src/Autocomplete/Autocomplete.tsx b/packages/mui-joy/src/Autocomplete/Autocomplete.tsx index 696feacf1ea575..dbbb1631332c72 100644 --- a/packages/mui-joy/src/Autocomplete/Autocomplete.tsx +++ b/packages/mui-joy/src/Autocomplete/Autocomplete.tsx @@ -211,7 +211,7 @@ const AutocompleteListbox = styled(StyledAutocompleteListbox, { slot: 'Listbox', overridesResolver: (props, styles) => styles.listbox, })<{ ownerState: OwnerState }>(({ theme }) => ({ - // `unstable_popup-zIndex` is a private variable that lets other component, e.g. Modal, to override the z-index so that the listbox can be displayed above the Modal. + // `unstable_popup-zIndex` is a private variable that lets other component, for example Modal, to override the z-index so that the listbox can be displayed above the Modal. zIndex: `var(--unstable_popup-zIndex, ${theme.vars.zIndex.popup})`, })); @@ -986,7 +986,7 @@ Autocomplete.propTypes /* remove-proptypes */ = { limitTags: integerPropType, /** * If `true`, the component is in a loading state. - * This shows the `loadingText` in place of suggestions (only if there are no suggestions to show, e.g. `options` are empty). + * This shows the `loadingText` in place of suggestions (only if there are no suggestions to show, for example `options` are empty). * @default false */ loading: PropTypes.bool, diff --git a/packages/mui-joy/src/Autocomplete/AutocompleteProps.ts b/packages/mui-joy/src/Autocomplete/AutocompleteProps.ts index b9c370d4d31636..94da1fc39386c3 100644 --- a/packages/mui-joy/src/Autocomplete/AutocompleteProps.ts +++ b/packages/mui-joy/src/Autocomplete/AutocompleteProps.ts @@ -242,7 +242,7 @@ type AutocompleteOwnProps< getLimitTagsText?: (more: string | number) => React.ReactNode; /** * If `true`, the component is in a loading state. - * This shows the `loadingText` in place of suggestions (only if there are no suggestions to show, e.g. `options` are empty). + * This shows the `loadingText` in place of suggestions (only if there are no suggestions to show, for example `options` are empty). * @default false */ loading?: boolean; diff --git a/packages/mui-joy/src/Button/Button.tsx b/packages/mui-joy/src/Button/Button.tsx index ebc5647bafddaa..75f3d037825f8d 100644 --- a/packages/mui-joy/src/Button/Button.tsx +++ b/packages/mui-joy/src/Button/Button.tsx @@ -118,7 +118,7 @@ export const getButtonStyles = ({ '--Button-gap': '0.5rem', minHeight: 'var(--Button-minHeight, 2.25rem)', // use min-height instead of height to make the button resilient to its content fontSize: theme.vars.fontSize.sm, - // internal --Button-paddingBlock is used to control the padding-block of the button from the outside, e.g. as a decorator of an Input + // internal --Button-paddingBlock is used to control the padding-block of the button from the outside, for example as a decorator of an Input paddingBlock: 'var(--Button-paddingBlock, 0.375rem)', // the padding-block act as a minimum spacing between content and root element paddingInline: '1rem', }), @@ -134,8 +134,8 @@ export const getButtonStyles = ({ }), WebkitTapHighlightColor: 'transparent', boxSizing: 'border-box', - borderRadius: `var(--Button-radius, ${theme.vars.radius.sm})`, // to be controlled by other components, e.g. Input - margin: `var(--Button-margin)`, // to be controlled by other components, e.g. Input + borderRadius: `var(--Button-radius, ${theme.vars.radius.sm})`, // to be controlled by other components, for example Input + margin: `var(--Button-margin)`, // to be controlled by other components, for example Input border: 'none', backgroundColor: 'transparent', cursor: 'pointer', diff --git a/packages/mui-joy/src/Chip/Chip.tsx b/packages/mui-joy/src/Chip/Chip.tsx index 429ede5c9f95d4..2c1ad24b5447e4 100644 --- a/packages/mui-joy/src/Chip/Chip.tsx +++ b/packages/mui-joy/src/Chip/Chip.tsx @@ -190,7 +190,7 @@ const ChipStartDecorator = styled('span', { '0 calc(-1 * var(--Chip-paddingInline) / 3) 0 calc(var(--Chip-decoratorChildOffset) * -1)', '--Icon-margin': '0 0 0 calc(var(--Chip-paddingInline) / -4)', display: 'inherit', - // set zIndex to 1 with order to stay on top of other controls, e.g. Checkbox, Radio + // set zIndex to 1 with order to stay on top of other controls, for example Checkbox, Radio order: 0, zIndex: 1, pointerEvents: 'none', @@ -205,7 +205,7 @@ const ChipEndDecorator = styled('span', { '0 calc(var(--Chip-decoratorChildOffset) * -1) 0 calc(-1 * var(--Chip-paddingInline) / 3)', '--Icon-margin': '0 calc(var(--Chip-paddingInline) / -4) 0 0', display: 'inherit', - // set zIndex to 1 with order to stay on top of other controls, e.g. Checkbox, Radio + // set zIndex to 1 with order to stay on top of other controls, for example Checkbox, Radio order: 2, zIndex: 1, pointerEvents: 'none', @@ -318,7 +318,7 @@ const Chip = React.forwardRef(function Chip(inProps, ref) { <SlotRoot {...rootProps}> {clickable && <SlotAction {...actionProps} />} - {/* label is always the first element for integrating with other controls, e.g. Checkbox, Radio. Use CSS order to rearrange position */} + {/* label is always the first element for integrating with other controls, for example Checkbox, Radio. Use CSS order to rearrange position */} <SlotLabel {...labelProps} id={id}> {children} </SlotLabel> diff --git a/packages/mui-joy/src/ChipDelete/ChipDelete.tsx b/packages/mui-joy/src/ChipDelete/ChipDelete.tsx index 914b8a7f802b83..d92bf1eff7b71e 100644 --- a/packages/mui-joy/src/ChipDelete/ChipDelete.tsx +++ b/packages/mui-joy/src/ChipDelete/ChipDelete.tsx @@ -40,7 +40,7 @@ const ChipDeleteRoot = styled(StyledIconButton as unknown as 'button', { minWidth: 'var(--IconButton-size, 2rem)', // use min-width instead of height to make the button resilient to its content minHeight: 'var(--IconButton-size, 2rem)', // use min-height instead of height to make the button resilient to its content fontSize: theme.vars.fontSize.sm, - paddingInline: '2px', // add a gap, in case the content is long, e.g. multiple icons + paddingInline: '2px', // add a gap, in case the content is long, for example multiple icons pointerEvents: 'visible', // force the ChipDelete to be hoverable because the decorator can have pointerEvents 'none' borderRadius: 'var(--Chip-deleteRadius, 50%)', zIndex: 1, // overflow above sibling button or anchor diff --git a/packages/mui-joy/src/FormControl/FormControl.tsx b/packages/mui-joy/src/FormControl/FormControl.tsx index aa27b875f52d7a..5562aaad8b76d2 100644 --- a/packages/mui-joy/src/FormControl/FormControl.tsx +++ b/packages/mui-joy/src/FormControl/FormControl.tsx @@ -72,7 +72,7 @@ export const FormControlRoot = styled('div', { '--FormHelperText-color': theme.variants.plainDisabled?.[ownerState.color || 'neutral']?.color, }, display: 'flex', - position: 'relative', // for keeping the control action area, e.g. Switch + position: 'relative', // for keeping the control action area, for example Switch flexDirection: ownerState.orientation === 'horizontal' ? 'row' : 'column', ...(ownerState.orientation === 'horizontal' && { [`& > label ~ .${switchClasses.root}`]: { diff --git a/packages/mui-joy/src/IconButton/IconButton.tsx b/packages/mui-joy/src/IconButton/IconButton.tsx index 5e0df7f9688d55..6d730c5c72c297 100644 --- a/packages/mui-joy/src/IconButton/IconButton.tsx +++ b/packages/mui-joy/src/IconButton/IconButton.tsx @@ -56,7 +56,7 @@ export const StyledIconButton = styled('button')<{ ownerState: IconButtonOwnerSt minWidth: 'var(--IconButton-size, 2rem)', // use min-width instead of height to make the button resilient to its content minHeight: 'var(--IconButton-size, 2rem)', // use min-height instead of height to make the button resilient to its content fontSize: theme.vars.fontSize.sm, - paddingInline: '2px', // add a gap, in case the content is long, e.g. multiple icons + paddingInline: '2px', // add a gap, in case the content is long, for example multiple icons }), ...(ownerState.size === 'md' && { '--Icon-fontSize': 'calc(var(--IconButton-size, 2.25rem) / 1.5)', // 1.5rem by default @@ -80,8 +80,8 @@ export const StyledIconButton = styled('button')<{ ownerState: IconButtonOwnerSt paddingBlock: 0, fontFamily: theme.vars.fontFamily.body, fontWeight: theme.vars.fontWeight.md, - margin: `var(--IconButton-margin)`, // to be controlled by other components, e.g. Input - borderRadius: `var(--IconButton-radius, ${theme.vars.radius.sm})`, // to be controlled by other components, e.g. Input + margin: `var(--IconButton-margin)`, // to be controlled by other components, for example Input + borderRadius: `var(--IconButton-radius, ${theme.vars.radius.sm})`, // to be controlled by other components, for example Input border: 'none', boxSizing: 'border-box', backgroundColor: 'transparent', diff --git a/packages/mui-joy/src/ListItem/ListItem.tsx b/packages/mui-joy/src/ListItem/ListItem.tsx index b195625fd273d8..3e9eb9baba8cc0 100644 --- a/packages/mui-joy/src/ListItem/ListItem.tsx +++ b/packages/mui-joy/src/ListItem/ListItem.tsx @@ -65,7 +65,7 @@ export const StyledListItem = styled('li')<{ ownerState: ListItemOwnerState }>( } as const), // Base styles { - // Integration with control elements, e.g. Checkbox, Radio. + // Integration with control elements, for example Checkbox, Radio. '--unstable_actionRadius': 'calc(var(--ListItem-radius) - var(--variant-borderWidth, 0px))', ...(ownerState.startAction && { '--unstable_startActionWidth': '2rem', // to add sufficient padding-left on ListItemButton diff --git a/packages/mui-joy/src/Menu/Menu.tsx b/packages/mui-joy/src/Menu/Menu.tsx index 70ba44aff2c3d0..df041f440ec211 100644 --- a/packages/mui-joy/src/Menu/Menu.tsx +++ b/packages/mui-joy/src/Menu/Menu.tsx @@ -52,7 +52,7 @@ const MenuRoot = styled(StyledList, { borderRadius: `var(--List-radius, ${theme.vars.radius.sm})`, boxShadow: theme.shadow.md, overflow: 'auto', - // `unstable_popup-zIndex` is a private variable that lets other component, e.g. Modal, to override the z-index so that the listbox can be displayed above the Modal. + // `unstable_popup-zIndex` is a private variable that lets other component, for example Modal, to override the z-index so that the listbox can be displayed above the Modal. zIndex: `var(--unstable_popup-zIndex, ${theme.vars.zIndex.popup})`, ...(!variantStyle?.backgroundColor && { backgroundColor: theme.vars.palette.background.popup, diff --git a/packages/mui-joy/src/Select/Select.tsx b/packages/mui-joy/src/Select/Select.tsx index ea95ca4d6263c7..017fd87c2c348e 100644 --- a/packages/mui-joy/src/Select/Select.tsx +++ b/packages/mui-joy/src/Select/Select.tsx @@ -246,7 +246,7 @@ const SelectListbox = styled(StyledList, { outline: 0, boxShadow: theme.shadow.md, borderRadius: `var(--List-radius, ${theme.vars.radius.sm})`, - // `unstable_popup-zIndex` is a private variable that lets other component, e.g. Modal, to override the z-index so that the listbox can be displayed above the Modal. + // `unstable_popup-zIndex` is a private variable that lets other component, for example Modal, to override the z-index so that the listbox can be displayed above the Modal. zIndex: `var(--unstable_popup-zIndex, ${theme.vars.zIndex.popup})`, ...(!variantStyle?.backgroundColor && { backgroundColor: theme.vars.palette.background.popup, diff --git a/packages/mui-joy/src/SvgIcon/SvgIcon.tsx b/packages/mui-joy/src/SvgIcon/SvgIcon.tsx index c559e595add814..80f09aef4b91d9 100644 --- a/packages/mui-joy/src/SvgIcon/SvgIcon.tsx +++ b/packages/mui-joy/src/SvgIcon/SvgIcon.tsx @@ -46,7 +46,7 @@ const SvgIconRoot = styled('svg', { height: '1em', display: 'inline-block', // the <svg> will define the property that has `currentColor` - // e.g. heroicons uses fill="none" and stroke="currentColor" + // for example heroicons uses fill="none" and stroke="currentColor" fill: ownerState.hasSvgAsChild ? undefined : 'currentColor', flexShrink: 0, fontSize: `var(--Icon-fontSize, ${theme.vars.fontSize[sizeMap[ownerState.size!]] || 'unset'})`, diff --git a/packages/mui-joy/src/Tooltip/Tooltip.test.tsx b/packages/mui-joy/src/Tooltip/Tooltip.test.tsx index 72d63129e669b4..7a277704e347dd 100644 --- a/packages/mui-joy/src/Tooltip/Tooltip.test.tsx +++ b/packages/mui-joy/src/Tooltip/Tooltip.test.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; +import { spy } from 'sinon'; +import { createRenderer, act } from '@mui-internal/test-utils'; import { unstable_capitalize as capitalize } from '@mui/utils'; import { PopperProps } from '@mui/base'; import { ThemeProvider } from '@mui/joy/styles'; @@ -130,4 +131,51 @@ describe('<Tooltip />', () => { }); }); }); + + describe('focus', () => { + // https://github.com/mui/mui-x/issues/12248 + it('should support event handlers with extra parameters', () => { + const handleFocus = spy((event, extra) => extra); + const handleBlur = spy((event, ...params) => params); + + const TextField = React.forwardRef< + HTMLDivElement, + { + onFocus: (event: React.FocusEvent, ...params: any[]) => void; + onBlur: (event: React.FocusEvent, ...params: any[]) => void; + } + >(function TextField(props, ref) { + const { onFocus, onBlur, ...other } = props; + return ( + <div ref={ref} {...other}> + <input + type="text" + onFocus={(event) => onFocus(event, 'focus')} + onBlur={(event) => onBlur(event, 'blur', 1)} + /> + </div> + ); + }); + const { getByRole } = render( + <Tooltip open title="test"> + <TextField onFocus={handleFocus} onBlur={handleBlur} /> + </Tooltip>, + ); + const input = getByRole('textbox'); + + act(() => { + input.focus(); + }); + + expect(handleFocus.callCount).to.equal(1); + expect(handleFocus.returnValues[0]).to.equal('focus'); + + act(() => { + input.blur(); + }); + + expect(handleBlur.callCount).to.equal(1); + expect(handleBlur.returnValues[0]).to.deep.equal(['blur', 1]); + }); + }); }); diff --git a/packages/mui-joy/src/Tooltip/Tooltip.tsx b/packages/mui-joy/src/Tooltip/Tooltip.tsx index d8ca461789e0b6..531d3a8b288c1b 100644 --- a/packages/mui-joy/src/Tooltip/Tooltip.tsx +++ b/packages/mui-joy/src/Tooltip/Tooltip.tsx @@ -179,14 +179,14 @@ function composeMouseEventHandler( } function composeFocusEventHandler( - handler: (event: React.FocusEvent<HTMLElement>) => void, - eventHandler: (event: React.FocusEvent<HTMLElement>) => void, + handler: (event: React.FocusEvent<HTMLElement>, ...params: any[]) => void, + eventHandler: (event: React.FocusEvent<HTMLElement>, ...params: any[]) => void, ) { - return (event: React.FocusEvent<HTMLElement>) => { + return (event: React.FocusEvent<HTMLElement>, ...params: any[]) => { if (eventHandler) { - eventHandler(event); + eventHandler(event, ...params); } - handler(event); + handler(event, ...params); }; } /** diff --git a/packages/mui-joy/src/Typography/Typography.tsx b/packages/mui-joy/src/Typography/Typography.tsx index 6e22027df5ad1f..e77423e8c6400e 100644 --- a/packages/mui-joy/src/Typography/Typography.tsx +++ b/packages/mui-joy/src/Typography/Typography.tsx @@ -30,7 +30,7 @@ if (process.env.NODE_ENV !== 'production') { * @internal * Typography's level will be inherit within this context unless an explicit `level` prop is provided. * - * This is used in components, e.g. Table, to inherit the parent's size by default. + * This is used in components, for example Table, to inherit the parent's size by default. */ export const TypographyInheritContext = React.createContext(false); diff --git a/packages/mui-joy/src/styles/types/theme.ts b/packages/mui-joy/src/styles/types/theme.ts index 8bc87c637edd05..c5a711544383e3 100644 --- a/packages/mui-joy/src/styles/types/theme.ts +++ b/packages/mui-joy/src/styles/types/theme.ts @@ -87,7 +87,7 @@ export interface ThemeVars extends ThemeScales, ColorSystemVars {} export interface ThemeCssVarOverrides {} /** - * For providing `sx` autocomplete, e.g. `color`, `bgcolor`, `borderColor`. + * For providing `sx` autocomplete, for example `color`, `bgcolor`, `borderColor`. */ export type TextColor = | NormalizeVars<Omit<ColorSystem['palette'], 'mode'>, '.'> diff --git a/packages/mui-lab/README.md b/packages/mui-lab/README.md index 3a38ed547576ff..09600169c250d9 100644 --- a/packages/mui-lab/README.md +++ b/packages/mui-lab/README.md @@ -9,7 +9,7 @@ Install the package in your project directory with: <!-- #default-branch-switch --> ```bash -npm install @mui/lab +npm install @mui/lab@next ``` The lab has peer dependencies on the Material Design components and on the Emotion library. @@ -18,11 +18,11 @@ If you are not already using them in your project, you can install with: <!-- #default-branch-switch --> ```bash -npm install @mui/material @emotion/react @emotion/styled +npm install @mui/material@next @emotion/react @emotion/styled ``` ## Documentation <!-- #default-branch-switch --> -Visit [https://mui.com/material-ui/about-the-lab/](https://mui.com/material-ui/about-the-lab/) to view the full documentation. +Visit [https://next.mui.com/material-ui/about-the-lab/](https://next.mui.com/material-ui/about-the-lab/) to view the full documentation. diff --git a/packages/mui-lab/package.json b/packages/mui-lab/package.json index 122c54a1b04020..0bb25dd837357b 100644 --- a/packages/mui-lab/package.json +++ b/packages/mui-lab/package.json @@ -1,6 +1,6 @@ { "name": "@mui/lab", - "version": "5.0.0-alpha.167", + "version": "5.0.0-alpha.169", "private": false, "author": "MUI Team", "description": "Laboratory for new MUI modules.", diff --git a/packages/mui-material-next/CONTRIBUTING.md b/packages/mui-material-next/CONTRIBUTING.md deleted file mode 100644 index 25505c81e5f22d..00000000000000 --- a/packages/mui-material-next/CONTRIBUTING.md +++ /dev/null @@ -1,34 +0,0 @@ -# Contributing - -The Material Design 3 components are targeted for v7, so they will be developed on the `material-next` package. - -The progress for each component will be tracked in a separate GitHub issue. If you wish to contribute to the migration go to a component's linked issue to see what tasks are missing (see progress tracker [here](https://github.com/mui/material-ui/issues/29345)). - -If the issue doesn't exist, create it, name it `[<ComponentName>][material-next] Add <ComponentName> component`, and assign @DiegoAndai. The issue has to contain (at least) a task for each of the steps below (use the [Slider issue](https://github.com/mui/material-ui/issues/37527) as a template). - -## Steps - -1. Copy component files from `material` to `material-next`, including tests, types, and utils. Keep in mind to: - - Add component export to `packages/mui-material-next/src/index.ts` - - Change imports from `@mui/material` to `@mui/material-next` - - If there are utils that don't exist in `material-next`, add them by copying from `material` - - Some utils imported from `../utils` might already exist in `@mui/utils`, if so, use the latter -2. Migrate component to Typescript. The extension `.d.ts` should be replaced with `.types.ts` (except for `index.d.ts` which won't be necessary) -3. Remove deprecated `components` (note the plural `s`, it's not the same as the `component` prop) and `componentsProps` props, replacing them with `slots` and `slotProps` -4. Drop support for `ThemeProvider` in favor of `CssVarsProvider`. In practice, this means: - - Consuming tokens from `theme.vars` instead of `theme` - - In tests, using `CssVarsProvider` and `extendTheme` (both imported from `@mui/material-next/styles`) instead of `ThemeProvider` and `createTheme`, as well as providing the same `CssVarsProvier` and `extendTheme` to `describeConformance`'s `ThemeProvider` and `createTheme` options. -5. Implement M3 design specs. Add missing tokens if necessary. Use [material-web tokens](https://github.com/material-components/material-web/tree/main/tokens) as a reference for token values -6. Add component playground to the docs, take the [Slider playground](https://mui.com/material-ui/react-slider/#material-3-slider) as an example -7. Refactor styles to use component CSS Variables, following [material-web tokens](https://github.com/material-components/material-web/tree/main/tokens) and Joy UI's equivalent component (if it exists) as guides. - -## Other things to keep in mind - -- Except for the first step, there's no particular order to follow, but the proposed order has provided the best experience so far -- For every step, checking the components that are already in `material-next` will be really helpful -- Try to avoid breaking changes, keeping the component's API the same: - - An exception to this is to use M3 nomenclature and naming conventions, even if it would be a breaking change. - - If breaking changes are inevitable, then document them right away in `/packages/mui-material-next/migration.md` and add the `breaking change` label to your PR. -- Divide the work in whatever way makes more sense. One PR for a few steps or one PR for each step, however keep in mind that smaller pull requests will be reviewed and merged faster -- Let everyone know what you're working on so we can keep the work coordinated and avoid overlap -- Keep [running tests](https://github.com/mui/material-ui/blob/master/test/README.md) every step of the way, this will help to identify changes as soon as possible and avoid hard debugging sessions at the end diff --git a/packages/mui-material-next/README.md b/packages/mui-material-next/README.md deleted file mode 100644 index 3873ce85ca4d45..00000000000000 --- a/packages/mui-material-next/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# @mui/material-next - -[Material Design 3](https://m3.material.io/) components built using [`@mui/base`](https://mui.com/base-ui/getting-started/overview/) with TypeScript. - -This package is a nursery for components that will ultimately replace those found in `@mui/material`. - -Follow the [migration guide](/packages/mui-material-next/migration.md) to migrate from `@mui/material` to `@mui/material-next`. - -## Contributing - -Read the [contributing guide](/CONTRIBUTING.md) to learn about our development process, how to propose bug fixes and improvements, and how to build and test your changes. - -Contributing to Material UI is about more than just issues and pull requests! -There are many other ways to [support MUI](https://mui.com/material-ui/getting-started/faq/#mui-is-awesome-how-can-i-support-the-project) beyond contributing to the code base. diff --git a/packages/mui-material-next/migration.md b/packages/mui-material-next/migration.md deleted file mode 100644 index f409872beebee7..00000000000000 --- a/packages/mui-material-next/migration.md +++ /dev/null @@ -1,614 +0,0 @@ -# Migration - -This is a reference guide on how to migrate from @mui/material to @mui/material-next. - -## Breaking changes: components - -This section lists all breaking changes related to Material Design 3 (M3) components and how to address them. - -## Overarching changes - -These are the changes that apply to all components. - -### Remove `components` and `componentsProps` props - -The deprecated `components` and `componentsProps` props are removed in `@mui/material-next`. -If you were using these, then you can use `slots` and `slotProps` props instead, which have the same functionality and API. -Here's an example of the change using the Badge component: - -```diff - <Badge - badgeContent={4} -- components={{ badge: (props) => <span {...props}>slot test</span> }} -+ slots={{ badge: (props) => <span {...props}>slot test</span> }} -- componentsProps={{ badge: { id: "test-id" } }} -+ slotProps={{ badge: { id: "test-id" } }} - > - <MailIcon color="action" /> - </Badge> -``` - -### Remove composed CSS classes and `styleOverrides` keys - -Classes composed of two or more existing classes are removed in `@mui/material-next`. -For example, the `MuiChip-filledPrimary` class is removed in favor of the `MuiChip-filled` and `MuiChip-colorPrimary` classes. -Composed `styleOverrides` keys are also removed. -Following the example above, the chip component's `filledPrimary` `styleOverrides` key is removed. -The specific classes and keys removed are listed in each component's section, as well as instructions on how to replace them. - -## Badge - -### Changed default color - -The default value for the color prop was changed to `"error"`. - -### Removed the "default" color option - -The `"default"` option is no longer accepted for the color prop. - -### Removed combined anchor-overlap styleOverrides keys - -The following `styleOverrides` `MuiBadge` keys were removed: - -- `anchorOriginTopLeftCircular` -- `anchorOriginTopLeftRectangular` -- `anchorOriginTopRightCircular` -- `anchorOriginTopRightRectangular` -- `anchorOriginBottomLeftCircular` -- `anchorOriginBottomLeftRectangular` -- `anchorOriginBottomRightCircular` -- `anchorOriginBottomRightRectangular` - -You can replace them by using a CSS selector inside the `MuiBadge` `styleOverrides` `badge` key. -The following example replaces the usage of `anchorOriginTopLeftCircular` with a CSS selector including the `MuiBadge-overlapCircular` class and the `MuiBadge-anchorOriginBottomLeft` class - -```diff - const theme = extendTheme({ - components: { - MuiBadge: { - styleOverrides: { -- anchorOriginBottomLeftCircular: { -+ badge: { -+ "&.MuiBadge-anchorOriginBottomLeft.MuiBadge-overlapCircular": { - background: "fuchsia" - } - } - } - } - } - }); -``` - -### Removed combined anchor-overlap classes - -The following classes were removed: - -- `MuiBadge-anchorOriginTopLeftCircular` -- `MuiBadge-anchorOriginTopLeftRectangular` -- `MuiBadge-anchorOriginTopRightCircular` -- `MuiBadge-anchorOriginTopRightRectangular` -- `MuiBadge-anchorOriginBottomLeftCircular` -- `MuiBadge-anchorOriginBottomLeftRectangular` -- `MuiBadge-anchorOriginBottomRightCircular` -- `MuiBadge-anchorOriginBottomRightRectangular` - -You can replace them with the `anchorOrigin` value and `overlap` value classes combined in a CSS selector. -The following example replaces the `MuiBadge-anchorOriginBottomLeftCircular` class using `MuiBadge-anchorOriginBottomLeft` and `MuiBadge-overlapCircular`: - -```diff -- .MuiBadge-anchorOriginBottomLeftCircular -+ .MuiBadge-anchorOriginBottomLeft.MuiBadge-overlapCircular -``` - -## Button - -See the [ButtonBase](https://github.com/mui/material-ui/blob/master/packages/mui-material-next/migration.md#buttonbase) section for more breaking changes. - -## ButtonBase - -The breaking changes in this section apply to the following components: - -- `Button` -- `ButtonBase` -<!-- Add other ButtonBase-based components when those are migrated --> - -So the examples below are interchangeable for these components. - -### Removed focusRipple - -The `focusRipple` prop was removed as ripples are absent in Material Design 3's focused states. - -### Prevent default on `key-up` and `key-down` events - -If you need to prevent default on a `key-up` and/or `key-down` event, then besides calling `preventDefault` you'll need to set `event.defaultMuiPrevented` to `true` as follows: - -```diff - const onKeyDown = (event) => { - event.preventDefault(); -+ event.defaultMuiPrevented = true; - }; - - const onKeyUp = (event) => { - event.preventDefault(); -+ event.defaultMuiPrevented = true; - }; - - <Button onKeyDown={onKeyDown} onKeyUp={onKeyUp}> - Button - </Button> -``` - -This is to ensure that default is prevented when the `ButtonBase` root is not a native button, for example, when the root element used is a `span`. - -## FilledInput - -### Removed `inputProps` - -`inputProps` are removed in favor of `slotProps.input`: - -```diff - <FilledInput -- inputProps={{ className: 'my-input' }} -+ slotProps={{ input: { className: 'my-input' } }} - /> -``` - -## FormControl - -### Renamed `FormControlState` - -The `FormControlState` interface was renamed to `FormControlContextValue`: - -```diff --import { FormControlState } from '@mui/material'; -+import { FormControlContextValue } from '@mui/material-next'; -``` - -### Removed the `standard` variant - -The standard variant is no longer supported in M3, use the `filled` or `outlined` variants instead. - -## FormLabel - -### Removed the `standard` variant - -The standard variant is no longer supported in M3, use the `filled` or `outlined` variants instead. - -## InputBase - -### Removed `inputProps` - -`inputProps` are deprecated in favor of `slotProps.input`: - -```diff - <InputBase -- inputProps={{ className: 'my-input' }} -+ slotProps={{ input: { className: 'my-input' } }} - /> -``` - -## InputLabel - -### Removed the `standard` variant - -The standard variant is no longer supported in M3, use the `filled` or `outlined` variants instead. - -## Chip - -### Removed the "default" color option - -The `"default"` option is no longer accepted for the color prop. - -### Removed combined styleOverrides keys - -The following `styleOverrides` `MuiChip` keys were removed: - -- `clickableColorPrimary` -- `clickableColorSecondary` -- `deletableColorPrimary` -- `deletableColorSecondary` -- `outlinedPrimary` -- `outlinedSecondary` -- `filledPrimary` -- `filledSecondary` -- `avatarSmall` -- `avatarMedium` -- `avatarColorPrimary` -- `avatarColorSecondary` -- `iconSmall` -- `iconMedium` -- `iconColorPrimary` -- `iconColorSecondary` -- `labelSmall` -- `labelMedium` -- `deleteIconSmall` -- `deleteIconMedium` -- `deleteIconColorPrimary` -- `deleteIconColorSecondary` -- `deleteIconOutlinedColorPrimary` -- `deleteIconOutlinedColorSecondary` -- `deleteIconFilledColorPrimary` -- `deleteIconFilledColorSecondary` - -You can replace them by using the variants API and CSS Selectors. -The following example replaces the usage of `filledPrimary` with the variants API: - -```diff - const theme = extendTheme({ - components: { - MuiBadge: { -- styleOverrides: { -- filledPrimary: { -- background: "fuchsia" -- } -- } -+ variants: [ -+ { -+ props: { variant: 'filled', color: 'primary' }, -+ style: { -+ background: "fuchsia" -+ }, -+ }, -+ ], - } - } - }); -``` - -### Removed combined classes - -The following classes were removed: - -- `MuiChip-clickableColorPrimary` -- `MuiChip-clickableColorSecondary` -- `MuiChip-deletableColorPrimary` -- `MuiChip-deletableColorSecondary` -- `MuiChip-outlinedPrimary` -- `MuiChip-outlinedSecondary` -- `MuiChip-filledPrimary` -- `MuiChip-filledSecondary` -- `MuiChip-avatarSmall` -- `MuiChip-avatarMedium` -- `MuiChip-avatarColorPrimary` -- `MuiChip-avatarColorSecondary` -- `MuiChip-iconSmall` -- `MuiChip-iconMedium` -- `MuiChip-iconColorPrimary` -- `MuiChip-iconColorSecondary` -- `MuiChip-labelSmall` -- `MuiChip-labelMedium` -- `MuiChip-deleteIconSmall` -- `MuiChip-deleteIconMedium` -- `MuiChip-deleteIconColorPrimary` -- `MuiChip-deleteIconColorSecondary` -- `MuiChip-deleteIconOutlinedColorPrimary` -- `MuiChip-deleteIconOutlinedColorSecondary` -- `MuiChip-deleteIconFilledColorPrimary` -- `MuiChip-deleteIconFilledColorSecondary` - -You can replace them by combining classes with a CSS selector. -The following example replaces the `MuiChip-filledPrimary` class using `MuiChip-filled` and `MuiChip-colorPrimary`: - -```diff -- .MuiChip-filledPrimary -+ .MuiChip-filled.MuiChip-colorPrimary -``` - -### `skipFocusWhenDisabled` replaced with `focusableWhenDisabled` - -The `skipFocusWhenDisabled` prop was replaced with `focusableWhenDisabled`, which is `false` by default. -Because of this, the default behavior changed: - -- In `@mui/material`, disabled chips are focusable by default -- In `@mui/material-next`, disabled chips are _not_ focusable by default - -If you were using the `skipFocusWhenDisabled` prop to make disabled chips not focusable (which is `@mui/material-next`'s default behavior), then you can remove the prop as follows: - -```diff - <Chip - label="Clickable" - onClick={handleClick} -- skipFocusWhenDisabled={true} - /> -``` - -If you wish to maintain @mui/material's default behavior, then you can achieve that as follows: - -```diff - <Chip - label="Clickable" - onClick={handleClick} -+ focusableWhenDisabled={true} - /> -``` - -If you were using the `skipFocusWhenDisabled` prop to explicitly make disabled chips focusable, then you can replace it with `focusableWhenDisabled` as follows: - -```diff - <Chip - label="Clickable" - onClick={handleClick} -- skipFocusWhenDisabled={false} -+ focusableWhenDisabled={true} - /> -``` - -## Slider - -### Thumb and Value Label slots must accept refs - -If you are using the `thumb` or `valueLabel` Slider slots, then make sure the components accept a `ref` and forward it to the outermost element: - -```diff --const ValueLabel = ({ value, ...props }) => { -+const ValueLabel = React.forwardRef(({ value, ...props }, ref) => { - return ( -- <span {...props}> -+ <span {...props} ref={ref}> - {value} - </span> - ); -- }; -+ }); - --const Thumb = ({ style, ...props }) => { -+const Thumb = React.forwardRef(({ style, ...props }, ref) => { -- return <span {...props} style={{ position: 'absolute', ...style }} />; -+ return <span {...props} style={{ position: 'absolute', ...style }} ref={ref} />; --}; -+}); - - <Slider slots={{ thumb: Thumb, valueLabel: ValueLabel }}/> -``` - -This is required in `@mui/material-next` as it's used to apply the overlap styles to these slots. For more info take a look into [M3's Slider overlapping handles guidelines](https://m3.material.io/components/sliders/guidelines#ad5ceb95-a690-4ddd-8243-53a8e13bdab6). - -## Divider - -### Removed the "light" prop and class - -The `"light"` prop is no longer accepted for the Divider component. - -If you were using the `light` prop to create a lighter Divider (which is not supported in version 6), please remove the prop as shown below: - -```diff - <Divider -- light={true} -+ sx={{ borderColor: '#eee' }} - /> -``` - -### Remove composed classes and `styleOverrides` keys - -The following classes were removed: - -- `MuiDivider-withChildrenVertical` - -The `MuiDivider-withChildrenVertical` class has been removed. To replace it, you can use the `MuiDivider-withChildren` class along with the `MuiDivider-vertical` class. Here's an updated example: - -```diff -- .MuiDivider-withChildrenVertical -+ .MuiDivider-withChildren.MuiDivider-vertical -``` - -## LinearProgress - -### Removed combined styleOverrides keys - -The following `styleOverrides` `MuiLinearProgress` keys were removed: - -- `dashedColorPrimary` -- `dashedColorSecondary` -- `barColorPrimary` -- `barColorSecondary` -- `bar1Indeterminate` -- `bar1Determinate` -- `bar1Buffer` -- `bar2Indeterminate` -- `bar2Buffer` - -The following `styleOverrides` `MuiLinearProgress` keys were added: - -- `bar1` -- `bar2` - -You can replace them by using the variants API and CSS Selectors. -The following example replaces the usage of `dashedPrimary` with the variants API: - -```diff - const theme = extendTheme({ - components: { - MuiLinearProgress: { - styleOverrides: { -- dashedColorPrimary: { -- background: "fuchsia" -- } -+ root: { -+ "&.MuiLinearProgress-colorPrimary > .MuiLinearProgress-dashed": { -+ background: "fuchsia" -+ } -+ } - } - } - } - }); -``` - -### Removed combined classes - -The following classes were removed: - -- `MuiLinearProgress-dashedColorPrimary` -- `MuiLinearProgress-dashedColorSecondary` -- `MuiLinearProgress-barColorPrimary` -- `MuiLinearProgress-barColorSecondary` -- `MuiLinearProgress-bar1Indeterminate` -- `MuiLinearProgress-bar1Determinate` -- `MuiLinearProgress-bar1Buffer` -- `MuiLinearProgress-bar2Indeterminate` -- `MuiLinearProgress-bar2Buffer` - -The following classes were added: - -- `MuiLinearProgress-bar1` -- `MuiLinearProgress-bar2` - -You can replace them by combining classes with a CSS selector. -The following example replaces the `MuiLinearProgress-dashedColorPrimary` class using `MuiLinearProgress-dashed` and `MuiLinearProgress-colorPrimary`: - -```diff -- .MuiLinearProgress-dashedColorPrimary -+ .MuiLinearProgress-colorPrimary .MuiLinearProgress-dashed -``` - -## CircularProgress - -### Removed combined styleOverrides keys - -The following `styleOverrides` `MuiCircularProgress` keys were removed: - -- `circleDeterminate` -- `circleIndeterminate` -- `circleDisableShrink` - -The following `styleOverrides` `MuiCircularProgress` keys were added: - -- `disableShrink` - -You can replace them by using the variants API and CSS Selectors. -The following example replaces the usage of `circleDeterminate` with the variants API: - -```diff - const theme = extendTheme({ - components: { - MuiCircularProgress: { - styleOverrides: { -- circleDeterminate: { -- background: "fuchsia" -- } -+ root: { -+ "&.MuiCircularProgress-determinate .MuiCircularProgress-circle": { -+ background: "fuchsia" -+ } -+ } - } - } - } - }); -``` - -### Removed combined classes - -The following classes were removed: - -- `MuiCircularProgress-circleDeterminate` -- `MuiCircularProgress-circleIndeterminate` -- `MuiCircularProgress-circleDisableShrink` - -The following classes were added: - -- `MuiCircularProgress-disableShrink` - -You can replace them by combining classes with a CSS selector. -The following example replaces the `MuiCircularProgress-circleDeterminate` class using `MuiCircularProgress-circle` and `MuiCircularProgress-determinate`: - -```diff -- .MuiCircularProgress-circleDeterminate -+ .MuiCircularProgress-determinate .MuiCircularProgress-circle -``` - -## ButtonGroup - -### Removed combined styleOverrides keys - -The following `styleOverrides` `MuiButtonGroup` keys were removed: - -- `contained` -- `groupedHorizontal` -- `groupedVertical` -- `groupedContained` -- `groupedText` -- `groupedOutlined` -- `groupedContainedHorizontal` -- `groupedTextHorizontal` -- `groupedOutlinedHorizontal` -- `groupedContainedVertical` -- `groupedTextVertical` -- `groupedOutlinedVertical` -- `groupedContainedPrimary` -- `groupedTextPrimary` -- `groupedOutlinedPrimary` -- `groupedContainedSecondary` -- `groupedTextSecondary` -- `groupedOutlinedSecondary` - -The following `styleOverrides` `MuiButtonGroup` keys were added: - -- `primary` -- `secondary` -- `tertiary` -- `filled` -- `filledPartial` -- `elevated` - -You can replace them by using the variants API and CSS Selectors. -The following example replaces the usage of `groupedOutlined` with the variants API: - -```diff - const theme = extendTheme({ - components: { - MuiButtonGroup: { - styleOverrides: { -- groupedOutlined: { -- background: "fuchsia" -- } -+ outlined: { -+ ".MuiButtonGroup-grouped": { -+ background: "fuchsia" -+ } -+ } - } - } - } - }); -``` - -### Removed combined classes - -The following classes were removed: - -- `MuiButtonGroup-contained` -- `MuiButtonGroup-groupedHorizontal` -- `MuiButtonGroup-groupedVertical` -- `MuiButtonGroup-groupedText` -- `MuiButtonGroup-groupedTextHorizontal` -- `MuiButtonGroup-groupedTextVertical` -- `MuiButtonGroup-groupedTextPrimary` -- `MuiButtonGroup-groupedTextSecondary` -- `MuiButtonGroup-groupedOutlined` -- `MuiButtonGroup-groupedOutlinedHorizontal` -- `MuiButtonGroup-groupedOutlinedVertical` -- `MuiButtonGroup-groupedOutlinedPrimary` -- `MuiButtonGroup-groupedOutlinedSecondary` -- `MuiButtonGroup-groupedContained` -- `MuiButtonGroup-groupedContainedHorizontal` -- `MuiButtonGroup-groupedContainedVertical` -- `MuiButtonGroup-groupedContainedPrimary` -- `MuiButtonGroup-groupedContainedSecondary` - -The following classes were added: - -- `MuiButtonGroup-filled` -- `MuiButtonGroup-filledTonal` -- `MuiButtonGroup-elevated` -- `MuiButtonGroup-primary` -- `MuiButtonGroup-secondary` -- `MuiButtonGroup-tertiary` - -You can replace them by combining classes with a CSS selector. -The following example replaces the `MuiButtonGroup-groupedOutlined` class using `MuiButtonGroup-grouped` and `MuiButtonGroup-outline`: - -```diff -- .MuiButtonGroup-groupedOutlined -+ .MuiButtonGroup-outlined .MuiButtonGroup-grouped -``` diff --git a/packages/mui-material-next/package.json b/packages/mui-material-next/package.json deleted file mode 100644 index f7bff874303d07..00000000000000 --- a/packages/mui-material-next/package.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "name": "@mui/material-next", - "version": "6.0.0-alpha.125", - "private": false, - "author": "MUI Team", - "description": "v6-alpha: React components that implement Google's Material Design", - "main": "./src/index.ts", - "keywords": [ - "react", - "react-component", - "mui", - "material-ui", - "material design" - ], - "repository": { - "type": "git", - "url": "https://github.com/mui/material-ui.git", - "directory": "packages/mui-material-next" - }, - "license": "MIT", - "bugs": { - "url": "https://github.com/mui/material-ui/issues" - }, - "homepage": "https://mui.com/material-ui/", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "scripts": { - "build": "pnpm build:modern && pnpm build:node && pnpm build:stable && pnpm build:types && pnpm build:copy-files", - "build:modern": "node ../../scripts/build.mjs modern", - "build:node": "node ../../scripts/build.mjs node", - "build:stable": "node ../../scripts/build.mjs stable", - "build:copy-files": "node ../../scripts/copyFiles.mjs", - "build:types": "node ../../scripts/buildTypes.mjs", - "prebuild": "rimraf build tsconfig.build.tsbuildinfo", - "release": "pnpm build && pnpm publish", - "test": "cd ../../ && cross-env NODE_ENV=test mocha 'packages/mui-material-next/**/*.test.{js,ts,tsx}'", - "typescript": "tsc -p tsconfig.json", - "typescript:module-augmentation": "node scripts/testModuleAugmentation.js" - }, - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/base": "workspace:*", - "@mui/material": "workspace:^", - "@mui/system": "workspace:^", - "@mui/types": "workspace:^", - "@mui/utils": "workspace:^", - "@types/react-transition-group": "^4.4.10", - "clsx": "^2.1.0", - "prop-types": "^15.8.1", - "react-is": "^18.2.0", - "react-transition-group": "^4.4.5" - }, - "devDependencies": { - "@emotion/react": "^11.11.4", - "@mui/internal-babel-macros": "workspace:^", - "@mui-internal/test-utils": "workspace:^", - "@testing-library/user-event": "^14.5.2", - "@types/chai": "^4.3.12", - "@types/prop-types": "^15.7.11", - "@types/react": "^18.2.55", - "@types/react-dom": "^18.2.19", - "@types/react-is": "^18.2.4", - "@types/sinon": "^10.0.20", - "chai": "^4.4.1", - "lodash": "^4.17.21", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-router-dom": "^6.21.3", - "sinon": "^15.2.0" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - } - }, - "sideEffects": false, - "publishConfig": { - "access": "public", - "directory": "build" - }, - "engines": { - "node": ">=12.0.0" - } -} diff --git a/packages/mui-material-next/src/Badge/Badge.spec.tsx b/packages/mui-material-next/src/Badge/Badge.spec.tsx deleted file mode 100644 index 5432f311480f9e..00000000000000 --- a/packages/mui-material-next/src/Badge/Badge.spec.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import * as React from 'react'; -import { expectType } from '@mui/types'; -import Badge, { BadgeOwnerState } from '@mui/material-next/Badge'; - -function classesTest() { - return ( - <Badge - badgeContent={4} - classes={{ badge: 'testBadgeClassName', colorError: 'colorErrorClass' }} - > - <div>Hello World</div> - </Badge> - ); -} - -<Badge />; - -<Badge component="div" />; - -// `size` -<Badge size="large" />; -<Badge size="small" />; - -// @ts-expect-error there is no 'medium' size -<Badge size="medium" />; - -// `variant` -<Badge variant="standard" />; -<Badge variant="dot" />; - -// @ts-expect-error there is no variant `filled` -<Badge variant="filled" />; - -// `color` -<Badge color="primary" />; -<Badge color="secondary" />; -<Badge color="tertiary" />; -<Badge color="error" />; -<Badge color="info" />; -<Badge color="warning" />; -<Badge color="success" />; - -// @ts-expect-error there is no color `default` -<Badge color="default" />; - -<Badge - slots={{ - root: 'div', - badge: 'div', - }} -/>; - -<Badge - slotProps={{ - root: { - id: 'test', - }, - badge: { - id: 'test', - }, - }} -/>; - -<Badge - slotProps={{ - root: (ownerState) => { - expectType<BadgeOwnerState, typeof ownerState>(ownerState); - return { - id: 'test', - }; - }, - badge: (ownerState) => { - expectType<BadgeOwnerState, typeof ownerState>(ownerState); - return { - id: 'test', - }; - }, - }} -/>; diff --git a/packages/mui-material-next/src/Badge/Badge.test.tsx b/packages/mui-material-next/src/Badge/Badge.test.tsx deleted file mode 100644 index 3a21443b79da0a..00000000000000 --- a/packages/mui-material-next/src/Badge/Badge.test.tsx +++ /dev/null @@ -1,342 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -import Badge, { badgeClasses as classes } from '@mui/material-next/Badge'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import describeConformance from '../../test/describeConformance'; - -function findBadgeRoot(container: HTMLElement) { - return container?.firstChild; -} - -function findBadge(container: HTMLElement) { - return (findBadgeRoot(container) as HTMLElement)?.querySelector('span') ?? null; -} - -describe('<Badge />', () => { - const { render } = createRenderer(); - - const defaultProps = { - children: ( - <div className="unique" data-testid="children"> - Hello World - </div> - ), - badgeContent: 10, - }; - - describeConformance( - <Badge> - <div /> - </Badge>, - () => ({ - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - classes, - inheritComponent: 'span', - render, - refInstanceof: window.HTMLSpanElement, - muiName: 'MuiBadge', - testVariantProps: { color: 'secondary', size: 'small' }, - slots: { - root: { - expectedClassName: classes.root, - }, - badge: { - expectedClassName: classes.badge, - }, - }, - skip: [ - 'componentsProp', - 'slotPropsCallback', // not supported yet - ], - }), - ); - - it('renders children and badgeContent', () => { - const children = <div id="child" data-testid="child" />; - const badge = <div id="badge" data-testid="badge" />; - const { container, getByTestId } = render(<Badge badgeContent={badge}>{children}</Badge>); - expect(container.firstChild).to.contain(getByTestId('child')); - expect(container.firstChild).to.contain(getByTestId('badge')); - }); - - it('applies customized classes', () => { - const customClasses = { - root: 'test-root', - anchorOriginTopRight: 'test-anchorOriginTopRight', - badge: 'test-badge', - colorSecondary: 'test-colorSecondary', - small: 'test-small', - dot: 'test-dot', - invisible: 'test-invisible', - overlapCircular: 'test-overlapCircular', - }; - - const { container } = render( - <Badge - {...defaultProps} - size="small" - variant="dot" - overlap="circular" - invisible - color="secondary" - classes={customClasses} - />, - ); - - expect(findBadgeRoot(container)).to.have.class(customClasses.root); - expect(findBadge(container)).to.have.class(customClasses.anchorOriginTopRight); - expect(findBadge(container)).to.have.class(customClasses.badge); - expect(findBadge(container)).to.have.class(customClasses.colorSecondary); - expect(findBadge(container)).to.have.class(customClasses.small); - expect(findBadge(container)).to.have.class(customClasses.dot); - expect(findBadge(container)).to.have.class(customClasses.invisible); - expect(findBadge(container)).to.have.class(customClasses.overlapCircular); - }); - - it('renders children', () => { - const { container, getByTestId } = render( - <Badge className="testClassName" {...defaultProps} />, - ); - expect(container.firstChild).to.contain(getByTestId('children')); - }); - - describe('prop: color', () => { - it('should have the colorPrimary class when color="primary"', () => { - const { container } = render(<Badge {...defaultProps} color="primary" />); - expect(findBadge(container)).to.have.class(classes.colorPrimary); - }); - - it('should have the colorSecondary class when color="secondary"', () => { - const { container } = render(<Badge {...defaultProps} color="secondary" />); - expect(findBadge(container)).to.have.class(classes.colorSecondary); - }); - - it('should have the colorError class when color="error"', () => { - const { container } = render(<Badge {...defaultProps} color="error" />); - expect(findBadge(container)).to.have.class(classes.colorError); - }); - }); - - describe('prop: invisible', () => { - it('should default to false', () => { - const { container } = render(<Badge {...defaultProps} />); - expect(findBadge(container)).not.to.have.class(classes.invisible); - }); - - it('should render without the invisible class when set to false', () => { - const { container } = render(<Badge {...defaultProps} invisible={false} />); - expect(findBadge(container)).not.to.have.class(classes.invisible); - }); - - it('should render with the invisible class when set to true', () => { - const { container } = render(<Badge {...defaultProps} invisible />); - expect(findBadge(container)).to.have.class(classes.invisible); - }); - - it('should render with the invisible class when empty and not dot', () => { - let container; - container = render(<Badge {...defaultProps} badgeContent={null} />).container; - expect(findBadge(container)).to.have.class(classes.invisible); - container = render(<Badge {...defaultProps} badgeContent={undefined} />).container; - expect(findBadge(container)).to.have.class(classes.invisible); - container = render( - <Badge {...defaultProps} badgeContent={undefined} variant="dot" />, - ).container; - expect(findBadge(container)).not.to.have.class(classes.invisible); - }); - - it('should render with the invisible class when empty and not small', () => { - let container; - container = render(<Badge {...defaultProps} badgeContent={null} />).container; - expect(findBadge(container)).to.have.class(classes.invisible); - container = render(<Badge {...defaultProps} badgeContent={undefined} />).container; - expect(findBadge(container)).to.have.class(classes.invisible); - container = render( - <Badge {...defaultProps} badgeContent={undefined} size="small" />, - ).container; - expect(findBadge(container)).not.to.have.class(classes.invisible); - }); - - it('should render with invisible class when invisible and showZero are set to false and content is 0', () => { - const { container } = render(<Badge badgeContent={0} showZero={false} invisible={false} />); - expect(findBadge(container)).to.have.class(classes.invisible); - expect(findBadge(container)).to.have.text(''); - }); - - it('should not render with invisible class when invisible and showZero are set to false and content is not 0', () => { - const { container } = render(<Badge badgeContent={1} showZero={false} invisible={false} />); - expect(findBadge(container)).not.to.have.class(classes.invisible); - expect(findBadge(container)).to.have.text('1'); - }); - }); - - describe('prop: showZero', () => { - it('should default to false', () => { - const { container } = render(<Badge {...defaultProps} badgeContent={0} />); - expect(findBadge(container)).to.have.class(classes.invisible); - }); - - it('should render without the invisible class when false and badgeContent is not 0', () => { - const { container } = render(<Badge {...defaultProps} showZero />); - expect(findBadge(container)).not.to.have.class(classes.invisible); - }); - - it('should render without the invisible class when true and badgeContent is 0', () => { - const { container } = render(<Badge {...defaultProps} badgeContent={0} showZero />); - expect(findBadge(container)).not.to.have.class(classes.invisible); - }); - - it('should render with the invisible class when false and badgeContent is 0', () => { - const { container } = render(<Badge {...defaultProps} badgeContent={0} showZero={false} />); - expect(findBadge(container)).to.have.class(classes.invisible); - }); - }); - - describe('prop: variant', () => { - it('should default to standard', () => { - const { container } = render(<Badge {...defaultProps} />); - expect(findBadge(container)).to.have.class(classes.badge); - expect(findBadge(container)).to.have.class(classes.standard); - expect(findBadge(container)).not.to.have.class(classes.dot); - }); - - it('should render with the standard class when variant="standard"', () => { - const { container } = render(<Badge {...defaultProps} variant="standard" />); - expect(findBadge(container)).to.have.class(classes.badge); - expect(findBadge(container)).to.have.class(classes.standard); - expect(findBadge(container)).not.to.have.class(classes.dot); - }); - - it('should not render badgeContent when variant="dot"', () => { - const { container } = render(<Badge {...defaultProps} variant="dot" />); - expect(findBadge(container)).to.have.class(classes.badge); - expect(findBadge(container)).to.have.class(classes.dot); - expect(findBadge(container)).to.have.text(''); - expect(findBadge(container)).not.to.have.class(classes.standard); - }); - }); - - describe('prop: size', () => { - it('should default to large', () => { - const { container } = render(<Badge {...defaultProps} />); - expect(findBadge(container)).to.have.class(classes.badge); - expect(findBadge(container)).to.have.class(classes.large); - expect(findBadge(container)).not.to.have.class(classes.small); - }); - - it('should render with the large class when size="large"', () => { - const { container } = render(<Badge {...defaultProps} size="large" />); - expect(findBadge(container)).to.have.class(classes.badge); - expect(findBadge(container)).to.have.class(classes.large); - expect(findBadge(container)).not.to.have.class(classes.small); - }); - - it('should not render badgeContent when size="small"', () => { - const { container } = render(<Badge {...defaultProps} size="small" />); - expect(findBadge(container)).to.have.class(classes.badge); - expect(findBadge(container)).to.have.class(classes.small); - expect(findBadge(container)).to.have.text(''); - expect(findBadge(container)).not.to.have.class(classes.large); - }); - }); - - describe('prop: max', () => { - it('should default to 99', () => { - const { container } = render(<Badge {...defaultProps} badgeContent={100} />); - expect(findBadge(container)).to.have.text('99+'); - }); - - it('should cap badgeContent', () => { - const { container } = render(<Badge {...defaultProps} badgeContent={1000} max={999} />); - expect(findBadge(container)).to.have.text('999+'); - }); - - it('should not cap if badgeContent and max are equal', () => { - const { container } = render(<Badge {...defaultProps} badgeContent={1000} max={1000} />); - expect(findBadge(container)).to.have.text('1000'); - }); - - it('should not cap if badgeContent is lower than max', () => { - const { container } = render(<Badge {...defaultProps} badgeContent={50} max={1000} />); - expect(findBadge(container)).to.have.text('50'); - }); - }); - - describe('prop: anchorOrigin', () => { - it('should apply style for top right by default', () => { - const { container } = render(<Badge {...defaultProps} />); - expect(findBadge(container)).to.have.class(classes.anchorOriginTopRight); - }); - - it('should apply style for top left', () => { - const { container } = render( - <Badge {...defaultProps} anchorOrigin={{ horizontal: 'left', vertical: 'top' }} />, - ); - expect(findBadge(container)).to.have.class(classes.anchorOriginTopLeft); - }); - - it('should apply style for top right', () => { - const { container } = render( - <Badge {...defaultProps} anchorOrigin={{ horizontal: 'right', vertical: 'top' }} />, - ); - expect(findBadge(container)).to.have.class(classes.anchorOriginTopRight); - }); - - it('should apply style for bottom left', () => { - const { container } = render( - <Badge {...defaultProps} anchorOrigin={{ horizontal: 'left', vertical: 'bottom' }} />, - ); - expect(findBadge(container)).to.have.class(classes.anchorOriginBottomLeft); - }); - - it('should apply style for bottom right', () => { - const { container } = render( - <Badge {...defaultProps} anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} />, - ); - expect(findBadge(container)).to.have.class(classes.anchorOriginBottomRight); - }); - }); - - describe('prop: overlap', () => { - it('should apply style for rectangular by default', () => { - const { container } = render(<Badge {...defaultProps} />); - expect(findBadge(container)).to.have.class(classes.overlapRectangular); - }); - - it('should apply style for rectangular', () => { - const { container } = render(<Badge {...defaultProps} overlap="rectangular" />); - expect(findBadge(container)).to.have.class(classes.overlapRectangular); - }); - - it('should apply style for circular', () => { - const { container } = render(<Badge {...defaultProps} overlap="circular" />); - expect(findBadge(container)).to.have.class(classes.overlapCircular); - }); - }); - - it('retains anchorOrigin, content, color, max, overlap, size, and variant when invisible is true for consistent disappearing transition', () => { - const { container, setProps } = render( - <Badge {...defaultProps} color="secondary" size="small" variant="dot" />, - ); - - setProps({ - badgeContent: 0, - color: 'primary', - size: 'large', - variant: 'standard', - overlap: 'circular', - anchorOrigin: { - vertical: 'bottom', - horizontal: 'left', - }, - }); - - expect(findBadge(container)).to.have.text(''); - expect(findBadge(container)).to.have.class(classes.colorSecondary); - expect(findBadge(container)).to.have.class(classes.small); - expect(findBadge(container)).to.have.class(classes.dot); - expect(findBadge(container)).to.have.class(classes.anchorOriginTopRight); - }); -}); diff --git a/packages/mui-material-next/src/Badge/Badge.tsx b/packages/mui-material-next/src/Badge/Badge.tsx deleted file mode 100644 index 0f402cd369306e..00000000000000 --- a/packages/mui-material-next/src/Badge/Badge.tsx +++ /dev/null @@ -1,378 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { usePreviousProps, unstable_capitalize as capitalize } from '@mui/utils'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { useBadge } from '@mui/base/useBadge'; -import { useSlotProps } from '@mui/base'; -import { OverridableComponent } from '@mui/types'; -import styled from '../styles/styled'; -import useTheme from '../styles/useTheme'; -import useThemeProps from '../styles/useThemeProps'; -import badgeClasses, { getBadgeUtilityClass } from './badgeClasses'; -import { BadgeOwnerState, BadgeProps, BadgeTypeMap } from './Badge.types'; -import { MD3ColorSchemeTokens } from '../styles'; - -const useUtilityClasses = (ownerState: BadgeOwnerState) => { - const { color, anchorOrigin, invisible, overlap, size, variant, classes = {} } = ownerState; - - const slots = { - root: ['root'], - badge: [ - 'badge', - size, - variant, - invisible && 'invisible', - `anchorOrigin${capitalize(anchorOrigin.vertical)}${capitalize(anchorOrigin.horizontal)}`, - `overlap${capitalize(overlap)}`, - `color${capitalize(color)}`, - ], - }; - - return composeClasses(slots, getBadgeUtilityClass, classes); -}; - -const BadgeRoot = styled('span', { - name: 'MuiBadge', - slot: 'Root', - overridesResolver: (props, styles) => styles.root, -})({ - position: 'relative', - display: 'inline-flex', - // For correct alignment with the text. - verticalAlign: 'middle', - flexShrink: 0, -}); - -const BadgeBadge = styled('span', { - name: 'MuiBadge', - slot: 'Badge', - overridesResolver: (props, styles) => { - const { ownerState }: { ownerState: BadgeOwnerState } = props; - - return [ - styles.badge, - styles[ownerState.size], - styles[ownerState.variant], - styles[ - `anchorOrigin${capitalize(ownerState.anchorOrigin.vertical)}${capitalize( - ownerState.anchorOrigin.horizontal, - )}` - ], - styles[`overlap${capitalize(ownerState.overlap)}`], - styles[`color${capitalize(ownerState.color)}`], - ownerState.invisible && styles.invisible, - ]; - }, -})<{ ownerState: BadgeOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - const letterSpacing = `${ - theme.sys.typescale.label.small.tracking / theme.sys.typescale.label.small.size - }rem`; - - const verticalPositionProperty = ownerState.anchorOrigin.vertical === 'top' ? 'bottom' : 'top'; - const horizontalPositionProperty = - ownerState.anchorOrigin.horizontal === 'left' ? 'right' : 'left'; - - return { - '--md-comp-badge-large-size': '16px', - '--md-comp-badge-small-size': '6px', - '--md-comp-badge-size': 'var(--md-comp-badge-large-size)', - '--md-comp-badge-padding-x': '4px', - '--md-comp-badge-inset': 'calc(var(--md-comp-badge-size) - 4px)', - ...(ownerState.size === 'small' && { - '--md-comp-badge-size': 'var(--md-comp-badge-small-size)', - '--md-comp-badge-inset': 'var(--md-comp-badge-size)', - '--md-comp-badge-padding-x': '0px', - }), - ...(ownerState.overlap === 'circular' && { - '--md-comp-badge-inset': 'calc(var(--md-comp-badge-size) / 2 + 14%)', - }), - display: 'flex', - flexDirection: 'row', - flexWrap: 'wrap', - justifyContent: 'center', - alignContent: 'center', - alignItems: 'center', - position: 'absolute', - boxSizing: 'border-box', - fontFamily: tokens.sys.typescale.label.small.family, - fontWeight: tokens.sys.typescale.label.small.weight, - fontSize: theme.typography.pxToRem(theme.sys.typescale.label.small.size), - lineHeight: `calc(${tokens.sys.typescale.label.small.lineHeight} / ${tokens.sys.typescale.label.small.size})`, - letterSpacing, - backgroundColor: tokens.sys.color[ownerState.color], - color: tokens.sys.color[`on${capitalize(ownerState.color)}` as keyof MD3ColorSchemeTokens], - minWidth: 'var(--md-comp-badge-size)', - height: 'var(--md-comp-badge-size)', - paddingRight: `calc(var(--md-comp-badge-padding-x) - ${letterSpacing})`, - paddingLeft: 'var(--md-comp-badge-padding-x)', - borderRadius: tokens.sys.shape.corner.full, - [verticalPositionProperty]: `calc(100% - var(--md-comp-badge-inset))`, - [horizontalPositionProperty]: `calc(100% - var(--md-comp-badge-inset))`, - transform: 'scale(1)', - transformOrigin: 'calc(var(--md-comp-badge-size) / 2)', - zIndex: 1, // Render the badge on top of potential ripples. - transition: theme.transitions.create('transform', { - easing: theme.transitions.easing.easeInOut, - duration: theme.transitions.duration.enteringScreen, - }), - [`&.${badgeClasses.invisible}`]: { - transform: 'scale(0)', - transition: theme.transitions.create('transform', { - easing: theme.transitions.easing.easeInOut, - duration: theme.transitions.duration.leavingScreen, - }), - }, - }; -}); - -const defaultLtRAnchorOrigin = { - vertical: 'top', - horizontal: 'right', -}; - -const defaultRtLAnchorOrigin = { - vertical: 'top', - horizontal: 'left', -}; - -function isSmall(size?: BadgeProps['size'], variant?: BadgeProps['variant']): boolean { - if (size) { - // size takes precedence - return size === 'small'; - } - - return variant === 'dot'; -} - -/** - * - * Demos: - * - * - [Badge](https://mui.com/material-ui/react-badge/) - * - * API: - * - * - [Badge API](https://mui.com/material-ui/api/badge/) - */ -const Badge = React.forwardRef(function Badge< - BaseComponentType extends React.ElementType = BadgeTypeMap['defaultComponent'], ->(inProps: BadgeProps<BaseComponentType>, ref: React.ForwardedRef<Element>) { - const props = useThemeProps({ props: inProps, name: 'MuiBadge' }); - - const theme = useTheme(); - const defaultAnchorOrigin = - theme.direction === 'rtl' ? defaultRtLAnchorOrigin : defaultLtRAnchorOrigin; - - const { - anchorOrigin: anchorOriginProp = defaultAnchorOrigin, - className, - classes: classesProp, - component, - children, - overlap: overlapProp = 'rectangular', - color: colorProp = 'error', - invisible: invisibleProp = false, - max: maxProp = 99, - badgeContent: badgeContentProp, - slots = {}, - slotProps = {}, - showZero = false, - size: sizeProp, - variant: variantProp, - ...other - } = props; - - const { - badgeContent, - invisible: invisibleFromHook, - max, - displayValue: displayValueFromHook, - } = useBadge({ - max: maxProp, - invisible: invisibleProp, - badgeContent: badgeContentProp, - showZero, - }); - - const prevProps = usePreviousProps({ - anchorOrigin: anchorOriginProp, - color: colorProp, - overlap: overlapProp, - size: sizeProp, - variant: variantProp, - badgeContent: badgeContentProp, - }); - - const invisible = invisibleFromHook || (badgeContent == null && !isSmall(sizeProp, variantProp)); - - const { - color = colorProp, - overlap = overlapProp, - anchorOrigin = anchorOriginProp, - size = sizeProp, - variant = variantProp, - } = invisible ? prevProps : props; - - const displayValue = !isSmall(size, variant) ? displayValueFromHook : undefined; - - const ownerState = { - ...props, - badgeContent, - invisible, - max, - displayValue, - showZero, - anchorOrigin, - color, - overlap, - size: isSmall(size, variant) ? 'small' : 'large', - variant: isSmall(size, variant) ? 'dot' : 'standard', - }; - - const classes = useUtilityClasses(ownerState); - - const RootSlot = slots.root ?? BadgeRoot; - const BadgeSlot = slots.badge ?? BadgeBadge; - - const rootProps = useSlotProps({ - elementType: RootSlot, - externalSlotProps: slotProps.root, - externalForwardedProps: other, - additionalProps: { - ref, - as: component, - }, - ownerState, - className: [classes.root, className], - }); - - const badgeProps = useSlotProps({ - elementType: BadgeSlot, - externalSlotProps: slotProps.badge, - ownerState, - className: classes.badge, - }); - - return ( - <RootSlot {...rootProps}> - {children} - <BadgeSlot {...badgeProps}>{displayValue}</BadgeSlot> - </RootSlot> - ); -}) as OverridableComponent<BadgeTypeMap>; - -Badge.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 anchor of the badge. - * @default { - * vertical: 'top', - * horizontal: 'right', - * } - */ - anchorOrigin: PropTypes.shape({ - horizontal: PropTypes.oneOf(['left', 'right']).isRequired, - vertical: PropTypes.oneOf(['bottom', 'top']).isRequired, - }), - /** - * The content rendered within the badge. - */ - badgeContent: PropTypes.node, - /** - * The badge will be added relative to this node. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'error' - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['error', 'info', 'primary', 'secondary', 'success', 'tertiary', 'warning']), - PropTypes.string, - ]), - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * If `true`, the badge is invisible. - * @default false - */ - invisible: PropTypes.bool, - /** - * Max count to show. - * @default 99 - */ - max: PropTypes.number, - /** - * Wrapped shape the badge should overlap. - * @default 'rectangular' - */ - overlap: PropTypes.oneOf(['circular', 'rectangular']), - /** - * Controls whether the badge is hidden when `badgeContent` is zero. - * @default false - */ - showZero: PropTypes.bool, - /** - * The size to use. - * @default 'large' - */ - size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['small', 'large']), - PropTypes.string, - ]), - /** - * The props used for each slot inside the Badge. - * @default {} - */ - slotProps: PropTypes.shape({ - badge: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - }), - /** - * The components used for each slot inside the Badge. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots: PropTypes.shape({ - badge: PropTypes.elementType, - root: PropTypes.elementType, - }), - /** - * 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 variant to use. - * @default 'standard' - * @deprecated Use `size = 'small' | 'large'` instead - */ - variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['dot', 'standard']), - PropTypes.string, - ]), -} as any; - -export default Badge; diff --git a/packages/mui-material-next/src/Badge/Badge.types.ts b/packages/mui-material-next/src/Badge/Badge.types.ts deleted file mode 100644 index fe5eaae5429514..00000000000000 --- a/packages/mui-material-next/src/Badge/Badge.types.ts +++ /dev/null @@ -1,133 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverridableStringUnion, OverrideProps, PartiallyRequired } from '@mui/types'; -import { SlotComponentProps } from '@mui/base'; -import { Theme } from '../styles'; -import { BadgeClasses } from './badgeClasses'; - -export interface BadgePropsSizeOverrides {} - -export interface BadgePropsVariantOverrides {} - -export interface BadgePropsColorOverrides {} - -export interface BadgeOrigin { - vertical: 'top' | 'bottom'; - horizontal: 'left' | 'right'; -} - -export interface BadgeSlots { - /** - * The component that renders the root. - * @default 'span' - */ - root?: React.ElementType; - /** - * The component that renders the badge. - * @default 'span' - */ - badge?: React.ElementType; -} - -export interface BadgeTypeMap< - DefaultComponent extends React.ElementType = 'span', - AdditionalProps = {}, -> { - props: AdditionalProps & { - /** - * The anchor of the badge. - * @default { - * vertical: 'top', - * horizontal: 'right', - * } - */ - anchorOrigin?: BadgeOrigin; - /** - * The content rendered within the badge. - */ - badgeContent?: React.ReactNode; - /** - * The badge will be added relative to this node. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<BadgeClasses>; - /** - * @ignore - */ - className?: string; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'error' - */ - color?: OverridableStringUnion< - 'primary' | 'secondary' | 'tertiary' | 'error' | 'info' | 'warning' | 'success', - BadgePropsColorOverrides - >; - /** - * If `true`, the badge is invisible. - * @default false - */ - invisible?: boolean; - /** - * Max count to show. - * @default 99 - */ - max?: number; - /** - * Wrapped shape the badge should overlap. - * @default 'rectangular' - */ - overlap?: 'rectangular' | 'circular'; - /** - * Controls whether the badge is hidden when `badgeContent` is zero. - * @default false - */ - showZero?: boolean; - /** - * The props used for each slot inside the Badge. - * @default {} - */ - slotProps?: { - root?: SlotComponentProps<'span', {}, BadgeOwnerState>; - badge?: SlotComponentProps<'span', {}, BadgeOwnerState>; - }; - /** - * The components used for each slot inside the Badge. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots?: BadgeSlots; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; - /** - * The size to use. - * @default 'large' - */ - size?: OverridableStringUnion<'small' | 'large', BadgePropsSizeOverrides>; - /** - * The variant to use. - * @default 'standard' - * @deprecated Use `size = 'small' | 'large'` instead - */ - variant?: OverridableStringUnion<'dot' | 'standard', BadgePropsVariantOverrides>; - }; - defaultComponent: DefaultComponent; -} - -export type BadgeProps< - RootComponent extends React.ElementType = BadgeTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<BadgeTypeMap<RootComponent, AdditionalProps>, RootComponent>; - -export interface BadgeOwnerState - extends PartiallyRequired< - BadgeProps, - 'anchorOrigin' | 'color' | 'overlap' | 'size' | 'variant' - > {} diff --git a/packages/mui-material-next/src/Badge/badgeClasses.ts b/packages/mui-material-next/src/Badge/badgeClasses.ts deleted file mode 100644 index 153f3bc7341248..00000000000000 --- a/packages/mui-material-next/src/Badge/badgeClasses.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface BadgeClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the badge `span` element. */ - badge: string; - /** Styles applied to the badge `span` element if `size="small"`. */ - small: string; - /** Styles applied to the badge `span` element if `size="large"`. */ - large: string; - /** Styles applied to the badge `span` element if `variant="dot"`. */ - dot: string; - /** Styles applied to the badge `span` element if `variant="standard"`. */ - standard: string; - /** Styles applied to the badge `span` element if `anchorOrigin={{ 'top', 'right' }}`. */ - anchorOriginTopRight: string; - /** Styles applied to the badge `span` element if `anchorOrigin={{ 'bottom', 'right' }}`. */ - anchorOriginBottomRight: string; - /** Styles applied to the badge `span` element if `anchorOrigin={{ 'top', 'left' }}`. */ - anchorOriginTopLeft: string; - /** Styles applied to the badge `span` element if `anchorOrigin={{ 'bottom', 'left' }}`. */ - anchorOriginBottomLeft: string; - /** State class applied to the badge `span` element if `invisible={true}`. */ - invisible: string; - /** Styles applied to the badge `span` element if `color="error"`. */ - colorError: string; - /** Styles applied to the badge `span` element if `color="primary"`. */ - colorPrimary: string; - /** Styles applied to the badge `span` element if `color="secondary"`. */ - colorSecondary: string; - /** Styles applied to the badge `span` element if `color="tertiary"`. */ - colorTertiary: string; - /** Styles applied to the badge `span` element if `color="info"`. */ - colorInfo: string; - /** Styles applied to the badge `span` element if `color="warning"`. */ - colorWarning: string; - /** Styles applied to the badge `span` element if `color="success"`. */ - colorSuccess: string; - /** Styles applied to the badge `span` element if `overlap="rectangular"`. */ - overlapRectangular: string; - /** Styles applied to the badge `span` element if `overlap="circular"`. */ - overlapCircular: string; -} - -export type BadgeClassKey = keyof BadgeClasses; - -export function getBadgeUtilityClass(slot: string): string { - return generateUtilityClass('MuiBadge', slot); -} - -const badgeClasses: BadgeClasses = generateUtilityClasses('MuiBadge', [ - 'root', - 'badge', - 'small', - 'large', - 'dot', - 'standard', - 'anchorOriginTopRight', - 'anchorOriginBottomRight', - 'anchorOriginTopLeft', - 'anchorOriginBottomLeft', - 'invisible', - 'colorError', - 'colorPrimary', - 'colorSecondary', - 'colorTertiary', - 'colorInfo', - 'colorWarning', - 'colorSuccess', - 'overlapRectangular', - 'overlapCircular', -]); - -export default badgeClasses; diff --git a/packages/mui-material-next/src/Badge/index.ts b/packages/mui-material-next/src/Badge/index.ts deleted file mode 100644 index 8887c322ea8710..00000000000000 --- a/packages/mui-material-next/src/Badge/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './Badge'; -export * from './Badge.types'; -export { default as badgeClasses } from './badgeClasses'; -export * from './badgeClasses'; diff --git a/packages/mui-material-next/src/Button/Button.spec.tsx b/packages/mui-material-next/src/Button/Button.spec.tsx deleted file mode 100644 index 1260f9caa21a3e..00000000000000 --- a/packages/mui-material-next/src/Button/Button.spec.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import * as React from 'react'; -import { Link as ReactRouterLink, LinkProps } from 'react-router-dom'; -import { expectType } from '@mui/types'; -import Button, { ButtonProps } from '@mui/material-next/Button'; - -const log = console.log; - -const TestOverride = React.forwardRef<HTMLDivElement, { x?: number }>((props, ref) => ( - <div ref={ref} /> -)); - -const FakeIcon = <div>Icon</div>; - -const buttonTest = () => ( - <div> - <Button>I am a button!</Button> - <Button color="secondary">Secondary</Button> - <Button disabled>Disabled</Button> - <Button href="#link-button">Link</Button> - <Button size="small">Small</Button> - <Button variant="filled">Contained</Button> - <Button variant="outlined" aria-label="add"> - Outlined - </Button> - <Button tabIndex={1} title="some button"> - Title - </Button> - <Button component="a">Simple Link</Button> - <Button component={(props: React.HTMLAttributes<HTMLAnchorElement>) => <a {...props} />}> - Complex Link - </Button> - <Button component={ReactRouterLink} to="/open-collective"> - Link - </Button> - <Button href="/open-collective">Link</Button> - <Button component={ReactRouterLink} to="/open-collective"> - Link - </Button> - <Button href="/open-collective">Link</Button> - {/* By default the underlying component is a button element */} - <Button - ref={(elem) => { - expectType<HTMLButtonElement | null, typeof elem>(elem); - }} - onClick={(e) => { - expectType<React.MouseEvent<HTMLButtonElement, MouseEvent>, typeof e>(e); - log(e); - }} - > - Button - </Button> - {/* If an href is provided, an anchor is used */} - <Button - href="/open-collective" - ref={(elem) => { - expectType<HTMLAnchorElement | null, typeof elem>(elem); - }} - onClick={(e) => { - expectType<React.MouseEvent<HTMLAnchorElement, MouseEvent>, typeof e>(e); - log(e); - }} - > - Link - </Button> - {/* If a component prop is specified, use that: */} - <Button<'div'> - component="div" - ref={(elem) => { - expectType<HTMLDivElement | null, typeof elem>(elem); - }} - onClick={(e) => { - expectType<React.MouseEvent<HTMLDivElement, MouseEvent>, typeof e>(e); - log(e); - }} - > - Div - </Button> - { - // Can't have an onClick handler if the overriding component doesn't specify one: - // @ts-expect-error - <Button<typeof TestOverride> component={TestOverride} onClick={log}> - TestOverride - </Button> - } - <Button startIcon={FakeIcon}>Start Icon</Button> - <Button endIcon={FakeIcon}>endIcon</Button> - </div> -); - -const ReactRouterLinkTest = () => { - function ButtonLink(props: ButtonProps<typeof ReactRouterLink>) { - return <Button {...props} component={ReactRouterLink} />; - } - - const reactRouterButtonLink1 = <ButtonLink to="/">Go Home</ButtonLink>; - - const MyLink = React.forwardRef<HTMLAnchorElement, LinkProps>((props, ref) => { - return <ReactRouterLink ref={ref} {...props} />; - }); - - const reactRouterButtonLink2 = ( - <Button component={MyLink} to="/router-future"> - Go Home - </Button> - ); -}; diff --git a/packages/mui-material-next/src/Button/Button.test.js b/packages/mui-material-next/src/Button/Button.test.js deleted file mode 100644 index bf13fae97f51b5..00000000000000 --- a/packages/mui-material-next/src/Button/Button.test.js +++ /dev/null @@ -1,239 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { spy } from 'sinon'; -import { createRenderer, fireEvent, act } from '@mui-internal/test-utils'; -import { camelCase } from 'lodash'; -import Button, { buttonClasses as classes } from '@mui/material-next/Button'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import describeConformance from '../../test/describeConformance'; - -describe('<Button />', () => { - const { render, renderToString } = createRenderer(); - - describeConformance(<Button startIcon="icon">Conformance?</Button>, () => ({ - classes, - render, - inheritComponent: 'button', - refInstanceof: window.HTMLButtonElement, - muiName: 'MuiButton', - testDeepOverrides: { slotName: 'startIcon', slotClassName: classes.startIcon }, - testVariantProps: { variant: 'contained', fullWidth: true }, - testStateOverrides: { prop: 'size', value: 'small', styleKey: 'sizeSmall' }, - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - skip: ['componentsProp'], - })); - - it('should render with the root, text and colorPrimary classes but no others', () => { - const { getByRole } = render(<Button>Hello World</Button>); - const button = getByRole('button'); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes.text); - expect(button).to.have.class(classes.colorPrimary); - expect(button).not.to.have.class(classes.filled); - expect(button).not.to.have.class(classes.filledTonal); - expect(button).not.to.have.class(classes.outlined); - expect(button).not.to.have.class(classes.elevated); - expect(button).not.to.have.class(classes.colorSecondary); - expect(button).not.to.have.class(classes.colorTertiary); - expect(button).not.to.have.class(classes.sizeSmall); - expect(button).not.to.have.class(classes.sizeLarge); - }); - - it('should render a text secondary button', () => { - const { getByRole } = render(<Button color="secondary">Hello World</Button>); - const button = getByRole('button'); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes.text); - expect(button).to.have.class(classes.colorSecondary); - expect(button).not.to.have.class(classes.colorPrimary); - }); - - ['filled', 'filledTonal', 'outlined', 'elevated'].forEach((variant) => { - it(`should render an ${variant} button`, () => { - const { getByRole } = render(<Button variant={variant}>Hello World</Button>); - const button = getByRole('button'); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes[variant]); - expect(button).to.have.class(classes.colorPrimary); - expect(button).not.to.have.class(classes.text); - }); - - // these two variants do not support different colors - if (variant !== 'elevated' && variant !== 'filledTonal') { - it(`should render an ${variant} secondary button`, () => { - const { getByRole } = render( - <Button variant={variant} color="secondary"> - Hello World - </Button>, - ); - const button = getByRole('button'); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes[variant]); - expect(button).to.have.class(classes.colorSecondary); - expect(button).not.to.have.class(classes.text); - expect(button).not.to.have.class(classes.colorPrimary); - }); - - it(`should render an ${variant} tertiary button`, () => { - const { getByRole } = render( - <Button variant={variant} color="tertiary"> - Hello World - </Button>, - ); - const button = getByRole('button'); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes[variant]); - expect(button).to.have.class(classes.colorTertiary); - expect(button).not.to.have.class(classes.text); - expect(button).not.to.have.class(classes.colorPrimary); - }); - } - }); - - it('should render a small button', () => { - const { getByRole } = render(<Button size="small">Hello World</Button>); - const button = getByRole('button'); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes.sizeSmall); - expect(button).not.to.have.class(classes.sizeMedium); - expect(button).not.to.have.class(classes.sizeLarge); - }); - - it('should render a large button', () => { - const { getByRole } = render(<Button size="large">Hello World</Button>); - const button = getByRole('button'); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes.sizeLarge); - expect(button).not.to.have.class(classes.sizeMedium); - expect(button).not.to.have.class(classes.sizeSmall); - }); - - it('should render a button with startIcon', () => { - const { getByRole } = render(<Button startIcon={<span>icon</span>}>Hello World</Button>); - const button = getByRole('button'); - const startIcon = button.querySelector(`.${classes.startIcon}`); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes.text); - expect(startIcon).not.to.have.class(classes.endIcon); - }); - - it('should render a button with endIcon', () => { - const { getByRole } = render(<Button endIcon={<span>icon</span>}>Hello World</Button>); - const button = getByRole('button'); - const endIcon = button.querySelector(`.${classes.endIcon}`); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes.text); - expect(endIcon).not.to.have.class(classes.startIcon); - }); - - it('can disable the elevation', () => { - const { getByRole } = render(<Button disableElevation>Hello World</Button>); - const button = getByRole('button'); - - expect(button).to.have.class(classes.disableElevation); - }); - - describe('server-side', () => { - before(function beforeHook() { - // Only run the test on node. - if (!/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - }); - - it('should server-side render', () => { - const { container } = renderToString(<Button>Hello World</Button>); - expect(container.firstChild).to.have.text('Hello World'); - }); - }); - - it('should automatically change the button to an anchor element when href is provided', () => { - const { container } = render(<Button href="https://google.com">Hello</Button>); - const button = container.firstChild; - - expect(button).to.have.property('nodeName', 'A'); - expect(button).not.to.have.attribute('role'); - expect(button).not.to.have.attribute('type'); - expect(button).to.have.attribute('href', 'https://google.com'); - }); - - it('should render disabled class', () => { - const disabledClassName = 'testDisabledClassName'; - const { container } = render(<Button disabled classes={{ disabled: disabledClassName }} />); - - expect(container.querySelector('button')).to.have.class(disabledClassName); - }); - - it('should render focused class', () => { - const focusedClassName = 'testFocusedClassName'; - const { container } = render(<Button classes={{ focusVisible: focusedClassName }} />); - - const button = container.querySelector('button'); - expect(button).not.to.equal(null); - expect(button).not.to.have.class(focusedClassName); - - act(() => { - button.focus(); - }); - - expect(button).to.have.class(focusedClassName); - }); - - it('should render active class', () => { - const activeClassName = 'testActiveClassName'; - const { container } = render(<Button classes={{ active: activeClassName }} />); - - const button = container.querySelector('button'); - expect(button).not.to.equal(null); - expect(button).not.to.have.class(activeClassName); - - fireEvent.mouseDown(button); - - expect(button).to.have.class(activeClassName); - }); - - describe('Event handlers', () => { - const events = ['click', 'focus', 'mouse-down', 'mouse-up']; - const withFocusEvents = ['key-down', 'key-up']; - - const eventHandlers = [ - ...events.map((event) => ({ - name: camelCase(`on-${event}`), - triggerFunction: fireEvent[camelCase(event)], - })), - ...withFocusEvents.map((event) => ({ - name: camelCase(`on-${event}`), - triggerFunction: (target) => { - target.focus(); - fireEvent[camelCase(event)](document.activeElement); - }, - })), - ]; - - eventHandlers.forEach(({ name, triggerFunction }) => { - it(`should call ${name} handler`, () => { - const handleSpy = spy(); - const handlerProp = { [name]: handleSpy }; - - const { container } = render(<Button {...handlerProp} />); - const button = container.querySelector('button'); - - act(() => { - triggerFunction(button); - }); - - expect(handleSpy.callCount).to.equal(1); - }); - }); - }); -}); diff --git a/packages/mui-material-next/src/Button/Button.tsx b/packages/mui-material-next/src/Button/Button.tsx deleted file mode 100644 index 724927f3ff6727..00000000000000 --- a/packages/mui-material-next/src/Button/Button.tsx +++ /dev/null @@ -1,528 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { - unstable_capitalize as capitalize, - internal_resolveProps as resolveProps, -} from '@mui/utils'; -import { useSlotProps } from '@mui/base/utils'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { useThemeProps, alpha, shouldForwardProp } from '@mui/system'; -import { MD3ColorSchemeTokens, styled } from '../styles'; -import { getButtonUtilityClass } from './buttonClasses'; -import buttonBaseClasses from '../ButtonBase/buttonBaseClasses'; -import { ButtonProps, ExtendButton, ButtonTypeMap, ButtonOwnerState } from './Button.types'; -import ButtonBase from '../ButtonBase'; -import ButtonGroupButtonContext from '../ButtonGroup/ButtonGroupButtonContext'; -import ButtonGroupContext from '../ButtonGroup/ButtonGroupContext'; - -const useUtilityClasses = (ownerState: ButtonOwnerState) => { - const { classes, color, disableElevation, fullWidth, size, variant } = ownerState; - - const slots = { - root: [ - 'root', - variant, - `color${capitalize(color ?? '')}`, - `size${capitalize(size ?? '')}`, - disableElevation && 'disableElevation', - fullWidth && 'fullWidth', - ], - label: ['label'], - startIcon: ['startIcon', `iconSize${capitalize(size ?? '')}`], - endIcon: ['endIcon', `iconSize${capitalize(size ?? '')}`], - }; - - const composedClasses = composeClasses(slots, getButtonUtilityClass, classes); - - return { - ...classes, // forward the focused, disabled, etc. classes to the ButtonBase - ...composedClasses, - }; -}; - -const commonIconStyles = ({ size }: ButtonOwnerState) => ({ - color: 'var(--md-comp-button-icon-color)', - ...(size === 'small' && { - '& > *:nth-of-type(1)': { - fontSize: 18, - }, - }), - ...(size === 'medium' && { - '& > *:nth-of-type(1)': { - fontSize: 20, - }, - }), - ...(size === 'large' && { - '& > *:nth-of-type(1)': { - fontSize: 22, - }, - }), -}); - -export const ButtonRoot = styled(ButtonBase, { - name: 'MuiButton', - slot: 'Root', - shouldForwardProp: (prop) => shouldForwardProp(prop), - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - styles[ownerState.variant], - styles[`${ownerState.variant}${capitalize(ownerState.color)}`], - styles[`size${capitalize(ownerState.size)}`], - styles[`${ownerState.variant}Size${capitalize(ownerState.size)}`], - ownerState.color === 'inherit' && styles.colorInherit, - ownerState.disableElevation && styles.disableElevation, - ownerState.fullWidth && styles.fullWidth, - ]; - }, -})<{ ownerState: ButtonOwnerState }>(({ ownerState, theme }) => { - const tokens = theme.vars || theme; - - const containerColor = { - elevated: `linear-gradient(0deg, rgba(103, 80, 164, 0.05), rgba(103, 80, 164, 0.05)), ${tokens.sys.color.surface}`, - filled: tokens.sys.color[ownerState.color ?? 'primary'], - filledTonal: tokens.sys.color.secondaryContainer, - outlined: 'transparent', - text: 'transparent', - }; - - const labelTextColor = { - elevated: tokens.sys.color.primary, - filled: - tokens.sys.color[ - `on${capitalize(ownerState.color ?? 'primary')}` as keyof MD3ColorSchemeTokens - ], - filledTonal: tokens.sys.color.onSecondaryContainer, - outlined: tokens.sys.color[ownerState.color ?? 'primary'], - text: tokens.sys.color[ownerState.color ?? 'primary'], - }; - - const disabledContainerColor = { - elevated: theme.vars - ? `rgba(${theme.vars.sys.color.onSurfaceChannel} / 0.12)` - : alpha(theme.sys.color.onSurface, 0.12), - filled: theme.vars - ? `rgba(${theme.vars.sys.color.onSurfaceChannel} / 0.12)` - : alpha(theme.sys.color.onSurface, 0.12), - filledTonal: theme.vars - ? `rgba(${theme.vars.sys.color.onSurfaceChannel} / 0.12)` - : alpha(theme.sys.color.onSurface, 0.12), - outlined: 'transparent', - text: 'transparent', - }; - - const hoveredContainerColor = { - elevated: theme.vars - ? `rgba(${tokens.sys.color.primaryChannel} / ${tokens.sys.state.hover.stateLayerOpacity})` - : alpha(theme.sys.color.primary, theme.sys.state.hover.stateLayerOpacity), - filled: theme.vars - ? `rgba(${tokens.sys.color[`${ownerState.color ?? 'primary'}Channel`]} / calc(1 - ${ - tokens.sys.state.hover.stateLayerOpacity - }))` - : alpha( - theme.sys.color[ownerState.color ?? 'primary'], - 1 - theme.sys.state.hover.stateLayerOpacity, - ), - filledTonal: theme.vars - ? `rgba(${tokens.sys.color.secondaryContainerChannel} / calc(1 - ${tokens.sys.state.hover.stateLayerOpacity}))` - : alpha(theme.sys.color.secondaryContainer, 1 - theme.sys.state.hover.stateLayerOpacity), - outlined: theme.vars - ? `rgba(${tokens.sys.color[`${ownerState.color ?? 'primary'}Channel`]} / ${ - tokens.sys.state.hover.stateLayerOpacity - })` - : alpha( - theme.sys.color[ownerState.color ?? 'primary'], - theme.sys.state.hover.stateLayerOpacity, - ), - text: theme.vars - ? `rgba(${tokens.sys.color[`${ownerState.color ?? 'primary'}Channel`]} / ${ - tokens.sys.state.hover.stateLayerOpacity - })` - : alpha( - theme.sys.color[ownerState.color ?? 'primary'], - theme.sys.state.hover.stateLayerOpacity, - ), - }; - - const pressedContainerColor = { - elevated: theme.vars - ? `rgba(${tokens.sys.color.primaryChannel} / ${tokens.sys.state.pressed.stateLayerOpacity})` - : alpha(theme.sys.color.primary, theme.sys.state.pressed.stateLayerOpacity), - filled: theme.vars - ? `rgba(${tokens.sys.color[`${ownerState.color ?? 'primary'}Channel`]} / calc(1 - ${ - tokens.sys.state.pressed.stateLayerOpacity - }))` - : alpha( - theme.sys.color[ownerState.color ?? 'primary'], - 1 - theme.sys.state.pressed.stateLayerOpacity, - ), - filledTonal: theme.vars - ? `rgba(${tokens.sys.color.secondaryContainerChannel} / calc(1 - ${tokens.sys.state.pressed.stateLayerOpacity}))` - : alpha(theme.sys.color.secondaryContainer, 1 - theme.sys.state.pressed.stateLayerOpacity), - outlined: theme.vars - ? `rgba(${tokens.sys.color[`${ownerState.color ?? 'primary'}Channel`]} / ${ - tokens.sys.state.pressed.stateLayerOpacity - })` - : alpha( - theme.sys.color[ownerState.color ?? 'primary'], - theme.sys.state.pressed.stateLayerOpacity, - ), - text: theme.vars - ? `rgba(${tokens.sys.color[`${ownerState.color ?? 'primary'}Channel`]} / ${ - tokens.sys.state.pressed.stateLayerOpacity - })` - : alpha( - theme.sys.color[ownerState.color ?? 'primary'], - theme.sys.state.pressed.stateLayerOpacity, - ), - }; - - const focusedContainerColor = { - elevated: theme.vars - ? `rgba(${tokens.sys.color.primaryChannel} / ${tokens.sys.state.focus.stateLayerOpacity})` - : alpha(theme.sys.color.primary, theme.sys.state.focus.stateLayerOpacity), - filled: theme.vars - ? `rgba(${tokens.sys.color[`${ownerState.color ?? 'primary'}Channel`]} / calc(1 - ${ - tokens.sys.state.focus.stateLayerOpacity - }))` - : alpha( - theme.sys.color[ownerState.color ?? 'primary'], - 1 - theme.sys.state.focus.stateLayerOpacity, - ), - // According to the spec, this should be: secondaryContainerChannel / 1 - focusStateLayerOpacity, but this doesn't have the enough contrast - filledTonal: theme.vars - ? `rgba(${tokens.sys.color.primaryChannel} / 0.3)` - : alpha(theme.sys.color.primary, 0.3), - outlined: theme.vars - ? `rgba(${tokens.sys.color[`${ownerState.color ?? 'primary'}Channel`]} / ${ - tokens.sys.state.focus.stateLayerOpacity - })` - : alpha( - theme.sys.color[ownerState.color ?? 'primary'], - theme.sys.state.focus.stateLayerOpacity, - ), - text: theme.vars - ? `rgba(${tokens.sys.color[`${ownerState.color ?? 'primary'}Channel`]} / ${ - tokens.sys.state.focus.stateLayerOpacity - })` - : alpha( - theme.sys.color[ownerState.color ?? 'primary'], - theme.sys.state.focus.stateLayerOpacity, - ), - }; - - const containerElevation = { - elevated: tokens.sys.elevation[1], - filled: tokens.sys.elevation[0], - filledTonal: tokens.sys.elevation[0], - outlined: tokens.sys.elevation[0], - text: tokens.sys.elevation[0], - }; - - const hoveredContainerElevation = { - elevated: tokens.sys.elevation[2], - filled: tokens.sys.elevation[1], - filledTonal: tokens.sys.elevation[1], - outlined: tokens.sys.elevation[0], - text: tokens.sys.elevation[0], - }; - - const focusedContainerElevation = { - elevated: tokens.sys.elevation[1], - filled: tokens.sys.elevation[0], - filledTonal: tokens.sys.elevation[0], - outlined: tokens.sys.elevation[0], - text: tokens.sys.elevation[0], - }; - - const pressedContainerElevation = { - elevated: tokens.sys.elevation[1], - filled: tokens.sys.elevation[0], - filledTonal: tokens.sys.elevation[0], - outlined: tokens.sys.elevation[0], - text: tokens.sys.elevation[0], - }; - - const disabledLabelTextColor = theme.vars - ? `rgba(${theme.vars.sys.color.onSurfaceChannel} / 0.38)` - : alpha(theme.sys.color.onSurface, 0.38); - - const letterSpacing = `${ - theme.sys.typescale.label.large.tracking / theme.sys.typescale.label.large.size - }rem`; - - const borderRadiusValue: string | number = tokens.sys.shape.corner.full; - const borderRadius = Number.isNaN(Number(borderRadiusValue)) - ? borderRadiusValue - : `${borderRadiusValue}px`; - - return { - // Icon variables default values - '--md-comp-button-icon-color': labelTextColor[ownerState.variant ?? 'text'], - '--md-comp-button-hovered-icon-color': labelTextColor[ownerState.variant ?? 'text'], // same as default - '--md-comp-button-pressed-icon-color': labelTextColor[ownerState.variant ?? 'text'], // same as default - '--md-comp-button-focused-icon-color': labelTextColor[ownerState.variant ?? 'text'], // same as default - '--md-comp-button-disabled-icon-color': disabledLabelTextColor, - padding: '10px 24px', - minWidth: 64, - letterSpacing, - transition: theme.sys.motion.create( - ['background-color', 'box-shadow', 'border-color', 'color'], - { - duration: tokens.sys.motion.duration.short3, - }, - ), - fontFamily: tokens.sys.typescale.label.large.family, - fontWeight: tokens.sys.typescale.label.large.weight, - fontSize: theme.typography.pxToRem(theme.sys.typescale.label.large.size), // the pxToRem should be moved to typescale in the future - lineHeight: `calc(${tokens.sys.typescale.label.large.lineHeight} / ${theme.sys.typescale.label.large.size})`, - borderRadius: `var(--Button-radius, ${borderRadius})`, - backgroundColor: containerColor[ownerState.variant ?? 'text'], - color: labelTextColor[ownerState.variant ?? 'text'], - boxShadow: containerElevation[ownerState.variant ?? 'text'], - // Outlined variant - ...(ownerState.variant === 'outlined' && { - border: `1px solid ${tokens.sys.color.outline}`, - padding: '9px 23px', - }), - '--Button-gap': '0.5rem', - // Sizes are not specified in Material Design 3, this need to be revised - ...(ownerState.size === 'small' && { - '--Button-gap': '0.45rem', - fontSize: theme.typography.pxToRem(theme.sys.typescale.label.large.size - 1), // the pxToRem should be moved to typescale in the future - padding: '8px 20px', - ...(ownerState.variant === 'outlined' && { - padding: '7px 19px', - }), - }), - ...(ownerState.size === 'large' && { - '--Button-gap': '0.55rem', - fontSize: theme.typography.pxToRem(theme.sys.typescale.label.large.size + 1), // the pxToRem should be moved to typescale in the future - padding: '12px 26px', - ...(ownerState.variant === 'outlined' && { - padding: '11px 25px', - }), - }), - '&:hover': { - '--md-comp-button-icon-color': 'var(--md-comp-button-hovered-icon-color)', - backgroundColor: hoveredContainerColor[ownerState.variant ?? 'text'], - boxShadow: hoveredContainerElevation[ownerState.variant ?? 'text'], - }, - '&:active': { - '--md-comp-button-icon-color': 'var(--md-comp-button-pressed-icon-color)', - ...((ownerState.disableRipple || ownerState.disableTouchRipple) && { - backgroundColor: pressedContainerColor[ownerState.variant ?? 'text'], - }), - boxShadow: pressedContainerElevation[ownerState.variant ?? 'text'], - }, - [`&.${buttonBaseClasses.focusVisible}`]: { - '--md-comp-button-icon-color': 'var(--md-comp-button-focused-icon-color)', - backgroundColor: focusedContainerColor[ownerState.variant ?? 'text'], - boxShadow: focusedContainerElevation[ownerState.variant ?? 'text'], - }, - [`&.${buttonBaseClasses.disabled}`]: { - // Allows developer to specify the disabled icon color var - '--md-comp-button-icon-color': 'var(--md-comp-button-disabled-icon-color)', - color: disabledLabelTextColor, - backgroundColor: disabledContainerColor[ownerState.variant ?? 'text'], - boxShadow: tokens.sys.elevation[0], - ...(ownerState.variant === 'outlined' && { - border: `1px solid ${ - theme.vars - ? `rgba(${theme.vars.sys.color.onSurfaceChannel} / 0.12)` - : alpha(theme.sys.color.onSurface, 0.12) - }`, - }), - }, - }; -}); - -const ButtonStartIcon = styled('span', { - name: 'MuiButton', - slot: 'StartIcon', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [styles.startIcon, styles[`iconSize${capitalize(ownerState.size)}`]]; - }, -})<{ ownerState: ButtonOwnerState }>(({ ownerState }) => ({ - display: 'inherit', - marginRight: 'var(--Button-gap)', - ...commonIconStyles(ownerState), -})); - -const ButtonEndIcon = styled('span', { - name: 'MuiButton', - slot: 'EndIcon', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [styles.endIcon, styles[`iconSize${capitalize(ownerState.size)}`]]; - }, -})<{ ownerState: ButtonOwnerState }>(({ ownerState }) => ({ - display: 'inherit', - marginLeft: 'var(--Button-gap)', - ...commonIconStyles(ownerState), -})); - -const Button = React.forwardRef(function Button< - BaseComponentType extends React.ElementType = ButtonTypeMap['defaultComponent'], ->(inProps: ButtonProps<BaseComponentType>, ref: React.ForwardedRef<any>) { - const contextProps = React.useContext(ButtonGroupContext); - const buttonGroupButtonContextPositionClassName = React.useContext(ButtonGroupButtonContext); - const resolvedProps = resolveProps( - (contextProps ?? {}) as ButtonProps<BaseComponentType>, - inProps, - ); - const props = useThemeProps({ props: resolvedProps, name: 'MuiButton' }); - const { - children, - classes: classesProp, - color = 'primary', - disableElevation = false, - endIcon: endIconProp, - fullWidth = false, - size = 'medium', - startIcon: startIconProp, - variant = 'text', - ...other - } = props; - - const ownerState = { - ...props, - classes: classesProp, - color, - disableElevation, - fullWidth, - size, - variant, - }; - - const classes = useUtilityClasses(ownerState); - - const positionClassName = buttonGroupButtonContextPositionClassName ?? ''; - - const rootProps = useSlotProps({ - elementType: ButtonRoot, - externalForwardedProps: other, - externalSlotProps: {}, - additionalProps: { - classes, - ref, - }, - ownerState, - className: clsx(contextProps?.className, positionClassName), - }); - - const startIcon = startIconProp && ( - <ButtonStartIcon className={classes.startIcon} ownerState={ownerState}> - {startIconProp} - </ButtonStartIcon> - ); - - const endIcon = endIconProp && ( - <ButtonEndIcon className={classes.endIcon} ownerState={ownerState}> - {endIconProp} - </ButtonEndIcon> - ); - - return ( - <ButtonRoot {...rootProps}> - {startIcon} - {children} - {endIcon} - </ButtonRoot> - ); -}) as ExtendButton<ButtonTypeMap>; - -Button.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, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['primary', 'secondary', 'tertiary']), - PropTypes.string, - ]), - /** - * If `true`, no elevation is used. - * @default false - */ - disableElevation: PropTypes.bool, - /** - * If `true`, the ripple effect is disabled. - * @default false - */ - disableRipple: PropTypes.bool, - /** - * If `true`, the touch ripple effect is disabled. - * @default false - */ - disableTouchRipple: PropTypes.bool, - /** - * Element placed after the children. - */ - endIcon: PropTypes.node, - /** - * If `true`, the button will take up the full width of its container. - * @default false - */ - fullWidth: PropTypes.bool, - /** - * @ignore - */ - href: PropTypes.string, - /** - * The size of the component. - * `small` is equivalent to the dense button styling. - * @default 'medium' - */ - size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['small', 'medium', 'large']), - PropTypes.string, - ]), - /** - * Element placed before the children. - */ - startIcon: PropTypes.node, - /** - * 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, - ]), - /** - * @default 0 - */ - tabIndex: PropTypes.number, - /** - * The variant to use. - * @default 'text' - */ - variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['text', 'outlined', 'filled', 'filledTonal', 'elevated']), - PropTypes.string, - ]), -} as any; - -export default Button; diff --git a/packages/mui-material-next/src/Button/Button.types.ts b/packages/mui-material-next/src/Button/Button.types.ts deleted file mode 100644 index ad10e8c796d020..00000000000000 --- a/packages/mui-material-next/src/Button/Button.types.ts +++ /dev/null @@ -1,115 +0,0 @@ -import * as React from 'react'; -import { - OverridableStringUnion, - OverrideProps, - OverridableComponent, - OverridableTypeMap, -} from '@mui/types'; -import { SxProps } from '../styles/Theme.types'; -import { ButtonClasses } from './buttonClasses'; - -export interface ButtonPropsVariantOverrides {} - -export interface ButtonPropsColorOverrides {} - -export interface ButtonPropsSizeOverrides {} - -export interface ButtonActions { - focusVisible(): void; -} - -export type ButtonTypeMap< - AdditionalProps = {}, - DefaultComponent extends React.ElementType = 'button', -> = { - props: AdditionalProps & { - /** - * The content of the component. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<ButtonClasses>; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color?: OverridableStringUnion<'primary' | 'secondary' | 'tertiary', ButtonPropsColorOverrides>; - /** - * If `true`, no elevation is used. - * @default false - */ - disableElevation?: boolean; - /** - * If `true`, the ripple effect is disabled. - * @default false - */ - disableRipple?: boolean; - /** - * If `true`, the touch ripple effect is disabled. - * @default false - */ - disableTouchRipple?: boolean; - /** - * Element placed after the children. - */ - endIcon?: React.ReactNode; - /** - * If `true`, the button will take up the full width of its container. - * @default false - */ - fullWidth?: boolean; - /** - * The size of the component. - * `small` is equivalent to the dense button styling. - * @default 'medium' - */ - size?: OverridableStringUnion<'small' | 'medium' | 'large', ButtonPropsSizeOverrides>; - /** - * Element placed before the children. - */ - startIcon?: React.ReactNode; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; - /** - * @default 0 - */ - tabIndex?: NonNullable<React.HTMLAttributes<any>['tabIndex']>; - /** - * The variant to use. - * @default 'text' - */ - variant?: OverridableStringUnion< - 'text' | 'outlined' | 'filled' | 'filledTonal' | 'elevated', - ButtonPropsVariantOverrides - >; - }; - defaultComponent: DefaultComponent; -}; - -export interface ButtonOwnerState extends ButtonProps {} - -/** - * A utility to create component types that inherit props from the Button. - * This component has an additional overload if the `href` prop is set which - * can make extension quite tricky - */ -export interface ExtendButtonTypeMap<TypeMap extends OverridableTypeMap> { - props: TypeMap['props'] & ButtonTypeMap['props']; - defaultComponent: TypeMap['defaultComponent']; -} - -export type ExtendButton<TypeMap extends OverridableTypeMap> = (( - props: { href: string } & OverrideProps<ExtendButtonTypeMap<TypeMap>, 'a'>, -) => JSX.Element) & - OverridableComponent<ExtendButtonTypeMap<TypeMap>> & { propTypes?: any }; - -export type ButtonProps< - RootComponent extends React.ElementType = ButtonTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<ButtonTypeMap<AdditionalProps, RootComponent>, RootComponent>; diff --git a/packages/mui-material-next/src/Button/buttonClasses.ts b/packages/mui-material-next/src/Button/buttonClasses.ts deleted file mode 100644 index ac219fe771e49f..00000000000000 --- a/packages/mui-material-next/src/Button/buttonClasses.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface ButtonClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `variant="text"`. */ - text: string; - /** Styles applied to the root element if `variant="outlined"`. */ - outlined: string; - /** Styles applied to the root element if `variant="filled"`. */ - filled: string; - /** Styles applied to the root element if `variant="filledTonal"`. */ - filledTonal: string; - /** Styles applied to the root element if `variant="elevated"`. */ - elevated: string; - /** Styles applied to the root element if `disableElevation={true}`. */ - disableElevation: string; - /** Styles applied to the root element if `color="primary"`. */ - colorPrimary: string; - /** Styles applied to the root element if `color="secondary"`. */ - colorSecondary: string; - /** Styles applied to the root element if `color="tertiary"`. */ - colorTertiary: string; - /** Styles applied to the root element if `size="small"`. */ - sizeSmall: string; - /** Styles applied to the root element if `size="medium"`. */ - sizeMedium: string; - /** Styles applied to the root element if `size="large"`. */ - sizeLarge: string; - /** Styles applied to the root element if `fullWidth={true}`. */ - fullWidth: string; - /** Styles applied to the startIcon element if supplied. */ - startIcon: string; - /** Styles applied to the endIcon element if supplied. */ - endIcon: string; - /** Styles applied to the icon element if supplied and `size="small"`. */ - iconSizeSmall: string; - /** Styles applied to the icon element if supplied and `size="medium"`. */ - iconSizeMedium: string; - /** Styles applied to the icon element if supplied and `size="large"`. */ - iconSizeLarge: string; -} - -export type ButtonClassKey = keyof ButtonClasses; - -export function getButtonUtilityClass(slot: string): string { - return generateUtilityClass('MuiButton', slot); -} - -const buttonClasses: ButtonClasses = generateUtilityClasses('MuiButton', [ - 'root', - 'text', - 'outlined', - 'filled', - 'filledTonal', - 'elevated', - 'colorPrimary', - 'colorSecondary', - 'colorTertiary', - 'disableElevation', - 'colorInherit', - 'sizeSmall', - 'sizeMedium', - 'sizeLarge', - 'fullWidth', - 'startIcon', - 'endIcon', - 'iconSizeSmall', - 'iconSizeMedium', - 'iconSizeLarge', -]); - -export default buttonClasses; diff --git a/packages/mui-material-next/src/Button/index.ts b/packages/mui-material-next/src/Button/index.ts deleted file mode 100644 index 226bd02a0f124c..00000000000000 --- a/packages/mui-material-next/src/Button/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -'use client'; -export { default } from './Button'; -export * from './Button'; - -export * from './Button.types'; - -export { default as buttonClasses } from './buttonClasses'; -export * from './buttonClasses'; diff --git a/packages/mui-material-next/src/ButtonBase/ButtonBase.test.tsx b/packages/mui-material-next/src/ButtonBase/ButtonBase.test.tsx deleted file mode 100644 index 6d4b178153bee7..00000000000000 --- a/packages/mui-material-next/src/ButtonBase/ButtonBase.test.tsx +++ /dev/null @@ -1,1089 +0,0 @@ -// @ts-check -import * as React from 'react'; -import { expect } from 'chai'; -import { spy, stub } from 'sinon'; -import userEvent from '@testing-library/user-event'; -import { - act, - createRenderer, - fireEvent, - screen, - focusVisible, - simulatePointerDevice, - programmaticFocusTriggersFocusVisible, -} from '@mui-internal/test-utils'; -import PropTypes from 'prop-types'; -import { MuiCancellableEventHandler } from '@mui/base/utils/MuiCancellableEvent'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import ButtonBase, { buttonBaseClasses as classes } from '@mui/material-next/ButtonBase'; -import { ButtonBaseActions } from './ButtonBase.types'; -import { TouchRippleActions } from './TouchRipple.types'; -import describeConformance from '../../test/describeConformance'; - -// TODO v6: initialize @testing-library/user-event using userEvent.setup() instead of directly calling methods e.g. userEvent.click() for all related tests in this file -// currently the setup() method uses the ClipboardEvent constructor which is incompatible with our lowest supported version of iOS Safari (12.2) https://github.com/mui/material-ui/blob/master/.browserslistrc#L44 -// userEvent.setup() requires Safari 14 or up to work - -describe('<ButtonBase />', () => { - const { render } = createRenderer(); - - // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/14156632/ - let canFireDragEvents = true; - - before(() => { - // browser testing config - try { - const EventConstructor = window.DragEvent || window.Event; - // eslint-disable-next-line no-new - new EventConstructor(''); - } catch (err) { - canFireDragEvents = false; - } - }); - - let originalMatchmedia: typeof window.matchMedia; - - beforeEach(() => { - originalMatchmedia = window.matchMedia; - window.matchMedia = () => - ({ - addListener: () => {}, - removeListener: () => {}, - }) as unknown as MediaQueryList; - }); - - afterEach(() => { - window.matchMedia = originalMatchmedia; - }); - - describeConformance(<ButtonBase />, () => ({ - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - classes, - inheritComponent: 'button', - render, - refInstanceof: window.HTMLButtonElement, - testComponentPropWith: 'a', - muiName: 'MuiButtonBase', - testVariantProps: { disabled: true }, - skip: ['componentsProp'], - })); - - describe('root node', () => { - it('should have default button type "button"', () => { - const { getByText, setProps } = render(<ButtonBase>Hello</ButtonBase>); - expect(getByText('Hello')).to.have.attribute('type', 'button'); - - setProps({ type: undefined }); - expect(getByText('Hello')).to.have.attribute('type', 'button'); - }); - - it('should change the button type', () => { - const { getByText } = render(<ButtonBase type="submit">Hello</ButtonBase>); - expect(getByText('Hello')).to.have.attribute('type', 'submit'); - }); - - it('should change the button component and add accessibility requirements', () => { - const { getByRole } = render( - <ButtonBase component="span" role="checkbox" aria-checked={false} />, - ); - const checkbox = getByRole('checkbox'); - expect(checkbox).to.have.property('nodeName', 'SPAN'); - expect(checkbox).attribute('tabIndex').to.equal('0'); - }); - - it('should not apply role="button" if type="button"', () => { - const { getByText } = render(<ButtonBase type="button">Hello</ButtonBase>); - expect(getByText('Hello')).not.to.have.attribute('role'); - }); - - it('should change the button type to span and set role="button"', () => { - const { getByRole } = render(<ButtonBase component="span">Hello</ButtonBase>); - const button = getByRole('button'); - - expect(button).to.have.property('nodeName', 'SPAN'); - expect(button).not.to.have.attribute('type'); - }); - - it('should automatically change the button to an anchor element when href is provided', () => { - const { container } = render(<ButtonBase href="https://google.com">Hello</ButtonBase>); - const button = container.firstChild; - - expect(button).to.have.property('nodeName', 'A'); - expect(button).not.to.have.attribute('role'); - expect(button).not.to.have.attribute('type'); - expect(button).to.have.attribute('href', 'https://google.com'); - }); - - it('should use custom LinkComponent when provided in the theme', () => { - const CustomLink = React.forwardRef((props, ref: React.ForwardedRef<HTMLAnchorElement>) => { - // eslint-disable-next-line jsx-a11y/anchor-has-content - return <a data-testid="customLink" ref={ref} {...props} />; - }); - const theme = extendTheme({ - components: { - MuiButtonBase: { - defaultProps: { - LinkComponent: CustomLink, - }, - }, - }, - }); - - const { container, getByTestId } = render( - <CssVarsProvider theme={theme}> - <ButtonBase href="https://google.com">Hello</ButtonBase> - </CssVarsProvider>, - ); - const button = container.firstChild; - expect(getByTestId('customLink')).not.to.equal(null); - expect(button).to.have.property('nodeName', 'A'); - expect(button).to.have.attribute('href', 'https://google.com'); - }); - - it('applies role="button" when an anchor is used without href', () => { - const { getByRole } = render(<ButtonBase component="a">Hello</ButtonBase>); - const button = getByRole('button'); - - expect(button).to.have.property('nodeName', 'A'); - expect(button).not.to.have.attribute('type'); - }); - - it('should not add role="button" if custom component and href are used', () => { - const CustomLink = React.forwardRef((props, ref: React.ForwardedRef<HTMLAnchorElement>) => { - // eslint-disable-next-line jsx-a11y/anchor-has-content - return <a data-testid="customLink" ref={ref} {...props} />; - }); - - const { container } = render( - <ButtonBase component={CustomLink} href="https://google.com"> - Hello - </ButtonBase>, - ); - const button = container.firstChild; - expect(button).to.have.property('nodeName', 'A'); - expect(button).to.have.attribute('data-testid', 'customLink'); - expect(button).to.have.attribute('href', 'https://google.com'); - expect(button).not.to.have.attribute('role', 'button'); - }); - - it('should not add role="button" if custom component and to are used', () => { - const CustomLink = React.forwardRef((props, ref: React.ForwardedRef<HTMLAnchorElement>) => { - // @ts-expect-error missing types in CustomLink - const { to, ...other } = props; - // eslint-disable-next-line jsx-a11y/anchor-has-content - return <a data-testid="customLink" ref={ref} {...other} href={to} />; - }); - - const { container } = render( - // @ts-expect-error missing types in CustomLink - <ButtonBase component={CustomLink} to="https://google.com"> - Hello - </ButtonBase>, - ); - const button = container.firstChild; - expect(button).not.to.have.attribute('role', 'button'); - }); - }); - - describe('event callbacks', () => { - it('should fire event callbacks', () => { - const onClick = spy(); - const onBlur = spy(); - const onFocus = spy(); - const onKeyUp = spy(); - const onKeyDown = spy(); - const onMouseDown = spy(); - const onMouseLeave = spy(); - const onMouseUp = spy(); - const onContextMenu = spy(); - const onDragEnd = spy(); - const onTouchStart = spy(); - const onTouchEnd = spy(); - - const { getByText } = render( - <ButtonBase - onClick={onClick} - onBlur={onBlur} - onFocus={onFocus} - onKeyUp={onKeyUp} - onKeyDown={onKeyDown} - onMouseDown={onMouseDown} - onMouseLeave={onMouseLeave} - onMouseUp={onMouseUp} - onContextMenu={onContextMenu} - onDragEnd={onDragEnd} - onTouchEnd={onTouchEnd} - onTouchStart={onTouchStart} - > - Hello - </ButtonBase>, - ); - const button = getByText('Hello'); - - // only run in supported browsers - if (typeof Touch !== 'undefined') { - const touch = new Touch({ identifier: 0, target: button, clientX: 0, clientY: 0 }); - - fireEvent.touchStart(button, { touches: [touch] }); - expect(onTouchStart.callCount).to.equal(1); - - fireEvent.touchEnd(button, { touches: [touch] }); - expect(onTouchEnd.callCount).to.equal(1); - } - - if (canFireDragEvents) { - fireEvent.dragEnd(button); - expect(onDragEnd.callCount).to.equal(1); - } - - fireEvent.mouseDown(button); - expect(onMouseDown.callCount).to.equal(1); - - fireEvent.mouseUp(button); - expect(onMouseUp.callCount).to.equal(1); - - fireEvent.contextMenu(button); - expect(onContextMenu.callCount).to.equal(1); - - fireEvent.click(button); - expect(onClick.callCount).to.equal(1); - - act(() => { - button.focus(); - }); - expect(onFocus.callCount).to.equal(1); - - fireEvent.keyDown(button); - expect(onKeyDown.callCount).to.equal(1); - - fireEvent.keyUp(button); - expect(onKeyUp.callCount).to.equal(1); - - act(() => { - button.blur(); - }); - expect(onBlur.callCount).to.equal(1); - - fireEvent.mouseLeave(button); - expect(onMouseLeave.callCount).to.equal(1); - }); - }); - - describe('ripple', () => { - describe('interactions', () => { - it('should start the ripple when the mouse is pressed', () => { - const { getByRole } = render( - <ButtonBase - TouchRippleProps={{ - classes: { - rippleVisible: 'ripple-visible', - child: 'child', - childLeaving: 'child-leaving', - }, - }} - />, - ); - const button = getByRole('button'); - - fireEvent.mouseDown(button); - - expect(button.querySelectorAll('.ripple-visible .child-leaving')).to.have.lengthOf(0); - expect( - button.querySelectorAll('.ripple-visible .child:not(.child-leaving)'), - ).to.have.lengthOf(1); - }); - - it('should stop the ripple when the mouse is released', () => { - const { getByRole } = render( - <ButtonBase - TouchRippleProps={{ - classes: { - rippleVisible: 'ripple-visible', - child: 'child', - childLeaving: 'child-leaving', - }, - }} - />, - ); - const button = getByRole('button'); - fireEvent.mouseDown(button); - - fireEvent.mouseUp(button); - - expect(button.querySelectorAll('.ripple-visible .child-leaving')).to.have.lengthOf(1); - expect( - button.querySelectorAll('.ripple-visible .child:not(.child-leaving)'), - ).to.have.lengthOf(0); - }); - - it('should stop the ripple when the button blurs', () => { - const { getByRole } = render( - <ButtonBase - TouchRippleProps={{ - classes: { - rippleVisible: 'ripple-visible', - child: 'child', - childLeaving: 'child-leaving', - }, - }} - />, - ); - const button = getByRole('button'); - fireEvent.mouseDown(button); - - button.blur(); - - expect(button.querySelectorAll('.ripple-visible .child-leaving')).to.have.lengthOf(0); - expect( - button.querySelectorAll('.ripple-visible .child:not(.child-leaving)'), - ).to.have.lengthOf(1); - }); - - it('should restart the ripple when the mouse is pressed again', () => { - const { getByRole } = render( - <ButtonBase - TouchRippleProps={{ - classes: { - rippleVisible: 'ripple-visible', - child: 'child', - childLeaving: 'child-leaving', - }, - }} - />, - ); - const button = getByRole('button'); - - fireEvent.mouseDown(button); - - expect(button.querySelectorAll('.ripple-visible .child-leaving')).to.have.lengthOf(0); - expect( - button.querySelectorAll('.ripple-visible .child:not(.child-leaving)'), - ).to.have.lengthOf(1); - - fireEvent.mouseUp(button); - fireEvent.mouseDown(button); - - expect(button.querySelectorAll('.ripple-visible .child-leaving')).to.have.lengthOf(1); - expect( - button.querySelectorAll('.ripple-visible .child:not(.child-leaving)'), - ).to.have.lengthOf(1); - }); - - it('should stop the ripple when the mouse leaves', () => { - const { getByRole } = render( - <ButtonBase - TouchRippleProps={{ - classes: { - rippleVisible: 'ripple-visible', - child: 'child', - childLeaving: 'child-leaving', - }, - }} - />, - ); - const button = getByRole('button'); - fireEvent.mouseDown(button); - - fireEvent.mouseLeave(button); - - expect(button.querySelectorAll('.ripple-visible .child-leaving')).to.have.lengthOf(1); - expect( - button.querySelectorAll('.ripple-visible .child:not(.child-leaving)'), - ).to.have.lengthOf(0); - }); - - it('should stop the ripple when dragging has finished', function test() { - if (!canFireDragEvents) { - this.skip(); - } - const { getByRole } = render( - <ButtonBase - TouchRippleProps={{ - classes: { - rippleVisible: 'ripple-visible', - child: 'child', - childLeaving: 'child-leaving', - }, - }} - />, - ); - const button = getByRole('button'); - fireEvent.mouseDown(button); - - fireEvent.dragLeave(button); - - expect(button.querySelectorAll('.ripple-visible .child-leaving')).to.have.lengthOf(1); - expect( - button.querySelectorAll('.ripple-visible .child:not(.child-leaving)'), - ).to.have.lengthOf(0); - }); - - it('should stop the ripple when the context menu opens', () => { - const { getByRole } = render( - <ButtonBase - TouchRippleProps={{ - classes: { - rippleVisible: 'ripple-visible', - child: 'child', - childLeaving: 'child-leaving', - }, - }} - />, - ); - const button = getByRole('button'); - fireEvent.mouseDown(button); - - expect(button.querySelectorAll('.ripple-visible .child-leaving')).to.have.lengthOf(0); - expect( - button.querySelectorAll('.ripple-visible .child:not(.child-leaving)'), - ).to.have.lengthOf(1); - - fireEvent.contextMenu(button); - - expect(button.querySelectorAll('.ripple-visible .child-leaving')).to.have.lengthOf(1); - expect( - button.querySelectorAll('.ripple-visible .child:not(.child-leaving)'), - ).to.have.lengthOf(0); - }); - }); - }); - - describe('prop: centerRipple', () => { - it('centers the TouchRipple', () => { - const { container, getByRole } = render( - <ButtonBase - centerRipple - TouchRippleProps={{ classes: { root: 'touch-ripple', ripple: 'touch-ripple-ripple' } }} - > - Hello - </ButtonBase>, - ); - // @ts-ignore - stub(container.querySelector('.touch-ripple'), 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 100, - bottom: 10, - left: 20, - top: 20, - })); - fireEvent.mouseDown(getByRole('button'), { clientX: 10, clientY: 10 }); - const rippleRipple = container.querySelector('.touch-ripple-ripple'); - expect(rippleRipple).not.to.equal(null); - // @ts-ignore - const rippleStyle = window.getComputedStyle(rippleRipple); - expect(rippleStyle).to.have.property('height', '101px'); - expect(rippleStyle).to.have.property('width', '101px'); - }); - - it('is disabled by default', () => { - const { container, getByRole } = render( - <ButtonBase - TouchRippleProps={{ classes: { root: 'touch-ripple', ripple: 'touch-ripple-ripple' } }} - > - Hello - </ButtonBase>, - ); - // @ts-ignore - stub(container.querySelector('.touch-ripple'), 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 100, - bottom: 10, - left: 20, - top: 20, - })); - fireEvent.mouseDown(getByRole('button'), { clientX: 10, clientY: 10 }); - const rippleRipple = container.querySelector('.touch-ripple-ripple'); - expect(rippleRipple).not.to.equal(null); - // @ts-ignore - const rippleStyle = window.getComputedStyle(rippleRipple); - expect(rippleStyle).not.to.have.property('height', '101px'); - expect(rippleStyle).not.to.have.property('width', '101px'); - }); - }); - - describe('prop: disabled', () => { - it('should have a the disabled property', () => { - const { getByText } = render(<ButtonBase disabled>Hello</ButtonBase>); - expect(getByText('Hello')).to.have.property('disabled'); - }); - - it('should forward it to native buttons', () => { - const { getByText } = render( - <ButtonBase disabled component="button"> - Hello - </ButtonBase>, - ); - expect(getByText('Hello')).to.have.property('disabled', true); - }); - - it('should reset the focused state upon being disabled', () => { - const { getByText, setProps } = render(<ButtonBase>Hello</ButtonBase>); - const button = getByText('Hello'); - simulatePointerDevice(); - - focusVisible(button); - - expect(button).to.have.class(classes.focusVisible); - - setProps({ disabled: true }); - - expect(button).not.to.have.class(classes.focusVisible); - }); - - it('should not use aria-disabled with button host', () => { - const { getByRole } = render(<ButtonBase disabled>Hello</ButtonBase>); - const button = getByRole('button'); - - expect(button).to.have.attribute('disabled'); - expect(button).not.to.have.attribute('aria-disabled'); - }); - - it('should use aria-disabled for other components', () => { - const { getByRole, setProps } = render( - <ButtonBase component="span" disabled> - Hello - </ButtonBase>, - ); - const button = getByRole('button'); - - expect(button).not.to.have.attribute('disabled'); - expect(button).to.have.attribute('aria-disabled', 'true'); - - setProps({ disabled: false }); - expect(button).not.to.have.attribute('aria-disabled'); - }); - }); - - describe('prop: focusableWhenDisabled', () => { - it('should be focusable when disabled if focusableWhenDisabled is true', () => { - const { getByText } = render( - <ButtonBase disabled focusableWhenDisabled> - Hello - </ButtonBase>, - ); - const button = getByText('Hello'); - simulatePointerDevice(); - - focusVisible(button); - - expect(button).to.have.class(classes.focusVisible); - }); - }); - - describe('prop: component', () => { - it('should allow to use a link component', () => { - const Link = React.forwardRef((props, ref: React.ForwardedRef<HTMLDivElement>) => ( - <div data-testid="link" ref={ref} {...props} /> - )); - const { getByTestId } = render(<ButtonBase component={Link}>Hello</ButtonBase>); - - expect(getByTestId('link')).to.have.attribute('role', 'button'); - }); - }); - - describe('event: focus', () => { - it('should call onFocus', async () => { - const onFocusSpy = spy(); - render( - <ButtonBase component="div" onFocus={onFocusSpy}> - Hello - </ButtonBase>, - ); - - await userEvent.keyboard('[Tab]'); - - expect(onFocusSpy.callCount).to.equal(1); - }); - - it('when disabled should not call onFocus', async () => { - const onFocusSpy = spy(); - render( - <ButtonBase component="div" disabled onFocus={onFocusSpy}> - Hello - </ButtonBase>, - ); - - await userEvent.keyboard('[Tab]'); - - expect(onFocusSpy.callCount).to.equal(0); - }); - - it('when disabled and focusableWhenDisabled should call onFocus', async () => { - const onFocusSpy = spy(); - render( - <ButtonBase component="div" disabled onFocus={onFocusSpy} focusableWhenDisabled> - Hello - </ButtonBase>, - ); - - await userEvent.keyboard('[Tab]'); - - expect(onFocusSpy.callCount).to.equal(1); - }); - - it('has a focus-visible polyfill', () => { - const { getByText } = render(<ButtonBase>Hello</ButtonBase>); - const button = getByText('Hello'); - simulatePointerDevice(); - - expect(button).not.to.have.class(classes.focusVisible); - - act(() => { - button.focus(); - }); - - if (programmaticFocusTriggersFocusVisible()) { - expect(button).to.have.class(classes.focusVisible); - } else { - expect(button).not.to.have.class(classes.focusVisible); - } - - focusVisible(button); - - expect(button).to.have.class(classes.focusVisible); - }); - - it('removes focus-visible if focus is re-targetted', () => { - const eventLog: string[] = []; - function Test() { - const focusRetargetRef = React.useRef<HTMLButtonElement>(null); - return ( - <div - onFocus={() => { - const { current: focusRetarget } = focusRetargetRef; - if (focusRetarget === null) { - throw new TypeError('Nothing to focus. Test cannot work.'); - } - focusRetarget.focus(); - }} - > - <button ref={focusRetargetRef} type="button"> - you cannot escape me - </button> - <ButtonBase - onBlur={() => eventLog.push('blur')} - onFocus={() => eventLog.push('focus')} - onFocusVisible={() => eventLog.push('focus-visible')} - > - Hello - </ButtonBase> - </div> - ); - } - const { getByText } = render(<Test />); - const buttonBase = getByText('Hello'); - const focusRetarget = getByText('you cannot escape me'); - simulatePointerDevice(); - - focusVisible(buttonBase); - - expect(focusRetarget).toHaveFocus(); - expect(eventLog).to.deep.equal(['focus-visible', 'focus', 'blur']); - expect(buttonBase).not.to.have.class(classes.focusVisible); - }); - - it('onFocusVisibleHandler() should propagate call to onFocusVisible prop', () => { - const onFocusVisibleSpy = spy(); - const { getByRole } = render( - <ButtonBase component="span" onFocusVisible={onFocusVisibleSpy}> - Hello - </ButtonBase>, - ); - simulatePointerDevice(); - - focusVisible(getByRole('button')); - - expect(onFocusVisibleSpy.calledOnce).to.equal(true); - expect(onFocusVisibleSpy.firstCall.args).to.have.lengthOf(1); - }); - - it('can be autoFocused', () => { - // as of react@16.8.6 autoFocus causes focus to be emitted before refs - // so we need to check if we're resilient against it - const { getByText } = render(<ButtonBase autoFocus>Hello</ButtonBase>); - - expect(getByText('Hello')).toHaveFocus(); - }); - }); - - describe('event: keydown', () => { - describe('prop: onKeyDown', () => { - it('call it when keydown events are dispatched', () => { - const onKeyDownSpy = spy(); - const { getByText } = render( - <ButtonBase autoFocus onKeyDown={onKeyDownSpy}> - Hello - </ButtonBase>, - ); - - fireEvent.keyDown(getByText('Hello')); - - expect(onKeyDownSpy.callCount).to.equal(1); - }); - }); - - describe('prop: disableTouchRipple', () => { - it('creates no ripples on click', () => { - const { getByText } = render( - <ButtonBase - disableTouchRipple - TouchRippleProps={{ - classes: { - rippleVisible: 'ripple-visible', - }, - }} - > - Hello - </ButtonBase>, - ); - const button = getByText('Hello'); - - fireEvent.click(button); - - expect(button).not.to.have.class('ripple-visible'); - }); - }); - - describe('prop: disableRipple', () => { - const touchRippleTestId = 'touch-ripple-test-id'; - - it('is enabled by default', () => { - const { getAllByTestId } = render( - <ButtonBase - TouchRippleProps={{ - // @ts-ignore allow for test id - 'data-testid': touchRippleTestId, - }} - > - Hello World - </ButtonBase>, - ); - - expect(getAllByTestId(touchRippleTestId).length).to.equal(1); - }); - - it('removes the TouchRipple when disableRipple is true', () => { - const { queryByTestId } = render( - <ButtonBase - TouchRippleProps={{ - // @ts-ignore allow for test id - 'data-testid': touchRippleTestId, - }} - disableRipple - > - Hello World - </ButtonBase>, - ); - - expect(queryByTestId(touchRippleTestId)).to.equal(null); - }); - }); - - describe('keyboard accessibility for non interactive elements', () => { - it('does not call onClick when a spacebar is pressed on the element but prevents the default', () => { - const onKeyDown = spy(); - const onClickSpy = spy(); - const { getByRole } = render( - <ButtonBase onClick={onClickSpy} onKeyDown={onKeyDown} component="div"> - Hello - </ButtonBase>, - ); - const button = getByRole('button'); - - act(() => { - button.focus(); - fireEvent.keyDown(button, { - key: ' ', - }); - }); - - expect(onClickSpy.callCount).to.equal(0); - expect(onKeyDown.callCount).to.equal(1); - expect(onKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('does call onClick when a spacebar is released on the element', () => { - const onClickSpy = spy(); - const { getByRole } = render( - <ButtonBase onClick={onClickSpy} component="div"> - Hello - </ButtonBase>, - ); - const button = getByRole('button'); - - act(() => { - button.focus(); - fireEvent.keyUp(button, { - key: ' ', - }); - }); - - expect(onClickSpy.callCount).to.equal(1); - expect(onClickSpy.firstCall.args[0]).to.have.property('defaultPrevented', false); - }); - - it('does not call onClick when a spacebar is released and the default is prevented', () => { - const onClickSpy = spy(); - const onKeyUp: MuiCancellableEventHandler<React.KeyboardEvent> = (event) => { - event.preventDefault(); - event.defaultMuiPrevented = true; - }; - const { getByRole } = render( - <ButtonBase onClick={onClickSpy} onKeyUp={onKeyUp} component="div"> - Hello - </ButtonBase>, - ); - const button = getByRole('button'); - - act(() => { - button.focus(); - fireEvent.keyUp(button, { - key: ' ', - }); - }); - - expect(onClickSpy.callCount).to.equal(0); - }); - - it('calls onClick when Enter is pressed on the element', () => { - const onClickSpy = spy(); - const { getByRole } = render( - <ButtonBase onClick={onClickSpy} component="div"> - Hello - </ButtonBase>, - ); - const button = getByRole('button'); - - act(() => { - button.focus(); - fireEvent.keyDown(button, { - key: 'Enter', - }); - }); - - expect(onClickSpy.calledOnce).to.equal(true); - expect(onClickSpy.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('does not call onClick if Enter was pressed on a child', () => { - const onClickSpy = spy(); - const onKeyDownSpy = spy(); - render( - <ButtonBase onClick={onClickSpy} onKeyDown={onKeyDownSpy} component="div"> - <input autoFocus type="text" /> - </ButtonBase>, - ); - - fireEvent.keyDown(screen.getByRole('textbox'), { - key: 'Enter', - }); - - expect(onKeyDownSpy.callCount).to.equal(1); - expect(onClickSpy.callCount).to.equal(0); - }); - - it('does not call onClick if Space was released on a child', () => { - const onClickSpy = spy(); - const onKeyUpSpy = spy(); - render( - <ButtonBase onClick={onClickSpy} onKeyUp={onKeyUpSpy} component="div"> - <input autoFocus type="text" /> - </ButtonBase>, - ); - - fireEvent.keyUp(screen.getByRole('textbox'), { - key: ' ', - }); - - expect(onKeyUpSpy.callCount).to.equal(1); - expect(onClickSpy.callCount).to.equal(0); - }); - - it('prevents default with an anchor and empty href', () => { - const onClickSpy = spy(); - const { getByRole } = render( - <ButtonBase component="a" onClick={onClickSpy}> - Hello - </ButtonBase>, - ); - const button = getByRole('button'); - - act(() => { - button.focus(); - fireEvent.keyDown(button, { key: 'Enter' }); - }); - - expect(onClickSpy.calledOnce).to.equal(true); - expect(onClickSpy.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('should ignore anchors with href', () => { - const onClick = spy(); - const onKeyDown = spy(); - const { getByText } = render( - <ButtonBase component="a" href="href" onClick={onClick} onKeyDown={onKeyDown}> - Hello - </ButtonBase>, - ); - const button = getByText('Hello'); - - act(() => { - button.focus(); - fireEvent.keyDown(button, { - key: 'Enter', - }); - }); - - expect(onClick.callCount).to.equal(0); - expect(onKeyDown.callCount).to.equal(1); - expect(onKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', false); - }); - }); - }); - - describe('prop: action', () => { - it('should be able to focus visible the button', () => { - /** - * @type {React.RefObject<import('./ButtonBase.types').ButtonBaseActions>} - */ - const buttonActionsRef = React.createRef<ButtonBaseActions>(); - const { getByText } = render( - <ButtonBase action={buttonActionsRef} focusVisibleClassName="focusVisible"> - Hello - </ButtonBase>, - ); - - // @ts-ignore - expect(typeof buttonActionsRef.current.focusVisible).to.equal('function'); - - act(() => { - // @ts-ignore - buttonActionsRef.current.focusVisible(); - }); - - expect(getByText('Hello')).toHaveFocus(); - expect(getByText('Hello')).to.match('.focusVisible'); - }); - }); - - describe('warnings', () => { - beforeEach(() => { - PropTypes.resetWarningCache(); - }); - - it('warns on invalid `component` prop: ref forward', function test() { - // Only run the test on node. On the browser the thrown error is not caught - if (!/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - function Component(props: React.HTMLAttributes<HTMLButtonElement>) { - return <button type="button" {...props} />; - } - - expect(() => { - PropTypes.checkPropTypes( - ButtonBase.propTypes, - { classes: {}, component: Component }, - 'prop', - 'MockedName', - ); - }).toErrorDev( - 'Invalid prop `component` supplied to `MockedName`. Expected an element type that can hold a ref', - ); - }); - - it('warns on invalid `component` prop: prop forward', () => { - const Component = React.forwardRef((props, ref: React.ForwardedRef<HTMLButtonElement>) => ( - <button type="button" ref={ref} {...props}> - Hello - </button> - )); - - // cant match the error message here because flakiness with mocha watchmode - - expect(() => { - render(<ButtonBase component={Component} />); - }).toErrorDev('Please make sure the children prop is rendered in this custom component.'); - }); - }); - - describe('prop: type', () => { - it('is `button` by default', () => { - render(<ButtonBase />); - - expect(screen.getByRole('button')).to.have.property('type', 'button'); - }); - - it('can be changed to other button types', () => { - render(<ButtonBase type="submit" />); - - expect(screen.getByRole('button')).to.have.property('type', 'submit'); - }); - - it('allows non-standard values', () => { - render(<ButtonBase type="fictional-type" />); - - expect(screen.getByRole('button')).to.have.attribute('type', 'fictional-type'); - // By spec non-supported types result in the default type for `<button>` which is `submit` - expect(screen.getByRole('button')).to.have.property('type', 'submit'); - }); - - it('is forwarded to anchor components', () => { - render(<ButtonBase component="a" href="some-recording.ogg" download type="audio/ogg" />); - - expect(screen.getByRole('link')).to.have.attribute('type', 'audio/ogg'); - expect(screen.getByRole('link')).to.have.property('type', 'audio/ogg'); - }); - - it('is forwarded to custom components', () => { - const CustomButton = React.forwardRef((props, ref: React.ForwardedRef<HTMLButtonElement>) => ( - <button ref={ref} {...props} /> - )); - render(<ButtonBase component={CustomButton} type="reset" />); - - expect(screen.getByRole('button')).to.have.property('type', 'reset'); - }); - }); - - describe('prop: touchRippleRef', () => { - it('should return a ref', () => { - const ref = React.createRef<TouchRippleActions>(); - render(<ButtonBase touchRippleRef={ref} />); - expect(ref.current).not.to.equal(null); - }); - }); - - describe('classes', () => { - it('should render focus visible class', () => { - const { container } = render(<ButtonBase />); - - const button = container.querySelector('button'); - expect(button).not.to.equal(null); - act(() => { - button!.focus(); - }); - - expect(button).to.have.class(classes.focusVisible); - }); - - it('should render active class', () => { - const { container } = render(<ButtonBase />); - - const button = container.querySelector('button'); - expect(button).not.to.equal(null); - fireEvent.mouseDown(button!); - - expect(button).to.have.class(classes.active); - }); - }); - - describe('prop: tabIndex', () => { - it('should apply user value of tabIndex', () => { - const { getByRole } = render(<ButtonBase tabIndex={5} />); - - expect(getByRole('button')).to.have.property('tabIndex', 5); - }); - }); -}); diff --git a/packages/mui-material-next/src/ButtonBase/ButtonBase.tsx b/packages/mui-material-next/src/ButtonBase/ButtonBase.tsx deleted file mode 100644 index fdd5360e6b40c7..00000000000000 --- a/packages/mui-material-next/src/ButtonBase/ButtonBase.tsx +++ /dev/null @@ -1,318 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { elementTypeAcceptingRef, refType, unstable_useForkRef as useForkRef } from '@mui/utils'; -import { - EventHandlers, - unstable_composeClasses as composeClasses, - useButton, - useSlotProps, -} from '@mui/base'; -import styled, { rootShouldForwardProp } from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; -import buttonBaseClasses, { getButtonBaseUtilityClass } from './buttonBaseClasses'; -import { - ButtonBaseOwnerState, - ButtonBaseProps, - ButtonBaseTypeMap, - ExtendButtonBase, -} from './ButtonBase.types'; -import TouchRipple from './TouchRipple'; -import useTouchRipple from './useTouchRipple'; -import { TouchRippleActions } from './TouchRipple.types'; - -const useUtilityClasses = (ownerState: ButtonBaseOwnerState) => { - const { disabled, focusVisible, active, focusVisibleClassName, classes } = ownerState; - - const slots = { - root: ['root', disabled && 'disabled', active && 'active', focusVisible && 'focusVisible'], - }; - - const composedClasses = composeClasses(slots, getButtonBaseUtilityClass, classes); - - if (focusVisible && focusVisibleClassName) { - composedClasses.root += ` ${focusVisibleClassName}`; - } - - return composedClasses; -}; - -export const ButtonBaseRoot = styled('button', { - name: 'MuiButtonBase', - slot: 'Root', - shouldForwardProp: (prop) => rootShouldForwardProp(prop) && prop !== 'touchRippleRef', - overridesResolver: (props, styles) => styles.root, -})({ - display: 'inline-flex', - alignItems: 'center', - justifyContent: 'center', - position: 'relative', - boxSizing: 'border-box', - WebkitTapHighlightColor: 'transparent', - backgroundColor: 'transparent', // Reset default value - // We disable the focus ring for mouse, touch and keyboard users. - outline: 0, - border: 0, - margin: 0, // Remove the margin in Safari - borderRadius: 0, - padding: 0, // Remove the padding in Firefox - cursor: 'pointer', - userSelect: 'none', - verticalAlign: 'middle', - MozAppearance: 'none', // Reset - WebkitAppearance: 'none', // Reset - textDecoration: 'none', - // So we take precedent over the style of a native <a /> element. - color: 'inherit', - '&::-moz-focus-inner': { - borderStyle: 'none', // Remove Firefox dotted outline. - }, - [`&.${buttonBaseClasses.disabled}`]: { - pointerEvents: 'none', // Disable link interactions - cursor: 'default', - }, - '@media print': { - colorAdjust: 'exact', - }, -}) as React.ElementType<any>; - -/** - * `ButtonBase` contains as few styles as possible. - * It aims to be a simple building block for creating a button. - * It contains a load of style reset and some focus/ripple logic. - * - * Demos: - * - * - [Button](https://mui.com/material-ui/react-button/) - * - * API: - * - * - [ButtonBase API](https://mui.com/material-ui/api/button-base/) - */ -const ButtonBase = React.forwardRef(function ButtonBase< - BaseComponentType extends React.ElementType = ButtonBaseTypeMap['defaultComponent'], ->(inProps: ButtonBaseProps<BaseComponentType>, ref: React.ForwardedRef<any>) { - const props = useThemeProps({ props: inProps, name: 'MuiButtonBase' }); - const { - action, - centerRipple = false, - children, - component = 'button', - disabled = false, - disableRipple = false, - disableTouchRipple = false, - focusVisibleClassName, - focusableWhenDisabled = false, - LinkComponent = 'a', - tabIndex = 0, - TouchRippleProps, - touchRippleRef, - type, - ...other - } = props; - - const buttonRef = React.useRef<HTMLButtonElement | HTMLAnchorElement | HTMLElement>(null); - const handleRef = useForkRef(buttonRef, ref); - const { focusVisible, active, setFocusVisible, getRootProps } = useButton({ - disabled, - focusableWhenDisabled, - href: props.href, - to: props.to, - tabIndex, - rootRef: handleRef, - }); - - let ComponentProp = component; - if (ComponentProp === 'button' && (other.href || other.to)) { - ComponentProp = LinkComponent; - } - - const rippleRef = React.useRef<TouchRippleActions>(null); - const handleRippleRef = useForkRef(rippleRef, touchRippleRef); - const { enableTouchRipple, getRippleHandlers } = useTouchRipple({ - disabled, - disableRipple, - disableTouchRipple, - rippleRef, - }); - - React.useImperativeHandle( - action, - () => ({ - focusVisible: () => { - setFocusVisible(true); - buttonRef.current?.focus(); - }, - }), - [setFocusVisible], - ); - - const ownerState = { - ...props, - centerRipple, - component, - disabled, - disableRipple, - disableTouchRipple, - focusVisible, - active, - }; - - const classes = useUtilityClasses(ownerState); - - const rootProps = useSlotProps({ - elementType: ButtonBaseRoot, - getSlotProps: (otherHandlers: EventHandlers) => ({ - ...getRootProps({ - ...otherHandlers, - ...getRippleHandlers(props), - }), - // If provided, the type prop overrides useButton's type which is more restricted - ...(!!type && { type }), - }), - externalForwardedProps: other, - externalSlotProps: {}, - additionalProps: { - as: ComponentProp, - }, - ownerState, - className: classes.root, - }); - - if (process.env.NODE_ENV !== 'production') { - // eslint-disable-next-line react-hooks/rules-of-hooks - React.useEffect(() => { - if (enableTouchRipple && !rippleRef.current) { - console.error( - [ - 'MUI: The `component` prop provided to ButtonBase is invalid.', - 'Please make sure the children prop is rendered in this custom component.', - ].join('\n'), - ); - } - }, [enableTouchRipple]); - } - - return ( - <ButtonBaseRoot {...rootProps}> - {children} - {enableTouchRipple ? ( - /* TouchRipple is only needed client-side, x2 boost on the server. */ - <TouchRipple center={centerRipple} {...TouchRippleProps} ref={handleRippleRef} /> - ) : null} - </ButtonBaseRoot> - ); -}) as ExtendButtonBase<ButtonBaseTypeMap>; - -ButtonBase.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * A ref for imperative actions. - * It currently only supports `focusVisible()` action. - */ - action: refType, - /** - * If `true`, the ripples are centered. - * They won't start at the cursor interaction position. - * @default false - */ - centerRipple: PropTypes.bool, - /** - * The content of the component. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: elementTypeAcceptingRef, - /** - * If `true`, the component is disabled. - * @default false - */ - disabled: PropTypes.bool, - /** - * If `true`, the ripple effect is disabled. - * - * ⚠️ Without a ripple there is no styling for :focus-visible by default. Be sure - * to highlight the element by applying separate styles with the `.Mui-focusVisible` class. - * @default false - */ - disableRipple: PropTypes.bool, - /** - * If `true`, the touch ripple effect is disabled. - * @default false - */ - disableTouchRipple: PropTypes.bool, - /** - * If `true`, allows a disabled button to receive focus. - * @default false - */ - focusableWhenDisabled: PropTypes.bool, - /** - * This prop can help identify which element has keyboard focus. - * The class name will be applied when the element gains the focus through keyboard interaction. - * It's a polyfill for the [CSS :focus-visible selector](https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo). - * The rationale for using this feature [is explained here](https://github.com/WICG/focus-visible/blob/HEAD/explainer.md). - * A [polyfill can be used](https://github.com/WICG/focus-visible) to apply a `focus-visible` class to other components - * if needed. - */ - focusVisibleClassName: PropTypes.string, - /** - * The URL to link to when the button is clicked. - * If defined, an `a` element will be used as the root node. - */ - href: PropTypes /* @typescript-to-proptypes-ignore */.any, - /** - * The component used to render a link when the `href` prop is provided. - * @default 'a' - */ - LinkComponent: PropTypes.elementType, - /** - * Callback fired when the component is focused with a keyboard. - * We trigger a `onFocus` callback too. - */ - onFocusVisible: PropTypes.func, - /** - * 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, - ]), - /** - * @default 0 - */ - tabIndex: PropTypes.number, - /** - * Props applied to the `TouchRipple` element. - */ - TouchRippleProps: PropTypes.object, - /** - * A ref that points to the `TouchRipple` element. - */ - touchRippleRef: PropTypes.oneOfType([ - PropTypes.func, - PropTypes.shape({ - current: PropTypes.shape({ - start: PropTypes.func.isRequired, - stop: PropTypes.func.isRequired, - }), - }), - ]), - /** - * Type attribute applied to the root component. - * @default 'button' - */ - type: PropTypes.string, -} as any; - -export default ButtonBase; diff --git a/packages/mui-material-next/src/ButtonBase/ButtonBase.types.ts b/packages/mui-material-next/src/ButtonBase/ButtonBase.types.ts deleted file mode 100644 index 1ed0777f27ecff..00000000000000 --- a/packages/mui-material-next/src/ButtonBase/ButtonBase.types.ts +++ /dev/null @@ -1,141 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverrideProps, OverridableComponent, OverridableTypeMap } from '@mui/types'; -import { Theme } from '../styles'; -import { TouchRippleActions, TouchRippleProps } from './TouchRipple.types'; -import { ButtonBaseClasses } from './buttonBaseClasses'; - -export interface ButtonBaseTypeMap< - AdditionalProps = {}, - DefaultComponent extends React.ElementType = 'button', -> { - props: AdditionalProps & { - /** - * A ref for imperative actions. - * It currently only supports `focusVisible()` action. - */ - action?: React.Ref<ButtonBaseActions>; - /** - * If `true`, the ripples are centered. - * They won't start at the cursor interaction position. - * @default false - */ - centerRipple?: boolean; - /** - * The content of the component. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<ButtonBaseClasses>; - /** - * If `true`, the component is disabled. - * @default false - */ - disabled?: boolean; - /** - * If `true`, the ripple effect is disabled. - * - * ⚠️ Without a ripple there is no styling for :focus-visible by default. Be sure - * to highlight the element by applying separate styles with the `.Mui-focusVisible` class. - * @default false - */ - disableRipple?: boolean; - /** - * If `true`, the touch ripple effect is disabled. - * @default false - */ - disableTouchRipple?: boolean; - /** - * If `true`, allows a disabled button to receive focus. - * @default false - */ - focusableWhenDisabled?: boolean; - /** - * This prop can help identify which element has keyboard focus. - * The class name will be applied when the element gains the focus through keyboard interaction. - * It's a polyfill for the [CSS :focus-visible selector](https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo). - * The rationale for using this feature [is explained here](https://github.com/WICG/focus-visible/blob/HEAD/explainer.md). - * A [polyfill can be used](https://github.com/WICG/focus-visible) to apply a `focus-visible` class to other components - * if needed. - */ - focusVisibleClassName?: string; - /** - * The URL to link to when the button is clicked. - * If defined, an `a` element will be used as the root node. - */ - href?: string; - /** - * The component used to render a link when the `href` prop is provided. - * @default 'a' - */ - LinkComponent?: React.ElementType; - /** - * Callback fired when the component is focused with a keyboard. - * We trigger a `onFocus` callback too. - */ - onFocusVisible?: React.FocusEventHandler<any>; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; - /** - * @default 0 - */ - tabIndex?: NonNullable<React.HTMLAttributes<any>['tabIndex']>; - /** - * Props applied to the `TouchRipple` element. - */ - TouchRippleProps?: Partial<TouchRippleProps>; - /** - * A ref that points to the `TouchRipple` element. - */ - touchRippleRef?: React.Ref<TouchRippleActions>; - /** - * Type attribute applied to the root component. - * @default 'button' - */ - type?: - | React.ButtonHTMLAttributes<HTMLButtonElement>['type'] - | React.AnchorHTMLAttributes<HTMLAnchorElement>['type']; - }; - defaultComponent: DefaultComponent; -} - -/** - * utility to create component types that inherit props from ButtonBase. - * This component has an additional overload if the `href` prop is set which - * can make extension quite tricky - */ -export interface ExtendButtonBaseTypeMap<TypeMap extends OverridableTypeMap> { - props: TypeMap['props'] & Omit<ButtonBaseTypeMap['props'], 'classes'>; - defaultComponent: TypeMap['defaultComponent']; -} - -export type ExtendButtonBase<TypeMap extends OverridableTypeMap> = (( - props: { href: string } & OverrideProps<ExtendButtonBaseTypeMap<TypeMap>, 'a'>, -) => JSX.Element) & - OverridableComponent<ExtendButtonBaseTypeMap<TypeMap>>; - -export type ButtonBaseProps< - RootComponent extends React.ElementType = ButtonBaseTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<ButtonBaseTypeMap<AdditionalProps, RootComponent>, RootComponent> & { - component?: React.ElementType; -}; - -export interface ButtonBaseOwnerState extends ButtonBaseProps { - /** - * If `true`, the button is active. - */ - active?: boolean; - /** - * If `true`, the button's focus is visible. - */ - focusVisible?: boolean; -} - -export interface ButtonBaseActions { - focusVisible(): void; -} diff --git a/packages/mui-material-next/src/ButtonBase/Ripple.test.js b/packages/mui-material-next/src/ButtonBase/Ripple.test.js deleted file mode 100644 index fa583ab6a62244..00000000000000 --- a/packages/mui-material-next/src/ButtonBase/Ripple.test.js +++ /dev/null @@ -1,88 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { spy } from 'sinon'; -import { createRenderer } from '@mui-internal/test-utils'; -import Ripple from './Ripple'; -import classes from './touchRippleClasses'; - -describe('<Ripple />', () => { - const { clock, render } = createRenderer(); - - it('should have the ripple className', () => { - const { container } = render( - <Ripple classes={classes} timeout={0} rippleX={0} rippleY={0} rippleSize={11} />, - ); - const ripple = container.querySelector('span'); - expect(ripple).to.have.class(classes.ripple); - expect(ripple).not.to.have.class(classes.fast); - }); - - describe('starting and stopping', () => { - it('should start the ripple', () => { - const { container, setProps } = render( - <Ripple classes={classes} timeout={0} rippleX={0} rippleY={0} rippleSize={11} />, - ); - - setProps({ in: true }); - - const ripple = container.querySelector('span'); - expect(ripple).to.have.class(classes.rippleVisible); - }); - - it('should stop the ripple', () => { - const { container, setProps } = render( - <Ripple classes={classes} in timeout={0} rippleX={0} rippleY={0} rippleSize={11} />, - ); - - setProps({ in: false }); - - const child = container.querySelector('span > span'); - expect(child).to.have.class(classes.childLeaving); - }); - }); - - describe('starting and stopping ripple 2', () => { - clock.withFakeTimers(); - - it('handleExit should trigger a timer', () => { - const handleExited = spy(); - const { setProps } = render( - <Ripple - classes={classes} - timeout={550} - in - onExited={handleExited} - rippleX={0} - rippleY={0} - rippleSize={11} - />, - ); - - setProps({ in: false }); - clock.tick(549); - expect(handleExited.callCount).to.equal(0); - clock.tick(1); - expect(handleExited.callCount).to.equal(1); - }); - - it('unmount should defuse the handleExit timer', () => { - const handleExited = spy(); - const { setProps, unmount } = render( - <Ripple - classes={classes} - timeout={550} - in - onExited={handleExited} - rippleX={0} - rippleY={0} - rippleSize={11} - />, - ); - - setProps({ in: false }); - unmount(); - clock.tick(550); - expect(handleExited.callCount).to.equal(0); - }); - }); -}); diff --git a/packages/mui-material-next/src/ButtonBase/Ripple.tsx b/packages/mui-material-next/src/ButtonBase/Ripple.tsx deleted file mode 100644 index 832a94c6829f3c..00000000000000 --- a/packages/mui-material-next/src/ButtonBase/Ripple.tsx +++ /dev/null @@ -1,89 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; - -export interface RippleProps { - className?: string; - classes: Record<string, string>; - rippleX: number; - rippleY: number; - rippleSize: number; - in?: boolean; - onExited?: () => void; - timeout: number; -} -/** - * @ignore - internal component. - */ -function Ripple(props: RippleProps) { - const { className, classes, rippleX, rippleY, rippleSize, in: inProp, onExited, timeout } = props; - const [leaving, setLeaving] = React.useState(false); - - const rippleClassName = clsx(className, classes.ripple, classes.rippleVisible); - - const rippleStyles = { - width: rippleSize, - height: rippleSize, - top: -(rippleSize / 2) + rippleY, - left: -(rippleSize / 2) + rippleX, - }; - - const childClassName = clsx(classes.child, { - [classes.childLeaving]: leaving, - }); - - if (!inProp && !leaving) { - setLeaving(true); - } - React.useEffect(() => { - if (!inProp && onExited != null) { - // react-transition-group#onExited - const timeoutId = setTimeout(onExited, timeout); - return () => { - clearTimeout(timeoutId); - }; - } - return undefined; - }, [onExited, inProp, timeout]); - - return ( - <span className={rippleClassName} style={rippleStyles}> - <span className={childClassName} /> - </span> - ); -} - -Ripple.propTypes = { - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object.isRequired, - className: PropTypes.string, - /** - * @ignore - injected from TransitionGroup - */ - in: PropTypes.bool, - /** - * @ignore - injected from TransitionGroup - */ - onExited: PropTypes.func, - /** - * Diameter of the ripple. - */ - rippleSize: PropTypes.number, - /** - * Horizontal position of the ripple center. - */ - rippleX: PropTypes.number, - /** - * Vertical position of the ripple center. - */ - rippleY: PropTypes.number, - /** - * exit delay - */ - timeout: PropTypes.number.isRequired, -} as any; - -export default Ripple as (props: RippleProps) => JSX.Element; diff --git a/packages/mui-material-next/src/ButtonBase/TouchRipple.test.js b/packages/mui-material-next/src/ButtonBase/TouchRipple.test.js deleted file mode 100644 index b47092a4efc36e..00000000000000 --- a/packages/mui-material-next/src/ButtonBase/TouchRipple.test.js +++ /dev/null @@ -1,271 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { act, createRenderer } from '@mui-internal/test-utils'; -import TouchRipple, { DELAY_RIPPLE } from './TouchRipple'; -import describeConformance from '../../test/describeConformance'; - -const cb = () => {}; - -describe('<TouchRipple />', () => { - const { clock, render } = createRenderer(); - - /** - * @param {object} other props to spread to TouchRipple - */ - function renderTouchRipple(other) { - const touchRippleRef = React.createRef(); - const { container, unmount } = render( - <TouchRipple - ref={touchRippleRef} - classes={{ - ripple: 'ripple', - rippleVisible: 'ripple-visible', - child: 'child', - childLeaving: 'child-leaving', - }} - {...other} - />, - ); - - return { - instance: touchRippleRef.current, - queryAllActiveRipples() { - return container.querySelectorAll('.ripple-visible .child:not(.child-leaving)'); - }, - queryAllStoppingRipples() { - return container.querySelectorAll('.ripple-visible .child-leaving'); - }, - queryRipple() { - return container.querySelector('.ripple'); - }, - unmount, - }; - } - - describeConformance(<TouchRipple />, () => ({ - classes: {}, - inheritComponent: 'span', - render, - refInstanceof: Object, - muiName: 'PrivateTouchRipple', - skip: [ - 'componentProp', - 'componentsProp', - 'refForwarding', - 'themeStyleOverrides', - 'themeVariants', - ], - })); - - describe('prop: center', () => { - it('should compute the right ripple dimensions', () => { - const { instance, queryRipple } = renderTouchRipple({ center: true }); - - act(() => { - instance.start( - {}, - { - fakeElement: true, - }, - cb, - ); - }); - - expect(queryRipple()).toHaveInlineStyle({ height: '1px' }); - expect(queryRipple()).toHaveInlineStyle({ width: '1px' }); - }); - }); - - it('should create individual ripples', () => { - const { instance, queryAllActiveRipples, queryAllStoppingRipples } = renderTouchRipple(); - - expect(queryAllActiveRipples()).to.have.lengthOf(0); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - - act(() => { - instance.start({ clientX: 0, clientY: 0 }, cb); - }); - - expect(queryAllActiveRipples()).to.have.lengthOf(1); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - - act(() => { - instance.start({ clientX: 0, clientY: 0 }, cb); - }); - - expect(queryAllActiveRipples()).to.have.lengthOf(2); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - - act(() => { - instance.start({ clientX: 0, clientY: 0 }, cb); - }); - - expect(queryAllActiveRipples()).to.have.lengthOf(3); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - - act(() => { - instance.stop({ type: 'mouseup' }); - }); - - expect(queryAllActiveRipples()).to.have.lengthOf(2); - expect(queryAllStoppingRipples()).to.have.lengthOf(1); - - act(() => { - instance.stop({ type: 'mouseup' }); - }); - - expect(queryAllActiveRipples()).to.have.lengthOf(1); - expect(queryAllStoppingRipples()).to.have.lengthOf(2); - - act(() => { - instance.stop({ type: 'mouseup' }); - }); - - expect(queryAllActiveRipples()).to.have.lengthOf(0); - expect(queryAllStoppingRipples()).to.have.lengthOf(3); - }); - - describe('creating unique ripples', () => { - it('should create a ripple', () => { - const { instance, queryAllActiveRipples, queryAllStoppingRipples } = renderTouchRipple(); - - act(() => { - instance.start( - {}, - { - pulsate: true, - fakeElement: true, - }, - cb, - ); - }); - - expect(queryAllActiveRipples()).to.have.lengthOf(1); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - }); - - it('should ignore a mousedown event after a touchstart event', () => { - const { instance, queryAllActiveRipples, queryAllStoppingRipples } = renderTouchRipple(); - - act(() => { - instance.start({ type: 'touchstart' }, cb); - instance.start({ type: 'mousedown' }, cb); - }); - - expect(queryAllActiveRipples()).to.have.lengthOf(1); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - }); - - it('should create a specific ripple', () => { - const { instance, queryAllActiveRipples, queryAllStoppingRipples, queryRipple } = - renderTouchRipple({ - center: true, - }); - const clientX = 1; - const clientY = 1; - - act(() => { - instance.start({ clientX, clientY }, { fakeElement: true }, cb); - }); - - expect(queryAllActiveRipples()).to.have.lengthOf(1); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - expect(queryRipple()).toHaveInlineStyle({ top: '-0.5px' }); - expect(queryRipple()).toHaveInlineStyle({ left: '-0.5px' }); - }); - }); - - describe('mobile', () => { - clock.withFakeTimers(); - - it('should delay the display of the ripples', () => { - const { instance, queryAllActiveRipples, queryAllStoppingRipples } = renderTouchRipple(); - - expect(queryAllActiveRipples()).to.have.lengthOf(0); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - - act(() => { - instance.start({ touches: [], clientX: 0, clientY: 0 }, { fakeElement: true }, cb); - }); - - expect(queryAllActiveRipples()).to.have.lengthOf(0); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - - clock.tick(DELAY_RIPPLE); - - expect(queryAllActiveRipples()).to.have.lengthOf(1); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - - clock.tick(DELAY_RIPPLE); - act(() => { - instance.stop({ type: 'touchend' }, cb); - }); - - expect(queryAllActiveRipples()).to.have.lengthOf(0); - expect(queryAllStoppingRipples()).to.have.lengthOf(1); - }); - - it('should trigger the ripple for short touch interactions', () => { - const { instance, queryAllActiveRipples, queryAllStoppingRipples } = renderTouchRipple(); - - expect(queryAllActiveRipples()).to.have.lengthOf(0); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - - act(() => { - instance.start({ touches: [], clientX: 0, clientY: 0 }, { fakeElement: true }, cb); - }); - - expect(queryAllActiveRipples()).to.have.lengthOf(0); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - - clock.tick(DELAY_RIPPLE / 2); - - expect(queryAllActiveRipples()).to.have.lengthOf(0); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - - act(() => { - instance.stop({ type: 'touchend' }, cb); - }); - - expect(queryAllActiveRipples()).to.have.lengthOf(1); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - - clock.tick(1); - - expect(queryAllActiveRipples()).to.have.lengthOf(0); - expect(queryAllStoppingRipples()).to.have.lengthOf(1); - }); - - it('should interrupt the ripple schedule', () => { - const { instance, queryAllActiveRipples, queryAllStoppingRipples } = renderTouchRipple(); - - expect(queryAllActiveRipples()).to.have.lengthOf(0); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - - instance.start({ touches: [], clientX: 0, clientY: 0 }, { fakeElement: true }, cb); - expect(queryAllActiveRipples()).to.have.lengthOf(0); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - - clock.tick(DELAY_RIPPLE / 2); - expect(queryAllActiveRipples()).to.have.lengthOf(0); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - - instance.stop({ type: 'touchmove' }); - clock.tick(DELAY_RIPPLE); - expect(queryAllActiveRipples()).to.have.lengthOf(0); - expect(queryAllStoppingRipples()).to.have.lengthOf(0); - }); - - it('should not leak on multi-touch', function multiTouchTest() { - const { instance, unmount } = renderTouchRipple(); - - instance.start({ type: 'touchstart', touches: [{}] }, () => {}); - instance.start({ type: 'touchstart', touches: [{}] }, () => {}); - unmount(); - - // expect this to run gracefully without - // "react state update on an unmounted component" - clock.runAll(); - }); - }); -}); diff --git a/packages/mui-material-next/src/ButtonBase/TouchRipple.tsx b/packages/mui-material-next/src/ButtonBase/TouchRipple.tsx deleted file mode 100644 index 4f5025e3025ca4..00000000000000 --- a/packages/mui-material-next/src/ButtonBase/TouchRipple.tsx +++ /dev/null @@ -1,295 +0,0 @@ -'use client'; -/* eslint-disable material-ui/mui-name-matches-component-name */ -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { TransitionGroup } from 'react-transition-group'; -import clsx from 'clsx'; -import { keyframes, useThemeProps } from '@mui/system'; -import { unstable_useTimeout as useTimeout } from '@mui/utils'; -import styled from '@mui/material/styles/styled'; -import { TouchRippleActions, TouchRippleProps } from './TouchRipple.types'; -import Ripple from './Ripple'; -import touchRippleClasses from './touchRippleClasses'; - -const DURATION = 550; -export const DELAY_RIPPLE = 80; - -const enterKeyframe = keyframes` - 0% { - transform: scale(0); - opacity: 0.1; - } - 100% { - transform: scale(1); - opacity: 0.12; - } -`; - -const exitKeyframe = keyframes` - 0% { - opacity: 1; - } - 100% { - opacity: 0; - } -`; - -export const TouchRippleRoot = styled('span', { - name: 'PrivateTouchRipple', - slot: 'Root', -})({ - overflow: 'hidden', - pointerEvents: 'none', - position: 'absolute', - zIndex: 0, - top: 0, - right: 0, - bottom: 0, - left: 0, - borderRadius: 'inherit', -}); - -// This `styled()` function invokes keyframes. `styled-components` only supports keyframes -// in string templates. Do not convert these styles in JS object as it will break. -export const TouchRippleRipple = styled(Ripple, { - name: 'PrivateTouchRipple', - slot: 'Ripple', -})` - opacity: 0; - position: absolute; - &.${touchRippleClasses.rippleVisible} { - opacity: 0.12; - transform: scale(1); - animation-name: ${enterKeyframe}; - animation-duration: ${DURATION}ms; - animation-timing-function: ${({ theme }) => theme.transitions.easing.easeInOut}; - } - & .${touchRippleClasses.child} { - opacity: 1; - display: block; - width: 100%; - height: 100%; - border-radius: 50%; - background-color: currentColor; - } - & .${touchRippleClasses.childLeaving} { - opacity: 0; - animation-name: ${exitKeyframe}; - animation-duration: ${DURATION}ms; - animation-timing-function: ${({ theme }) => theme.transitions.easing.easeInOut}; - } -`; - -/** - * @ignore - internal component. - */ -const TouchRipple = React.forwardRef<TouchRippleActions, TouchRippleProps>( - function TouchRipple(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'PrivateTouchRipple' }); - - const { center: centerProp = false, classes = {}, className, ...other } = props; - const [ripples, setRipples] = React.useState<React.ReactNode[]>([]); - const nextKey = React.useRef(0); - const rippleCallback = React.useRef<(() => void) | null>(); - - React.useEffect(() => { - if (rippleCallback.current) { - rippleCallback.current(); - rippleCallback.current = null; - } - }, [ripples]); - - // Used to filter out mouse emulated events on mobile. - const ignoringMouseDown = React.useRef(false); - // We use a timer in order to only show the ripples for touch "click" like events. - // We don't want to display the ripple for touch scroll events. - const startTimer = useTimeout(); - - // This is the hook called once the previous timeout is ready. - const startTimerCommit = React.useRef<(() => void) | null>(null); - const container = React.useRef<HTMLSpanElement>(null); - - const startCommit = React.useCallback( - (params: { rippleX: number; rippleY: number; rippleSize: number; cb: () => void }) => { - const { rippleX, rippleY, rippleSize, cb } = params; - - setRipples((oldRipples) => [ - ...oldRipples, - <TouchRippleRipple - key={nextKey.current} - classes={{ - ripple: clsx(classes.ripple, touchRippleClasses.ripple), - rippleVisible: clsx(classes.rippleVisible, touchRippleClasses.rippleVisible), - child: clsx(classes.child, touchRippleClasses.child), - childLeaving: clsx(classes.childLeaving, touchRippleClasses.childLeaving), - }} - timeout={DURATION} - rippleX={rippleX} - rippleY={rippleY} - rippleSize={rippleSize} - />, - ]); - nextKey.current += 1; - rippleCallback.current = cb; - }, - [classes], - ); - - const start = React.useCallback<TouchRippleActions['start']>( - (event, options = {}, cb = () => {}) => { - const { - center = centerProp, - // @ts-ignore For test purposes - fakeElement = false, - } = options; - - if (event?.type === 'mousedown' && ignoringMouseDown.current) { - ignoringMouseDown.current = false; - return; - } - - if (event?.type === 'touchstart') { - ignoringMouseDown.current = true; - } - - const element = fakeElement ? null : container.current; - const rect = element - ? element.getBoundingClientRect() - : { - width: 0, - height: 0, - left: 0, - top: 0, - }; - - // Get the size of the ripple - let rippleX: number; - let rippleY: number; - let rippleSize: number; - - if ( - center || - ((event as React.MouseEvent)?.clientX === 0 && - (event as React.MouseEvent)?.clientY === 0) || - (!(event as React.MouseEvent)?.clientX && !(event as React.TouchEvent)?.touches) - ) { - rippleX = Math.round(rect.width / 2); - rippleY = Math.round(rect.height / 2); - } else { - const { clientX, clientY } = - event && (event as React.TouchEvent).touches - ? (event as React.TouchEvent).touches[0] - : (event as React.MouseEvent); - rippleX = Math.round(clientX - rect.left); - rippleY = Math.round(clientY - rect.top); - } - - if (center) { - rippleSize = Math.max(rect.width, rect.height); - - // For some reason the animation is broken on Mobile Chrome if the size is even. - if (rippleSize % 2 === 0) { - rippleSize += 1; - } - } else { - const sizeX = - Math.max(Math.abs((element ? element.clientWidth : 0) - rippleX), rippleX) * 2 + 2; - const sizeY = - Math.max(Math.abs((element ? element.clientHeight : 0) - rippleY), rippleY) * 2 + 2; - rippleSize = Math.sqrt(sizeX ** 2 + sizeY ** 2); - } - - // Touche devices - if ((event as React.TouchEvent)?.touches) { - // check that this isn't another touchstart due to multitouch - // otherwise we will only clear a single timer when unmounting while two - // are running - if (startTimerCommit.current === null) { - // Prepare the ripple effect. - startTimerCommit.current = () => { - startCommit({ rippleX, rippleY, rippleSize, cb }); - }; - // Delay the execution of the ripple effect. - // We have to make a tradeoff with this delay value. - startTimer.start(DELAY_RIPPLE, () => { - if (startTimerCommit.current) { - startTimerCommit.current(); - startTimerCommit.current = null; - } - }); - } - } else { - startCommit({ rippleX, rippleY, rippleSize, cb }); - } - }, - [centerProp, startCommit, startTimer], - ); - - const stop = React.useCallback<TouchRippleActions['stop']>( - (event, cb) => { - startTimer.clear(); - - // The touch interaction occurs too quickly. - // We still want to show ripple effect. - if (event?.type === 'touchend' && startTimerCommit.current) { - startTimerCommit.current(); - startTimerCommit.current = null; - startTimer.start(0, () => { - stop(event, cb); - }); - return; - } - - startTimerCommit.current = null; - - setRipples((oldRipples) => { - if (oldRipples.length > 0) { - return oldRipples.slice(1); - } - return oldRipples; - }); - rippleCallback.current = cb; - }, - [startTimer], - ); - - React.useImperativeHandle( - ref, - () => ({ - start, - stop, - }), - [start, stop], - ); - - return ( - <TouchRippleRoot - className={clsx(classes.root, touchRippleClasses.root, className)} - {...other} - ref={container} - > - <TransitionGroup component={null} exit> - {ripples} - </TransitionGroup> - </TouchRippleRoot> - ); - }, -) as React.ForwardRefExoticComponent<TouchRippleProps & React.RefAttributes<TouchRippleActions>>; - -TouchRipple.propTypes = { - /** - * If `true`, the ripple starts at the center of the component - * rather than at the point of interaction. - */ - center: PropTypes.bool, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, -} as any; - -export default TouchRipple; diff --git a/packages/mui-material-next/src/ButtonBase/TouchRipple.types.ts b/packages/mui-material-next/src/ButtonBase/TouchRipple.types.ts deleted file mode 100644 index 3a395bd1a76b76..00000000000000 --- a/packages/mui-material-next/src/ButtonBase/TouchRipple.types.ts +++ /dev/null @@ -1,28 +0,0 @@ -import * as React from 'react'; -// eslint-disable-next-line no-restricted-imports -import { InternalStandardProps as StandardProps } from '@mui/material'; -import { TouchRippleClasses } from './touchRippleClasses'; - -export type { TouchRippleClassKey } from './touchRippleClasses'; - -export interface StartActionOptions { - pulsate?: boolean; - center?: boolean; -} - -export interface TouchRippleActions { - start: ( - event?: React.SyntheticEvent, - options?: StartActionOptions, - callback?: () => void, - ) => void; - stop: (event?: React.SyntheticEvent, callback?: () => void) => void; -} - -export type TouchRippleProps = StandardProps<React.HTMLAttributes<HTMLElement>> & { - center?: boolean; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<TouchRippleClasses>; -}; diff --git a/packages/mui-material-next/src/ButtonBase/buttonBaseClasses.ts b/packages/mui-material-next/src/ButtonBase/buttonBaseClasses.ts deleted file mode 100644 index df950c4c9b47c6..00000000000000 --- a/packages/mui-material-next/src/ButtonBase/buttonBaseClasses.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface ButtonBaseClasses { - /** 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 keyboard focused. */ - focusVisible: string; - /** State class applied to the root element if the element is active. */ - active: string; -} - -export type ButtonBaseClassKey = keyof ButtonBaseClasses; - -export function getButtonBaseUtilityClass(slot: string): string { - return generateUtilityClass('MuiButtonBase', slot); -} - -const buttonBaseClasses: ButtonBaseClasses = generateUtilityClasses('MuiButtonBase', [ - 'root', - 'disabled', - 'focusVisible', - 'active', -]); - -export default buttonBaseClasses; diff --git a/packages/mui-material-next/src/ButtonBase/index.ts b/packages/mui-material-next/src/ButtonBase/index.ts deleted file mode 100644 index 80ec01dcc9e59d..00000000000000 --- a/packages/mui-material-next/src/ButtonBase/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './ButtonBase'; - -export { default as buttonBaseClasses } from './buttonBaseClasses'; -export * from './buttonBaseClasses'; diff --git a/packages/mui-material-next/src/ButtonBase/touchRippleClasses.ts b/packages/mui-material-next/src/ButtonBase/touchRippleClasses.ts deleted file mode 100644 index 1ea587476b1a3d..00000000000000 --- a/packages/mui-material-next/src/ButtonBase/touchRippleClasses.ts +++ /dev/null @@ -1,37 +0,0 @@ -import generateUtilityClass from '@mui/utils/generateUtilityClass'; -import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; - -export interface TouchRippleClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the internal `Ripple` components `ripple` class. */ - ripple: string; - /** Styles applied to the internal `Ripple` components `rippleVisible` class. */ - rippleVisible: string; - /** Styles applied to the internal `Ripple` components `ripplePulsate` class. */ - ripplePulsate: string; - /** Styles applied to the internal `Ripple` components `child` class. */ - child: string; - /** Styles applied to the internal `Ripple` components `childLeaving` class. */ - childLeaving: string; - /** Styles applied to the internal `Ripple` components `childPulsate` class. */ - childPulsate: string; -} - -export type TouchRippleClassKey = keyof TouchRippleClasses; - -export function getTouchRippleUtilityClass(slot: string): string { - return generateUtilityClass('MuiTouchRipple', slot); -} - -const touchRippleClasses: TouchRippleClasses = generateUtilityClasses('MuiTouchRipple', [ - 'root', - 'ripple', - 'rippleVisible', - 'ripplePulsate', - 'child', - 'childLeaving', - 'childPulsate', -]); - -export default touchRippleClasses; diff --git a/packages/mui-material-next/src/ButtonBase/useTouchRipple.ts b/packages/mui-material-next/src/ButtonBase/useTouchRipple.ts deleted file mode 100644 index 0411092b8efc3a..00000000000000 --- a/packages/mui-material-next/src/ButtonBase/useTouchRipple.ts +++ /dev/null @@ -1,106 +0,0 @@ -'use client'; -import * as React from 'react'; -import { useEventCallback } from '@mui/material/utils'; -import { TouchRippleActions } from './TouchRipple.types'; - -interface UseTouchRippleProps { - disabled: boolean; - disableFocusRipple?: boolean; - disableRipple?: boolean; - disableTouchRipple?: boolean; - rippleRef: React.RefObject<TouchRippleActions>; -} - -interface RippleEventHandlers { - onBlur: React.FocusEventHandler; - onContextMenu: React.MouseEventHandler; - onDragLeave: React.DragEventHandler; - onMouseDown: React.MouseEventHandler; - onMouseLeave: React.MouseEventHandler; - onMouseUp: React.MouseEventHandler; - onTouchEnd: React.TouchEventHandler; - onTouchMove: React.TouchEventHandler; - onTouchStart: React.TouchEventHandler; -} - -const useTouchRipple = (props: UseTouchRippleProps) => { - const { disabled, disableRipple, disableTouchRipple, rippleRef } = props; - - function useRippleHandler( - rippleAction: keyof TouchRippleActions, - skipRippleAction = disableTouchRipple, - ) { - return useEventCallback((event: React.SyntheticEvent) => { - if (!skipRippleAction && rippleRef.current) { - rippleRef.current[rippleAction](event); - } - - return true; - }); - } - - const handleBlur = useRippleHandler('stop', false); - const handleMouseDown = useRippleHandler('start'); - const handleContextMenu = useRippleHandler('stop'); - const handleDragLeave = useRippleHandler('stop'); - const handleMouseUp = useRippleHandler('stop'); - const handleMouseLeave = useRippleHandler('stop'); - const handleTouchStart = useRippleHandler('start'); - const handleTouchEnd = useRippleHandler('stop'); - const handleTouchMove = useRippleHandler('stop'); - - const [mountedState, setMountedState] = React.useState(false); - - React.useEffect(() => { - setMountedState(true); - }, []); - - const enableTouchRipple = mountedState && !disableRipple && !disabled; - - const getRippleHandlers = React.useMemo(() => { - const rippleHandlers = { - onBlur: handleBlur, - onMouseDown: handleMouseDown, - onMouseUp: handleMouseUp, - onMouseLeave: handleMouseLeave, - onContextMenu: handleContextMenu, - onDragLeave: handleDragLeave, - onTouchStart: handleTouchStart, - onTouchEnd: handleTouchEnd, - onTouchMove: handleTouchMove, - } as RippleEventHandlers; - - return (otherEvents: Partial<RippleEventHandlers> = {}) => { - const eventNames = Object.keys(rippleHandlers) as (keyof RippleEventHandlers)[]; - const wrappedEvents = eventNames.map((eventName) => ({ - name: eventName, - handler: (ev: any) => { - otherEvents[eventName]?.(ev); - rippleHandlers[eventName](ev); - }, - })); - - return wrappedEvents.reduce((acc, current) => { - acc[current.name] = current.handler; - return acc; - }, {} as RippleEventHandlers); - }; - }, [ - handleBlur, - handleMouseDown, - handleMouseUp, - handleMouseLeave, - handleContextMenu, - handleDragLeave, - handleTouchStart, - handleTouchEnd, - handleTouchMove, - ]); - - return { - enableTouchRipple, - getRippleHandlers, - }; -}; - -export default useTouchRipple; diff --git a/packages/mui-material-next/src/ButtonGroup/ButtonGroup.test.tsx b/packages/mui-material-next/src/ButtonGroup/ButtonGroup.test.tsx deleted file mode 100644 index 448828f269e12b..00000000000000 --- a/packages/mui-material-next/src/ButtonGroup/ButtonGroup.test.tsx +++ /dev/null @@ -1,281 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer, screen } from '@mui-internal/test-utils'; -import ButtonGroup, { buttonGroupClasses as classes } from '@mui/material-next/ButtonGroup'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import Button, { buttonClasses } from '@mui/material-next/Button'; -import ButtonGroupContext, { ButtonGroupContextType } from './ButtonGroupContext'; -import describeConformance from '../../test/describeConformance'; - -describe('<ButtonGroup />', () => { - const { render } = createRenderer(); - - let originalMatchmedia: typeof window.matchMedia; - - beforeEach(() => { - originalMatchmedia = window.matchMedia; - window.matchMedia = () => - ({ - addListener: () => {}, - removeListener: () => {}, - }) as unknown as MediaQueryList; - }); - - afterEach(() => { - window.matchMedia = originalMatchmedia; - }); - - describeConformance( - <ButtonGroup> - <Button>Conformance?</Button> - </ButtonGroup>, - () => ({ - classes, - inheritComponent: 'div', - render, - refInstanceof: window.HTMLDivElement, - testComponentPropWith: 'span', - muiName: 'MuiButtonGroup', - testVariantProps: { variant: 'filled' }, - skip: ['componentsProp'], - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - }), - ); - - it('should render with the root class but no others', () => { - const { container } = render( - <ButtonGroup> - <Button>Hello World</Button> - </ButtonGroup>, - ); - const buttonGroup = container.firstChild; - expect(buttonGroup).to.have.class(classes.root); - expect(buttonGroup).not.to.have.class(classes.filled); - expect(buttonGroup).not.to.have.class(classes.fullWidth); - }); - - it('should render an outlined button', () => { - const { getByRole } = render( - <ButtonGroup> - <Button>Hello World</Button> - </ButtonGroup>, - ); - const button = getByRole('button'); - const buttonGroup = getByRole('group'); - expect(button).to.have.class(buttonClasses.outlined); - expect(button).to.have.class(classes.grouped); - expect(buttonGroup).to.have.class(classes.outlined); - expect(buttonGroup).to.have.class(classes.primary); - expect(buttonGroup).not.to.have.class(classes.secondary); - }); - - it('can render an outlined primary button', () => { - const { getByRole } = render( - <ButtonGroup color="primary"> - <Button>Hello World</Button> - </ButtonGroup>, - ); - const button = getByRole('button'); - const buttonGroup = getByRole('group'); - expect(button).to.have.class(buttonClasses.outlined); - expect(button).to.have.class(buttonClasses.colorPrimary); - expect(button).to.have.class(classes.grouped); - expect(buttonGroup).to.have.class(classes.outlined); - expect(buttonGroup).to.have.class(classes.primary); - expect(buttonGroup).not.to.have.class(classes.secondary); - }); - - it('can render a filled button', () => { - const { getByRole } = render( - <ButtonGroup variant="filled"> - <Button>Hello World</Button> - </ButtonGroup>, - ); - const button = getByRole('button'); - const buttonGroup = getByRole('group'); - expect(button).to.have.class(buttonClasses.filled); - expect(button).to.have.class(classes.grouped); - expect(buttonGroup).to.have.class(classes.filled); - expect(buttonGroup).to.have.class(classes.primary); - expect(buttonGroup).not.to.have.class(classes.secondary); - }); - - it('can render a small button', () => { - const { getByRole } = render( - <ButtonGroup size="small"> - <Button>Hello World</Button> - </ButtonGroup>, - ); - const button = getByRole('button'); - expect(button).to.have.class(buttonClasses.outlined); - expect(button).to.have.class(buttonClasses.sizeSmall); - }); - - it('can render a large button', () => { - const { getByRole } = render( - <ButtonGroup size="large"> - <Button>Hello World</Button> - </ButtonGroup>, - ); - const button = getByRole('button'); - expect(button).to.have.class(buttonClasses.outlined); - expect(button).to.have.class(buttonClasses.sizeLarge); - }); - - it('should have a ripple by default', () => { - const props = { TouchRippleProps: { classes: { root: 'touchRipple' } } }; - const { container } = render( - <ButtonGroup> - <Button {...props}>Hello World</Button> - </ButtonGroup>, - ); - expect(container.querySelector('.touchRipple')).not.to.equal(null); - }); - - it('can disable the elevation', () => { - const { getByRole } = render( - <ButtonGroup disableElevation> - <Button>Hello World</Button> - </ButtonGroup>, - ); - const button = getByRole('button'); - expect(button).to.have.class(buttonClasses.disableElevation); - }); - - it('can disable the ripple', () => { - const props = { TouchRippleProps: { classes: { root: 'touchRipple' } } }; - const { container } = render( - <ButtonGroup disableRipple> - <Button {...props}>Hello World</Button> - </ButtonGroup>, - ); - expect(container.querySelector('.touchRipple')).to.equal(null); - }); - - it('should not be fullWidth by default', () => { - const { container, getByRole } = render( - <ButtonGroup> - <Button>Hello World</Button> - </ButtonGroup>, - ); - const button = getByRole('button'); - const buttonGroup = container.firstChild; - expect(buttonGroup).not.to.have.class(classes.fullWidth); - expect(button).not.to.have.class(buttonClasses.fullWidth); - }); - - it('can pass fullWidth to Button', () => { - const { container, getByRole } = render( - <ButtonGroup fullWidth> - <Button>Hello World</Button> - </ButtonGroup>, - ); - const buttonGroup = container.firstChild; - const button = getByRole('button'); - expect(buttonGroup).to.have.class(classes.fullWidth); - expect(button).to.have.class(buttonClasses.fullWidth); - }); - - it('classes.grouped should be merged with Button className', () => { - render( - <ButtonGroup> - <Button className="foo-bar">Hello World</Button> - </ButtonGroup>, - ); - expect(screen.getByRole('button')).to.have.class(classes.grouped); - expect(screen.getByRole('button')).to.have.class('foo-bar'); - }); - - it('should forward the context to children', () => { - let context: ButtonGroupContextType | null; - render( - <ButtonGroup size="large" variant="filled"> - <ButtonGroupContext.Consumer> - {(value) => { - context = value; - return <Button />; - }} - </ButtonGroupContext.Consumer> - </ButtonGroup>, - ); - expect(context!.variant).to.equal('filled'); - expect(context!.size).to.equal('large'); - expect(context!.fullWidth).to.equal(false); - expect(context!.disableRipple).to.equal(false); - expect(context!.disableTouchRipple).to.equal(false); - expect(context!.disableElevation).to.equal(false); - expect(context!.disabled).to.equal(false); - expect(context!.color).to.equal('primary'); - }); - - describe('theme default props on Button', () => { - it('should override default variant prop', () => { - render( - <CssVarsProvider - theme={extendTheme({ - components: { - MuiButton: { - defaultProps: { - color: 'primary', - size: 'medium', - variant: 'text', - }, - }, - }, - })} - > - <ButtonGroup variant="outlined" size="small" color="secondary"> - <Button>Hello World</Button> - </ButtonGroup> - </CssVarsProvider>, - ); - - expect(screen.getByRole('button')).to.have.class(buttonClasses.outlined); - expect(screen.getByRole('button')).to.have.class(buttonClasses.sizeSmall); - expect(screen.getByRole('button')).to.have.class(buttonClasses.colorSecondary); - }); - }); - - describe('position classes', () => { - it('correctly applies position classes to buttons', () => { - render( - <ButtonGroup> - <Button>Button 1</Button> - <Button>Button 2</Button> - <Button>Button 3</Button> - </ButtonGroup>, - ); - - const firstButton = screen.getAllByRole('button')[0]; - const middleButton = screen.getAllByRole('button')[1]; - const lastButton = screen.getAllByRole('button')[2]; - - expect(firstButton).to.have.class(classes.firstButton); - expect(firstButton).not.to.have.class(classes.middleButton); - expect(firstButton).not.to.have.class(classes.lastButton); - - expect(middleButton).to.have.class(classes.middleButton); - expect(middleButton).not.to.have.class(classes.firstButton); - expect(middleButton).not.to.have.class(classes.lastButton); - - expect(lastButton).to.have.class(classes.lastButton); - expect(lastButton).not.to.have.class(classes.middleButton); - expect(lastButton).not.to.have.class(classes.firstButton); - }); - - it('does not apply any position classes to a single button', () => { - render( - <ButtonGroup> - <Button>Single Button</Button> - </ButtonGroup>, - ); - - const button = screen.getByRole('button'); - - expect(button).not.to.have.class(classes.firstButton); - expect(button).not.to.have.class(classes.middleButton); - expect(button).not.to.have.class(classes.lastButton); - }); - }); -}); diff --git a/packages/mui-material-next/src/ButtonGroup/ButtonGroup.tsx b/packages/mui-material-next/src/ButtonGroup/ButtonGroup.tsx deleted file mode 100644 index e61482b570cf53..00000000000000 --- a/packages/mui-material-next/src/ButtonGroup/ButtonGroup.tsx +++ /dev/null @@ -1,371 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { getValidReactChildren } from '@mui/utils'; -import { OverridableComponent } from '@mui/types'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; -import buttonGroupClasses, { getButtonGroupUtilityClass } from './buttonGroupClasses'; -import ButtonGroupContext from './ButtonGroupContext'; -import ButtonGroupButtonContext from './ButtonGroupButtonContext'; -import { ButtonGroupOwnerState, ButtonGroupProps, ButtonGroupTypeMap } from './ButtonGroup.types'; - -const useUtilityClasses = (ownerState: ButtonGroupOwnerState) => { - const { classes, color, disabled, disableElevation, fullWidth, orientation, variant } = - ownerState; - - const slots = { - root: [ - 'root', - variant, - color, - orientation === 'vertical' && 'vertical', - fullWidth && 'fullWidth', - disableElevation && 'disableElevation', - ], - grouped: ['grouped', disabled && 'disabled'], - firstButton: ['firstButton'], - lastButton: ['lastButton'], - middleButton: ['middleButton'], - }; - - return composeClasses(slots, getButtonGroupUtilityClass, classes); -}; - -const ButtonGroupRoot = styled('div', { - name: 'MuiButtonGroup', - slot: 'Root', - overridesResolver(props, styles) { - const { - ownerState: { color, disableElevation, fullWidth, orientation, variant }, - } = props; - - return [ - { [`& .${buttonGroupClasses.grouped}`]: styles.grouped }, - { - [`& .${buttonGroupClasses.firstButton}`]: styles.firstButton, - }, - { - [`& .${buttonGroupClasses.lastButton}`]: styles.lastButton, - }, - { - [`& .${buttonGroupClasses.middleButton}`]: styles.middleButton, - }, - styles.root, - styles[color], - styles[variant], - disableElevation === true && styles.disableElevation, - fullWidth && styles.fullWidth, - orientation === 'vertical' && styles.vertical, - ]; - }, -})<{ ownerState: ButtonGroupOwnerState }>( - ({ - theme: { vars: tokens }, - ownerState: { disabled, disableElevation, fullWidth, orientation, variant }, - }) => ({ - display: 'inline-flex', - borderRadius: tokens.sys.shape.corner.full, - ...(variant === 'elevated' && { - boxShadow: tokens.sys.elevation[1], - }), - ...((disableElevation || disabled) && { - boxShadow: 'none', - }), - ...(fullWidth && { - width: '100%', - }), - ...(orientation === 'vertical' && { - flexDirection: 'column', - }), - [`& .${buttonGroupClasses.grouped}`]: { - minWidth: 40, - '&:hover, &:focus': { - boxShadow: 'none', - }, - boxShadow: 'none', - }, - [`& .${buttonGroupClasses.firstButton},& .${buttonGroupClasses.middleButton}`]: { - ...(orientation === 'horizontal' && { - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - }), - ...(orientation === 'vertical' && { - borderBottomRightRadius: 0, - borderBottomLeftRadius: 0, - }), - ...(variant === 'filled' && - orientation === 'horizontal' && { - borderRight: `1px solid ${tokens.sys.color.outline}`, - [`&.${buttonGroupClasses.disabled}`]: { - borderRightColor: `rgba(${tokens.sys.color.outline}, 0.12)`, - }, - }), - ...(variant === 'filled' && - orientation === 'vertical' && { - borderBottom: `1px solid ${tokens.sys.color.outline}`, - [`&.${buttonGroupClasses.disabled}`]: { - borderBottomColor: `rgba(${tokens.sys.color.outline}, 0.12)`, - }, - }), - ...(variant === 'outlined' && - orientation === 'horizontal' && { - borderRightColor: 'transparent', - }), - ...(variant === 'outlined' && - orientation === 'vertical' && { - borderBottomColor: 'transparent', - }), - ...((variant === 'text' || variant === 'filledTonal' || variant === 'elevated') && - orientation === 'horizontal' && { - borderRight: `1px solid ${tokens.sys.color.outlineVariant}`, - [`&.${buttonGroupClasses.disabled}`]: { - borderRightColor: `rgba(${tokens.sys.color.outlineVariant}, 0.12)`, - }, - }), - ...((variant === 'text' || variant === 'filledTonal' || variant === 'elevated') && - orientation === 'vertical' && { - borderBottom: `1px solid ${tokens.sys.color.outlineVariant}`, - [`&.${buttonGroupClasses.disabled}`]: { - borderBottomColor: `rgba(${tokens.sys.color.outlineVariant}, 0.12)`, - }, - }), - '&:hover': { - ...(variant === 'outlined' && - orientation === 'horizontal' && { - borderRightColor: 'currentColor', - }), - ...(variant === 'outlined' && - orientation === 'vertical' && { - borderBottomColor: 'currentColor', - }), - }, - }, - [`& .${buttonGroupClasses.lastButton},& .${buttonGroupClasses.middleButton}`]: { - ...(orientation === 'horizontal' && { - borderTopLeftRadius: 0, - borderBottomLeftRadius: 0, - }), - ...(orientation === 'vertical' && { - borderTopRightRadius: 0, - borderTopLeftRadius: 0, - }), - ...(variant === 'outlined' && - orientation === 'horizontal' && { - marginLeft: -1, - }), - ...(variant === 'outlined' && - orientation === 'vertical' && { - marginTop: -1, - }), - }, - }), -); - -/** - * - * Demos: - * - * - [Button Group](https://mui.com/material-ui/react-button-group/) - * - * API: - * - * - [ButtonGroup API](https://mui.com/material-ui/api/button-group/) - */ -const ButtonGroup = React.forwardRef(function ButtonGroup< - BaseComponentType extends React.ElementType = ButtonGroupTypeMap['defaultComponent'], ->(inProps: ButtonGroupProps<BaseComponentType>, ref: React.ForwardedRef<HTMLDivElement>) { - const props = useThemeProps({ props: inProps, name: 'MuiButtonGroup' }); - const { - children, - className, - color = 'primary', - component = 'div', - disabled = false, - disableElevation = false, - disableTouchRipple = false, - disableRipple = false, - fullWidth = false, - orientation = 'horizontal', - size = 'medium', - variant = 'outlined', - ...other - } = props; - - const ownerState = { - ...props, - color, - component, - disabled, - disableElevation, - disableTouchRipple, - disableRipple, - fullWidth, - orientation, - size, - variant, - }; - - const classes = useUtilityClasses(ownerState); - - const context = React.useMemo( - () => ({ - className: classes.grouped, - color, - disabled, - disableElevation, - disableTouchRipple, - disableRipple, - fullWidth, - size, - variant, - }), - [ - color, - disabled, - disableElevation, - disableTouchRipple, - disableRipple, - fullWidth, - size, - variant, - classes.grouped, - ], - ); - - const validChildren = getValidReactChildren(children); - const childrenCount = validChildren.length; - - const getButtonPositionClassName = (index: number) => { - const isFirstButton = index === 0; - const isLastButton = index === childrenCount - 1; - - if (isFirstButton && isLastButton) { - return ''; - } - if (isFirstButton) { - return classes.firstButton; - } - if (isLastButton) { - return classes.lastButton; - } - return classes.middleButton; - }; - - return ( - <ButtonGroupRoot - as={component} - role="group" - className={clsx(classes.root, className)} - ref={ref} - ownerState={ownerState} - {...other} - > - <ButtonGroupContext.Provider value={context}> - {validChildren.map((child, index) => { - return ( - <ButtonGroupButtonContext.Provider - key={index} - value={getButtonPositionClassName(index)} - > - {child} - </ButtonGroupButtonContext.Provider> - ); - })} - </ButtonGroupContext.Provider> - </ButtonGroupRoot> - ); -}) as OverridableComponent<ButtonGroupTypeMap>; - -ButtonGroup.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, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['primary', 'secondary', 'tertiary']), - PropTypes.string, - ]), - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * If `true`, the component is disabled. - * @default false - */ - disabled: PropTypes.bool, - /** - * If `true`, no elevation is used. - * @default false - */ - disableElevation: PropTypes.bool, - /** - * If `true`, the button ripple effect is disabled. - * @default false - */ - disableRipple: PropTypes.bool, - /** - * If `true`, the touch ripple effect is disabled. - * @default false - */ - disableTouchRipple: PropTypes.bool, - /** - * If `true`, the buttons will take up the full width of its container. - * @default false - */ - fullWidth: PropTypes.bool, - /** - * The component orientation (layout flow direction). - * @default 'horizontal' - */ - orientation: PropTypes.oneOf(['horizontal', 'vertical']), - /** - * The size of the component. - * `small` is equivalent to the dense button styling. - * @default 'medium' - */ - size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['small', 'medium', 'large']), - PropTypes.string, - ]), - /** - * 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 variant to use. - * @default 'outlined' - */ - variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['elevated', 'filled', 'filledTonal', 'outlined', 'text']), - PropTypes.string, - ]), -} as any; - -export default ButtonGroup; diff --git a/packages/mui-material-next/src/ButtonGroup/ButtonGroup.types.ts b/packages/mui-material-next/src/ButtonGroup/ButtonGroup.types.ts deleted file mode 100644 index 0a4e713b418a0d..00000000000000 --- a/packages/mui-material-next/src/ButtonGroup/ButtonGroup.types.ts +++ /dev/null @@ -1,107 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverridableStringUnion, OverrideProps, PartiallyRequired } from '@mui/types'; -import { Theme } from '../styles'; -import { ButtonGroupClasses } from './buttonGroupClasses'; - -export interface ButtonGroupPropsColorOverrides {} -export interface ButtonGroupPropsVariantOverrides {} -export interface ButtonGroupPropsSizeOverrides {} - -export interface ButtonGroupOwnProps { - /** - * The content of the component. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<ButtonGroupClasses>; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color?: OverridableStringUnion< - 'primary' | 'secondary' | 'tertiary', - ButtonGroupPropsColorOverrides - >; - /** - * If `true`, the component is disabled. - * @default false - */ - disabled?: boolean; - /** - * If `true`, no elevation is used. - * @default false - */ - disableElevation?: boolean; - /** - * If `true`, the button ripple effect is disabled. - * @default false - */ - disableRipple?: boolean; - /** - * If `true`, the touch ripple effect is disabled. - * @default false - */ - disableTouchRipple?: boolean; - /** - * If `true`, the buttons will take up the full width of its container. - * @default false - */ - fullWidth?: boolean; - /** - * The component orientation (layout flow direction). - * @default 'horizontal' - */ - orientation?: 'vertical' | 'horizontal'; - /** - * The size of the component. - * `small` is equivalent to the dense button styling. - * @default 'medium' - */ - size?: OverridableStringUnion<'small' | 'medium' | 'large', ButtonGroupPropsSizeOverrides>; - /** - * The variant to use. - * @default 'outlined' - */ - variant?: OverridableStringUnion< - 'text' | 'outlined' | 'filled' | 'filledTonal' | 'elevated', - ButtonGroupPropsVariantOverrides - >; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -export interface ButtonGroupTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'div', -> { - props: AdditionalProps & ButtonGroupOwnProps; - defaultComponent: RootComponent; -} - -export type ButtonGroupProps< - RootComponent extends React.ElementType = ButtonGroupTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<ButtonGroupTypeMap<AdditionalProps, RootComponent>, RootComponent> & { - component?: React.ElementType; -}; - -export interface ButtonGroupOwnerState - extends PartiallyRequired< - ButtonGroupProps, - | 'color' - | 'disabled' - | 'disableElevation' - | 'disableRipple' - | 'disableTouchRipple' - | 'fullWidth' - | 'orientation' - | 'size' - | 'variant' - > {} diff --git a/packages/mui-material-next/src/ButtonGroup/ButtonGroupButtonContext.ts b/packages/mui-material-next/src/ButtonGroup/ButtonGroupButtonContext.ts deleted file mode 100644 index a93c0436fb729a..00000000000000 --- a/packages/mui-material-next/src/ButtonGroup/ButtonGroupButtonContext.ts +++ /dev/null @@ -1,14 +0,0 @@ -import * as React from 'react'; - -type ButtonPositionClassName = string; - -/** - * @ignore - internal component. - */ -const ButtonGroupButtonContext = React.createContext<ButtonPositionClassName | null>(null); - -if (process.env.NODE_ENV !== 'production') { - ButtonGroupButtonContext.displayName = 'ButtonGroupButtonContext'; -} - -export default ButtonGroupButtonContext; diff --git a/packages/mui-material-next/src/ButtonGroup/ButtonGroupContext.ts b/packages/mui-material-next/src/ButtonGroup/ButtonGroupContext.ts deleted file mode 100644 index 179c343793af2e..00000000000000 --- a/packages/mui-material-next/src/ButtonGroup/ButtonGroupContext.ts +++ /dev/null @@ -1,25 +0,0 @@ -import * as React from 'react'; -import type { ButtonGroupProps } from './ButtonGroup.types'; - -export interface ButtonGroupContextType { - className?: string; - color?: ButtonGroupProps['color']; - disabled?: boolean; - disableElevation?: boolean; - disableTouchRipple?: boolean; - disableRipple?: boolean; - fullWidth?: boolean; - size?: ButtonGroupProps['size']; - variant?: ButtonGroupProps['variant']; -} - -/** - * @ignore - internal component. - */ -const ButtonGroupContext = React.createContext<ButtonGroupContextType | null>(null); - -if (process.env.NODE_ENV !== 'production') { - ButtonGroupContext.displayName = 'ButtonGroupContext'; -} - -export default ButtonGroupContext; diff --git a/packages/mui-material-next/src/ButtonGroup/buttonGroupClasses.ts b/packages/mui-material-next/src/ButtonGroup/buttonGroupClasses.ts deleted file mode 100644 index 9eba539d56689b..00000000000000 --- a/packages/mui-material-next/src/ButtonGroup/buttonGroupClasses.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { - unstable_generateUtilityClass as generateUtilityClass, - unstable_generateUtilityClasses as generateUtilityClasses, -} from '@mui/utils'; - -export interface ButtonGroupClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `variant="text"`. */ - text: string; - /** Styles applied to the root element if `variant="outlined"`. */ - outlined: string; - /** Styles applied to the root element if `variant="filled"`. */ - filled: string; - /** Styles applied to the root element if `variant="filledTonal"`. */ - filledTonal: string; - /** Styles applied to the root element if `variant="elevated"`. */ - elevated: string; - /** State class applied to the root element if `color="primary"`. */ - primary: string; - /** State class applied to the toot element if `color="secondary"`. */ - secondary: string; - /** State class applied to the root element if `color="tertiary"`. */ - tertiary: string; - /** Styles applied to the root element if `disableElevation={true}`. */ - disableElevation: string; - /** State class applied to the child elements if `disabled={true}`. */ - disabled: string; - /** Styles applied to the first button in the button group. */ - firstButton: string; - /** Styles applied to the root element if `fullWidth={true}`. */ - fullWidth: string; - /** Styles applied to the root element if `orientation="vertical"`. */ - vertical: string; - /** Styles applied to the children. */ - grouped: string; - /** Styles applied to the last button in the button group. */ - lastButton: string; - /** Styles applied to buttons in the middle of the button group. */ - middleButton: string; -} - -export type ButtonGroupClassKey = keyof ButtonGroupClasses; - -export function getButtonGroupUtilityClass(slot: string): string { - return generateUtilityClass('MuiButtonGroup', slot); -} - -const buttonGroupClasses: ButtonGroupClasses = generateUtilityClasses('MuiButtonGroup', [ - 'root', - 'text', - 'outlined', - 'filled', - 'filledTonal', - 'elevated', - 'primary', - 'secondary', - 'tertiary', - 'disableElevation', - 'disabled', - 'firstButton', - 'fullWidth', - 'vertical', - 'grouped', - 'lastButton', - 'middleButton', -]); - -export default buttonGroupClasses; diff --git a/packages/mui-material-next/src/ButtonGroup/index.ts b/packages/mui-material-next/src/ButtonGroup/index.ts deleted file mode 100644 index 36b1a38cd0f773..00000000000000 --- a/packages/mui-material-next/src/ButtonGroup/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -'use client'; -export { default } from './ButtonGroup'; -export { default as buttonGroupClasses } from './buttonGroupClasses'; -export * from './buttonGroupClasses'; -export { default as ButtonGroupButtonContext } from './ButtonGroupButtonContext'; -export { default as ButtonGroupContext } from './ButtonGroupContext'; diff --git a/packages/mui-material-next/src/Chip/Chip.test.tsx b/packages/mui-material-next/src/Chip/Chip.test.tsx deleted file mode 100644 index afe4eadf86d1b6..00000000000000 --- a/packages/mui-material-next/src/Chip/Chip.test.tsx +++ /dev/null @@ -1,754 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { spy, stub } from 'sinon'; -import userEvent from '@testing-library/user-event'; -import { - act, - createRenderer, - fireEvent, - focusVisible, - simulatePointerDevice, - programmaticFocusTriggersFocusVisible, -} from '@mui-internal/test-utils'; -import { hexToRgb } from '@mui/system'; -import { unstable_capitalize as capitalize } from '@mui/utils'; -import { createTheme } from '@mui/material/styles'; -import Avatar from '@mui/material/Avatar'; -import Chip, { chipClasses as classes } from '@mui/material-next/Chip'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import CheckBox from '../internal/svg-icons/CheckBox'; -import { ChipProps } from './Chip.types'; -import describeConformance from '../../test/describeConformance'; - -// TODO: remove after migrating SvgIcon to support Material Design 3 colors -const MaterialV5DefaultTheme = createTheme(); - -describe('<Chip />', () => { - let originalMatchmedia: typeof window.matchMedia; - - beforeEach(() => { - originalMatchmedia = window.matchMedia; - window.matchMedia = () => - ({ - addListener: () => {}, - removeListener: () => {}, - }) as unknown as MediaQueryList; - }); - - afterEach(() => { - window.matchMedia = originalMatchmedia; - }); - - const { render } = createRenderer(); - - describeConformance(<Chip />, () => ({ - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - classes, - inheritComponent: 'div', - render, - muiName: 'MuiChip', - testDeepOverrides: { slotName: 'label', slotClassName: classes.label }, - testVariantProps: { variant: 'outlined' }, - testStateOverrides: { prop: 'size', value: 'small', styleKey: 'sizeSmall' }, - refInstanceof: window.HTMLDivElement, - testComponentPropWith: 'span', - skip: ['componentsProp'], - })); - - describe('text only', () => { - it('is not in tab order', () => { - const { container } = render(<Chip label="My text Chip" />); - - expect(container.querySelectorAll('[tabindex]')).to.have.length(0); - }); - - it('should renders certain classes and contains a label', () => { - const { container } = render(<Chip label="My text Chip" />); - - const chip = container.querySelector(`.${classes.root}`); - const label = container.querySelector(`.${classes.label}`); - - expect(label).to.have.tagName('span'); - expect(label).to.have.text('My text Chip'); - - expect(chip).to.have.class(classes.root); - expect(chip).not.to.have.class(classes.colorPrimary); - expect(chip).not.to.have.class(classes.colorSecondary); - expect(chip).not.to.have.class(classes.clickable); - expect(chip).not.to.have.class(classes.deletable); - }); - - it('should render with the color class name based on the color prop', () => { - const colorOptions: NonNullable<ChipProps['color']>[] = [ - 'primary', - 'secondary', - 'info', - 'error', - 'warning', - 'success', - ]; - colorOptions.forEach((color) => { - const { container } = render(<Chip color={color} />); - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes[`color${capitalize(color)}` as keyof typeof classes]); - }); - }); - }); - - describe('clickable chip', () => { - it('renders as a button in taborder with the label as the accessible name', () => { - const { getByRole } = render(<Chip label="My Chip" onClick={() => {}} />); - - const button = getByRole('button'); - expect(button).to.have.property('tabIndex', 0); - expect(button).toHaveAccessibleName('My Chip'); - }); - - it('should render link with the button base', () => { - const { getByTestId } = render( - <Chip data-testid="root" component="a" clickable label="My text Chip" />, - ); - - const chipRoot = getByTestId('root'); - - expect(chipRoot).to.have.class('MuiButtonBase-root'); - expect(chipRoot).to.have.tagName('a'); - }); - - it('should render ripple', () => { - const { getByTestId } = render( - <Chip data-testid="root" component="a" clickable label="My text Chip" />, - ); - - const chipRoot = getByTestId('root'); - - expect(chipRoot.querySelector('.MuiTouchRipple-root')).not.to.equal(null); - }); - - it('should disable ripple when MuiButtonBase has disableRipple in theme', () => { - const theme = extendTheme({ - components: { - MuiButtonBase: { - defaultProps: { - disableRipple: true, - }, - }, - }, - }); - - const { getByTestId } = render( - <CssVarsProvider theme={theme}> - <Chip data-testid="root" clickable label="My Chip" /> - </CssVarsProvider>, - ); - - const chipRoot = getByTestId('root'); - - expect(chipRoot).to.have.class('MuiButtonBase-root'); - expect(chipRoot.querySelector('.MuiTouchRipple-root')).to.equal(null); - }); - - it('should apply user value of tabIndex', () => { - const { getByRole } = render(<Chip label="My Chip" onClick={() => {}} tabIndex={5} />); - - expect(getByRole('button')).to.have.property('tabIndex', 5); - }); - - it('should render with the root and clickable class', () => { - const { container } = render(<Chip label="My Chip" onClick={() => {}} />); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.root); - expect(chip).to.have.class(classes.clickable); - }); - - it('should render with the root, clickable, and colorPrimary class', () => { - const { getByRole } = render(<Chip label="My Chip" onClick={() => {}} color="primary" />); - - const button = getByRole('button'); - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes.colorPrimary); - expect(button).to.have.class(classes.clickable); - }); - - it('should render with the root, outlined, clickable, and colorPrimary class', () => { - const { container } = render( - <Chip color="primary" label="My Chip" onClick={() => {}} variant="outlined" />, - ); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.root); - expect(chip).to.have.class(classes.colorPrimary); - expect(chip).to.have.class(classes.clickable); - expect(chip).to.have.class(classes.outlined); - }); - - it('should render with the root, outlined, clickable, and colorSecondary class', () => { - const { getByRole } = render( - <Chip color="secondary" label="My Chip" onClick={() => {}} variant="outlined" />, - ); - - const button = getByRole('button'); - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes.colorSecondary); - expect(button).to.have.class(classes.clickable); - expect(button).to.have.class(classes.outlined); - }); - - it('should render with the root, filled, clickable, and colorPrimary class', () => { - const { getByRole } = render( - <Chip color="primary" label="My Chip" onClick={() => {}} variant="filled" />, - ); - - const chip = getByRole('button'); - expect(chip).to.have.class(classes.root); - expect(chip).to.have.class(classes.colorPrimary); - expect(chip).to.have.class(classes.clickable); - expect(chip).to.have.class(classes.filled); - }); - - it('should not be focused when a deletable chip is disabled', () => { - const { getByTestId } = render( - <Chip label="My Chip" disabled data-testid="chip" onDelete={() => {}} />, - ); - - const chip = getByTestId('chip'); - - simulatePointerDevice(); - act(() => { - fireEvent.keyDown(document.body, { key: 'Tab' }); - }); - - expect(chip).to.have.class(classes.root); - expect(chip).not.to.have.class(classes.focusVisible); - }); - - it('should render with the root, filled, clickable, and colorSecondary class', () => { - const { getByRole } = render( - <Chip color="secondary" label="My Chip" onClick={() => {}} variant="filled" />, - ); - - const chip = getByRole('button'); - expect(chip).to.have.class(classes.root); - expect(chip).to.have.class(classes.colorSecondary); - expect(chip).to.have.class(classes.clickable); - expect(chip).to.have.class(classes.filled); - }); - }); - - describe('deletable Avatar chip', () => { - it('should render a button in tab order with the avatar', () => { - const { container, getByRole } = render( - <Chip - avatar={<Avatar id="avatar">MB</Avatar>} - label="Text Avatar Chip" - onDelete={() => {}} - />, - ); - - expect(getByRole('button')).to.have.property('tabIndex', 0); - expect(container.querySelector('#avatar')).not.to.equal(null); - }); - - it('should not create ripples', () => { - const { getByTestId } = render( - <Chip data-testid="root" avatar={<Avatar id="avatar">MB</Avatar>} onDelete={() => {}} />, - ); - - const chipRoot = getByTestId('root'); - - expect(chipRoot.querySelector('.MuiTouchRipple-root')).to.equal(null); - }); - - it('should apply user value of tabIndex', () => { - const { container, getByRole } = render( - <Chip - avatar={<Avatar id="avatar">MB</Avatar>} - label="Text Avatar Chip" - onDelete={() => {}} - tabIndex={5} - />, - ); - - expect(getByRole('button')).to.have.property('tabIndex', 5); - const elementsInTabOrder = Array.from(container.querySelectorAll('[tabIndex]')).filter( - (element) => (element as HTMLElement).tabIndex >= 0, - ); - expect(elementsInTabOrder).to.have.length(1); - }); - - it('fires onDelete when clicking the delete icon', () => { - const handleDelete = spy(); - const { getByTestId } = render( - <Chip - avatar={<Avatar id="avatar">MB</Avatar>} - label="Text Avatar Chip" - onDelete={handleDelete} - deleteIcon={<div data-testid="delete-icon" />} - />, - ); - const deleteIcon = getByTestId('delete-icon'); - - fireEvent.click(deleteIcon); - - expect(handleDelete.callCount).to.equal(1); - }); - - it('should stop propagation when clicking the delete icon', () => { - const handleClick = spy(); - const { getByTestId } = render( - <Chip - avatar={<Avatar id="avatar">MB</Avatar>} - label="Text Avatar Chip" - onClick={handleClick} - onDelete={() => {}} - deleteIcon={<div data-testid="delete-icon" />} - />, - ); - const deleteIcon = getByTestId('delete-icon'); - - fireEvent.click(deleteIcon); - - expect(handleClick.callCount).to.equal(0); - }); - - it('should render with the root, deletable classes', () => { - const { container } = render( - <Chip - avatar={<Avatar id="avatar">MB</Avatar>} - label="Text Avatar Chip" - onDelete={() => {}} - />, - ); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.deletable); - }); - - it('should render with the root, deletable, and colorPrimary classes', () => { - const { container } = render( - <Chip - avatar={<Avatar className="my-Avatar">MB</Avatar>} - label="Text Avatar Chip" - onDelete={() => {}} - color="primary" - />, - ); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.colorPrimary); - expect(chip).to.have.class(classes.deletable); - }); - - it('should render with the root, deletable, and colorSecondary classes', () => { - const { container } = render( - <Chip - avatar={<Avatar>MB</Avatar>} - label="Text Avatar Chip" - onDelete={() => {}} - color="secondary" - />, - ); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.colorSecondary); - expect(chip).to.have.class(classes.deletable); - }); - }); - - describe('prop: deleteIcon', () => { - it('should render a default icon with the root, deletable and deleteIcon classes', () => { - const { getByRole, getByTestId } = render( - <Chip label="Custom delete icon Chip" onDelete={() => {}} />, - ); - - const chip = getByRole('button'); - const icon = getByTestId('ClearIcon'); - - expect(chip).to.have.class(classes.deletable); - expect(chip).to.contain(icon); - expect(icon).to.have.class(classes.deleteIcon); - }); - - it('should render default icon with the root, deletable, colorPrimary, and deleteIcon classes', () => { - const { container, getByTestId } = render( - <Chip label="Custom delete icon Chip" onDelete={() => {}} color="primary" />, - ); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.colorPrimary); - expect(chip).to.have.class(classes.deletable); - const icon = getByTestId('ClearIcon'); - expect(icon).to.have.class(classes.deleteIcon); - }); - - it('should render default icon with the root, deletable, colorSecondary, and deleteIcon classes', () => { - const { container, getByTestId } = render( - <Chip label="Custom delete icon Chip" onDelete={() => {}} color="secondary" />, - ); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.colorSecondary); - expect(chip).to.have.class(classes.deletable); - const icon = getByTestId('ClearIcon'); - expect(icon).to.have.class(classes.deleteIcon); - }); - - it('should render default icon with the root, deletable, deleteIcon primary class and deleteIcon filled primary class', () => { - const { container, getByTestId } = render( - <Chip - label="Custom delete icon Chip" - onDelete={() => {}} - color="primary" - variant="filled" - />, - ); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.colorPrimary); - expect(chip).to.have.class(classes.deletable); - const icon = getByTestId('ClearIcon'); - expect(icon).to.have.class(classes.deleteIcon); - }); - - it('should render default icon with the root, deletable, deleteIcon primary class and deleteIcon outlined primary class', () => { - const { container, getByTestId } = render( - <Chip - label="Custom delete icon Chip" - onDelete={() => {}} - color="primary" - variant="outlined" - />, - ); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.colorPrimary); - expect(chip).to.have.class(classes.deletable); - const icon = getByTestId('ClearIcon'); - expect(icon).to.have.class(classes.deleteIcon); - }); - - it('accepts a custom icon', () => { - const handleDelete = spy(); - const { getByTestId } = render( - <Chip label="Custom delete icon Chip" onDelete={handleDelete} deleteIcon={<CheckBox />} />, - ); - - fireEvent.click(getByTestId('CheckBoxIcon')); - - expect(handleDelete.callCount).to.equal(1); - }); - }); - - describe('reacts to keyboard chip', () => { - it('should call onKeyDown when a key is pressed', () => { - const handleKeydown = stub().callsFake((event) => event.key); - const { getByRole } = render(<Chip onClick={() => {}} onKeyDown={handleKeydown} />); - const chip = getByRole('button'); - act(() => { - chip.focus(); - }); - - fireEvent.keyDown(chip, { key: 'p' }); - - expect(handleKeydown.callCount).to.equal(1); - expect(handleKeydown.firstCall.returnValue).to.equal('p'); - }); - - it('should unfocus when a esc key is pressed', () => { - const handleBlur = spy(); - const { getByRole } = render(<Chip onBlur={handleBlur} onClick={() => {}} />); - const chip = getByRole('button'); - act(() => { - chip.focus(); - }); - - fireEvent.keyUp(chip, { key: 'Escape' }); - - expect(handleBlur.callCount).to.equal(1); - expect(chip).not.toHaveFocus(); - }); - - it('should call onClick when `space` is released ', async function test() { - const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); - // userEvent.setup() requires Safari 14 or up to work - // We can remove this check once we drop support for Safari 13 - if (isSafari) { - this.skip(); - } - - const user = userEvent.setup(); - const handleClick = spy(); - const { getByRole } = render(<Chip onClick={handleClick} />); - const chip = getByRole('button'); - act(() => { - chip.focus(); - }); - - await user.keyboard('{ >}'); // press space without releasing - - expect(handleClick.callCount).to.equal(0); - - await user.keyboard('{/ }'); // release space - - expect(handleClick.callCount).to.equal(1); - }); - - it('should call onClick when `enter` is pressed ', async () => { - const handleClick = spy(); - const { getByRole } = render(<Chip onClick={handleClick} />); - const chip = getByRole('button'); - act(() => { - chip.focus(); - }); - - await userEvent.keyboard('{enter}'); - - expect(handleClick.callCount).to.equal(1); - }); - - describe('prop: onDelete', () => { - ['Backspace', 'Delete'].forEach((key) => { - it(`should call onDelete '${key}' is released`, () => { - const handleDelete = spy(); - const handleKeyDown = spy(); - const { getAllByRole } = render( - <Chip onClick={() => {}} onKeyDown={handleKeyDown} onDelete={handleDelete} />, - ); - const chip = getAllByRole('button')[0]; - act(() => { - chip.focus(); - }); - - fireEvent.keyDown(chip, { key }); - - // defaultPrevented? - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - expect(handleDelete.callCount).to.equal(0); - - fireEvent.keyUp(chip, { key }); - - expect(handleDelete.callCount).to.equal(1); - }); - }); - - it('should not prevent default on input', () => { - const handleKeyDown = spy(); - const { getByTestId } = render( - <Chip label={<input data-testid="input" />} onKeyDown={handleKeyDown} />, - ); - const input = getByTestId('input'); - - act(() => { - input.focus(); - }); - fireEvent.keyDown(input, { key: 'Backspace' }); - - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', false); - }); - }); - - describe('with children that generate events', () => { - ['Backspace', 'Delete'].forEach((key) => { - it(`should not call onDelete for child keyup event when '${key}' is released`, () => { - const handleDelete = spy(); - const handleKeyUp = spy(); - const { getByTestId } = render( - <Chip - onDelete={handleDelete} - label={ - <input - data-testid="input" - autoFocus - className="child-input" - onKeyUp={handleKeyUp} - /> - } - />, - ); - - fireEvent.keyUp(getByTestId('input'), { key }); - - expect(handleKeyUp.callCount).to.equal(1); - expect(handleDelete.callCount).to.equal(0); - }); - }); - - it(`should not call onClick for child keyup event when 'Space' is released`, () => { - const handleClick = spy(); - const handleKeyUp = spy(); - const { getByTestId } = render( - <Chip - onClick={handleClick} - label={ - <input data-testid="input" autoFocus className="child-input" onKeyUp={handleKeyUp} /> - } - />, - ); - - fireEvent.keyUp(getByTestId('input'), { key: ' ' }); - expect(handleKeyUp.callCount).to.equal(1); - expect(handleClick.callCount).to.equal(0); - }); - - it(`should not call onClick for child keydown event when 'Enter' is pressed`, () => { - const handleClick = spy(); - const handleKeyDown = spy(); - const { getByTestId } = render( - <Chip - onClick={handleClick} - label={ - <input - data-testid="input" - autoFocus - className="child-input" - onKeyDown={handleKeyDown} - /> - } - />, - ); - - fireEvent.keyDown(getByTestId('input'), { key: 'Enter' }); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleClick.callCount).to.equal(0); - }); - - it('should not call onClick for child event when `space` is released', () => { - const handleClick = spy(); - const handleKeyUp = spy(); - const { getByTestId } = render( - <Chip - onClick={handleClick} - label={ - <input data-testid="input" autoFocus className="child-input" onKeyUp={handleKeyUp} /> - } - />, - ); - - fireEvent.keyUp(getByTestId('input'), { key: ' ' }); - - expect(handleClick.callCount).to.equal(0); - expect(handleKeyUp.callCount).to.equal(1); - }); - - it('should not call onClick for child event when `enter` is pressed', () => { - const handleClick = spy(); - const handleKeyDown = spy(); - const { getByTestId } = render( - <Chip - onClick={handleClick} - label={ - <input - data-testid="input" - autoFocus - className="child-input" - onKeyDown={handleKeyDown} - /> - } - />, - ); - - fireEvent.keyDown(getByTestId('input'), { key: 'Enter' }); - - expect(handleClick.callCount).to.equal(0); - expect(handleKeyDown.callCount).to.equal(1); - }); - }); - }); - - describe('prop: icon', () => { - it('should render the icon', () => { - const { getByTestId } = render(<Chip icon={<span data-testid="test-icon" />} />); - - expect(getByTestId('test-icon')).to.have.class(classes.icon); - }); - - it("should not override the icon's custom color", () => { - const { getByTestId } = render( - <React.Fragment> - <Chip icon={<CheckBox data-testid="test-icon" color="success" />} />, - <Chip icon={<CheckBox data-testid="test-icon2" color="success" />} color="error" />, - </React.Fragment>, - ); - - expect(getByTestId('test-icon')).toHaveComputedStyle({ - color: hexToRgb(MaterialV5DefaultTheme.palette.success.main), - }); - expect(getByTestId('test-icon2')).toHaveComputedStyle({ - color: hexToRgb(MaterialV5DefaultTheme.palette.success.main), - }); - }); - }); - - describe('prop: size', () => { - it('should render with the sizeSmall class', () => { - const { container } = render(<Chip size="small" />); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.sizeSmall); - }); - }); - - describe('event: focus', () => { - it('has a focus-visible polyfill', () => { - const { getByTestId } = render( - <Chip data-testid="root" label="Test Chip" onClick={() => {}} />, - ); - const chip = getByTestId('root'); - simulatePointerDevice(); - - expect(chip).not.to.have.class(classes.focusVisible); - - act(() => { - chip.focus(); - }); - - if (programmaticFocusTriggersFocusVisible()) { - expect(chip).to.have.class(classes.focusVisible); - } else { - expect(chip).not.to.have.class(classes.focusVisible); - } - - focusVisible(chip); - - expect(chip).to.have.class(classes.focusVisible); - }); - - it('should reset the focused state', () => { - const { getByTestId, setProps } = render( - <Chip data-testid="root" label="Test Chip" onClick={() => {}} />, - ); - const chip = getByTestId('root'); - - simulatePointerDevice(); - focusVisible(chip); - - expect(chip).to.have.class(classes.focusVisible); - - setProps({ disabled: true }); - - expect(chip).not.to.have.class(classes.focusVisible); - }); - }); - - describe('CSS vars', () => { - it('should not throw when there is theme value is CSS variable', () => { - const theme = extendTheme(); - theme.palette = theme.colorSchemes.light.palette; - theme.palette.text = { - ...theme.palette.text, - primary: 'var(--mui-palette-grey-900)', - }; - expect(() => - render( - <CssVarsProvider theme={theme}> - <Chip label="Test Chip" /> - </CssVarsProvider>, - ), - ).not.to.throw(); - }); - }); -}); diff --git a/packages/mui-material-next/src/Chip/Chip.tsx b/packages/mui-material-next/src/Chip/Chip.tsx deleted file mode 100644 index 13d4e20900a53f..00000000000000 --- a/packages/mui-material-next/src/Chip/Chip.tsx +++ /dev/null @@ -1,560 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { EventHandlers, unstable_composeClasses as composeClasses, useSlotProps } from '@mui/base'; -import { - unstable_capitalize as capitalize, - unstable_useForkRef as useForkRef, - unstable_unsupportedProp as unsupportedProp, -} from '@mui/utils'; -import { OverridableComponent } from '@mui/types'; -import ClearIcon from '../internal/svg-icons/Clear'; -import { useThemeProps, styled, MD3ColorSchemeTokens } from '../styles'; -import ButtonBase from '../ButtonBase'; -import chipClasses, { getChipUtilityClass } from './chipClasses'; -import { ChipOwnerState, ChipProps, ChipTypeMap } from './Chip.types'; - -const useUtilityClasses = (ownerState: ChipOwnerState) => { - const { classes, disabled, size, color, hasDeleteIcon, clickable, variant } = ownerState; - - const slots = { - root: [ - 'root', - variant, - disabled && 'disabled', - `size${capitalize(size)}`, - color && `color${capitalize(color)}`, - clickable && 'clickable', - hasDeleteIcon && 'deletable', - ], - label: ['label'], - avatar: ['avatar'], - icon: ['icon'], - deleteIcon: ['deleteIcon'], - }; - - const composedClasses = composeClasses(slots, getChipUtilityClass, classes); - - return { - ...classes, // forward the focused, disabled, etc. classes to the ButtonBase - ...composedClasses, - }; -}; - -const ChipRoot = styled('div', { - name: 'MuiChip', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - const { color, clickable, hasDeleteIcon, size, variant } = ownerState; - - return [ - { [`& .${chipClasses.avatar}`]: styles.avatar }, - { [`& .${chipClasses.icon}`]: styles.icon }, - { [`& .${chipClasses.deleteIcon}`]: styles.deleteIcon }, - styles.root, - styles[`size${capitalize(size)}`], - color && styles[`color${capitalize(color)}`], - clickable && styles.clickable, - hasDeleteIcon && styles.deletable, - styles[variant], - ]; - }, -})<{ ownerState: ChipOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - const containerColor = { - filled: - tokens.sys.color[`${ownerState.color ?? 'secondary'}Container` as keyof MD3ColorSchemeTokens], - outlined: 'transparent', - elevated: - tokens.sys.color[ - (ownerState.color - ? `${ownerState.color}Container` - : 'surfaceContainerLow') as keyof MD3ColorSchemeTokens - ], - }; - - const labelTextColor = { - filled: - tokens.sys.color[ - `on${capitalize(ownerState.color ?? 'secondary')}Container` as keyof MD3ColorSchemeTokens - ], - outlined: tokens.sys.color.onSurface, - elevated: - tokens.sys.color[ - (ownerState.color - ? `on${capitalize(ownerState.color)}Container` - : 'onSurface') as keyof MD3ColorSchemeTokens - ], - }; - - const containerElevation = { - filled: tokens.sys.elevation[0], - outlined: tokens.sys.elevation[0], - elevated: tokens.sys.elevation[1], - }; - - const disabledContainerColor = { - filled: `rgba(${tokens.sys.color.onSurfaceChannel} / 0.12)`, - outlined: 'transparent', - elevated: `rgba(${tokens.sys.color.onSurfaceChannel} / 0.12)`, - }; - - const disabledContainerBorder = { - filled: null, - outlined: `1px solid rgba(${tokens.sys.color.onSurfaceChannel} / 0.12)`, - elevated: null, - }; - - const disabledElevation = { - filled: tokens.sys.elevation[0], - outlined: tokens.sys.elevation[0], - elevated: tokens.sys.elevation[0], - }; - - const stateLayerBackgroundColor = { - filled: tokens.sys.color.onSecondaryContainer, - outlined: tokens.sys.color.onSurfaceVariant, - elevated: tokens.sys.color.onSurfaceVariant, - }; - - const hoveredContainerElevation = { - filled: tokens.sys.elevation[1], - outlined: tokens.sys.elevation[0], - elevated: tokens.sys.elevation[2], - }; - - const focusedContainerElevation = { - filled: tokens.sys.elevation[0], - outlined: tokens.sys.elevation[0], - elevated: tokens.sys.elevation[1], - }; - - const pressedContainerElevation = { - filled: tokens.sys.elevation[1], - outlined: tokens.sys.elevation[0], - elevated: tokens.sys.elevation[1], - }; - - const letterSpacing = `${ - theme.sys.typescale.label.large.tracking / theme.sys.typescale.label.large.size - }rem`; - - return { - '--md-comp-chip-container-color': containerColor[ownerState.variant], - '--md-comp-chip-label-padding-y': '16px', - '--md-comp-chip-label-padding-left': 'var(--md-comp-chip-label-padding-y)', - '--md-comp-chip-label-padding-right': 'var(--md-comp-chip-label-padding-y)', - '--md-comp-chip-icon-size': '18px', - position: 'relative', - maxWidth: '100%', - fontFamily: tokens.sys.typescale.label.large.family, - fontSize: theme.typography.pxToRem(theme.sys.typescale.label.large.size), // the pxToRem should be moved to typescale in the future - fontWeight: tokens.sys.typescale.label.large.weight, - lineHeight: `calc(${tokens.sys.typescale.label.large.lineHeight} / ${theme.sys.typescale.label.large.size})`, - letterSpacing, - display: 'inline-flex', - alignItems: 'center', - justifyContent: 'center', - height: 32, - color: labelTextColor[ownerState.variant], - backgroundColor: 'var(--md-comp-chip-container-color)', - borderRadius: tokens.sys.shape.corner.small, - whiteSpace: 'nowrap', - overflow: 'hidden', - transition: theme.transitions.create(['background-color', 'box-shadow']), - boxSizing: 'border-box', - boxShadow: containerElevation[ownerState.variant], - ...(ownerState.size === 'small' && { - '--md-comp-chip-label-padding-y': '10px', - '--md-comp-chip-icon-size': '16px', - height: 24, - }), - ...(ownerState.variant === 'outlined' && { - border: `1px solid ${tokens.sys.color[ownerState.color ?? 'outline']}`, - '--md-comp-chip-label-padding-left': 'calc(var(--md-comp-chip-label-padding-y) - 1px)', - '--md-comp-chip-label-padding-right': 'calc(var(--md-comp-chip-label-padding-y) - 1px)', - }), - ...((ownerState.hasIcon || ownerState.hasAvatar) && { - '--md-comp-chip-label-padding-left': ownerState.size === 'small' ? '6px' : '8px', - }), - ...(ownerState.hasDeleteIcon && { - '--md-comp-chip-label-padding-right': '5px', - cursor: 'auto', - }), - [`&.${chipClasses.disabled}`]: { - backgroundColor: disabledContainerColor[ownerState.variant], - border: disabledContainerBorder[ownerState.variant], - color: `rgba(${tokens.sys.color.onSurfaceChannel} / 0.38)`, - boxShadow: disabledElevation[ownerState.variant], - pointerEvents: 'none', - }, - ...((ownerState.clickable || ownerState.hasDeleteIcon) && { - [`&.${chipClasses.focusVisible}`]: { - boxShadow: focusedContainerElevation[ownerState.variant], - '--md-comp-chip-container-color': `color-mix(in srgb, ${ - stateLayerBackgroundColor[ownerState.variant] - } calc(${tokens.sys.state.focus.stateLayerOpacity} * 100%), ${ - containerColor[ownerState.variant] - })`, - }, - }), - ...(ownerState.clickable && { - WebkitTapHighlightColor: 'transparent', - cursor: 'pointer', - '&:hover': { - boxShadow: hoveredContainerElevation[ownerState.variant], - backgroundColor: `color-mix(in srgb, ${ - stateLayerBackgroundColor[ownerState.variant] - } calc(${ - tokens.sys.state.hover.stateLayerOpacity - } * 100%), var(--md-comp-chip-container-color))`, - }, - '&:active': { - boxShadow: pressedContainerElevation[ownerState.variant], - }, - }), - [`& .${chipClasses.avatar}`]: { - width: 24, - height: 24, - marginLeft: 4, - color: - tokens.sys.color[ - `on${capitalize(ownerState.color ?? 'secondary')}` as keyof MD3ColorSchemeTokens - ], - backgroundColor: tokens.sys.color[ownerState.color ?? 'secondary'], - fontSize: theme.typography.pxToRem(theme.sys.typescale.label.large.size), // the pxToRem should be moved to typescale in the future - ...(ownerState.size === 'small' && { - height: 18, - width: 18, - fontSize: theme.typography.pxToRem(theme.sys.typescale.label.small.size), // the pxToRem should be moved to typescale in the future - }), - }, - [`& .${chipClasses.icon}`]: { - height: 'var(--md-comp-chip-icon-size)', - width: 'var(--md-comp-chip-icon-size)', - marginLeft: 8, - }, - [`& .${chipClasses.deleteIcon}`]: { - boxSizing: 'content-box', - zIndex: 1, - height: 'var(--md-comp-chip-icon-size)', - width: 'var(--md-comp-chip-icon-size)', - padding: 3, - marginRight: 5, - ...(ownerState.size === 'small' && { - padding: 1, - }), - WebkitTapHighlightColor: 'transparent', - cursor: 'pointer', - borderRadius: tokens.sys.shape.corner.full, - '&:hover': { - backgroundColor: `color-mix(in srgb, ${ - stateLayerBackgroundColor[ownerState.variant] - } calc(${ - tokens.sys.state.hover.stateLayerOpacity - } * 100%), var(--md-comp-chip-container-color))`, - }, - }, - }; -}); - -const ChipLabel = styled('span', { - name: 'MuiChip', - slot: 'Label', -})<{ ownerState: ChipOwnerState }>({ - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - paddingLeft: 'var(--md-comp-chip-label-padding-left)', - paddingRight: 'var(--md-comp-chip-label-padding-right)', -}); - -function isDeleteKeyboardEvent(keyboardEvent: React.KeyboardEvent<HTMLDivElement>) { - return keyboardEvent.key === 'Backspace' || keyboardEvent.key === 'Delete'; -} - -/** - * Chips represent complex entities in small blocks, such as a contact. - * - * Demos: - * - * - [Chip](https://mui.com/material-ui/react-chip/) - * - * API: - * - * - [Chip API](https://mui.com/material-ui/api/chip/) - */ -const Chip = React.forwardRef(function Chip< - BaseComponentType extends React.ElementType = ChipTypeMap['defaultComponent'], ->(inProps: ChipProps<BaseComponentType>, ref: React.ForwardedRef<Element>) { - const props = useThemeProps<typeof inProps & ChipProps>({ props: inProps, name: 'MuiChip' }); - const { - avatar: avatarProp, - className, - clickable: clickableProp, - color, - component: ComponentProp, - deleteIcon: deleteIconProp, - disabled = false, - icon: iconProp, - label, - onClick, - onDelete, - onKeyDown, - onKeyUp, - size = 'medium', - variant = 'filled', - ...other - } = props; - - const chipRef = React.useRef<HTMLDivElement | null>(null); - const handleRef = useForkRef(chipRef, ref); - - const handleDeleteIconClick = (event: React.MouseEvent<SVGSVGElement>) => { - // Stop the event from bubbling up to the `Chip` - event.stopPropagation(); - if (onDelete) { - onDelete(event); - } - }; - - const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => { - // Ignore events from children of `Chip`. - if (event.currentTarget === event.target && isDeleteKeyboardEvent(event)) { - // Will be handled in keyUp, otherwise some browsers - // might init navigation - event.preventDefault(); - } - - if (onKeyDown) { - onKeyDown(event); - } - }; - - const handleKeyUp = (event: React.KeyboardEvent<HTMLDivElement>) => { - // Ignore events from children of `Chip`. - if (event.currentTarget === event.target) { - if (onDelete && isDeleteKeyboardEvent(event)) { - onDelete(event); - } else if (event.key === 'Escape' && chipRef.current) { - chipRef.current.blur(); - } - } - - if (onKeyUp) { - onKeyUp(event); - } - }; - - const clickable = clickableProp !== false && onClick ? true : clickableProp; - const isButton = clickable || !!onDelete; - - const component = isButton ? ButtonBase : ComponentProp || 'div'; - - const hasIcon = !!iconProp && React.isValidElement(iconProp); - const hasDeleteIcon = !!onDelete; - const hasAvatar = !!avatarProp && React.isValidElement(avatarProp); - - const ownerState: ChipOwnerState = { - ...props, - component, - disabled, - size, - color, - clickable, - variant, - hasIcon, - hasDeleteIcon, - hasAvatar, - }; - - const classes = useUtilityClasses(ownerState); - - const buttonProps = { - disabled: clickable && disabled, - component: ComponentProp, - focusVisibleClassName: chipClasses.focusVisible, - ...(onDelete && { disableRipple: true }), - }; - - let deleteIcon = null; - if (onDelete) { - deleteIcon = - deleteIconProp && React.isValidElement(deleteIconProp) ? ( - React.cloneElement(deleteIconProp as React.ReactElement, { - className: clsx((deleteIconProp.props as any)?.className, classes.deleteIcon), - onClick: handleDeleteIconClick, - }) - ) : ( - <ClearIcon className={clsx(classes.deleteIcon)} onClick={handleDeleteIconClick} /> - ); - } - - let avatar = null; - if (hasAvatar) { - avatar = React.cloneElement(avatarProp as React.ReactElement, { - className: clsx(classes.avatar, (avatarProp.props as any)?.className), - }); - } - - let icon = null; - if (hasIcon) { - icon = React.cloneElement(iconProp as React.ReactElement, { - className: clsx(classes.icon, (iconProp.props as any)?.className), - }); - } - - if (process.env.NODE_ENV !== 'production') { - if (avatar && icon) { - console.error( - 'MUI: The Chip component can not handle the avatar ' + - 'and the icon prop at the same time. Pick one.', - ); - } - } - - const rootProps = useSlotProps({ - elementType: ChipRoot, - getSlotProps: (otherHandlers: EventHandlers) => ({ - ...otherHandlers, - onKeyDown: handleKeyDown, - onKeyUp: handleKeyUp, - }), - externalForwardedProps: { - onClick, - ...other, - }, - externalSlotProps: {}, - additionalProps: { - as: component, - ref: handleRef, - ...(isButton && buttonProps), - }, - ownerState, - className: [classes.root, className], - }); - - return ( - <ChipRoot {...rootProps}> - {avatar || icon} - <ChipLabel className={clsx(classes.label)} ownerState={ownerState}> - {label} - </ChipLabel> - {deleteIcon} - </ChipRoot> - ); -}) as OverridableComponent<ChipTypeMap>; - -Chip.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 Avatar element to display. - */ - avatar: PropTypes.element, - /** - * This prop isn't supported. - * Use the `component` prop if you need to change the children structure. - */ - children: unsupportedProp, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * If `true`, the chip will appear clickable, and will raise when pressed, - * even if the onClick prop is not defined. - * If `false`, the chip will not appear clickable, even if onClick prop is defined. - * This can be used, for example, - * along with the component prop to indicate an anchor Chip is clickable. - * Note: this controls the UI and does not affect the onClick event. - */ - clickable: PropTypes.bool, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['primary', 'secondary', 'tertiary', 'error', 'info', 'success', 'warning']), - PropTypes.string, - ]), - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * Override the default delete icon element. Shown only if `onDelete` is set. - */ - deleteIcon: PropTypes.element, - /** - * If `true`, the component is disabled. - * @default false - */ - disabled: PropTypes.bool, - /** - * Icon element. - */ - icon: PropTypes.element, - /** - * The content of the component. - */ - label: PropTypes.node, - /** - * @ignore - */ - onClick: PropTypes.func, - /** - * Callback fired when the delete icon is clicked. - * If set, the delete icon will be shown. - */ - onDelete: PropTypes.func, - /** - * @ignore - */ - onKeyDown: PropTypes.func, - /** - * @ignore - */ - onKeyUp: PropTypes.func, - /** - * The size of the component. - * @default 'medium' - */ - size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['medium', 'small']), - PropTypes.string, - ]), - /** - * 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, - ]), - /** - * @ignore - */ - tabIndex: PropTypes.number, - /** - * The variant to use. - * @default 'filled' - */ - variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['filled', 'outlined', 'elevated']), - PropTypes.string, - ]), -} as any; - -export default Chip; diff --git a/packages/mui-material-next/src/Chip/Chip.types.ts b/packages/mui-material-next/src/Chip/Chip.types.ts deleted file mode 100644 index 5595d7abdb8a7f..00000000000000 --- a/packages/mui-material-next/src/Chip/Chip.types.ts +++ /dev/null @@ -1,108 +0,0 @@ -import * as React from 'react'; -import { OverridableStringUnion, OverrideProps } from '@mui/types'; -import { SxProps } from '../styles/Theme.types'; -import { ChipClasses } from './chipClasses'; - -export interface ChipPropsVariantOverrides {} - -export interface ChipPropsSizeOverrides {} - -export interface ChipPropsColorOverrides {} - -export interface ChipTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'div', -> { - props: AdditionalProps & { - /** - * The Avatar element to display. - */ - avatar?: React.ReactElement; - /** - * This prop isn't supported. - * Use the `component` prop if you need to change the children structure. - */ - children?: null; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<ChipClasses>; - /** - * If `true`, the chip will appear clickable, and will raise when pressed, - * even if the onClick prop is not defined. - * If `false`, the chip will not appear clickable, even if onClick prop is defined. - * This can be used, for example, - * along with the component prop to indicate an anchor Chip is clickable. - * Note: this controls the UI and does not affect the onClick event. - */ - clickable?: boolean; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - */ - color?: OverridableStringUnion< - 'primary' | 'secondary' | 'tertiary' | 'error' | 'info' | 'success' | 'warning', - ChipPropsColorOverrides - >; - /** - * Override the default delete icon element. Shown only if `onDelete` is set. - */ - deleteIcon?: React.ReactElement; - /** - * If `true`, the component is disabled. - * @default false - */ - disabled?: boolean; - /** - * Icon element. - */ - icon?: React.ReactElement; - /** - * The content of the component. - */ - label?: React.ReactNode; - /** - * Callback fired when the delete icon is clicked. - * If set, the delete icon will be shown. - */ - onDelete?: React.EventHandler<any>; - /** - * The size of the component. - * @default 'medium' - */ - size?: OverridableStringUnion<'small' | 'medium', ChipPropsSizeOverrides>; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; - /** - * @ignore - */ - tabIndex?: number; - /** - * The variant to use. - * @default 'filled' - */ - variant?: OverridableStringUnion<'filled' | 'outlined' | 'elevated', ChipPropsVariantOverrides>; - }; - defaultComponent: RootComponent; -} - -export type ChipProps< - RootComponent extends React.ElementType = ChipTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<ChipTypeMap<AdditionalProps, RootComponent>, RootComponent> & { - component?: React.ElementType; -}; - -export interface ChipOwnerState extends ChipProps { - variant: NonNullable<ChipProps['variant']>; - size: NonNullable<ChipProps['size']>; - /** - * color for the icon component - */ - hasIcon: boolean; - hasDeleteIcon: boolean; - hasAvatar: boolean; -} diff --git a/packages/mui-material-next/src/Chip/chipClasses.ts b/packages/mui-material-next/src/Chip/chipClasses.ts deleted file mode 100644 index 7c4f1fc48e5bd9..00000000000000 --- a/packages/mui-material-next/src/Chip/chipClasses.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface ChipClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `size="small"`. */ - sizeSmall: string; - /** Styles applied to the root element if `size="medium"`. */ - sizeMedium: string; - /** Styles applied to the root element if `color="error"`. */ - colorError: string; - /** Styles applied to the root element if `color="info"`. */ - colorInfo: string; - /** Styles applied to the root element if `color="primary"`. */ - colorPrimary: string; - /** Styles applied to the root element if `color="secondary"`. */ - colorSecondary: string; - /** Styles applied to the root element if `color="tertiary"`. */ - colorTertiary: string; - /** Styles applied to the root element if `color="success"`. */ - colorSuccess: string; - /** Styles applied to the root element if `color="warning"`. */ - colorWarning: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; - /** Styles applied to the root element if `onClick` is defined or `clickable={true}`. */ - clickable: string; - /** Styles applied to the root element if `onDelete` is defined. */ - deletable: string; - /** Styles applied to the root element if `variant="outlined"`. */ - outlined: string; - /** Styles applied to the root element if `variant="filled"`. */ - filled: string; - /** Styles applied to the avatar element. */ - avatar: string; - /** Styles applied to the icon element. */ - icon: string; - /** Styles applied to the label `span` element. */ - label: string; - /** Styles applied to the deleteIcon element. */ - deleteIcon: string; - /** State class applied to the root element if keyboard focused. */ - focusVisible: string; -} - -export type ChipClassKey = keyof ChipClasses; - -export function getChipUtilityClass(slot: string): string { - return generateUtilityClass('MuiChip', slot); -} - -const chipClasses: ChipClasses = generateUtilityClasses('MuiChip', [ - 'root', - 'sizeSmall', - 'sizeMedium', - 'colorError', - 'colorInfo', - 'colorPrimary', - 'colorSecondary', - 'colorTertiary', - 'colorSuccess', - 'colorWarning', - 'disabled', - 'clickable', - 'deletable', - 'outlined', - 'filled', - 'avatar', - 'icon', - 'label', - 'deleteIcon', - 'focusVisible', -]); - -export default chipClasses; diff --git a/packages/mui-material-next/src/Chip/index.ts b/packages/mui-material-next/src/Chip/index.ts deleted file mode 100644 index 81875ce0035fcc..00000000000000 --- a/packages/mui-material-next/src/Chip/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './Chip'; - -export { default as chipClasses } from './chipClasses'; -export * from './chipClasses'; diff --git a/packages/mui-material-next/src/CircularProgress/CircularProgress.test.tsx b/packages/mui-material-next/src/CircularProgress/CircularProgress.test.tsx deleted file mode 100644 index 80d77d35675a24..00000000000000 --- a/packages/mui-material-next/src/CircularProgress/CircularProgress.test.tsx +++ /dev/null @@ -1,191 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -import CircularProgress, { - circularProgressClasses as classes, -} from '@mui/material-next/CircularProgress'; -import { CssVarsProvider, extendTheme } from '../styles'; -import describeConformance from '../../test/describeConformance'; - -describe('<CircularProgress />', () => { - const { render } = createRenderer(); - - describeConformance(<CircularProgress />, () => ({ - classes, - inheritComponent: 'span', - render, - muiName: 'MuiCircularProgress', - testDeepOverrides: { slotName: 'circle', slotClassName: classes.circle }, - testVariantProps: { variant: 'determinate' }, - refInstanceof: window.HTMLSpanElement, - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - skip: ['componentProp', 'componentsProp'], - })); - - it('should render with the primary color by default', () => { - const { getByRole } = render(<CircularProgress />); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.colorPrimary); - }); - - it('should render with the primary color', () => { - const { getByRole } = render(<CircularProgress color="primary" />); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.colorPrimary); - }); - - it('should render with the secondary color', () => { - const { getByRole } = render(<CircularProgress color="secondary" />); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.colorSecondary); - }); - - it('should render with the tertiary color', () => { - const { getByRole } = render(<CircularProgress color="tertiary" />); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.colorTertiary); - }); - - it('should contain an SVG with the svg class, and a circle with the circle class', () => { - const { container, getByRole } = render(<CircularProgress />); - const circularProgress = getByRole('progressbar'); - const svg = container.querySelector<SVGElement>('svg'); - const circle = container.querySelector<SVGCircleElement>('circle'); - expect(svg).to.have.tagName('svg'); - expect(circularProgress).to.have.class(classes.indeterminate); - expect(circle).to.have.tagName('circle'); - expect(circle).to.have.class(classes.circle); - }); - - it('should render indeterminate variant by default', () => { - const { container, getByRole } = render(<CircularProgress />); - const circularProgress = getByRole('progressbar'); - const circle = container.querySelector<SVGCircleElement>('circle'); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgress).to.have.class(classes.indeterminate); - expect(circle).to.have.class(classes.circle); - }); - - it('should render with a different size', async function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - // only run on browser, because getComputedStyle only works in browser - this.skip(); - } - const { container, getByRole } = render(<CircularProgress size={60} />); - const circularProgress = getByRole('progressbar'); - const circularProgressStyle = window.getComputedStyle(circularProgress); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgressStyle.width).to.equal('60px', 'should have width correctly set'); - expect(circularProgressStyle.height).to.equal('60px', 'should have height correctly set'); - const svg = container.querySelector<SVGElement>('svg'); - expect(svg).to.have.tagName('svg'); - const circle = container.querySelector<SVGCircleElement>('circle'); - expect(circle).to.have.tagName('circle'); - expect(circle).to.have.attribute('cx', '48'); - expect(circle).to.have.attribute('cy', '48'); - }); - - describe('prop: variant="determinate"', () => { - it('should render with determinate classes', () => { - const { container, getByRole } = render(<CircularProgress variant="determinate" />); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgress).not.to.have.class(classes.indeterminate); - const svg = container.querySelector<SVGElement>('svg'); - expect(svg).to.have.tagName('svg'); - const circle = container.querySelector<SVGCircleElement>('circle'); - expect(circle).to.have.class(classes.circle); - }); - - it('should set strokeDasharray of circle', () => { - const { container, getByRole } = render( - <CircularProgress variant="determinate" value={70} />, - ); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.root); - const circle = container.querySelector<SVGCircleElement>('circle')!; - expect(circle.style.strokeDasharray).to.match( - /138\.230?(px)?/gm, - 'should have strokeDasharray set', - ); - expect(circle.style.strokeDashoffset).to.equal( - '41.469px', - 'should have strokeDashoffset set', - ); - expect(circularProgress).to.have.attribute('aria-valuenow', '70'); - }); - }); - - describe('prop: disableShrink ', () => { - it('should default to false', () => { - const { container, getByRole } = render(<CircularProgress variant="indeterminate" />); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgress).not.to.have.class(classes.disableShrink); - const circle = container.querySelector<SVGCircleElement>('circle'); - expect(circle).to.have.tagName('circle'); - expect(circle).to.have.class(classes.circle); - }); - - it('should render without disableShrink class when set to false', () => { - const { container, getByRole } = render( - <CircularProgress variant="indeterminate" disableShrink={false} />, - ); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgress).not.to.have.class(classes.disableShrink); - const circle = container.querySelector<SVGCircleElement>('circle'); - expect(circle).to.have.tagName('circle'); - expect(circle).to.have.class(classes.circle); - }); - - it('should render with disableShrink class when set to true', () => { - const { container, getByRole } = render( - <CircularProgress variant="indeterminate" disableShrink />, - ); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgress).to.have.class(classes.disableShrink); - const circle = container.querySelector<SVGCircleElement>('circle'); - expect(circle).to.have.tagName('circle'); - expect(circle).to.have.class(classes.circle); - }); - }); - - describe('prop: fourColor ', () => { - it('should default to false', () => { - const { container, getByRole } = render(<CircularProgress variant="indeterminate" />); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgress).not.to.have.class(classes.fourColor); - const circle = container.querySelector<SVGCircleElement>('circle'); - expect(circle).to.have.tagName('circle'); - expect(circle).to.have.class(classes.circle); - }); - - it('should render without fourColor class when set to false', () => { - const { container, getByRole } = render( - <CircularProgress variant="indeterminate" fourColor={false} />, - ); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgress).not.to.have.class(classes.fourColor); - const circle = container.querySelector<SVGCircleElement>('circle'); - expect(circle).to.have.tagName('circle'); - expect(circle).to.have.class(classes.circle); - }); - - it('should render with fourColor class when set to true', () => { - const { container, getByRole } = render( - <CircularProgress variant="indeterminate" fourColor />, - ); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgress).to.have.class(classes.fourColor); - const circle = container.querySelector<SVGCircleElement>('circle'); - expect(circle).to.have.tagName('circle'); - expect(circle).to.have.class(classes.circle); - }); - }); -}); diff --git a/packages/mui-material-next/src/CircularProgress/CircularProgress.tsx b/packages/mui-material-next/src/CircularProgress/CircularProgress.tsx deleted file mode 100644 index b8dbeeddebaf58..00000000000000 --- a/packages/mui-material-next/src/CircularProgress/CircularProgress.tsx +++ /dev/null @@ -1,359 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { chainPropTypes, unstable_capitalize as capitalize } from '@mui/utils'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { keyframes } from '@mui/system'; -import { OverridableComponent } from '@mui/types'; -import useThemeProps from '../styles/useThemeProps'; -import styled from '../styles/styled'; -import { getCircularProgressUtilityClass } from './circularProgressClasses'; -import { - CircularProgressOwnerState, - CircularProgressProps, - CircularProgressTypeMap, -} from './CircularProgress.types'; - -const SIZE = 48; -const ARC_DURATION = 1333; // milliseconds -const CYCLE_DURATION = 4 * ARC_DURATION; // milliseconds -const LINEAR_ROTATE_DURATION = (ARC_DURATION * 360) / 306; // milliseconds - -const circularRotateKeyframe = keyframes` - to { - transform: rotate(360deg); - } -`; - -const circularDashKeyframe = keyframes` - 0% { - stroke-dasharray: 10px, 200px; - stroke-dashoffset: 0px; - } - - 50% { - stroke-dasharray: 139px, 10px; - stroke-dashoffset: -10px; - } - - 100% { - stroke-dasharray: 139px, 129px; - stroke-dashoffset: -139px; - } -`; - -const fourColorKeyframe = keyframes` - 0% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-one-color); - } - 15% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-one-color); - } - 25% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-two-color); - } - 40% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-two-color); - } - 50% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-three-color); - } - 65% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-three-color); - } - 75% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-four-color); - } - 90% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-four-color); - } - 100% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-one-color); - } -`; - -const useUtilityClasses = (ownerState: CircularProgressOwnerState) => { - const { classes, variant, color, disableShrink, fourColor } = ownerState; - - const slots = { - root: [ - 'root', - variant, - `color${capitalize(color)}`, - fourColor && 'fourColor', - disableShrink && 'disableShrink', - ], - svg: ['svg'], - circle: ['circle'], - }; - - return composeClasses(slots, getCircularProgressUtilityClass, classes); -}; - -const CircularProgressRoot = styled('span', { - name: 'MuiCircularProgress', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - styles[ownerState.variant], - styles[`color${capitalize(ownerState.color)}`], - ownerState.fourColor && styles.fourColor, - ownerState.disableShrink && styles.disableShrink, - ]; - }, -})<{ ownerState: CircularProgressOwnerState }>(({ ownerState, theme: { vars: tokens } }) => ({ - '--md-comp-linear-progress-indicator-active-indicator-color': - ownerState.color !== 'inherit' ? tokens.sys.color[ownerState.color] : 'currentColor', - '--md-comp-linear-progress-indicator-active-indicator-width': ownerState.thickness, - '--md-comp-linear-progress-indicator-size': ownerState.size, - '--md-comp-linear-progress-indicator-four-color-active-indicator-one-color': - tokens.sys.color.primary, - '--md-comp-linear-progress-indicator-four-color-active-indicator-two-color': - tokens.sys.color.onPrimaryContainer, - '--md-comp-linear-progress-indicator-four-color-active-indicator-three-color': - tokens.sys.color.tertiary, - '--md-comp-linear-progress-indicator-four-color-active-indicator-four-color': - tokens.sys.color.onTertiaryContainer, - display: 'inline-block', - height: 'var(--md-comp-linear-progress-indicator-size)', - width: 'var(--md-comp-linear-progress-indicator-size)', - ...(ownerState.variant === 'determinate' && { - transition: `transform ${tokens.sys.motion.duration.medium2} ${tokens.sys.motion.easing.legacy}`, - }), - ...(ownerState.variant === 'indeterminate' && { - animation: `linear infinite ${circularRotateKeyframe}`, - animationDuration: `${LINEAR_ROTATE_DURATION}ms`, - }), -})); - -const CircularProgressSVG = styled('svg', { - name: 'MuiCircularProgress', - slot: 'Svg', - overridesResolver: (props, styles) => styles.svg, -})<{ ownerState: CircularProgressOwnerState }>({ - display: 'block', // Keeps the progress centered -}); - -const CircularProgressCircle = styled('circle', { - name: 'MuiCircularProgress', - slot: 'Circle', - overridesResolver: (props, styles) => styles.circle, -})<{ ownerState: CircularProgressOwnerState }>(({ ownerState, theme: { vars: tokens } }) => ({ - stroke: 'var(--md-comp-linear-progress-indicator-active-indicator-color)', - strokeWidth: 'var(--md-comp-linear-progress-indicator-active-indicator-width)', - // Use butt to follow the specification, by chance, it's already the default CSS value. - // strokeLinecap: 'butt', - ...(ownerState.variant === 'determinate' && { - transition: `stroke-dashoffset ${tokens.sys.motion.duration.medium2} ${tokens.sys.motion.easing.legacy}`, - }), - ...(ownerState.variant === 'indeterminate' && { - // Some default value that looks fine waiting for the animation to kicks in. - strokeDasharray: '10px, 200px', - strokeDashoffset: 0, // Add the unit to fix a Edge 16 and below bug. - }), - ...(ownerState.variant === 'indeterminate' && - !ownerState.disableShrink && { - animation: circularDashKeyframe, - animationDuration: `${ARC_DURATION}ms, ${CYCLE_DURATION}ms`, - animationIterationCount: 'infinite', - animationFillMode: 'both', - animationTimingFunction: tokens.sys.motion.easing.legacy, - ...(ownerState.fourColor && { - animationName: `${circularDashKeyframe}, ${fourColorKeyframe}`, - }), - }), -})); - -/** - * ## ARIA - * - * If the progress bar is describing the loading progress of a particular region of a page, - * you should use `aria-describedby` to point to the progress bar, and set the `aria-busy` - * attribute to `true` on that region until it has finished loading. - * - * Demos: - * - * - [Progress](https://mui.com/material-ui/react-progress/) - * - * API: - * - * - [CircularProgress API](https://mui.com/material-ui/api/circular-progress/) - */ -const CircularProgress = React.forwardRef(function CircularProgress< - BaseComponentType extends React.ElementType = CircularProgressTypeMap['defaultComponent'], ->(inProps: CircularProgressProps<BaseComponentType>, ref: React.ForwardedRef<HTMLSpanElement>) { - const props = useThemeProps({ props: inProps, name: 'MuiCircularProgress' }); - const { - className, - color = 'primary', - disableShrink = false, - fourColor = false, - style, - size = 48, - thickness = 4, - value = 0, - variant = 'indeterminate', - ...other - } = props; - - const ownerState = { - ...props, - color, - disableShrink, - fourColor, - size: typeof size === 'number' ? `${size}px` : size, - thickness, - value, - variant, - }; - - const classes = useUtilityClasses(ownerState); - - const circleStyle: React.CSSProperties = {}; - const rootStyle: React.CSSProperties = {}; - const rootProps: React.DetailedHTMLProps< - React.HTMLAttributes<HTMLSpanElement>, - HTMLSpanElement - > = {}; - - if (variant === 'determinate') { - const circumference = 2 * Math.PI * ((SIZE - thickness) / 2); - circleStyle.strokeDasharray = circumference.toFixed(3); - rootProps['aria-valuenow'] = Math.round(value); - circleStyle.strokeDashoffset = `${(((100 - value) / 100) * circumference).toFixed(3)}px`; - rootStyle.transform = 'rotate(-90deg)'; - } - - return ( - <CircularProgressRoot - className={clsx(classes.root, className)} - style={{ ...rootStyle, ...style }} - ownerState={ownerState} - ref={ref} - role="progressbar" - {...rootProps} - {...other} - > - <CircularProgressSVG - className={classes.svg} - ownerState={ownerState} - viewBox={`${SIZE / 2} ${SIZE / 2} ${SIZE} ${SIZE}`} - > - <CircularProgressCircle - className={classes.circle} - style={circleStyle} - ownerState={ownerState} - cx={SIZE} - cy={SIZE} - r={(SIZE - thickness) / 2} - fill="none" - /> - </CircularProgressSVG> - </CircularProgressRoot> - ); -}) as OverridableComponent<CircularProgressTypeMap>; - -CircularProgress.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, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color: PropTypes.oneOf([ - 'error', - 'info', - 'inherit', - 'primary', - 'secondary', - 'success', - 'tertiary', - 'warning', - ]), - /** - * If `true`, the shrink animation is disabled. - * This only works if variant is `indeterminate`. - * @default false - */ - disableShrink: chainPropTypes(PropTypes.bool, (props) => { - if (props.disableShrink && props.variant && props.variant !== 'indeterminate') { - return new Error( - 'MUI: You have provided the `disableShrink` prop ' + - 'with a variant other than `indeterminate`. This will have no effect.', - ); - } - return null; - }), - /** - * If `true`, the component render indeterminate mode using four colors instead of one. - * This only works if variant is `indeterminate`. - * @default false - */ - fourColor: chainPropTypes(PropTypes.bool, (props) => { - if (props.fourColor && props.variant && props.variant !== 'indeterminate') { - return new Error( - 'MUI: You have provided the `fourColor` prop ' + - 'with a variant other than `indeterminate`. This will have no effect.', - ); - } - return null; - }), - /** - * The size of the component. - * If using a number, the pixel unit is assumed. - * If using a string, you need to provide the CSS unit, e.g. '3rem'. - * @default 48 - */ - size: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - /** - * @ignore - */ - 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 thickness of the circle. - * @default 4 - */ - thickness: PropTypes.number, - /** - * The value of the progress indicator for the determinate variant. - * Value between 0 and 100. - * @default 0 - */ - value: PropTypes.number, - /** - * The variant to use. - * Use indeterminate when there is no progress value. - * @default 'indeterminate' - */ - variant: PropTypes.oneOf(['determinate', 'indeterminate']), -} as any; - -export default CircularProgress; diff --git a/packages/mui-material-next/src/CircularProgress/CircularProgress.types.ts b/packages/mui-material-next/src/CircularProgress/CircularProgress.types.ts deleted file mode 100644 index 621245c1e28d32..00000000000000 --- a/packages/mui-material-next/src/CircularProgress/CircularProgress.types.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverridableStringUnion, OverrideProps, PartiallyRequired } from '@mui/types'; -import { Theme } from '../styles'; -import { CircularProgressClasses } from './circularProgressClasses'; - -export interface CircularProgressPropsColorOverrides {} - -export interface CircularProgressOwnProps { - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<CircularProgressClasses>; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color?: OverridableStringUnion< - 'primary' | 'secondary' | 'tertiary' | 'error' | 'info' | 'success' | 'warning' | 'inherit', - CircularProgressPropsColorOverrides - >; - /** - * If `true`, the shrink animation is disabled. - * This only works if variant is `indeterminate`. - * @default false - */ - disableShrink?: boolean; - /** - * If `true`, the component render indeterminate mode using four colors instead of one. - * This only works if variant is `indeterminate`. - * @default false - */ - fourColor?: boolean; - /** - * The size of the component. - * If using a number, the pixel unit is assumed. - * If using a string, you need to provide the CSS unit, e.g. '3rem'. - * @default 48 - */ - size?: number | string; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; - /** - * The thickness of the circle. - * @default 4 - */ - thickness?: number; - /** - * The value of the progress indicator for the determinate variant. - * Value between 0 and 100. - * @default 0 - */ - value?: number; - /** - * The variant to use. - * Use indeterminate when there is no progress value. - * @default 'indeterminate' - */ - variant?: 'determinate' | 'indeterminate'; -} - -export interface CircularProgressTypeMap< - AdditionalProps = {}, - RootComponentType extends React.ElementType = 'span', -> { - props: CircularProgressOwnProps & AdditionalProps; - defaultComponent: RootComponentType; -} - -export type CircularProgressProps< - RootComponentType extends React.ElementType = CircularProgressTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<CircularProgressTypeMap<AdditionalProps, RootComponentType>, RootComponentType>; - -export interface CircularProgressOwnerState - extends PartiallyRequired< - CircularProgressProps, - 'color' | 'disableShrink' | 'fourColor' | 'size' | 'thickness' | 'value' | 'variant' - > {} diff --git a/packages/mui-material-next/src/CircularProgress/circularProgressClasses.ts b/packages/mui-material-next/src/CircularProgress/circularProgressClasses.ts deleted file mode 100644 index 62a9b71c57be87..00000000000000 --- a/packages/mui-material-next/src/CircularProgress/circularProgressClasses.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface CircularProgressClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `variant="determinate"`. */ - determinate: string; - /** Styles applied to the root element if `variant="indeterminate"`. */ - indeterminate: string; - /** Styles applied to the root element if `color="primary"`. */ - colorPrimary: string; - /** Styles applied to the root element if `color="secondary"`. */ - colorSecondary: string; - /** Styles applied to the root element if `color="tertiary"`. */ - colorTertiary: string; - /** Styles applied to the root element if `fourColor={true}`. */ - fourColor: string; - /** Styles applied to the root element if `disableShrink={true}`. */ - disableShrink: string; - /** Styles applied to the svg element. */ - svg: string; - /** Styles applied to the `circle` svg path. */ - circle: string; -} - -export type CircularProgressClassKey = keyof CircularProgressClasses; - -export function getCircularProgressUtilityClass(slot: string): string { - return generateUtilityClass('MuiCircularProgress', slot); -} - -const circularProgressClasses: CircularProgressClasses = generateUtilityClasses( - 'MuiCircularProgress', - [ - 'root', - 'determinate', - 'indeterminate', - 'colorPrimary', - 'colorSecondary', - 'colorTertiary', - 'fourColor', - 'disableShrink', - 'svg', - 'circle', - ], -); - -export default circularProgressClasses; diff --git a/packages/mui-material-next/src/CircularProgress/index.ts b/packages/mui-material-next/src/CircularProgress/index.ts deleted file mode 100644 index 0541327cc71d2d..00000000000000 --- a/packages/mui-material-next/src/CircularProgress/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './CircularProgress'; - -export { default as circularProgressClasses } from './circularProgressClasses'; -export * from './circularProgressClasses'; diff --git a/packages/mui-material-next/src/Divider/Divider.test.tsx b/packages/mui-material-next/src/Divider/Divider.test.tsx deleted file mode 100644 index 4622af20f6f404..00000000000000 --- a/packages/mui-material-next/src/Divider/Divider.test.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -import Divider, { dividerClasses as classes } from '@mui/material-next/Divider'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import describeConformance from '../../test/describeConformance'; - -describe('<Divider />', () => { - const { render } = createRenderer(); - - describeConformance(<Divider />, () => ({ - classes, - inheritComponent: 'hr', - render, - muiName: 'MuiDivider', - refInstanceof: window.HTMLHRElement, - testComponentPropWith: 'div', - testVariantProps: { orientation: 'vertical', flexItem: true, textAlign: 'left' }, - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - skip: ['componentsProp'], - })); - - it('should set the absolute class', () => { - const { container } = render(<Divider absolute />); - expect(container.firstChild).to.have.class(classes.absolute); - }); - - it('should set the flexItem class', () => { - const { container } = render(<Divider flexItem />); - expect(container.firstChild).to.have.class(classes.flexItem); - }); - - describe('prop: children', () => { - it('should render with the children', () => { - const text = 'test content'; - const { container } = render(<Divider>{text}</Divider>); - expect(container.querySelectorAll('span').length).to.equal(1); - expect(container.querySelectorAll('span')[0].textContent).to.equal(text); - }); - - it('should set the default text class', () => { - const { container } = render(<Divider>content</Divider>); - expect(container.firstChild).to.have.class(classes.withChildren); - }); - - describe('prop: orientation', () => { - it('should set the textVertical class', () => { - const { container } = render(<Divider orientation="vertical">content</Divider>); - expect(container.querySelectorAll(`.${classes.wrapperVertical}`).length).to.equal(1); - }); - }); - - describe('prop: textAlign', () => { - it('should set the textAlignRight class', () => { - const { container } = render(<Divider textAlign="right">content</Divider>); - expect(container.querySelectorAll(`.${classes.textAlignRight}`).length).to.equal(1); - }); - - it('should set the textAlignLeft class', () => { - const { container } = render(<Divider textAlign="left">content</Divider>); - expect(container.querySelectorAll(`.${classes.textAlignLeft}`).length).to.equal(1); - }); - - it('should not set the textAlignRight class if orientation="vertical"', () => { - const { container } = render( - <Divider textAlign="right" orientation="vertical"> - content - </Divider>, - ); - expect(container.querySelectorAll(`.${classes.textAlignRight}`).length).to.equal(0); - }); - - it('should not set the textAlignLeft class if orientation="vertical"', () => { - const { container } = render( - <Divider textAlign="left" orientation="vertical"> - content - </Divider>, - ); - expect(container.querySelectorAll(`.${classes.textAlignLeft}`).length).to.equal(0); - }); - }); - }); - - describe('prop: variant', () => { - it('should default to variant="fullWidth"', () => { - const { container } = render(<Divider />); - expect(container.firstChild).not.to.have.class(classes.inset); - expect(container.firstChild).not.to.have.class(classes.middle); - }); - - describe('prop: variant="fullWidth" ', () => { - it('should render with the root and default class', () => { - const { container } = render(<Divider />); - expect(container.firstChild).to.have.class(classes.root); - }); - }); - - describe('prop: variant="inset" ', () => { - it('should set the inset class', () => { - const { container } = render(<Divider variant="inset" />); - expect(container.firstChild).to.have.class(classes.inset); - }); - }); - - describe('prop: variant="middle" with default orientation (horizontal)', () => { - it('should set the middle class', () => { - const { container } = render(<Divider variant="middle" />); - expect(container.firstChild).to.have.class(classes.middle); - expect(container.firstChild).toHaveComputedStyle({ - marginLeft: '16px', - marginRight: '16px', - }); - }); - }); - - describe('prop: variant="middle" with orientation="vertical"', () => { - it('should set the middle class with marginTop & marginBottom styles', () => { - const { container } = render(<Divider variant="middle" orientation="vertical" />); - expect(container.firstChild).toHaveComputedStyle({ - marginTop: '16px', - marginBottom: '16px', - }); - }); - }); - }); - - describe('role', () => { - it('avoids adding implicit aria semantics', () => { - const { container } = render(<Divider />); - expect(container.firstChild).not.to.have.attribute('role'); - }); - - it('adds a proper role if none is specified', () => { - const { container } = render(<Divider component="div" />); - expect(container.firstChild).to.have.attribute('role', 'separator'); - }); - - it('overrides the computed role with the provided one', () => { - // presentation is the only valid aria role - const { container } = render(<Divider role="presentation" />); - expect(container.firstChild).to.have.attribute('role', 'presentation'); - }); - }); -}); diff --git a/packages/mui-material-next/src/Divider/Divider.tsx b/packages/mui-material-next/src/Divider/Divider.tsx deleted file mode 100644 index 1c8ce1610605e4..00000000000000 --- a/packages/mui-material-next/src/Divider/Divider.tsx +++ /dev/null @@ -1,306 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { OverridableComponent } from '@mui/types'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; -import { getDividerUtilityClass } from './dividerClasses'; -import { DividerOwnerState, DividerProps, DividerTypeMap } from './Divider.types'; - -const useUtilityClasses = (ownerState: DividerOwnerState) => { - const { absolute, children, classes, flexItem, orientation, textAlign, variant } = ownerState; - - const slots = { - root: [ - 'root', - absolute && 'absolute', - variant, - orientation === 'vertical' && 'vertical', - flexItem && 'flexItem', - !!children && 'withChildren', - textAlign === 'right' && orientation !== 'vertical' && 'textAlignRight', - textAlign === 'left' && orientation !== 'vertical' && 'textAlignLeft', - ], - wrapper: ['wrapper', orientation === 'vertical' && 'wrapperVertical'], - }; - - return composeClasses(slots, getDividerUtilityClass, classes); -}; - -const DividerRoot = styled('div', { - name: 'MuiDivider', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - ownerState.absolute && styles.absolute, - styles[ownerState.variant], - ownerState.orientation === 'vertical' && styles.vertical, - ownerState.flexItem && styles.flexItem, - ownerState.children && styles.withChildren, - ownerState.textAlign === 'right' && - ownerState.orientation !== 'vertical' && - styles.textAlignRight, - ownerState.textAlign === 'left' && - ownerState.orientation !== 'vertical' && - styles.textAlignLeft, - ]; - }, -})<{ ownerState: DividerOwnerState }>( - ({ theme, ownerState }) => { - const { vars: tokens } = theme; - - return { - margin: 0, // Reset browser default style. - flexShrink: 0, - borderWidth: 0, - borderStyle: 'solid', - borderColor: tokens.sys.color.outlineVariant, - borderBottomWidth: '1px', - ...(ownerState.absolute && { - position: 'absolute', - bottom: 0, - left: 0, - width: '100%', - }), - ...(ownerState.variant === 'inset' && - ownerState.orientation === 'horizontal' && { - marginLeft: '16px', - }), - ...(ownerState.variant === 'inset' && - ownerState.orientation === 'vertical' && { - marginTop: '16px', - }), - ...(ownerState.variant === 'middle' && - ownerState.orientation === 'horizontal' && { - marginLeft: '16px', - marginRight: '16px', - }), - ...(ownerState.variant === 'middle' && - ownerState.orientation === 'vertical' && { - marginTop: '16px', - marginBottom: '16px', - }), - ...(ownerState.orientation === 'vertical' && { - height: '100%', - borderBottomWidth: 0, - borderRightWidth: '1px', - }), - ...(ownerState.flexItem && { - alignSelf: 'stretch', - height: 'auto', - }), - }; - }, - ({ ownerState }) => ({ - ...(ownerState.children && { - display: 'flex', - whiteSpace: 'nowrap', - textAlign: 'center', - border: 0, - '&::before, &::after': { - content: '""', - alignSelf: 'center', - }, - }), - }), - ({ theme: { vars: tokens }, ownerState }) => ({ - ...(ownerState.children && - ownerState.orientation !== 'vertical' && { - '&::before, &::after': { - width: '100%', - borderTop: `1px solid ${tokens.sys.color.outlineVariant}`, - }, - }), - }), - ({ theme: { vars: tokens }, ownerState }) => ({ - ...(ownerState.children && - ownerState.orientation === 'vertical' && { - flexDirection: 'column', - '&::before, &::after': { - height: '100%', - borderLeft: `1px solid ${tokens.sys.color.outlineVariant}`, - }, - }), - }), - ({ ownerState }) => ({ - ...(ownerState.textAlign === 'right' && - ownerState.orientation !== 'vertical' && { - '&::before': { - width: '90%', - }, - '&::after': { - width: '10%', - }, - }), - ...(ownerState.textAlign === 'left' && - ownerState.orientation !== 'vertical' && { - '&::before': { - width: '10%', - }, - '&::after': { - width: '90%', - }, - }), - }), -); - -const DividerWrapper = styled('span', { - name: 'MuiDivider', - slot: 'Wrapper', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [styles.wrapper, ownerState.orientation === 'vertical' && styles.wrapperVertical]; - }, -})<{ ownerState: DividerOwnerState }>(({ theme, ownerState }) => ({ - display: 'inline-block', - paddingLeft: `calc(${theme.spacing(1)} * 1.2)`, - paddingRight: `calc(${theme.spacing(1)} * 1.2)`, - ...(ownerState.orientation === 'vertical' && { - paddingTop: `calc(${theme.spacing(1)} * 1.2)`, - paddingBottom: `calc(${theme.spacing(1)} * 1.2)`, - }), -})); - -/** - * Dividers separate content into clear groups. - * - * Demos: - * - * - [Divider](https://mui.com/material-ui/react-divider/) - * - [Lists](https://mui.com/material-ui/react-list/) - * - * API: - * - * - [Divider API](https://mui.com/material-ui/api/divider/) - */ -const Divider = React.forwardRef(function Divider< - BaseComponentType extends React.ElementType = DividerTypeMap['defaultComponent'], ->(inProps: DividerProps<BaseComponentType>, ref: React.ForwardedRef<any>) { - const props = useThemeProps({ - props: inProps, - name: 'MuiDivider', - }); - - const { - absolute = false, - children, - className, - component = children ? 'div' : 'hr', - flexItem = false, - orientation = 'horizontal', - role = component !== 'hr' ? 'separator' : undefined, - textAlign = 'center', - variant = 'fullWidth', - ...other - } = props; - - const ownerState: DividerOwnerState = { - ...props, - absolute, - component, - flexItem, - orientation, - role, - textAlign, - variant, - }; - - const classes = useUtilityClasses(ownerState); - - return ( - <DividerRoot - as={component} - className={clsx(classes.root, className)} - role={role} - ref={ref} - ownerState={ownerState} - {...other} - > - {children ? ( - <DividerWrapper className={classes.wrapper} ownerState={ownerState}> - {children} - </DividerWrapper> - ) : null} - </DividerRoot> - ); -}) as OverridableComponent<DividerTypeMap>; - -/** - * The following flag is used to ensure that this component isn't tabbable i.e. - * does not get highlight/focus inside of MUI List. - */ -// @ts-ignore internal logic -Divider.muiSkipListHighlight = true; - -Divider.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * Absolutely position the element. - * @default false - */ - absolute: PropTypes.bool, - /** - * The content of the component. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * If `true`, a vertical divider will have the correct height when used in flex container. - * (By default, a vertical divider will have a calculated height of `0px` if it is the child of a flex container.) - * @default false - */ - flexItem: PropTypes.bool, - /** - * The component orientation. - * @default 'horizontal' - */ - orientation: PropTypes.oneOf(['horizontal', 'vertical']), - /** - * @ignore - */ - role: PropTypes /* @typescript-to-proptypes-ignore */.string, - /** - * 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 text alignment. - * @default 'center' - */ - textAlign: PropTypes.oneOf(['center', 'left', 'right']), - /** - * The variant to use. - * @default 'fullWidth' - */ - variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['fullWidth', 'inset', 'middle']), - PropTypes.string, - ]), -} as any; - -export default Divider; diff --git a/packages/mui-material-next/src/Divider/Divider.types.ts b/packages/mui-material-next/src/Divider/Divider.types.ts deleted file mode 100644 index 9d95204aaa8a6d..00000000000000 --- a/packages/mui-material-next/src/Divider/Divider.types.ts +++ /dev/null @@ -1,66 +0,0 @@ -import * as React from 'react'; -import { OverridableStringUnion, OverrideProps } from '@mui/types'; -import { SxProps } from '@mui/system'; -import { Theme } from '../styles'; -import { DividerClasses } from './dividerClasses'; - -export interface DividerPropsVariantOverrides {} - -export interface DividerOwnerState extends DividerProps {} - -export interface DividerTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'hr', -> { - props: AdditionalProps & { - /** - * Absolutely position the element. - * @default false - */ - absolute?: boolean; - /** - * The content of the component. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<DividerClasses>; - /** - * If `true`, a vertical divider will have the correct height when used in flex container. - * (By default, a vertical divider will have a calculated height of `0px` if it is the child of a flex container.) - * @default false - */ - flexItem?: boolean; - /** - * The component orientation. - * @default 'horizontal' - */ - orientation?: 'horizontal' | 'vertical'; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; - /** - * The text alignment. - * @default 'center' - */ - textAlign?: 'center' | 'right' | 'left'; - /** - * The variant to use. - * @default 'fullWidth' - */ - variant?: OverridableStringUnion< - 'fullWidth' | 'inset' | 'middle', - DividerPropsVariantOverrides - >; - }; - defaultComponent: RootComponent; -} - -export type DividerProps< - RootComponent extends React.ElementType = DividerTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<DividerTypeMap<AdditionalProps, RootComponent>, RootComponent> & { - component?: React.ElementType; -}; diff --git a/packages/mui-material-next/src/Divider/dividerClasses.ts b/packages/mui-material-next/src/Divider/dividerClasses.ts deleted file mode 100644 index e397f1d44a4643..00000000000000 --- a/packages/mui-material-next/src/Divider/dividerClasses.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface DividerClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `absolute={true}`. */ - absolute: string; - /** Styles applied to the root element if `variant="inset"`. */ - inset: string; - /** Styles applied to the root element if `variant="fullWidth"`. */ - fullWidth: string; - /** Styles applied to the root element if `variant="middle"`. */ - middle: string; - /** Styles applied to the root element if `orientation="vertical"`. */ - vertical: string; - /** Styles applied to the root element if `flexItem={true}`. */ - flexItem: string; - /** Styles applied to the root element if divider have text. */ - withChildren: string; - /** Styles applied to the root element if `textAlign="right" orientation="horizontal"`. */ - textAlignRight: string; - /** Styles applied to the root element if `textAlign="left" orientation="horizontal"`. */ - textAlignLeft: string; - /** Styles applied to the span children element if `orientation="horizontal"`. */ - wrapper: string; - /** Styles applied to the span children element if `orientation="vertical"`. */ - wrapperVertical: string; -} - -export type DividerClassKey = keyof DividerClasses; - -export function getDividerUtilityClass(slot: string): string { - return generateUtilityClass('MuiDivider', slot); -} - -const dividerClasses: DividerClasses = generateUtilityClasses('MuiDivider', [ - 'root', - 'absolute', - 'fullWidth', - 'inset', - 'middle', - 'flexItem', - 'vertical', - 'withChildren', - 'textAlignRight', - 'textAlignLeft', - 'wrapper', - 'wrapperVertical', -]); - -export default dividerClasses; diff --git a/packages/mui-material-next/src/Divider/index.ts b/packages/mui-material-next/src/Divider/index.ts deleted file mode 100644 index 6f37070be42115..00000000000000 --- a/packages/mui-material-next/src/Divider/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './Divider'; - -export { default as dividerClasses } from './dividerClasses'; -export * from './dividerClasses'; diff --git a/packages/mui-material-next/src/FilledInput/FilledInput.spec.tsx b/packages/mui-material-next/src/FilledInput/FilledInput.spec.tsx deleted file mode 100644 index 85956d7f730606..00000000000000 --- a/packages/mui-material-next/src/FilledInput/FilledInput.spec.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import * as React from 'react'; -import FilledInput from '@mui/material-next/FilledInput'; - -function TestHiddenLabel() { - return <FilledInput hiddenLabel />; -} diff --git a/packages/mui-material-next/src/FilledInput/FilledInput.test.tsx b/packages/mui-material-next/src/FilledInput/FilledInput.test.tsx deleted file mode 100644 index 032dbf30375e1e..00000000000000 --- a/packages/mui-material-next/src/FilledInput/FilledInput.test.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { ClassNames } from '@emotion/react'; -import { createRenderer } from '@mui-internal/test-utils'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import FilledInput, { filledInputClasses as classes } from '@mui/material-next/FilledInput'; -import InputBase from '@mui/material-next/InputBase'; -import describeConformance from '../../test/describeConformance'; - -describe('<FilledInput />', () => { - const { render } = createRenderer(); - - describeConformance(<FilledInput open />, () => ({ - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - classes, - inheritComponent: InputBase, - render, - refInstanceof: window.HTMLDivElement, - muiName: 'MuiFilledInput', - testDeepOverrides: { slotName: 'input', slotClassName: classes.input }, - testVariantProps: { variant: 'contained', fullWidth: true }, - testStateOverrides: { prop: 'size', value: 'small', styleKey: 'sizeSmall' }, - slots: { - root: { expectedClassName: classes.root }, - input: { expectedClassName: classes.input, testWithElement: 'input' }, - }, - skip: [ - 'componentProp', - 'componentsProp', - 'slotPropsCallback', // not supported yet - ], - })); - - it('should have the underline class', () => { - const { getByTestId } = render(<FilledInput data-testid="test-input" />); - const root = getByTestId('test-input'); - expect(root).not.to.equal(null); - expect(root).to.have.class(classes.underline); - }); - - it('color={undefined} should not result in crash', () => { - expect(() => { - render(<FilledInput color={undefined} />); - }).not.toErrorDev(); - }); - - it('can disable the underline', () => { - const { getByTestId } = render(<FilledInput data-testid="test-input" disableUnderline />); - const root = getByTestId('test-input'); - expect(root).not.to.have.class(classes.underline); - }); - - it('should forward classes to InputBase', () => { - render(<FilledInput error classes={{ error: 'error' }} />); - expect(document.querySelector('.error')).not.to.equal(null); - }); - - it('should respect the classes coming from InputBase', () => { - const { getByTestId } = render( - <FilledInput - data-testid="test-input" - multiline - sx={{ [`&.${classes.multiline}`]: { mt: '10px' } }} - />, - ); - const root = getByTestId('test-input'); - expect(root).toHaveComputedStyle({ marginTop: '10px' }); - }); - - describe('Emotion compatibility', () => { - it('classes.root should overwrite built-in styles.', () => { - const { getByTestId } = render( - <ClassNames> - {({ css }) => ( - <FilledInput data-testid="root" classes={{ root: css({ position: 'static' }) }} /> - )} - </ClassNames>, - ); - const input = getByTestId('root'); - - expect(getComputedStyle(input).position).to.equal('static'); - }); - - it('className should overwrite classes.root and built-in styles.', () => { - const { getByTestId } = render( - <ClassNames> - {({ css }) => ( - <FilledInput - data-testid="root" - className={css({ position: 'sticky' })} - classes={{ root: css({ position: 'static' }) }} - /> - )} - </ClassNames>, - ); - const input = getByTestId('root'); - - expect(getComputedStyle(input).position).to.equal('sticky'); - }); - }); -}); diff --git a/packages/mui-material-next/src/FilledInput/FilledInput.tsx b/packages/mui-material-next/src/FilledInput/FilledInput.tsx deleted file mode 100644 index b5d9bbc5af2c2f..00000000000000 --- a/packages/mui-material-next/src/FilledInput/FilledInput.tsx +++ /dev/null @@ -1,508 +0,0 @@ -'use client'; -import * as React from 'react'; -import { refType } from '@mui/utils'; -import PropTypes from 'prop-types'; -import { unstable_composeClasses as composeClasses, useSlotProps } from '@mui/base'; -import { DefaultComponentProps, OverrideProps } from '@mui/types'; -import { useThemeProps, styled } from '../styles'; -import { rootShouldForwardProp } from '../styles/styled'; -import { - InputBaseRoot, - InputBaseInput, - rootOverridesResolver as inputBaseRootOverridesResolver, - inputOverridesResolver as inputBaseInputOverridesResolver, -} from '../InputBase/InputBase'; -import InputBase from '../InputBase'; -import { InputBaseOwnerState } from '../InputBase/InputBase.types'; -import filledInputClasses, { getFilledInputUtilityClass } from './filledInputClasses'; -import { FilledInputOwnerState, FilledInputProps, FilledInputTypeMap } from './FilledInput.types'; - -const useUtilityClasses = (ownerState: FilledInputOwnerState) => { - const { classes, disableUnderline } = ownerState; - - const slots = { - root: ['root', !disableUnderline && 'underline'], - input: ['input'], - }; - - const composedClasses = composeClasses(slots, getFilledInputUtilityClass, classes); - - return { - ...classes, // forward classes to the InputBase - ...composedClasses, - }; -}; - -const FilledInputRoot = styled(InputBaseRoot, { - shouldForwardProp: (prop) => rootShouldForwardProp(prop) || prop === 'classes', - name: 'MuiFilledInput', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - ...inputBaseRootOverridesResolver(props, styles), - !ownerState.disableUnderline && styles.underline, - ]; - }, -})<{ ownerState: FilledInputOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - return { - '--md-comp-filled-input-active-indicator-color': tokens.sys.color.onSurfaceVariant, - '--md-comp-filled-input-container-color': tokens.sys.color.surfaceContainerHighest, - '--md-comp-filled-input-disabled-container-color': tokens.sys.color.onSurface, - '--md-comp-filled-input-disabled-container-opacity': 0.04, - '--md-comp-filled-input-disabled-active-indicator-color': tokens.sys.color.onSurface, - '--md-comp-filled-input-disabled-active-indicator-opacity': 0.38, - '--md-comp-filled-input-error-active-indicator-color': tokens.sys.color.error, - '--md-comp-filled-input-error-hover-active-indicator-color': tokens.sys.color.onErrorContainer, - '--md-comp-filled-input-focus-active-indicator-color': - tokens.sys.color[ownerState.color ?? 'primary'], - '--md-comp-filled-input-hover-active-indicator-color': tokens.sys.color.onSurface, - '--md-comp-filled-input-hover-state-layer-opacity': tokens.sys.state.hover.stateLayerOpacity, - position: 'relative', - backgroundColor: 'var(--md-comp-filled-input-container-color)', - borderTopLeftRadius: tokens.shape.borderRadius, - borderTopRightRadius: tokens.shape.borderRadius, - transition: theme.transitions.create('background-color', { - duration: theme.transitions.duration.shorter, - easing: theme.transitions.easing.easeOut, - }), - '&:hover': { - backgroundColor: - 'color-mix(in srgb, var(--md-comp-filled-input-hover-active-indicator-color) calc(var(--md-comp-filled-input-hover-state-layer-opacity) * 100%), var(--md-comp-filled-input-container-color))', - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: 'var(--md-comp-filled-input-container-color)', - }, - }, - [`&.${filledInputClasses.focused}`]: { - backgroundColor: 'var(--md-comp-filled-input-container-color)', - '&::after': { - borderColor: 'var(--md-comp-filled-input-focus-active-indicator-color)', - }, - }, - [`&.${filledInputClasses.disabled}`]: { - backgroundColor: - 'color-mix(in srgb, var(--md-comp-filled-input-disabled-container-color) calc(var(--md-comp-filled-input-disabled-container-opacity) * 100%), transparent)', - }, - ...(!ownerState.disableUnderline && { - '&::after': { - borderBottom: '2px solid var(--md-comp-filled-input-active-indicator-color)', - left: 0, - bottom: 0, - // Doing the other way around crash on IE11 "''" https://github.com/cssinjs/jss/issues/242 - content: '""', - position: 'absolute', - right: 0, - transform: 'scaleX(0)', - transition: theme.transitions.create('transform', { - duration: theme.transitions.duration.shorter, - easing: theme.transitions.easing.easeOut, - }), - pointerEvents: 'none', // Transparent to the hover style. - }, - [`&.${filledInputClasses.focused}:after`]: { - // translateX(0) is a workaround for Safari transform scale bug - // See https://github.com/mui/material-ui/issues/31766 - transform: 'scaleX(1) translateX(0)', - }, - [`&.${filledInputClasses.error}`]: { - '&::before, &::after': { - borderBottomColor: 'var(--md-comp-filled-input-error-active-indicator-color)', - }, - }, - '&::before': { - borderBottom: '1px solid var(--md-comp-filled-input-active-indicator-color)', - left: 0, - bottom: 0, - // Doing the other way around crash on IE11 "''" https://github.com/cssinjs/jss/issues/242 - content: '"\\00a0"', - position: 'absolute', - right: 0, - transition: theme.transitions.create('border-bottom-color', { - duration: theme.transitions.duration.shorter, - }), - pointerEvents: 'none', // Transparent to the hover style. - }, - [`&:hover:not(.${filledInputClasses.disabled}, .${filledInputClasses.error}):before`]: { - borderBottom: '1px solid var(--md-comp-filled-input-active-indicator-color)', - }, - [`&.${filledInputClasses.disabled}:before`]: { - borderColor: - 'color-mix(in srgb, var(--md-comp-filled-input-disabled-active-indicator-color) calc(var(--md-comp-filled-input-disabled-active-indicator-opacity) * 100%), transparent)', - }, - }), - ...(ownerState.startAdornment && { - paddingLeft: 12, - }), - ...(ownerState.endAdornment && { - paddingRight: 12, - }), - ...(ownerState.multiline && { - padding: '25px 12px 8px', - ...(ownerState.size === 'small' && { - paddingTop: 21, - paddingBottom: 4, - }), - ...(ownerState.hiddenLabel && { - paddingTop: 16, - paddingBottom: 17, - }), - }), - }; -}); - -const FilledInputInput = styled(InputBaseInput, { - name: 'MuiFilledInput', - slot: 'Input', - overridesResolver: inputBaseInputOverridesResolver, -})<{ ownerState: FilledInputOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - return { - paddingTop: 25, - paddingRight: 16, - paddingBottom: 8, - paddingLeft: 16, - ...(!tokens - ? { - [theme.getColorSchemeSelector('light')]: { - '&:-webkit-autofill': { - WebkitBoxShadow: null, - WebkitTextFillColor: null, - caretColor: null, - borderTopLeftRadius: 'inherit', - borderTopRightRadius: 'inherit', - }, - }, - [theme.getColorSchemeSelector('dark')]: { - '&:-webkit-autofill': { - WebkitBoxShadow: '0 0 0 100px #266798 inset', - WebkitTextFillColor: '#fff', - caretColor: '#fff', - borderTopLeftRadius: 'inherit', - borderTopRightRadius: 'inherit', - }, - }, - } - : { - '&:-webkit-autofill': { - borderTopLeftRadius: 'inherit', - borderTopRightRadius: 'inherit', - }, - // this could be undefined in unit tests - ...(theme.getColorSchemeSelector && { - [theme.getColorSchemeSelector('dark')]: { - '&:-webkit-autofill': { - WebkitBoxShadow: '0 0 0 100px #266798 inset', - WebkitTextFillColor: '#fff', - caretColor: '#fff', - }, - }, - }), - }), - ...(ownerState.size === 'small' && { - paddingTop: 21, - paddingBottom: 4, - }), - ...(ownerState.hiddenLabel && { - paddingTop: 16, - paddingBottom: 17, - }), - ...(ownerState.multiline && { - paddingTop: 0, - paddingBottom: 0, - paddingLeft: 0, - paddingRight: 0, - }), - ...(ownerState.startAdornment && { - paddingLeft: 0, - }), - ...(ownerState.endAdornment && { - paddingRight: 0, - }), - ...(ownerState.hiddenLabel && - ownerState.size === 'small' && { - paddingTop: 8, - paddingBottom: 9, - }), - }; -}); - -const FilledInput = React.forwardRef(function FilledInput< - RootComponentType extends React.ElementType, ->(inProps: FilledInputProps<RootComponentType>, forwardedRef: React.ForwardedRef<Element>) { - const props = useThemeProps({ props: inProps, name: 'MuiFilledInput' }); - - const { - disableUnderline, - fullWidth = false, - hiddenLabel, // declare here to prevent spreading to DOM - inputComponent = 'input', - multiline = false, - type = 'text', - slotProps = {}, - slots = {}, - ...other - } = props; - - const ownerState: FilledInputOwnerState = { - ...props, - disableUnderline, - fullWidth, - inputComponent, - multiline, - type, - }; - - const classes = useUtilityClasses(ownerState); - - const Root = slots.root ?? FilledInputRoot; - const Input = slots.input ?? FilledInputInput; - - const rootProps = useSlotProps({ - elementType: Root, - externalSlotProps: slotProps.root, - additionalProps: { - ref: forwardedRef, - fullWidth, - inputComponent, - multiline, - type, - }, - externalForwardedProps: other, - ownerState: ownerState as FilledInputOwnerState & InputBaseOwnerState, - className: [classes.root], - }); - - const inputProps = useSlotProps({ - elementType: Input, - externalSlotProps: slotProps.input, - ownerState: ownerState as FilledInputOwnerState & InputBaseOwnerState, - className: [classes.input], - }); - - return ( - <InputBase - slots={{ root: Root, [multiline ? 'textarea' : 'input']: Input }} - slotProps={{ - input: inputProps, - }} - {...rootProps} - /> - ); -}) as FilledInputComponent; - -interface FilledInputComponent { - <C extends React.ElementType>( - props: { - /** - * The component used for the input node. - * Either a string to use a HTML element or a component. - * @default 'input' - */ - inputComponent?: C; - } & OverrideProps<FilledInputTypeMap, C>, - ): JSX.Element | null; - (props: DefaultComponentProps<FilledInputTypeMap>): JSX.Element | null; - propTypes?: any; - muiName?: string; -} - -FilledInput.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * This prop helps users to fill forms faster, especially on mobile devices. - * The name can be confusing, as it's more like an autofill. - * You can learn more about it [following the specification](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill). - */ - autoComplete: PropTypes.string, - /** - * If `true`, the `input` element is focused during the first mount. - */ - autoFocus: PropTypes.bool, - /** - * @ignore - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * The prop defaults to the value (`'primary'`) inherited from the parent FormControl component. - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['error', 'info', 'primary', 'secondary', 'success', 'tertiary', 'warning']), - PropTypes.string, - ]), - /** - * The default value. Use when the component is not controlled. - */ - defaultValue: PropTypes.any, - /** - * If `true`, the component is disabled. - * The prop defaults to the value (`false`) inherited from the parent FormControl component. - */ - disabled: PropTypes.bool, - /** - * If `true`, the input will not have an underline. - */ - disableUnderline: PropTypes.bool, - /** - * End `InputAdornment` for this component. - */ - endAdornment: PropTypes.node, - /** - * If `true`, the `input` will indicate an error. - * The prop defaults to the value (`false`) inherited from the parent FormControl component. - */ - error: PropTypes.bool, - /** - * If `true`, the `input` will take up the full width of its container. - * @default false - */ - fullWidth: PropTypes.bool, - /** - * If `true`, the label is hidden. - * This is used to increase density for a `FilledInput`. - * Be sure to add `aria-label` to the `input` element. - * @default false - */ - hiddenLabel: PropTypes.bool, - /** - * The id of the `input` element. - */ - id: PropTypes.string, - /** - * The component used for the input node. - * Either a string to use a HTML element or a component. - * @default 'input' - */ - inputComponent: PropTypes /* @typescript-to-proptypes-ignore */.elementType, - /** - * Pass a ref to the `input` element. - */ - inputRef: refType, - /** - * If `dense`, will adjust vertical spacing. This is normally obtained via context from - * FormControl. - * The prop defaults to the value (`'none'`) inherited from the parent FormControl component. - */ - margin: PropTypes.oneOf(['dense', 'none']), - /** - * Maximum number of rows to display when multiline option is set to true. - */ - maxRows: PropTypes.number, - /** - * Minimum number of rows to display when multiline option is set to true. - */ - minRows: PropTypes.number, - /** - * If `true`, a `textarea` element is rendered. - * @default false - */ - multiline: PropTypes.bool, - /** - * Name attribute of the `input` element. - */ - name: PropTypes.string, - /** - * Callback fired when the value is changed. - * - * @param {React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>} event The event source of the callback. - * You can pull out the new value by accessing `event.target.value` (string). - */ - onChange: PropTypes.func, - /** - * The short hint displayed in the `input` before the user enters a value. - */ - placeholder: PropTypes.string, - /** - * It prevents the user from changing the value of the field - * (not from interacting with the field). - */ - readOnly: PropTypes.bool, - /** - * If `true`, the `input` element is required. - * The prop defaults to the value (`false`) inherited from the parent FormControl component. - */ - required: PropTypes.bool, - /** - * Number of rows to display when multiline option is set to true. - */ - rows: PropTypes.number, - /** - * The props used for each slot inside the Input. - * @default {} - */ - slotProps: PropTypes.shape({ - input: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - }), - /** - * The components used for each slot inside the InputBase. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots: PropTypes.shape({ - input: PropTypes.elementType, - root: PropTypes.elementType, - }), - /** - * Start `InputAdornment` for this component. - */ - startAdornment: PropTypes.node, - /** - * 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, - ]), - /** - * Type of the `input` element. It should be [a valid HTML5 input type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Form_%3Cinput%3E_types). - * @default 'text' - */ - type: PropTypes /* @typescript-to-proptypes-ignore */.oneOf([ - 'button', - 'checkbox', - 'color', - 'date', - 'datetime-local', - 'email', - 'file', - 'hidden', - 'image', - 'month', - 'number', - 'password', - 'radio', - 'range', - 'reset', - 'search', - 'submit', - 'tel', - 'text', - 'time', - 'url', - 'week', - ]), - /** - * The value of the `input` element, required for a controlled component. - */ - value: PropTypes.any, -} as any; - -FilledInput.muiName = 'Input'; - -export default FilledInput; diff --git a/packages/mui-material-next/src/FilledInput/FilledInput.types.ts b/packages/mui-material-next/src/FilledInput/FilledInput.types.ts deleted file mode 100644 index fe229efca59b7f..00000000000000 --- a/packages/mui-material-next/src/FilledInput/FilledInput.types.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { SxProps } from '@mui/system'; -// TODO v6: port to material-next -// eslint-disable-next-line no-restricted-imports -import { InternalStandardProps as StandardProps } from '@mui/material'; -import { OverrideProps, Simplify } from '@mui/types'; -import { Theme } from '../styles/Theme.types'; -import { InputBaseProps } from '../InputBase/InputBase.types'; -import { FilledInputClasses } from './filledInputClasses'; - -export interface FilledInputOwnProps - extends StandardProps<Omit<InputBaseProps, 'children' | 'slots'>> { - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<FilledInputClasses>; - /** - * If `true`, the label is hidden. - * This is used to increase density for a `FilledInput`. - * Be sure to add `aria-label` to the `input` element. - * @default false - */ - hiddenLabel?: boolean; - /** - * If `true`, the input will not have an underline. - */ - disableUnderline?: boolean; - /** - * The components used for each slot inside the InputBase. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots?: FilledInputSlots; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -export interface FilledInputSlots { - /** - * The component that renders the root. - * @default 'div' - */ - root?: React.ElementType; - /** - * The component that renders the input. - * @default 'input' - */ - input?: React.ElementType; -} - -export interface FilledInputTypeMap< - AdditionalProps = {}, - RootComponentType extends React.ElementType = 'div', -> { - props: FilledInputOwnProps & AdditionalProps; - defaultComponent: RootComponentType; -} - -export type FilledInputProps< - RootComponentType extends React.ElementType = FilledInputTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<FilledInputTypeMap<AdditionalProps, RootComponentType>, RootComponentType> & { - inputComponent?: React.ElementType; -}; - -export type FilledInputOwnerState = Simplify< - FilledInputOwnProps & { - disableUnderline?: boolean; - fullWidth: boolean; - inputComponent: React.ElementType; - multiline: boolean; - type?: React.InputHTMLAttributes<HTMLInputElement>['type']; - } ->; diff --git a/packages/mui-material-next/src/FilledInput/filledInputClasses.ts b/packages/mui-material-next/src/FilledInput/filledInputClasses.ts deleted file mode 100644 index b9935def8117cd..00000000000000 --- a/packages/mui-material-next/src/FilledInput/filledInputClasses.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { - unstable_generateUtilityClass as generateUtilityClass, - unstable_generateUtilityClasses as generateUtilityClasses, -} from '@mui/utils'; -import { inputBaseClasses } from '../InputBase'; - -export interface FilledInputClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if color secondary. */ - colorSecondary: string; - /** Styles applied to the root element unless `disableUnderline={true}`. */ - underline: string; - /** State class applied to the root element if the component is focused. */ - focused: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; - /** Styles applied to the root element if `startAdornment` is provided. */ - adornedStart: string; - /** Styles applied to the root element if `endAdornment` is provided. */ - adornedEnd: string; - /** State class applied to the root element if `error={true}`. */ - error: string; - /** Styles applied to the input element if `size="small"`. */ - sizeSmall: string; - /** Styles applied to the root element if `multiline={true}`. */ - multiline: string; - /** Styles applied to the root element if `hiddenLabel={true}`. */ - hiddenLabel: string; - /** Styles applied to the input element. */ - input: string; - /** Styles applied to the input element if `size="small"`. */ - inputSizeSmall: string; - /** Styles applied to the `input` if in `<FormControl hiddenLabel />`. */ - inputHiddenLabel: string; - /** Styles applied to the input element if `multiline={true}`. */ - inputMultiline: string; - /** Styles applied to the input element if `startAdornment` is provided. */ - inputAdornedStart: string; - /** Styles applied to the input element if `endAdornment` is provided. */ - inputAdornedEnd: string; - /** Styles applied to the input element if `type="search"`. */ - inputTypeSearch: string; -} - -export type FilledInputClassKey = keyof FilledInputClasses; - -export function getFilledInputUtilityClass(slot: string): string { - return generateUtilityClass('MuiFilledInput', slot); -} - -const filledInputClasses: FilledInputClasses = { - ...inputBaseClasses, - ...generateUtilityClasses('MuiFilledInput', ['root', 'underline', 'input']), -}; - -export default filledInputClasses; diff --git a/packages/mui-material-next/src/FilledInput/index.ts b/packages/mui-material-next/src/FilledInput/index.ts deleted file mode 100644 index d1afd2384ad9b5..00000000000000 --- a/packages/mui-material-next/src/FilledInput/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './FilledInput'; - -export { default as filledInputClasses } from './filledInputClasses'; -export * from './filledInputClasses'; diff --git a/packages/mui-material-next/src/FormControl/FormControl.test.tsx b/packages/mui-material-next/src/FormControl/FormControl.test.tsx deleted file mode 100644 index e764a8181c2e73..00000000000000 --- a/packages/mui-material-next/src/FormControl/FormControl.test.tsx +++ /dev/null @@ -1,481 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { spy } from 'sinon'; -import { ClassNames } from '@emotion/react'; -import { act, createRenderer, fireEvent } from '@mui-internal/test-utils'; -import FormControl, { formControlClasses as classes } from '@mui/material-next/FormControl'; -import FilledInput from '@mui/material-next/FilledInput'; -import InputBase from '@mui/material-next/InputBase'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -// TODO v6: replace with material-next/Select -import Select from '@mui/material/Select'; -import useFormControl from './useFormControl'; -import describeConformance from '../../test/describeConformance'; - -type TestFormControlledComponent = { - onFilled: () => {}; - onEmpty: () => {}; - onFocus: () => {}; - onBlur: () => {}; -}; - -describe('<FormControl />', () => { - const { render } = createRenderer(); - - interface TestComponentProps { - contextCallback: (context: ReturnType<typeof useFormControl>) => void; - } - - function TestComponent(props: TestComponentProps) { - const context = useFormControl(); - React.useEffect(() => { - props.contextCallback(context); - }); - return null; - } - - describeConformance(<FormControl />, () => ({ - classes, - inheritComponent: 'div', - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - render, - refInstanceof: window.HTMLDivElement, - muiName: 'MuiFormControl', - slots: { - root: { - expectedClassName: classes.root, - testWithElement: 'fieldset', - }, - }, - testRootOverrides: { slotName: 'root', slotClassName: classes.root }, - testComponentPropWith: 'fieldset', - testVariantProps: { margin: 'dense' }, - skip: ['componentsProp'], - })); - - describe('initial state', () => { - it('should have no margin', () => { - const { container } = render(<FormControl />); - const root = container.firstChild; - - expect(root).not.to.have.class(classes.marginNormal); - }); - - it('can have the margin normal class', () => { - const { container } = render(<FormControl margin="normal" />); - const root = container.firstChild; - - expect(root).to.have.class(classes.marginNormal); - }); - - it('can have the margin dense class', () => { - const { container } = render(<FormControl margin="dense" />); - const root = container.firstChild; - - expect(root).to.have.class(classes.marginDense); - expect(root).not.to.have.class(classes.marginNormal); - }); - - it('should not be filled initially', () => { - const readContext = spy(); - render( - <FormControl> - <TestComponent contextCallback={readContext} /> - </FormControl>, - ); - expect(readContext.args[0][0]).to.have.property('filled', false); - }); - - it('should not be focused initially', () => { - const readContext = spy(); - render( - <FormControl> - <TestComponent contextCallback={readContext} /> - </FormControl>, - ); - expect(readContext.args[0][0]).to.have.property('focused', false); - }); - }); - - describe('prop: required', () => { - it('should not apply it to the DOM', () => { - const { container } = render(<FormControl required />); - expect(container.firstChild).not.to.have.attribute('required'); - }); - }); - - describe('prop: disabled', () => { - it('will be unfocused if it gets disabled', () => { - const readContext = spy(); - const { container, setProps } = render( - <FormControl> - <InputBase /> - <TestComponent contextCallback={readContext} /> - </FormControl>, - ); - expect(readContext.args[0][0]).to.have.property('focused', false); - - act(() => { - container.querySelector('input')?.focus(); - }); - expect(readContext.lastCall.args[0]).to.have.property('focused', true); - - setProps({ disabled: true }); - expect(readContext.lastCall.args[0]).to.have.property('focused', false); - }); - }); - - describe('prop: focused', () => { - it('should display input in focused state', () => { - const readContext = spy(); - const { container } = render( - <FormControl focused> - <InputBase /> - <TestComponent contextCallback={readContext} /> - </FormControl>, - ); - - expect(readContext.args[0][0]).to.have.property('focused', true); - container.querySelector('input')?.blur(); - expect(readContext.args[0][0]).to.have.property('focused', true); - }); - - it('ignores focused when disabled', () => { - const readContext = spy(); - render( - <FormControl focused disabled> - <InputBase /> - <TestComponent contextCallback={readContext} /> - </FormControl>, - ); - expect(readContext.args[0][0]).to.include({ disabled: true, focused: false }); - }); - }); - - describe('registering input', () => { - it("should warn if more than one input is rendered regardless how it's nested", () => { - expect(() => { - render( - <FormControl> - <InputBase /> - <div> - {/* should work regardless how it's nested */} - <InputBase /> - </div> - </FormControl>, - ); - }).toErrorDev([ - 'MUI: There are multiple `InputBase` components inside a FormControl.\nThis creates visual inconsistencies, only use one `InputBase`.', - // React 18 Strict Effects run mount effects twice - React.version.startsWith('18') && - 'MUI: There are multiple `InputBase` components inside a FormControl.\nThis creates visual inconsistencies, only use one `InputBase`.', - ]); - }); - - it('should not warn if only one input is rendered', () => { - expect(() => { - render( - <FormControl> - <InputBase /> - </FormControl>, - ); - }).not.toErrorDev(); - }); - - it('should not warn when toggling between inputs', () => { - // this will ensure that deregistering was called during unmount - function ToggleFormInputs() { - const [flag, setFlag] = React.useState(true); - - return ( - <FormControl> - {flag ? ( - <InputBase /> - ) : ( - // TODO v6: use material-next/Select - <Select native> - <option value="">empty</option> - </Select> - )} - <button type="button" onClick={() => setFlag(!flag)}> - toggle - </button> - </FormControl> - ); - } - - const { getByText } = render(<ToggleFormInputs />); - expect(() => { - fireEvent.click(getByText('toggle')); - }).not.toErrorDev(); - }); - }); - - describe('input', () => { - it('should be filled when a value is set', () => { - const readContext = spy(); - render( - <FormControl> - <FilledInput value="bar" /> - <TestComponent contextCallback={readContext} /> - </FormControl>, - ); - expect(readContext.args[0][0]).to.have.property('filled', true); - }); - - it('should be filled when a value is set through slotProps.input', () => { - const readContext = spy(); - render( - <FormControl> - <FilledInput slotProps={{ input: { value: 'bar' } }} /> - <TestComponent contextCallback={readContext} /> - </FormControl>, - ); - expect(readContext.args[0][0]).to.have.property('filled', true); - }); - - it('should be filled when a defaultValue is set', () => { - const readContext = spy(); - render( - <FormControl> - <FilledInput defaultValue="bar" /> - <TestComponent contextCallback={readContext} /> - </FormControl>, - ); - expect(readContext.args[0][0]).to.have.property('filled', true); - }); - - it('should not be adornedStart with an endAdornment', () => { - const readContext = spy(); - render( - <FormControl> - <FilledInput endAdornment={<div />} /> - <TestComponent contextCallback={readContext} /> - </FormControl>, - ); - expect(readContext.args[0][0]).to.have.property('adornedStart', false); - }); - - it('should be adornedStart with a startAdornment', () => { - const readContext = spy(); - render( - <FormControl> - <FilledInput startAdornment={<div />} /> - <TestComponent contextCallback={readContext} /> - </FormControl>, - ); - expect(readContext.args[0][0]).to.have.property('adornedStart', true); - }); - }); - - // TODO v6: needs material-next/Select + FormControl integrated - // eslint-disable-next-line mocha/no-skipped-tests - describe.skip('select', () => { - it('should not be adorned without a startAdornment', () => { - const readContext = spy(); - render( - <FormControl> - <Select value="" /> - <TestComponent contextCallback={readContext} /> - </FormControl>, - ); - expect(readContext.args[0][0]).to.have.property('adornedStart', false); - }); - - it('should be adorned with a startAdornment', () => { - const readContext = spy(); - render( - <FormControl> - <Select value="" input={<InputBase startAdornment={<div />} />} /> - <TestComponent contextCallback={readContext} /> - </FormControl>, - ); - expect(readContext.args[0][0].adornedStart, 'true'); - }); - }); - - describe('useFormControl', () => { - const FormController = React.forwardRef((_, ref) => { - const formControl = useFormControl(); - React.useImperativeHandle(ref, () => formControl, [formControl]); - return null; - }); - - const FormControlled = React.forwardRef(function FormControlled(props, ref) { - return ( - <FormControl {...props}> - <FormController ref={ref} /> - </FormControl> - ); - }); - - describe('from props', () => { - it('should have the required prop from the instance', () => { - const formControlRef = React.createRef<TestFormControlledComponent>(); - const { setProps } = render(<FormControlled ref={formControlRef} />); - - expect(formControlRef.current).to.have.property('required', false); - - setProps({ required: true }); - expect(formControlRef.current).to.have.property('required', true); - }); - - it('should have the error prop from the instance', () => { - const formControlRef = React.createRef<TestFormControlledComponent>(); - const { setProps } = render(<FormControlled ref={formControlRef} />); - - expect(formControlRef.current).to.have.property('error', false); - - setProps({ error: true }); - expect(formControlRef.current).to.have.property('error', true); - }); - - it('should have the margin prop from the instance', () => { - const formControlRef = React.createRef<TestFormControlledComponent>(); - const { setProps } = render(<FormControlled ref={formControlRef} />); - - expect(formControlRef.current).to.have.property('size', 'medium'); - - setProps({ size: 'small' }); - expect(formControlRef.current).to.have.property('size', 'small'); - }); - - it('should have the fullWidth prop from the instance', () => { - const formControlRef = React.createRef<TestFormControlledComponent>(); - const { setProps } = render(<FormControlled ref={formControlRef} />); - - expect(formControlRef.current).to.have.property('fullWidth', false); - - setProps({ fullWidth: true }); - expect(formControlRef.current).to.have.property('fullWidth', true); - }); - }); - - describe('callbacks', () => { - describe('onFilled', () => { - it('should set the filled state', () => { - const formControlRef = React.createRef<TestFormControlledComponent>(); - render(<FormControlled ref={formControlRef} />); - - expect(formControlRef.current).to.have.property('filled', false); - - act(() => { - formControlRef.current?.onFilled(); - }); - - expect(formControlRef.current).to.have.property('filled', true); - - act(() => { - formControlRef.current?.onFilled(); - }); - - expect(formControlRef.current).to.have.property('filled', true); - }); - }); - - describe('onEmpty', () => { - it('should clean the filled state', () => { - const formControlRef = React.createRef<TestFormControlledComponent>(); - render(<FormControlled ref={formControlRef} />); - - act(() => { - formControlRef.current?.onFilled(); - }); - - expect(formControlRef.current).to.have.property('filled', true); - - act(() => { - formControlRef.current?.onEmpty(); - }); - - expect(formControlRef.current).to.have.property('filled', false); - - act(() => { - formControlRef.current?.onEmpty(); - }); - - expect(formControlRef.current).to.have.property('filled', false); - }); - }); - - describe('handleFocus', () => { - it('should set the focused state', () => { - const formControlRef = React.createRef<TestFormControlledComponent>(); - render(<FormControlled ref={formControlRef} />); - expect(formControlRef.current).to.have.property('focused', false); - - act(() => { - formControlRef.current?.onFocus(); - }); - - expect(formControlRef.current).to.have.property('focused', true); - - act(() => { - formControlRef.current?.onFocus(); - }); - - expect(formControlRef.current).to.have.property('focused', true); - }); - }); - - describe('handleBlur', () => { - it('should clear the focused state', () => { - const formControlRef = React.createRef<TestFormControlledComponent>(); - render(<FormControlled ref={formControlRef} />); - expect(formControlRef.current).to.have.property('focused', false); - - act(() => { - formControlRef.current?.onFocus(); - }); - - expect(formControlRef.current).to.have.property('focused', true); - - act(() => { - formControlRef.current?.onBlur(); - }); - - expect(formControlRef.current).to.have.property('focused', false); - - act(() => { - formControlRef.current?.onBlur(); - }); - - expect(formControlRef.current).to.have.property('focused', false); - }); - }); - }); - }); - - describe('Emotion compatibility', () => { - it('classes.root should overwrite built-in styles.', () => { - const { getByTestId } = render( - <ClassNames> - {({ css }) => ( - <FormControl data-testid="root" classes={{ root: css({ display: 'inline' }) }} /> - )} - </ClassNames>, - ); - const root = getByTestId('root'); - - expect(getComputedStyle(root).display).to.equal('inline'); - }); - - it('className should overwrite classes.root and built-in styles.', () => { - const { getByTestId } = render( - <ClassNames> - {({ css }) => ( - <FormControl - data-testid="root" - className={css({ display: 'inline-block' })} - classes={{ root: css({ display: 'inline' }) }} - /> - )} - </ClassNames>, - ); - const root = getByTestId('root'); - - expect(getComputedStyle(root).display).to.equal('inline-block'); - }); - }); -}); diff --git a/packages/mui-material-next/src/FormControl/FormControl.tsx b/packages/mui-material-next/src/FormControl/FormControl.tsx deleted file mode 100644 index 68aac7ad936019..00000000000000 --- a/packages/mui-material-next/src/FormControl/FormControl.tsx +++ /dev/null @@ -1,366 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { useSlotProps } from '@mui/base'; -import { OverridableComponent } from '@mui/types'; -import { - unstable_capitalize as capitalize, - unstable_isMuiElement as isMuiElement, -} from '@mui/utils'; -import useThemeProps from '../styles/useThemeProps'; -import styled from '../styles/styled'; -import { isFilled, isAdornedStart } from '../InputBase/utils'; -import FormControlContext from './FormControlContext'; -import { FormControlTypeMap, FormControlOwnerState, FormControlProps } from './FormControl.types'; -import { getFormControlUtilityClasses } from './formControlClasses'; - -const useUtilityClasses = (ownerState: FormControlOwnerState) => { - const { classes, margin, fullWidth } = ownerState; - const slots = { - root: ['root', margin !== 'none' && `margin${capitalize(margin)}`, fullWidth && 'fullWidth'], - }; - - return composeClasses(slots, getFormControlUtilityClasses, classes); -}; - -const FormControlRoot = styled('div', { - name: 'MuiFormControl', - slot: 'Root', - overridesResolver: ({ ownerState }, styles) => { - return [ - styles.root, - styles[`margin${capitalize(ownerState.margin)}`], - ownerState.fullWidth && styles.fullWidth, - ]; - }, -})<{ ownerState: FormControlOwnerState }>(({ ownerState }) => ({ - display: 'inline-flex', - flexDirection: 'column', - position: 'relative', - // Reset fieldset default style. - minWidth: 0, - padding: 0, - margin: 0, - border: 0, - verticalAlign: 'top', // Fix alignment issue on Safari. - ...(ownerState.margin === 'normal' && { - marginTop: 16, - marginBottom: 8, - }), - ...(ownerState.margin === 'dense' && { - marginTop: 8, - marginBottom: 4, - }), - ...(ownerState.fullWidth && { - width: '100%', - }), -})); - -/** - * Provides context such as filled/focused/error/required for form inputs. - * Relying on the context provides high flexibility and ensures that the state always stays - * consistent across the children of the `FormControl`. - * This context is used by the following components: - * - * - FormLabel - * - FormHelperText - * - Input - * - InputLabel - * - * You can find one composition example below and more going to [the demos](/material-ui/react-text-field/#components). - * - * ```jsx - * <FormControl> - * <InputLabel htmlFor="my-input">Email address</InputLabel> - * <Input id="my-input" aria-describedby="my-helper-text" /> - * <FormHelperText id="my-helper-text">We'll never share your email.</FormHelperText> - * </FormControl> - * ``` - * - * ⚠️ Only one `InputBase` can be used within a FormControl because it creates visual inconsistencies. - * For instance, only one input can be focused at the same time, the state shouldn't be shared. - */ -const FormControl = React.forwardRef(function FormControl< - RootComponentType extends React.ElementType = FormControlTypeMap['defaultComponent'], ->(inProps: FormControlProps<RootComponentType>, forwardedRef: React.ForwardedRef<any>) { - const props = useThemeProps({ props: inProps, name: 'MuiFormControl' }); - const { - children, - classes: classesProp = {}, - color = 'primary', - component: componentProp, - disabled = false, - error = false, - focused: visuallyFocused, - fullWidth = false, - hiddenLabel = false, - margin = 'none', - required = false, - size = 'medium', - slotProps = {}, - slots = {}, - variant = 'outlined', - ...other - } = props; - - const [adornedStart, setAdornedStart] = React.useState(() => { - // We need to iterate through the children and find the Input in order - // to fully support server-side rendering. - let initialAdornedStart = false; - - if (children) { - React.Children.forEach(children, (child) => { - if (!isMuiElement(child, ['Input', 'Select'])) { - return; - } - - const input = - React.isValidElement(child) && isMuiElement(child, ['Select']) - ? child.props.input - : child; - - if (input && isAdornedStart(input.props)) { - initialAdornedStart = true; - } - }); - } - return initialAdornedStart; - }); - - const [filled, setFilled] = React.useState(() => { - // We need to iterate through the children and find the Input in order - // to fully support server-side rendering. - let initialFilled = false; - - if (children) { - React.Children.forEach(children, (child) => { - if (!isMuiElement(child, ['Input', 'Select'])) { - return; - } - - if ( - React.isValidElement(child) && - (isFilled(child.props, true) || isFilled(child.props.slotProps?.input, true)) - ) { - initialFilled = true; - } - }); - } - - return initialFilled; - }); - - const [focusedState, setFocused] = React.useState(false); - if (disabled && focusedState) { - setFocused(false); - } - - const focused = visuallyFocused !== undefined && !disabled ? visuallyFocused : focusedState; - - let registerEffect: undefined | (() => () => void); - if (process.env.NODE_ENV !== 'production') { - // eslint-disable-next-line react-hooks/rules-of-hooks - const registeredInput = React.useRef(false); - registerEffect = () => { - if (registeredInput.current) { - console.error( - [ - 'MUI: There are multiple `InputBase` components inside a FormControl.', - 'This creates visual inconsistencies, only use one `InputBase`.', - ].join('\n'), - ); - } - - registeredInput.current = true; - return () => { - registeredInput.current = false; - }; - }; - } - - const ownerState = { - ...props, - classes: classesProp, - color, - component: componentProp, - disabled, - error, - filled, - focused, - fullWidth, - hiddenLabel, - margin, - required, - size, - variant, - }; - - const classes = useUtilityClasses(ownerState); - - const childContext = React.useMemo(() => { - return { - adornedStart, - setAdornedStart, - color, - disabled, - error, - filled, - focused, - fullWidth, - hiddenLabel, - size, - onBlur: () => { - setFocused(false); - }, - onEmpty: () => { - setFilled(false); - }, - onFilled: () => { - setFilled(true); - }, - onFocus: () => { - setFocused(true); - }, - registerEffect, - required, - variant, - }; - }, [ - adornedStart, - color, - disabled, - error, - filled, - focused, - fullWidth, - hiddenLabel, - registerEffect, - required, - size, - variant, - ]); - - const Root = slots.root ?? FormControlRoot; - const rootProps = useSlotProps({ - elementType: Root, - externalSlotProps: slotProps.root, - externalForwardedProps: other, - additionalProps: { - ref: forwardedRef, - as: componentProp, - }, - ownerState, - className: classes.root, - }); - - return ( - <FormControlContext.Provider value={childContext}> - <Root {...rootProps}>{children}</Root> - </FormControlContext.Provider> - ); -}) as OverridableComponent<FormControlTypeMap>; - -FormControl.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, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['primary', 'secondary', 'error', 'info', 'success', 'warning']), - PropTypes.string, - ]), - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * If `true`, the label, input and helper text should be displayed in a disabled state. - * @default false - */ - disabled: PropTypes.bool, - /** - * If `true`, the label is displayed in an error state. - * @default false - */ - error: PropTypes.bool, - /** - * If `true`, the component is displayed in focused state. - */ - focused: PropTypes.bool, - /** - * If `true`, the component will take up the full width of its container. - * @default false - */ - fullWidth: PropTypes.bool, - /** - * If `true`, the label is hidden. - * This is used to increase density for a `FilledInput`. - * Be sure to add `aria-label` to the `input` element. - * @default false - */ - hiddenLabel: PropTypes.bool, - /** - * If `dense` or `normal`, will adjust vertical spacing of this and contained components. - * @default 'none' - */ - margin: PropTypes.oneOf(['dense', 'none', 'normal']), - /** - * If `true`, the label will indicate that the `input` is required. - * @default false - */ - required: PropTypes.bool, - /** - * The size of the component. - * @default 'medium' - */ - size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['medium', 'small']), - PropTypes.string, - ]), - /** - * The props used for each slot inside the FormControl. - * @default {} - */ - slotProps: PropTypes.shape({ - root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - }), - /** - * The components used for each slot inside the FormControl. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots: PropTypes.shape({ - root: PropTypes.elementType, - }), - /** - * 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 variant to use. - * @default 'outlined' - */ - variant: PropTypes.oneOf(['filled', 'outlined']), -} as any; - -export default FormControl; diff --git a/packages/mui-material-next/src/FormControl/FormControl.types.ts b/packages/mui-material-next/src/FormControl/FormControl.types.ts deleted file mode 100644 index 272ca958a160f9..00000000000000 --- a/packages/mui-material-next/src/FormControl/FormControl.types.ts +++ /dev/null @@ -1,93 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { FormControlOwnProps as BaseFormControlOwnProps } from '@mui/base/FormControl'; -import { OverridableStringUnion, OverrideProps, Simplify } from '@mui/types'; -import { Theme } from '../styles'; -import { FormControlClasses } from './formControlClasses'; - -export interface FormControlPropsSizeOverrides {} -export interface FormControlPropsColorOverrides {} - -export interface FormControlOwnProps extends BaseFormControlOwnProps { - /** - * The content of the component. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<FormControlClasses>; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color?: OverridableStringUnion< - 'primary' | 'secondary' | 'tertiary' | 'error' | 'info' | 'success' | 'warning', - FormControlPropsColorOverrides - >; - /** - * If `true`, the component will take up the full width of its container. - * @default false - */ - fullWidth?: boolean; - /** - * If `true`, the component is displayed in focused state. - */ - focused?: boolean; - /** - * If `true`, the label is hidden. - * This is used to increase density for a `FilledInput`. - * Be sure to add `aria-label` to the `input` element. - * @default false - */ - hiddenLabel?: boolean; - /** - * If `dense` or `normal`, will adjust vertical spacing of this and contained components. - * @default 'none' - */ - margin?: 'dense' | 'normal' | 'none'; - /** - * The size of the component. - * @default 'medium' - */ - size?: OverridableStringUnion<'small' | 'medium', FormControlPropsSizeOverrides>; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; - /** - * The variant to use. - * @default 'outlined' - */ - variant?: 'outlined' | 'filled'; -} - -export interface FormControlTypeMap< - AdditionalProps = {}, - RootComponentType extends React.ElementType = 'div', -> { - props: FormControlOwnProps & AdditionalProps; - defaultComponent: RootComponentType; -} - -export type FormControlProps< - RootComponentType extends React.ElementType = FormControlTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<FormControlTypeMap<AdditionalProps, RootComponentType>, RootComponentType> & { - component?: React.ElementType; -}; - -type MaterialDesignOwnerStateKeys = - | 'classes' - | 'color' - | 'margin' - | 'size' - | 'fullWidth' - | 'hiddenLabel' - | 'variant'; - -export type FormControlOwnerState = Simplify< - Required<Pick<FormControlOwnProps, MaterialDesignOwnerStateKeys>> & FormControlProps ->; diff --git a/packages/mui-material-next/src/FormControl/FormControlContext.ts b/packages/mui-material-next/src/FormControl/FormControlContext.ts deleted file mode 100644 index 16cb4e9c83ca55..00000000000000 --- a/packages/mui-material-next/src/FormControl/FormControlContext.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as React from 'react'; -import { FormControlProps } from './FormControl.types'; - -type ContextFromPropsKey = - | 'color' - | 'disabled' - | 'error' - | 'fullWidth' - | 'hiddenLabel' - | 'margin' - | 'required' - | 'size' - | 'variant'; - -export interface FormControlContextValue extends Pick<FormControlProps, ContextFromPropsKey> { - adornedStart: boolean; - filled: boolean; - focused: boolean; - onBlur: (event?: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void; - onFocus: (event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void; - onEmpty: () => void; - onFilled: () => void; - registerEffect: undefined | (() => () => void); - setAdornedStart: React.Dispatch<React.SetStateAction<boolean>>; -} - -/** - * @internal - */ -const FormControlContext = React.createContext<FormControlContextValue | undefined>(undefined); - -if (process.env.NODE_ENV !== 'production') { - FormControlContext.displayName = 'FormControlContext'; -} - -export default FormControlContext; diff --git a/packages/mui-material-next/src/FormControl/formControlClasses.ts b/packages/mui-material-next/src/FormControl/formControlClasses.ts deleted file mode 100644 index adcdd4ff8cfd16..00000000000000 --- a/packages/mui-material-next/src/FormControl/formControlClasses.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { - unstable_generateUtilityClass as generateUtilityClass, - unstable_generateUtilityClasses as generateUtilityClasses, -} from '@mui/utils'; - -export interface FormControlClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `margin="normal"`. */ - marginNormal: string; - /** Styles applied to the root element if `margin="dense"`. */ - marginDense: string; - /** Styles applied to the root element if `fullWidth={true}`. */ - fullWidth: string; -} - -export type FormControlClassKey = keyof FormControlClasses; - -export function getFormControlUtilityClasses(slot: string): string { - return generateUtilityClass('MuiFormControl', slot); -} - -const formControlClasses: FormControlClasses = generateUtilityClasses('MuiFormControl', [ - 'root', - 'marginNone', - 'marginNormal', - 'marginDense', - 'fullWidth', - 'disabled', -]); - -export default formControlClasses; diff --git a/packages/mui-material-next/src/FormControl/formControlState.js b/packages/mui-material-next/src/FormControl/formControlState.js deleted file mode 100644 index 86c999f26e24c6..00000000000000 --- a/packages/mui-material-next/src/FormControl/formControlState.js +++ /dev/null @@ -1,15 +0,0 @@ -// TODO v6: decide whether to update/refactor this, keep as-is, or drop it -export default function formControlState({ props, states, muiFormControl }) { - // for every prop in `states` that is undefined, set it with the value from formControlContext - return states.reduce((acc, state) => { - acc[state] = props[state]; - - if (muiFormControl) { - if (typeof props[state] === 'undefined') { - acc[state] = muiFormControl[state]; - } - } - - return acc; - }, {}); -} diff --git a/packages/mui-material-next/src/FormControl/index.ts b/packages/mui-material-next/src/FormControl/index.ts deleted file mode 100644 index 4112e5a979f5f1..00000000000000 --- a/packages/mui-material-next/src/FormControl/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -'use client'; -export { default } from './FormControl'; -export { default as useFormControl } from './useFormControl'; - -export { default as formControlClasses } from './formControlClasses'; -export * from './formControlClasses'; diff --git a/packages/mui-material-next/src/FormControl/useFormControl.ts b/packages/mui-material-next/src/FormControl/useFormControl.ts deleted file mode 100644 index 8cf6ac69fbce7f..00000000000000 --- a/packages/mui-material-next/src/FormControl/useFormControl.ts +++ /dev/null @@ -1,7 +0,0 @@ -'use client'; -import * as React from 'react'; -import FormControlContext, { FormControlContextValue } from './FormControlContext'; - -export default function useFormControl(): FormControlContextValue | undefined { - return React.useContext(FormControlContext); -} diff --git a/packages/mui-material-next/src/FormHelperText/FormHelperText.spec.tsx b/packages/mui-material-next/src/FormHelperText/FormHelperText.spec.tsx deleted file mode 100644 index 02e801acbf0683..00000000000000 --- a/packages/mui-material-next/src/FormHelperText/FormHelperText.spec.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import * as React from 'react'; -import { expectType } from '@mui/types'; -import FormHelperText from '@mui/material-next/FormHelperText'; -import { FormHelperTextProps } from './FormHelperText.types'; - -const CustomComponent: React.FC<{ stringProp: string; numberProp: number }> = - function CustomComponent() { - return <div />; - }; - -const props1: FormHelperTextProps<'div'> = { - component: 'div', - onChange: (event) => { - expectType<React.FormEvent<HTMLDivElement>, typeof event>(event); - }, -}; - -const props2: FormHelperTextProps = { - onChange: (event) => { - expectType<React.FormEvent<HTMLParagraphElement>, typeof event>(event); - }, -}; - -const props3: FormHelperTextProps<typeof CustomComponent> = { - component: CustomComponent, - stringProp: '2', - numberProp: 2, -}; - -const props4: FormHelperTextProps<typeof CustomComponent> = { - component: CustomComponent, - stringProp: '2', - numberProp: 2, - // @ts-expect-error CustomComponent does not accept incorrectProp - incorrectProp: 3, -}; - -// @ts-expect-error missing props -const props5: FormHelperTextProps<typeof CustomComponent> = { - component: CustomComponent, -}; - -const TestComponent = () => { - return ( - <React.Fragment> - <FormHelperText /> - <FormHelperText component={'a'} href="/test" /> - - <FormHelperText component={CustomComponent} stringProp="s" numberProp={1} /> - { - // @ts-expect-error missing props - <FormHelperText component={CustomComponent} /> - } - <FormHelperText - component="span" - onChange={(event) => { - expectType<React.FormEvent<HTMLSpanElement>, typeof event>(event); - }} - /> - </React.Fragment> - ); -}; diff --git a/packages/mui-material-next/src/FormHelperText/FormHelperText.test.tsx b/packages/mui-material-next/src/FormHelperText/FormHelperText.test.tsx deleted file mode 100644 index 06830cec394053..00000000000000 --- a/packages/mui-material-next/src/FormHelperText/FormHelperText.test.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -import FormHelperText, { - formHelperTextClasses as classes, - FormHelperTextClasses, -} from '@mui/material-next/FormHelperText'; -import FormControl from '@mui/material-next/FormControl'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import describeConformance from '../../test/describeConformance'; - -describe('<FormHelperText />', () => { - let originalMatchmedia: typeof window.matchMedia; - - beforeEach(() => { - originalMatchmedia = window.matchMedia; - window.matchMedia = () => - ({ - addListener: () => {}, - removeListener: () => {}, - }) as unknown as MediaQueryList; - }); - - afterEach(() => { - window.matchMedia = originalMatchmedia; - }); - - const { render } = createRenderer(); - - describeConformance(<FormHelperText />, () => ({ - classes, - inheritComponent: 'p', - render, - refInstanceof: window.HTMLParagraphElement, - testComponentPropWith: 'div', - muiName: 'MuiFormHelperText', - testVariantProps: { size: 'small' }, - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - slots: { - root: { - expectedClassName: classes.root, - }, - }, - skip: ['componentsProp'], - })); - - describe('prop: error', () => { - it('should have an error class', () => { - const { container } = render(<FormHelperText error />); - expect(container.firstChild).to.have.class(classes.error); - }); - }); - - describe('with FormControl', () => { - ['error', 'disabled'].forEach((visualState) => { - describe(visualState, () => { - function FormHelperTextInFormControl(props: { children: React.ReactNode }) { - return ( - <FormControl {...{ [visualState]: true }}> - <FormHelperText {...props}>Foo</FormHelperText> - </FormControl> - ); - } - - it(`should have the ${visualState} class`, () => { - const { getByText } = render( - <FormHelperTextInFormControl>Foo</FormHelperTextInFormControl>, - ); - - expect(getByText(/Foo/)).to.have.class( - classes[visualState as keyof FormHelperTextClasses], - ); - }); - - it('should be overrideable by props', () => { - const { getByText, setProps } = render( - <FormHelperTextInFormControl {...{ [visualState]: false }}> - Foo - </FormHelperTextInFormControl>, - ); - - expect(getByText(/Foo/)).not.to.have.class( - classes[visualState as keyof FormHelperTextClasses], - ); - - setProps({ [visualState]: true }); - expect(getByText(/Foo/)).to.have.class( - classes[visualState as keyof FormHelperTextClasses], - ); - }); - }); - }); - - describe('size', () => { - describe('small margin FormControl', () => { - it('should have the small class', () => { - const { getByText } = render( - <FormControl size="small"> - <FormHelperText>Foo</FormHelperText> - </FormControl>, - ); - - expect(getByText(/Foo/)).to.have.class(classes.sizeSmall); - }); - }); - - it('should be overrideable by props', () => { - function FormHelperTextInFormControl(props: { children: React.ReactNode }) { - return ( - <FormControl size="medium"> - <FormHelperText {...props}>Foo</FormHelperText> - </FormControl> - ); - } - - const { getByText, setProps } = render( - <FormHelperTextInFormControl>Foo</FormHelperTextInFormControl>, - ); - - expect(getByText(/Foo/)).not.to.have.class(classes.sizeSmall); - setProps({ size: 'small' }); - expect(getByText(/Foo/)).to.have.class(classes.sizeSmall); - }); - }); - }); -}); diff --git a/packages/mui-material-next/src/FormHelperText/FormHelperText.tsx b/packages/mui-material-next/src/FormHelperText/FormHelperText.tsx deleted file mode 100644 index 3f63eb73d17947..00000000000000 --- a/packages/mui-material-next/src/FormHelperText/FormHelperText.tsx +++ /dev/null @@ -1,235 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { unstable_composeClasses as composeClasses, useSlotProps } from '@mui/base'; -import { OverridableComponent } from '@mui/types'; -import { unstable_capitalize as capitalize } from '@mui/utils'; -import { useThemeProps, styled } from '../styles'; -import useFormControl from '../FormControl/useFormControl'; -import formHelperTextClasses, { getFormHelperTextUtilityClasses } from './formHelperTextClasses'; -import { - FormHelperTextOwnerState, - FormHelperTextProps, - FormHelperTextTypeMap, -} from './FormHelperText.types'; - -const useUtilityClasses = (ownerState: FormHelperTextOwnerState) => { - const { classes, contained, size, disabled, error, filled, focused, required } = ownerState; - const slots = { - root: [ - 'root', - disabled && 'disabled', - error && 'error', - size && `size${capitalize(size)}`, - contained && 'contained', - focused && 'focused', - filled && 'filled', - required && 'required', - ], - }; - - return composeClasses(slots, getFormHelperTextUtilityClasses, classes); -}; - -const FormHelperTextRoot = styled('p', { - name: 'MuiFormHelperText', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - ownerState.size && styles[`size${capitalize(ownerState.size)}`], - ownerState.contained && styles.contained, - ownerState.filled && styles.filled, - ]; - }, -})<{ ownerState: FormHelperTextOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - const pxFontSize = theme.sys.typescale.body.small.size; - const lineHeight = `calc(${tokens.sys.typescale.body.small.lineHeight} / ${pxFontSize})`; - - return { - '--md-comp-form-helper-text-color': tokens.sys.color.onSurfaceVariant, - '--md-comp-form-helper-text-font-family': tokens.sys.typescale.body.small.family, - '--md-comp-form-helper-text-font-size': theme.typography.pxToRem(pxFontSize), // the pxToRem should be moved to typescale in the future, - '--md-comp-form-helper-text-font-weight': tokens.sys.typescale.body.small.weight, - '--md-comp-form-helper-text-line-height': lineHeight, - '--md-comp-form-helper-text-disabled-color': tokens.sys.color.onSurface, - '--md-comp-form-helper-text-disabled-opacity': 0.38, - '--md-comp-form-helper-text-error-color': tokens.sys.color.error, - color: 'var(--md-comp-form-helper-text-color)', - fontFamily: 'var(--md-comp-form-helper-text-font-family)', - fontSize: 'var(--md-comp-form-helper-text-font-size)', - lineHeight: `var(--md-comp-form-helper-text-line-height)`, - textAlign: 'left', - marginTop: 3, - marginRight: 0, - marginBottom: 0, - marginLeft: 0, - [`&.${formHelperTextClasses.disabled}`]: { - color: - 'color-mix(in srgb, var(--md-comp-form-helper-text-disabled-color) calc(var(--md-comp-form-helper-text-disabled-opacity) * 100%), transparent)', - }, - [`&.${formHelperTextClasses.error}`]: { - color: 'var(--md-comp-form-helper-text-error-color)', - }, - ...(ownerState.size === 'small' && { - marginTop: 4, - }), - ...(ownerState.contained && { - marginLeft: 16, - marginRight: 16, - }), - }; -}); - -const FormHelperText = React.forwardRef(function FormHelperText< - RootComponentType extends React.ElementType = FormHelperTextTypeMap['defaultComponent'], ->(inProps: FormHelperTextProps<RootComponentType>, forwardedRef: React.ForwardedRef<Element>) { - const props = useThemeProps({ props: inProps, name: 'MuiFormHelperText' }); - const { - children, - component = 'p', - disabled: disabledProp, - error: errorProp, - filled: filledProp, - focused: focusedProp, - margin, - required: requiredProp, - size: sizeProp, - variant: variantProp, - slots = {}, - slotProps = {}, - ...other - } = props; - - const muiFormControl = useFormControl(); - - const variant = variantProp ?? muiFormControl?.variant ?? ''; - - const ownerState: FormHelperTextOwnerState = { - ...props, - component, - contained: variant === 'filled' || variant === 'outlined', - variant, - size: sizeProp ?? muiFormControl?.size, - disabled: disabledProp ?? muiFormControl?.disabled, - error: errorProp ?? muiFormControl?.error, - filled: filledProp ?? muiFormControl?.filled, - focused: focusedProp ?? muiFormControl?.focused, - required: requiredProp ?? muiFormControl?.required, - }; - - const classes = useUtilityClasses(ownerState); - - const RootSlot = slots?.root ?? FormHelperTextRoot; - - const rootProps = useSlotProps({ - elementType: RootSlot, - externalSlotProps: slotProps.root, - externalForwardedProps: other, - additionalProps: { - as: component, - ref: forwardedRef, - }, - ownerState, - className: [classes.root], - }); - - return ( - <RootSlot {...rootProps}> - {children === ' ' ? ( - // notranslate needed while Google Translate will not fix zero-width space issue - <span className="notranslate">​</span> - ) : ( - children - )} - </RootSlot> - ); -}) as OverridableComponent<FormHelperTextTypeMap>; - -FormHelperText.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. - * - * If `' '` is provided, the component reserves one line height for displaying a future message. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * If `true`, the helper text should be displayed in a disabled state. - */ - disabled: PropTypes.bool, - /** - * If `true`, helper text should be displayed in an error state. - */ - error: PropTypes.bool, - /** - * If `true`, the helper text should use filled classes key. - */ - filled: PropTypes.bool, - /** - * If `true`, the helper text should use focused classes key. - */ - focused: PropTypes.bool, - /** - * If `dense`, will adjust vertical spacing. This is normally obtained via context from - * FormControl. - */ - margin: PropTypes.oneOf(['dense']), - /** - * If `true`, the helper text should use required classes key. - */ - required: PropTypes.bool, - /** - * The props used for each slot inside the FormHelperText. - * @default {} - */ - slotProps: PropTypes.shape({ - root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - }), - /** - * The components used for each slot inside the FormHelperText. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots: PropTypes.shape({ - root: PropTypes.elementType, - }), - /** - * 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 variant to use. - * @default 'outlined' - */ - variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['filled', 'outlined', 'standard']), - PropTypes.string, - ]), -} as any; - -export default FormHelperText; diff --git a/packages/mui-material-next/src/FormHelperText/FormHelperText.types.ts b/packages/mui-material-next/src/FormHelperText/FormHelperText.types.ts deleted file mode 100644 index 81dce8e3fa70aa..00000000000000 --- a/packages/mui-material-next/src/FormHelperText/FormHelperText.types.ts +++ /dev/null @@ -1,102 +0,0 @@ -import * as React from 'react'; -import { SlotComponentProps } from '@mui/base'; -import { SxProps } from '@mui/system'; -import { OverrideProps, OverridableStringUnion } from '@mui/types'; -import { Theme } from '../styles'; -import { FormControlProps } from '../FormControl/FormControl.types'; -import { FormHelperTextClasses } from './formHelperTextClasses'; - -export interface FormHelperTextPropsVariantOverrides {} - -export interface FormHelperTextOwnProps { - /** - * The content of the component. - * - * If `' '` is provided, the component reserves one line height for displaying a future message. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<FormHelperTextClasses>; - /** - * @ignore - */ - className?: string; - /** - * If `true`, the helper text should be displayed in a disabled state. - */ - disabled?: boolean; - /** - * If `true`, helper text should be displayed in an error state. - */ - error?: boolean; - /** - * If `true`, the helper text should use filled classes key. - */ - filled?: boolean; - /** - * If `true`, the helper text should use focused classes key. - */ - focused?: boolean; - /** - * If `dense`, will adjust vertical spacing. This is normally obtained via context from - * FormControl. - */ - margin?: 'dense'; - /** - * If `true`, the helper text should use required classes key. - */ - required?: boolean; - /** - * The props used for each slot inside the FormHelperText. - * @default {} - */ - slotProps?: { - root?: SlotComponentProps<'p', {}, FormHelperTextOwnerState>; - }; - /** - * The components used for each slot inside the FormHelperText. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots?: FormHelperTextSlots; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; - /** - * The variant to use. - * @default 'outlined' - */ - variant?: OverridableStringUnion<'outlined' | 'filled', FormHelperTextPropsVariantOverrides>; -} - -export interface FormHelperTextSlots { - /** - * The component that renders the root. - * @default 'p' - */ - root?: React.ElementType; -} - -export interface FormHelperTextTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'p', -> { - props: AdditionalProps & FormHelperTextOwnProps; - defaultComponent: RootComponent; -} - -export type FormHelperTextProps< - RootComponent extends React.ElementType = FormHelperTextTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<FormHelperTextTypeMap<AdditionalProps, RootComponent>, RootComponent> & { - component?: React.ElementType; -}; - -export interface FormHelperTextOwnerState extends FormHelperTextProps { - contained: boolean; - size: NonNullable<FormControlProps['size']>; - variant: NonNullable<FormControlProps['variant']>; -} diff --git a/packages/mui-material-next/src/FormHelperText/formHelperTextClasses.ts b/packages/mui-material-next/src/FormHelperText/formHelperTextClasses.ts deleted file mode 100644 index 4b9afb25838503..00000000000000 --- a/packages/mui-material-next/src/FormHelperText/formHelperTextClasses.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface FormHelperTextClasses { - /** Styles applied to the root element. */ - root: string; - /** State class applied to the root element if `error={true}`. */ - error: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; - /** Styles applied to the root element if `size="small"`. */ - sizeSmall: string; - /** Styles applied to the root element if `variant="filled"` or `variant="outlined"`. */ - contained: string; - /** State class applied to the root element if `focused={true}`. */ - focused: string; - /** State class applied to the root element if `filled={true}`. */ - filled: string; - /** State class applied to the root element if `required={true}`. */ - required: string; -} - -export type FormHelperTextClassKey = keyof FormHelperTextClasses; - -export function getFormHelperTextUtilityClasses(slot: string): string { - return generateUtilityClass('MuiFormHelperText', slot); -} - -const formHelperTextClasses: FormHelperTextClasses = generateUtilityClasses('MuiFormHelperText', [ - 'root', - 'error', - 'disabled', - 'sizeSmall', - 'sizeMedium', - 'contained', - 'focused', - 'filled', - 'required', -]); - -export default formHelperTextClasses; diff --git a/packages/mui-material-next/src/FormHelperText/index.ts b/packages/mui-material-next/src/FormHelperText/index.ts deleted file mode 100644 index e2e5b0a06e3444..00000000000000 --- a/packages/mui-material-next/src/FormHelperText/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './FormHelperText'; - -export { default as formHelperTextClasses } from './formHelperTextClasses'; -export * from './formHelperTextClasses'; diff --git a/packages/mui-material-next/src/FormLabel/FormLabel.test.tsx b/packages/mui-material-next/src/FormLabel/FormLabel.test.tsx deleted file mode 100644 index 4abf9cdc3f5291..00000000000000 --- a/packages/mui-material-next/src/FormLabel/FormLabel.test.tsx +++ /dev/null @@ -1,226 +0,0 @@ -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { expect } from 'chai'; -import { act, createRenderer } from '@mui-internal/test-utils'; -import FormLabel, { formLabelClasses as classes } from '@mui/material-next/FormLabel'; -import FormControl, { useFormControl } from '@mui/material-next/FormControl'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import describeConformance from '../../test/describeConformance'; - -describe('<FormLabel />', () => { - let originalMatchmedia: typeof window.matchMedia; - - beforeEach(() => { - originalMatchmedia = window.matchMedia; - window.matchMedia = () => - ({ - addListener: () => {}, - removeListener: () => {}, - }) as unknown as MediaQueryList; - }); - - afterEach(() => { - window.matchMedia = originalMatchmedia; - }); - - const { render } = createRenderer(); - - describeConformance(<FormLabel />, () => ({ - classes, - inheritComponent: 'label', - render, - refInstanceof: window.HTMLLabelElement, - testComponentPropWith: 'div', - muiName: 'MuiFormLabel', - testVariantProps: { color: 'secondary' }, - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - slots: { - root: { - expectedClassName: classes.root, - }, - }, - skip: ['componentsProp'], - })); - - describe('prop: required', () => { - it('should visually show an asterisk but not include it in the a11y tree', () => { - const { container } = render(<FormLabel required>name</FormLabel>); - - expect(container.querySelector('label')).to.have.text('name\u2009*'); - expect(container.querySelectorAll(`.${classes.asterisk}`)).to.have.lengthOf(1); - expect(container.querySelectorAll(`.${classes.asterisk}`)[0]).toBeAriaHidden(); - }); - - it('should not show an asterisk by default', () => { - const { container } = render(<FormLabel>name</FormLabel>); - - expect(container.querySelector('label')).to.have.text('name'); - expect(container.querySelectorAll(`.${classes.asterisk}`)).to.have.lengthOf(0); - }); - }); - - describe('prop: error', () => { - it('should have an error class', () => { - const { container } = render(<FormLabel required error />); - - expect(container.querySelectorAll(`.${classes.asterisk}`)).to.have.lengthOf(1); - expect(container.querySelector(`.${classes.asterisk}`)).to.have.class(classes.error); - expect(container.querySelectorAll(`.${classes.asterisk}`)[0]).toBeAriaHidden(); - expect(container.firstChild).to.have.class(classes.error); - }); - }); - - describe('with FormControl', () => { - describe('error', () => { - function Wrapper(props: { children?: React.ReactNode }) { - return <FormControl error {...props} />; - } - - it(`should have the error class`, () => { - const { container } = render(<FormLabel />, { - wrapper: Wrapper, - }); - - expect(container.querySelector('label')).to.have.class(classes.error); - }); - - it('should be overridden by props', () => { - const { container, setProps } = render( - <FormLabel data-testid="FormLabel" error={false} />, - { - wrapper: Wrapper, - }, - ); - - expect(container.querySelector('label')).not.to.have.class(classes.error); - - setProps({ error: true }); - expect(container.querySelector('label')).to.have.class(classes.error); - }); - }); - - describe('focused', () => { - const FormController = React.forwardRef((_, ref) => { - const formControl = useFormControl(); - React.useImperativeHandle(ref, () => formControl, [formControl]); - return null; - }); - - it(`should have the focused class`, () => { - const formControlRef = React.createRef<{ onFocus: () => void }>(); - const { container } = render( - <FormControl error> - <FormLabel data-testid="FormLabel" /> - <FormController ref={formControlRef} /> - </FormControl>, - ); - - expect(container.querySelector('label')).not.to.have.class(classes.focused); - - act(() => { - formControlRef.current?.onFocus(); - }); - expect(container.querySelector('label')).to.have.class(classes.focused); - }); - - it('should be overridden by props', () => { - const formControlRef = React.createRef<{ onFocus: () => void }>(); - function Wrapper({ children }: { children?: React.ReactNode }) { - return ( - <FormControl error> - {children} - <FormController ref={formControlRef} /> - </FormControl> - ); - } - Wrapper.propTypes = { children: PropTypes.node }; - const { container, setProps } = render(<FormLabel data-testid="FormLabel" />, { - wrapper: Wrapper, - }); - act(() => { - formControlRef.current?.onFocus(); - }); - - expect(container.querySelector('label')).to.have.class(classes.focused); - - setProps({ focused: false }); - expect(container.querySelector('label')).not.to.have.class(classes.focused); - - setProps({ focused: true }); - expect(container.querySelector('label')).to.have.class(classes.focused); - }); - }); - - describe('required', () => { - it('should show an asterisk', () => { - const { container } = render( - <FormControl required> - <FormLabel>name</FormLabel> - </FormControl>, - ); - - expect(container).to.have.text('name\u2009*'); - }); - - it('should be overridden by props', () => { - const { container, setProps } = render(<FormLabel required={false}>name</FormLabel>, { - wrapper: (props) => <FormControl required {...props} />, - }); - - expect(container).to.have.text('name'); - - setProps({ required: true }); - expect(container).to.have.text('name\u2009*'); - }); - }); - }); - - const theme = extendTheme({ - components: { - MuiFormLabel: { - styleOverrides: { - root: { - [`&.${classes.focused}`]: { - mixBlendMode: 'darken', - }, - [`&.${classes.error}`]: { - mixBlendMode: 'lighten', - }, - }, - }, - }, - }, - }); - - describe('prop: color', () => { - it('should have color secondary class', () => { - const { container } = render(<FormLabel color="secondary" />); - expect(container.firstChild).to.have.class(classes.colorSecondary); - }); - - it('should have the focused class and style', () => { - const { container, getByTestId } = render( - <CssVarsProvider theme={theme}> - <FormLabel data-testid="FormLabel" color="secondary" focused /> - </CssVarsProvider>, - ); - expect(container.querySelector(`.${classes.colorSecondary}`)).to.have.class(classes.focused); - expect(getByTestId('FormLabel')).toHaveComputedStyle({ - mixBlendMode: 'darken', - }); - }); - - it('should have the error class and style, even when focused', () => { - const { container, getByTestId } = render( - <CssVarsProvider theme={theme}> - <FormLabel data-testid="FormLabel" color="secondary" focused error /> - </CssVarsProvider>, - ); - expect(container.querySelector(`.${classes.colorSecondary}`)).to.have.class(classes.error); - expect(getByTestId('FormLabel')).toHaveComputedStyle({ - mixBlendMode: 'lighten', - }); - }); - }); -}); diff --git a/packages/mui-material-next/src/FormLabel/FormLabel.tsx b/packages/mui-material-next/src/FormLabel/FormLabel.tsx deleted file mode 100644 index 68e7048c2c4c13..00000000000000 --- a/packages/mui-material-next/src/FormLabel/FormLabel.tsx +++ /dev/null @@ -1,225 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { unstable_composeClasses as composeClasses, useSlotProps } from '@mui/base'; -import { OverridableComponent } from '@mui/types'; -import { unstable_capitalize as capitalize } from '@mui/utils'; -import { useThemeProps, styled } from '../styles'; -import useFormControl from '../FormControl/useFormControl'; -import formLabelClasses, { getFormLabelUtilityClasses } from './formLabelClasses'; -import { FormLabelProps, FormLabelTypeMap, FormLabelOwnerState } from './FormLabel.types'; - -const useUtilityClasses = (ownerState: FormLabelOwnerState) => { - const { classes, color, focused, disabled, error, filled, required } = ownerState; - const slots = { - root: [ - 'root', - `color${capitalize(color)}`, - disabled && 'disabled', - error && 'error', - filled && 'filled', - focused && 'focused', - required && 'required', - ], - asterisk: ['asterisk', error && 'error'], - }; - - return composeClasses(slots, getFormLabelUtilityClasses, classes); -}; - -export const FormLabelRoot = styled('label', { - name: 'MuiFormLabel', - slot: 'Root', - overridesResolver: ({ ownerState }, styles) => { - return [ - styles.root, - ownerState.color === 'secondary' && styles.colorSecondary, - ownerState.filled && styles.filled, - ]; - }, -})<{ ownerState: FormLabelOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - const pxFontSize = tokens.sys.typescale.body.large.size; - - const letterSpacing = `${theme.sys.typescale.body.large.tracking / pxFontSize}rem`; - - return { - '--md-comp-form-label-color': tokens.sys.color.secondary, - '--md-comp-form-label-font-family': tokens.sys.typescale.body.large.family, - '--md-comp-form-label-font-size': theme.typography.pxToRem(pxFontSize), // the pxToRem should be moved to typescale in the future, - '--md-comp-form-label-font-weight': tokens.sys.typescale.body.large.weight, - '--md-comp-form-label-letter-spacing': letterSpacing, - '--md-comp-form-label-line-height': '1.5rem', - '--md-comp-form-label-disabled-color': tokens.sys.color.onSurface, - '--md-comp-form-label-disabled-opacity': 0.38, - '--md-comp-form-label-error-color': tokens.sys.color.error, - '--md-comp-form-label-focus-color': tokens.sys.color[ownerState.color], - color: 'var(--md-comp-form-label-color)', - fontFamily: 'var(--md-comp-form-label-font-family)', - fontSize: 'var(--md-comp-form-label-font-size)', - fontWeight: 'var(--md-comp-form-label-font-weight)', - lineHeight: 'var(--md-comp-form-label-line-height)', - padding: 0, - position: 'relative', - [`&.${formLabelClasses.focused}`]: { - color: 'var(--md-comp-form-label-focus-color)', - }, - [`&.${formLabelClasses.disabled}`]: { - color: - 'color-mix(in srgb, var(--md-comp-form-label-disabled-color) calc(var(--md-comp-form-label-disabled-opacity) * 100%), transparent)', - }, - [`&.${formLabelClasses.error}`]: { - color: 'var(--md-comp-form-label-error-color)', - }, - }; -}); - -const AsteriskComponent = styled('span', { - name: 'MuiFormLabel', - slot: 'Asterisk', - overridesResolver: (_props, styles) => styles.asterisk, -})<{ ownerState: FormLabelOwnerState }>(() => ({ - [`&.${formLabelClasses.error}`]: { - color: 'var(--md-comp-form-label-error-color)', - }, -})); - -const FormLabel = React.forwardRef(function FormLabel< - RootComponentType extends React.ElementType = FormLabelTypeMap['defaultComponent'], ->(inProps: FormLabelProps<RootComponentType>, forwardedRef: React.ForwardedRef<Element>) { - const props = useThemeProps({ props: inProps, name: 'MuiFormLabel' }); - const { - children, - color: colorProp = 'primary', - component = 'label', - disabled: disabledProp, - error: errorProp, - filled: filledProp, - focused: focusedProp, - required: requiredProp, - slots = {}, - slotProps = {}, - ...other - } = props; - - const muiFormControl = useFormControl(); - - const required = requiredProp ?? muiFormControl?.required; - - const ownerState: FormLabelOwnerState = { - ...props, - color: muiFormControl?.color ?? colorProp, - component, - disabled: muiFormControl?.disabled ?? disabledProp, - error: errorProp ?? muiFormControl?.error, - filled: muiFormControl?.filled ?? filledProp, - focused: focusedProp ?? muiFormControl?.focused, - required, - }; - - const classes = useUtilityClasses(ownerState); - - const RootSlot = slots?.root ?? FormLabelRoot; - - const rootProps = useSlotProps({ - elementType: RootSlot, - externalSlotProps: slotProps.root, - externalForwardedProps: other, - additionalProps: { - as: component, - ref: forwardedRef, - }, - ownerState, - className: [classes.root], - }); - - return ( - <RootSlot {...rootProps}> - {children} - {required && ( - <AsteriskComponent ownerState={ownerState} aria-hidden className={classes.asterisk}> -  {'*'} - </AsteriskComponent> - )} - </RootSlot> - ); -}) as OverridableComponent<FormLabelTypeMap>; - -FormLabel.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, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['error', 'info', 'primary', 'secondary', 'tertiary', 'success', 'warning']), - PropTypes.string, - ]), - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * If `true`, the label should be displayed in a disabled state. - */ - disabled: PropTypes.bool, - /** - * If `true`, the label is displayed in an error state. - */ - error: PropTypes.bool, - /** - * If `true`, the label should use filled classes key. - */ - filled: PropTypes.bool, - /** - * If `true`, the input of this label is focused (used by `FormGroup` components). - */ - focused: PropTypes.bool, - /** - * If `true`, the label will indicate that the `input` is required. - */ - required: PropTypes.bool, - /** - * The props used for each slot inside the FormLabel. - * @default {} - */ - slotProps: PropTypes.shape({ - root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - }), - /** - * The components used for each slot inside the FormLabel. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots: PropTypes.shape({ - root: PropTypes.elementType, - }), - /** - * 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 FormLabel; diff --git a/packages/mui-material-next/src/FormLabel/FormLabel.types.ts b/packages/mui-material-next/src/FormLabel/FormLabel.types.ts deleted file mode 100644 index 13f6df5f685f68..00000000000000 --- a/packages/mui-material-next/src/FormLabel/FormLabel.types.ts +++ /dev/null @@ -1,101 +0,0 @@ -import * as React from 'react'; -import { SlotComponentProps } from '@mui/base'; -import { SxProps } from '@mui/system'; -import { OverridableStringUnion, OverrideProps, OverridableTypeMap } from '@mui/types'; -import { Theme } from '../styles'; -import { FormLabelClasses } from './formLabelClasses'; - -export interface FormLabelPropsColorOverrides {} - -export interface FormLabelOwnProps { - /** - * The content of the component. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<FormLabelClasses>; - /** - * @ignore - */ - className?: string; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - */ - color?: OverridableStringUnion< - 'primary' | 'secondary' | 'tertiary' | 'error' | 'info' | 'warning' | 'success', - FormLabelPropsColorOverrides - >; - /** - * If `true`, the label should be displayed in a disabled state. - */ - disabled?: boolean; - /** - * If `true`, the label is displayed in an error state. - */ - error?: boolean; - /** - * If `true`, the label should use filled classes key. - */ - filled?: boolean; - /** - * If `true`, the input of this label is focused (used by `FormGroup` components). - */ - focused?: boolean; - /** - * If `true`, the label will indicate that the `input` is required. - */ - required?: boolean; - /** - * The props used for each slot inside the FormLabel. - * @default {} - */ - slotProps?: { - root?: SlotComponentProps<'label', {}, FormLabelOwnerState>; - }; - /** - * The components used for each slot inside the FormLabel. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots?: FormLabelSlots; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -export interface FormLabelSlots { - /** - * The component that renders the root. - * @default 'span' - */ - root?: React.ElementType; -} - -export interface FormLabelTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'label', -> { - props: AdditionalProps & FormLabelOwnProps; - defaultComponent: RootComponent; -} - -export interface ExtendFormLabelTypeMap<TypeMap extends OverridableTypeMap> { - props: TypeMap['props'] & Pick<FormLabelOwnProps, 'filled' | 'color'>; - defaultComponent: TypeMap['defaultComponent']; -} - -export type FormLabelProps< - RootComponent extends React.ElementType = FormLabelTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<FormLabelTypeMap<AdditionalProps, RootComponent>, RootComponent> & { - component?: React.ElementType; -}; - -export interface FormLabelOwnerState extends FormLabelProps { - color: NonNullable<FormLabelProps['color']>; -} diff --git a/packages/mui-material-next/src/FormLabel/formLabelClasses.ts b/packages/mui-material-next/src/FormLabel/formLabelClasses.ts deleted file mode 100644 index b1b69e98466e86..00000000000000 --- a/packages/mui-material-next/src/FormLabel/formLabelClasses.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { - unstable_generateUtilityClass as generateUtilityClass, - unstable_generateUtilityClasses as generateUtilityClasses, -} from '@mui/utils'; - -export interface FormLabelClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if the color is secondary. */ - colorSecondary: string; - /** State class applied to the root element if `focused={true}`. */ - focused: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; - /** State class applied to the root element if `error={true}`. */ - error: string; - /** State class applied to the root element if `filled={true}`. */ - filled: string; - /** State class applied to the root element if `required={true}`. */ - required: string; - /** Styles applied to the asterisk element. */ - asterisk: string; -} - -export type FormLabelClassKey = keyof FormLabelClasses; - -export function getFormLabelUtilityClasses(slot: string): string { - return generateUtilityClass('MuiFormLabel', slot); -} - -const formLabelClasses: FormLabelClasses = generateUtilityClasses('MuiFormLabel', [ - 'root', - 'colorSecondary', - 'focused', - 'disabled', - 'error', - 'filled', - 'required', - 'asterisk', -]); - -export default formLabelClasses; diff --git a/packages/mui-material-next/src/FormLabel/index.ts b/packages/mui-material-next/src/FormLabel/index.ts deleted file mode 100644 index db05e838ce6da7..00000000000000 --- a/packages/mui-material-next/src/FormLabel/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -'use client'; -export { default } from './FormLabel'; -export * from './FormLabel'; - -export { default as formLabelClasses } from './formLabelClasses'; -export * from './formLabelClasses'; diff --git a/packages/mui-material-next/src/IconButton/IconButton.d.ts b/packages/mui-material-next/src/IconButton/IconButton.d.ts deleted file mode 100644 index d81b2dc621da6d..00000000000000 --- a/packages/mui-material-next/src/IconButton/IconButton.d.ts +++ /dev/null @@ -1,91 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverridableStringUnion, OverrideProps } from '@mui/types'; -import { Theme } from '@mui/material'; -import { ExtendButtonBase, ExtendButtonBaseTypeMap } from '../ButtonBase/ButtonBase.types'; -import { IconButtonClasses } from './iconButtonClasses'; - -export interface IconButtonPropsColorOverrides {} - -export interface IconButtonPropsSizeOverrides {} - -export interface IconButtonOwnProps { - /** - * The icon to display. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<IconButtonClasses>; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'default' - */ - color?: OverridableStringUnion< - 'inherit' | 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning', - IconButtonPropsColorOverrides - >; - /** - * If `true`, the component is disabled. - * @default false - */ - disabled?: boolean; - /** - * If `true`, the keyboard focus ripple is disabled. - * @default false - */ - disableFocusRipple?: boolean; - /** - * If given, uses a negative margin to counteract the padding on one - * side (this is often helpful for aligning the left or right - * side of the icon with content above or below, without ruining the border - * size and shape). - * @default false - */ - edge?: 'start' | 'end' | false; - /** - * The size of the component. - * `small` is equivalent to the dense button styling. - * @default 'medium' - */ - size?: OverridableStringUnion<'small' | 'medium' | 'large', IconButtonPropsSizeOverrides>; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -export type IconButtonTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'button', -> = ExtendButtonBaseTypeMap<{ - props: AdditionalProps & IconButtonOwnProps; - defaultComponent: RootComponent; -}>; - -/** - * Refer to the [Icons](https://mui.com/material-ui/icons/) section of the documentation - * regarding the available icon options. - * - * Demos: - * - * - [Button](https://mui.com/material-ui/react-button/) - * - * API: - * - * - [IconButton API](https://mui.com/material-ui/api/icon-button/) - * - inherits [ButtonBase API](https://mui.com/material-ui/api/button-base/) - */ -declare const IconButton: ExtendButtonBase<IconButtonTypeMap>; - -export type IconButtonProps< - RootComponent extends React.ElementType = IconButtonTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<IconButtonTypeMap<AdditionalProps, RootComponent>, RootComponent> & { - component?: React.ElementType; -}; - -export default IconButton; diff --git a/packages/mui-material-next/src/IconButton/IconButton.js b/packages/mui-material-next/src/IconButton/IconButton.js deleted file mode 100644 index b8fafc50b3f520..00000000000000 --- a/packages/mui-material-next/src/IconButton/IconButton.js +++ /dev/null @@ -1,251 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { chainPropTypes, unstable_capitalize as capitalize } from '@mui/utils'; -import { unstable_composeClasses as composeClasses } from '@mui/base'; -import { alpha } from '@mui/system'; -import ButtonBase from '@mui/material/ButtonBase'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; -import iconButtonClasses, { getIconButtonUtilityClass } from './iconButtonClasses'; - -const useUtilityClasses = (ownerState) => { - const { classes, disabled, color, edge, size } = ownerState; - - const slots = { - root: [ - 'root', - disabled && 'disabled', - color !== 'default' && `color${capitalize(color)}`, - edge && `edge${capitalize(edge)}`, - `size${capitalize(size)}`, - ], - }; - - return composeClasses(slots, getIconButtonUtilityClass, classes); -}; - -const IconButtonRoot = styled(ButtonBase, { - name: 'MuiIconButton', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - ownerState.color !== 'default' && styles[`color${capitalize(ownerState.color)}`], - ownerState.edge && styles[`edge${capitalize(ownerState.edge)}`], - styles[`size${capitalize(ownerState.size)}`], - ]; - }, -})( - ({ theme, ownerState }) => ({ - textAlign: 'center', - flex: '0 0 auto', - fontSize: theme.typography.pxToRem(24), - padding: 8, - borderRadius: '50%', - overflow: 'visible', // Explicitly set the default value to solve a bug on IE11. - color: (theme.vars || theme).palette.action.active, - transition: theme.transitions.create('background-color', { - duration: theme.transitions.duration.shortest, - }), - ...(!ownerState.disableRipple && { - '&:hover': { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.action.activeChannel} / ${theme.vars.palette.action.hoverOpacity})` - : alpha(theme.palette.action.active, theme.palette.action.hoverOpacity), - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - }, - }), - ...(ownerState.edge === 'start' && { - marginLeft: ownerState.size === 'small' ? -3 : -12, - }), - ...(ownerState.edge === 'end' && { - marginRight: ownerState.size === 'small' ? -3 : -12, - }), - }), - ({ theme, ownerState }) => { - const palette = (theme.vars || theme).palette?.[ownerState.color]; - return { - ...(ownerState.color === 'inherit' && { - color: 'inherit', - }), - ...(ownerState.color !== 'inherit' && - ownerState.color !== 'default' && { - color: palette?.main, - ...(!ownerState.disableRipple && { - '&:hover': { - ...(palette && { - backgroundColor: theme.vars - ? `rgba(${palette.mainChannel} / ${theme.vars.palette.action.hoverOpacity})` - : alpha(palette.main, theme.palette.action.hoverOpacity), - }), - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - }, - }), - }), - ...(ownerState.size === 'small' && { - padding: 5, - fontSize: theme.typography.pxToRem(18), - }), - ...(ownerState.size === 'large' && { - padding: 12, - fontSize: theme.typography.pxToRem(28), - }), - [`&.${iconButtonClasses.disabled}`]: { - backgroundColor: 'transparent', - color: (theme.vars || theme).palette.action.disabled, - }, - }; - }, -); - -/** - * Refer to the [Icons](/material-ui/icons/) section of the documentation - * regarding the available icon options. - */ -const IconButton = React.forwardRef(function IconButton(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiIconButton' }); - const { - edge = false, - children, - className, - color = 'default', - disabled = false, - disableFocusRipple = false, - size = 'medium', - ...other - } = props; - - const ownerState = { - ...props, - edge, - color, - disabled, - disableFocusRipple, - size, - }; - - const classes = useUtilityClasses(ownerState); - - return ( - <IconButtonRoot - className={clsx(classes.root, className)} - centerRipple - focusRipple={!disableFocusRipple} - disabled={disabled} - ref={ref} - ownerState={ownerState} - {...other} - > - {children} - </IconButtonRoot> - ); -}); - -IconButton.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The icon to display. - */ - children: chainPropTypes(PropTypes.node, (props) => { - const found = React.Children.toArray(props.children).some( - (child) => React.isValidElement(child) && child.props.onClick, - ); - - if (found) { - return new Error( - [ - 'MUI: You are providing an onClick event listener to a child of a button element.', - 'Prefer applying it to the IconButton directly.', - 'This guarantees that the whole <button> will be responsive to click events.', - ].join('\n'), - ); - } - - return null; - }), - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'default' - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf([ - 'inherit', - 'default', - 'primary', - 'secondary', - 'error', - 'info', - 'success', - 'warning', - ]), - PropTypes.string, - ]), - /** - * If `true`, the component is disabled. - * @default false - */ - disabled: PropTypes.bool, - /** - * If `true`, the keyboard focus ripple is disabled. - * @default false - */ - disableFocusRipple: PropTypes.bool, - /** - * If `true`, the ripple effect is disabled. - * - * ⚠️ Without a ripple there is no styling for :focus-visible by default. Be sure - * to highlight the element by applying separate styles with the `.Mui-focusVisible` class. - * @default false - */ - disableRipple: PropTypes.bool, - /** - * If given, uses a negative margin to counteract the padding on one - * side (this is often helpful for aligning the left or right - * side of the icon with content above or below, without ruining the border - * size and shape). - * @default false - */ - edge: PropTypes.oneOf(['end', 'start', false]), - /** - * The size of the component. - * `small` is equivalent to the dense button styling. - * @default 'medium' - */ - size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['small', 'medium', 'large']), - PropTypes.string, - ]), - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), - PropTypes.func, - PropTypes.object, - ]), -}; - -export default IconButton; diff --git a/packages/mui-material-next/src/IconButton/IconButton.test.js b/packages/mui-material-next/src/IconButton/IconButton.test.js deleted file mode 100644 index c98c84f44c6394..00000000000000 --- a/packages/mui-material-next/src/IconButton/IconButton.test.js +++ /dev/null @@ -1,137 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import PropTypes from 'prop-types'; -import { createRenderer } from '@mui-internal/test-utils'; -import { unstable_capitalize as capitalize } from '@mui/utils'; -import Icon from '@mui/material/Icon'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; -import ButtonBase from '@mui/material/ButtonBase'; -import { iconButtonClasses as classes } from '@mui/material/IconButton'; -import IconButton from '.'; -import describeConformance from '../../test/describeConformance'; - -describe('<IconButton />', () => { - const { render } = createRenderer(); - - describeConformance(<IconButton>book</IconButton>, () => ({ - classes, - inheritComponent: ButtonBase, - render, - refInstanceof: window.HTMLButtonElement, - muiName: 'MuiIconButton', - testVariantProps: { edge: 'end', disabled: true }, - skip: ['componentProp', 'componentsProp'], - })); - - it('should render Icon children with right classes', () => { - const childClassName = 'child-woof'; - const iconChild = <Icon data-testid="icon" className={childClassName} />; - const { getByTestId } = render(<IconButton>{iconChild}</IconButton>); - - expect(getByTestId('icon')).to.have.class(childClassName); - }); - - it('should have a ripple by default', () => { - const { container } = render( - <IconButton TouchRippleProps={{ className: 'touch-ripple' }}>book</IconButton>, - ); - expect(container.querySelector('.touch-ripple')).not.to.equal(null); - }); - - it('can disable the ripple and hover effect', () => { - const { container } = render( - <IconButton disableRipple TouchRippleProps={{ className: 'touch-ripple' }}> - book - </IconButton>, - ); - expect(container.querySelector('.touch-ripple')).to.equal(null); - }); - - describe('prop: size', () => { - it('should render the right class', () => { - let root; - root = render(<IconButton size="small">book</IconButton>).container.firstChild; - expect(root).to.have.class(classes.sizeSmall); - - root = render(<IconButton size="medium">book</IconButton>).container.firstChild; - expect(root).not.to.have.class(classes.sizeSmall); - - root = render(<IconButton size="large">book</IconButton>).container.firstChild; - expect(root).to.have.class(classes.sizeLarge); - - root = render(<IconButton>book</IconButton>).container.firstChild; - expect(root).not.to.have.class(classes.sizeSmall); - expect(root).not.to.have.class(classes.sizeLarge); - }); - }); - - describe('prop: edge', () => { - it('edge="start" should render the right class', () => { - const { container } = render(<IconButton edge="start">book</IconButton>); - - expect(container.firstChild).to.have.class(classes.edgeStart); - }); - - it('edge="end" should render the right class', () => { - const { container } = render(<IconButton edge="end">book</IconButton>); - - expect(container.firstChild).to.have.class(classes.edgeEnd); - }); - - it('no edge should render the right class', () => { - const { container } = render(<IconButton>book</IconButton>); - - expect(container.firstChild).not.to.have.class(classes.edgeStart); - expect(container.firstChild).not.to.have.class(classes.edgeEnd); - }); - }); - - describe('prop: disabled', () => { - it('should disable the component', () => { - const { getByRole } = render(<IconButton disabled>book</IconButton>); - const button = getByRole('button'); - - expect(button).to.have.property('disabled', true); - expect(button).to.have.class(classes.disabled); - }); - }); - - describe('prop: color', () => { - ['primary', 'secondary', 'error', 'info', 'success', 'warning'].forEach((color) => { - it(`should render the ${color} class`, () => { - const { getByRole } = render(<IconButton color={color}>Hello World</IconButton>); - const button = getByRole('button'); - expect(button).to.have.class(classes[`color${capitalize(color)}`]); - }); - }); - }); - - it('should raise a warning about onClick in children because of Firefox', () => { - expect(() => { - PropTypes.checkPropTypes( - IconButton.propTypes, - { classes: {}, children: <svg onClick={() => {}} /> }, - 'prop', - 'MockedName', - ); - }).toErrorDev(['MUI: You are providing an onClick event listener']); - }); - - it('should not throw error for a custom color', () => { - expect(() => ( - <ThemeProvider - theme={createTheme({ - components: { - MuiIconButton: { - defaultProps: { - color: 'custom', - }, - }, - }, - })} - > - <IconButton /> - </ThemeProvider> - )).not.to.throw(); - }); -}); diff --git a/packages/mui-material-next/src/IconButton/iconButtonClasses.ts b/packages/mui-material-next/src/IconButton/iconButtonClasses.ts deleted file mode 100644 index c20c909d8ec356..00000000000000 --- a/packages/mui-material-next/src/IconButton/iconButtonClasses.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface IconButtonClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `edge="start"`. */ - edgeStart: string; - /** Styles applied to the root element if `edge="end"`. */ - edgeEnd: string; - /** Styles applied to the root element if `color="inherit"`. */ - colorInherit: string; - /** Styles applied to the root element if `color="primary"`. */ - colorPrimary: string; - /** Styles applied to the root element if `color="secondary"`. */ - colorSecondary: string; - /** Styles applied to the root element if `color="error"`. */ - colorError: string; - /** Styles applied to the root element if `color="info"`. */ - colorInfo: string; - /** Styles applied to the root element if `color="success"`. */ - colorSuccess: string; - /** Styles applied to the root element if `color="warning"`. */ - colorWarning: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; - /** Styles applied to the root element if `size="small"`. */ - sizeSmall: string; - /** Styles applied to the root element if `size="medium"`. */ - sizeMedium: string; - /** Styles applied to the root element if `size="large"`. */ - sizeLarge: string; -} - -export type IconButtonClassKey = keyof IconButtonClasses; - -export function getIconButtonUtilityClass(slot: string): string { - return generateUtilityClass('MuiIconButton', slot); -} - -const iconButtonClasses: IconButtonClasses = generateUtilityClasses('MuiIconButton', [ - 'root', - 'disabled', - 'colorInherit', - 'colorPrimary', - 'colorSecondary', - 'colorError', - 'colorInfo', - 'colorSuccess', - 'colorWarning', - 'edgeStart', - 'edgeEnd', - 'sizeSmall', - 'sizeMedium', - 'sizeLarge', -]); - -export default iconButtonClasses; diff --git a/packages/mui-material-next/src/IconButton/index.d.ts b/packages/mui-material-next/src/IconButton/index.d.ts deleted file mode 100644 index aecf12553a0334..00000000000000 --- a/packages/mui-material-next/src/IconButton/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './IconButton'; -export * from './IconButton'; - -export { default as iconButtonClasses } from './iconButtonClasses'; -export * from './iconButtonClasses'; diff --git a/packages/mui-material-next/src/IconButton/index.js b/packages/mui-material-next/src/IconButton/index.js deleted file mode 100644 index f80565936a4435..00000000000000 --- a/packages/mui-material-next/src/IconButton/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './IconButton'; - -export { default as iconButtonClasses } from './iconButtonClasses'; -export * from './iconButtonClasses'; diff --git a/packages/mui-material-next/src/InputAdornment/InputAdornment.d.ts b/packages/mui-material-next/src/InputAdornment/InputAdornment.d.ts deleted file mode 100644 index a4769605aabfa5..00000000000000 --- a/packages/mui-material-next/src/InputAdornment/InputAdornment.d.ts +++ /dev/null @@ -1,69 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverridableComponent, OverrideProps } from '@mui/types'; -import { Theme } from '..'; -import { InputAdornmentClasses } from './inputAdornmentClasses'; - -export interface InputAdornmentOwnProps { - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<InputAdornmentClasses>; - /** - * The content of the component, normally an `IconButton` or string. - */ - children?: React.ReactNode; - /** - * Disable pointer events on the root. - * This allows for the content of the adornment to focus the `input` on click. - * @default false - */ - disablePointerEvents?: boolean; - /** - * If children is a string then disable wrapping in a Typography component. - * @default false - */ - disableTypography?: boolean; - /** - * The position this adornment should appear relative to the `Input`. - */ - position: 'start' | 'end'; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; - /** - * The variant to use. - * Note: If you are using the `TextField` component or the `FormControl` component - * you do not have to set this manually. - */ - variant?: 'standard' | 'outlined' | 'filled'; -} - -export interface InputAdornmentTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'div', -> { - props: AdditionalProps & InputAdornmentOwnProps; - defaultComponent: RootComponent; -} -/** - * - * Demos: - * - * - [Text Field](https://mui.com/material-ui/react-text-field/) - * - * API: - * - * - [InputAdornment API](https://mui.com/material-ui/api/input-adornment/) - */ -declare const InputAdornment: OverridableComponent<InputAdornmentTypeMap>; - -export type InputAdornmentProps< - RootComponent extends React.ElementType = InputAdornmentTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<InputAdornmentTypeMap<AdditionalProps, RootComponent>, RootComponent> & { - component?: React.ElementType; -}; - -export default InputAdornment; diff --git a/packages/mui-material-next/src/InputAdornment/InputAdornment.js b/packages/mui-material-next/src/InputAdornment/InputAdornment.js deleted file mode 100644 index c761b11b6fb1dd..00000000000000 --- a/packages/mui-material-next/src/InputAdornment/InputAdornment.js +++ /dev/null @@ -1,194 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_capitalize as capitalize } from '@mui/utils'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import Typography from '@mui/material/Typography'; -import FormControlContext from '../FormControl/FormControlContext'; -import useFormControl from '../FormControl/useFormControl'; -import styled from '../styles/styled'; -import inputAdornmentClasses, { getInputAdornmentUtilityClass } from './inputAdornmentClasses'; -import useThemeProps from '../styles/useThemeProps'; - -const overridesResolver = (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - styles[`position${capitalize(ownerState.position)}`], - ownerState.disablePointerEvents === true && styles.disablePointerEvents, - styles[ownerState.variant], - ]; -}; - -const useUtilityClasses = (ownerState) => { - const { classes, disablePointerEvents, hiddenLabel, position, size, variant } = ownerState; - const slots = { - root: [ - 'root', - disablePointerEvents && 'disablePointerEvents', - position && `position${capitalize(position)}`, - variant, - hiddenLabel && 'hiddenLabel', - size && `size${capitalize(size)}`, - ], - }; - - return composeClasses(slots, getInputAdornmentUtilityClass, classes); -}; - -const InputAdornmentRoot = styled('div', { - name: 'MuiInputAdornment', - slot: 'Root', - overridesResolver, -})(({ theme, ownerState }) => ({ - display: 'flex', - height: '0.01em', // Fix IE11 flexbox alignment. To remove at some point. - maxHeight: '2em', - alignItems: 'center', - whiteSpace: 'nowrap', - color: (theme.vars || theme).palette.action.active, - ...(ownerState.variant === 'filled' && { - // Styles applied to the root element if `variant="filled"`. - [`&.${inputAdornmentClasses.positionStart}&:not(.${inputAdornmentClasses.hiddenLabel})`]: { - marginTop: 16, - }, - }), - ...(ownerState.position === 'start' && { - // Styles applied to the root element if `position="start"`. - marginRight: 8, - }), - ...(ownerState.position === 'end' && { - // Styles applied to the root element if `position="end"`. - marginLeft: 8, - }), - ...(ownerState.disablePointerEvents === true && { - // Styles applied to the root element if `disablePointerEvents={true}`. - pointerEvents: 'none', - }), -})); - -const InputAdornment = React.forwardRef(function InputAdornment(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiInputAdornment' }); - const { - children, - className, - component = 'div', - disablePointerEvents = false, - disableTypography = false, - position, - variant: variantProp, - ...other - } = props; - - const muiFormControl = useFormControl() || {}; - - let variant = variantProp; - - if (variantProp && muiFormControl.variant) { - if (process.env.NODE_ENV !== 'production') { - if (variantProp === muiFormControl.variant) { - console.error( - 'MUI: The `InputAdornment` variant infers the variant prop ' + - 'you do not have to provide one.', - ); - } - } - } - - if (muiFormControl && !variant) { - variant = muiFormControl.variant; - } - - const ownerState = { - ...props, - hiddenLabel: muiFormControl.hiddenLabel, - size: muiFormControl.size, - disablePointerEvents, - position, - variant, - }; - - const classes = useUtilityClasses(ownerState); - - return ( - <FormControlContext.Provider value={null}> - <InputAdornmentRoot - as={component} - ownerState={ownerState} - className={clsx(classes.root, className)} - ref={ref} - {...other} - > - {typeof children === 'string' && !disableTypography ? ( - <Typography color="text.secondary">{children}</Typography> - ) : ( - <React.Fragment> - {/* To have the correct vertical alignment baseline */} - {position === 'start' ? ( - /* notranslate needed while Google Translate will not fix zero-width space issue */ - <span className="notranslate">​</span> - ) : null} - {children} - </React.Fragment> - )} - </InputAdornmentRoot> - </FormControlContext.Provider> - ); -}); - -InputAdornment.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The content of the component, normally an `IconButton` or string. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * Disable pointer events on the root. - * This allows for the content of the adornment to focus the `input` on click. - * @default false - */ - disablePointerEvents: PropTypes.bool, - /** - * If children is a string then disable wrapping in a Typography component. - * @default false - */ - disableTypography: PropTypes.bool, - /** - * The position this adornment should appear relative to the `Input`. - */ - position: PropTypes.oneOf(['end', 'start']).isRequired, - /** - * 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 variant to use. - * Note: If you are using the `TextField` component or the `FormControl` component - * you do not have to set this manually. - */ - variant: PropTypes.oneOf(['filled', 'outlined', 'standard']), -}; - -export default InputAdornment; diff --git a/packages/mui-material-next/src/InputAdornment/InputAdornment.test.js b/packages/mui-material-next/src/InputAdornment/InputAdornment.test.js deleted file mode 100644 index 0b0ddd480fd0eb..00000000000000 --- a/packages/mui-material-next/src/InputAdornment/InputAdornment.test.js +++ /dev/null @@ -1,217 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer, strictModeDoubleLoggingSuppressed } from '@mui-internal/test-utils'; -import { typographyClasses } from '@mui/material/Typography'; -import InputAdornment, { inputAdornmentClasses as classes } from '@mui/material/InputAdornment'; -import TextField from '@mui/material/TextField'; -import FormControl from '@mui/material/FormControl'; -import Input from '@mui/material/Input'; -import describeConformance from '../../test/describeConformance'; - -describe('<InputAdornment />', () => { - const { render } = createRenderer(); - - describeConformance(<InputAdornment position="start">foo</InputAdornment>, () => ({ - classes, - inheritComponent: 'div', - render, - muiName: 'MuiInputAdornment', - testVariantProps: { color: 'primary' }, - refInstanceof: window.HTMLDivElement, - skip: ['componentsProp'], - testComponentPropWith: 'span', - })); - - it('should wrap text children in a Typography', () => { - const { container } = render(<InputAdornment position="start">foo</InputAdornment>); - const typography = container.querySelector(`.${typographyClasses.root}`); - - expect(typography).not.to.equal(null); - expect(typography).to.have.text('foo'); - }); - - it('should have the root and start class when position is start', () => { - const { container } = render(<InputAdornment position="start">foo</InputAdornment>); - const adornment = container.firstChild; - - expect(adornment).to.have.class(classes.root); - expect(adornment).to.have.class(classes.positionStart); - }); - - it('should have the root and end class when position is end', () => { - const { container } = render(<InputAdornment position="end">foo</InputAdornment>); - const adornment = container.firstChild; - - expect(adornment).to.have.class(classes.root); - expect(adornment).to.have.class(classes.positionEnd); - }); - - describe('prop: variant', () => { - it("should inherit the TextField's variant", () => { - const { getByTestId } = render( - <TextField - fullWidth - placeholder="Search" - label="Search" - variant="filled" - InputProps={{ - startAdornment: ( - <InputAdornment data-testid="InputAdornment" position="start"> - foo - </InputAdornment> - ), - }} - />, - ); - const adornment = getByTestId('InputAdornment'); - - expect(adornment).to.have.class(classes.root); - expect(adornment).to.have.class(classes.positionStart); - expect(adornment).to.have.class(classes.filled); - }); - - it("should inherit the FormControl's variant", () => { - const { getByTestId } = render( - <FormControl variant="filled"> - <InputAdornment data-testid="InputAdornment" position="start"> - foo - </InputAdornment> - </FormControl>, - ); - const adornment = getByTestId('InputAdornment'); - - expect(adornment).to.have.class(classes.root); - expect(adornment).to.have.class(classes.positionStart); - expect(adornment).to.have.class(classes.filled); - }); - - it('should override the inherited variant', () => { - const { getByTestId } = render( - <TextField - fullWidth - placeholder="Search" - label="Search" - variant="filled" - InputProps={{ - startAdornment: ( - <InputAdornment data-testid="InputAdornment" variant="standard" position="start"> - foo - </InputAdornment> - ), - }} - />, - ); - const adornment = getByTestId('InputAdornment'); - - expect(adornment).to.have.class(classes.root); - expect(adornment).to.have.class(classes.positionStart); - expect(adornment).not.to.have.class(classes.filled); - }); - - it('should have the filled root and class when variant is filled', () => { - const { container } = render( - <InputAdornment variant="filled" position="start"> - foo - </InputAdornment>, - ); - const adornment = container.firstChild; - - expect(adornment).to.have.class(classes.root); - expect(adornment).to.have.class(classes.positionStart); - expect(adornment).to.have.class(classes.filled); - }); - - it('should warn if the variant supplied is equal to the variant inferred', () => { - expect(() => { - render( - <FormControl variant="filled"> - <Input - startAdornment={ - <InputAdornment variant="filled" position="start"> - foo - </InputAdornment> - } - /> - </FormControl>, - ); - }).toErrorDev([ - 'MUI: The `InputAdornment` variant infers the variant ' + - 'prop you do not have to provide one.', - !strictModeDoubleLoggingSuppressed && - 'MUI: The `InputAdornment` variant infers the variant ' + - 'prop you do not have to provide one.', - ]); - }); - }); - - it('should have the disabled pointer events class when disabledPointerEvents true', () => { - const { container } = render( - <InputAdornment disablePointerEvents position="start"> - foo - </InputAdornment>, - ); - const adornment = container.firstChild; - - expect(adornment).to.have.class(classes.disablePointerEvents); - }); - - it('should not wrap text children in a Typography when disableTypography true', () => { - const { container } = render( - <InputAdornment disableTypography position="start"> - foo - </InputAdornment>, - ); - - expect(container.querySelector(`.${typographyClasses.root}`)).to.equal(null); - }); - - it('should render children', () => { - const { container } = render( - <InputAdornment position="end"> - <div>foo</div> - </InputAdornment>, - ); - const adornment = container.firstChild; - - expect(adornment.firstChild).to.have.property('nodeName', 'DIV'); - }); - - describe('prop: position', () => { - it('should render span for vertical baseline alignment', () => { - const { container } = render( - <InputAdornment position="start"> - <div>foo</div> - </InputAdornment>, - ); - const adornment = container.firstChild; - - expect(adornment.firstChild).to.have.tagName('span'); - expect(adornment.firstChild).to.have.class('notranslate'); - expect(adornment.childNodes[1]).to.have.tagName('div'); - }); - }); - - it('applies a size small class inside <FormControl size="small" />', () => { - const { getByTestId } = render( - <FormControl size="small"> - <InputAdornment position="start" data-testid="root"> - $ - </InputAdornment> - </FormControl>, - ); - - expect(getByTestId('root')).to.have.class(classes.sizeSmall); - }); - - it('applies a hiddenLabel class inside <FormControl hiddenLabel />', () => { - const { getByTestId } = render( - <FormControl hiddenLabel> - <InputAdornment position="start" data-testid="root"> - $ - </InputAdornment> - </FormControl>, - ); - - expect(getByTestId('root')).to.have.class(classes.hiddenLabel); - }); -}); diff --git a/packages/mui-material-next/src/InputAdornment/index.d.ts b/packages/mui-material-next/src/InputAdornment/index.d.ts deleted file mode 100644 index 48e5ebc6741b70..00000000000000 --- a/packages/mui-material-next/src/InputAdornment/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './InputAdornment'; -export { default } from './InputAdornment'; - -export { default as inputAdornmentClasses } from './inputAdornmentClasses'; -export * from './inputAdornmentClasses'; diff --git a/packages/mui-material-next/src/InputAdornment/index.js b/packages/mui-material-next/src/InputAdornment/index.js deleted file mode 100644 index e8757bd82b4074..00000000000000 --- a/packages/mui-material-next/src/InputAdornment/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './InputAdornment'; - -export { default as inputAdornmentClasses } from './inputAdornmentClasses'; -export * from './inputAdornmentClasses'; diff --git a/packages/mui-material-next/src/InputAdornment/inputAdornmentClasses.ts b/packages/mui-material-next/src/InputAdornment/inputAdornmentClasses.ts deleted file mode 100644 index 1b1fadb95e731f..00000000000000 --- a/packages/mui-material-next/src/InputAdornment/inputAdornmentClasses.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface InputAdornmentClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `variant="filled"`. */ - filled: string; - /** Styles applied to the root element if `variant="outlined"`. */ - outlined: string; - /** Styles applied to the root element if `variant="standard"`. */ - standard: string; - /** Styles applied to the root element if `position="start"`. */ - positionStart: string; - /** Styles applied to the root element if `position="end"`. */ - positionEnd: string; - /** Styles applied to the root element if `disablePointerEvents={true}`. */ - disablePointerEvents: string; - /** Styles applied if the adornment is used inside <FormControl hiddenLabel />. */ - hiddenLabel: string; - /** Styles applied if the adornment is used inside <FormControl size="small" />. */ - sizeSmall: string; -} - -export type InputAdornmentClassKey = keyof InputAdornmentClasses; - -export function getInputAdornmentUtilityClass(slot: string): string { - return generateUtilityClass('MuiInputAdornment', slot); -} - -const inputAdornmentClasses: InputAdornmentClasses = generateUtilityClasses('MuiInputAdornment', [ - 'root', - 'filled', - 'standard', - 'outlined', - 'positionStart', - 'positionEnd', - 'disablePointerEvents', - 'hiddenLabel', - 'sizeSmall', -]); - -export default inputAdornmentClasses; diff --git a/packages/mui-material-next/src/InputBase/InputBase.spec.tsx b/packages/mui-material-next/src/InputBase/InputBase.spec.tsx deleted file mode 100644 index 4589308db05d7a..00000000000000 --- a/packages/mui-material-next/src/InputBase/InputBase.spec.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import * as React from 'react'; -import { expectType } from '@mui/types'; -import InputBase from '@mui/material-next/InputBase'; - -<InputBase - onInvalid={(event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>) => { - expectType<React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, typeof event>(event); - }} -/>; - -// Tests presence of `sx` prop on input and root slot -<InputBase - slotProps={{ - input: { - sx: { - background: 'white', - }, - }, - root: { - sx: { - background: 'black', - }, - }, - }} -/>; diff --git a/packages/mui-material-next/src/InputBase/InputBase.test.tsx b/packages/mui-material-next/src/InputBase/InputBase.test.tsx deleted file mode 100644 index 13bd511fdcc3e2..00000000000000 --- a/packages/mui-material-next/src/InputBase/InputBase.test.tsx +++ /dev/null @@ -1,757 +0,0 @@ -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { expect } from 'chai'; -import { spy } from 'sinon'; -import { act, createRenderer, fireEvent, screen } from '@mui-internal/test-utils'; -import FormControl, { useFormControl } from '@mui/material-next/FormControl'; -// TODO v6: replace with material-next/InputAdornment -import InputAdornment from '@mui/material/InputAdornment'; -// TODO v6: replace with material-next/TextField -import TextField from '@mui/material/TextField'; -// TODO v6: replace with material-next/Select -import Select from '@mui/material/Select'; -import InputBase, { inputBaseClasses as classes } from '@mui/material-next/InputBase'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import { - InputBaseInputSlotPropsOverrides, - InputBaseOwnerState, - InputBaseProps, -} from './InputBase.types'; -import describeConformance from '../../test/describeConformance'; - -describe('<InputBase />', () => { - const { render } = createRenderer(); - - describeConformance(<InputBase />, () => ({ - classes, - inheritComponent: 'div', - render, - refInstanceof: window.HTMLDivElement, - muiName: 'MuiInputBase', - testVariantProps: { size: 'small' }, - testLegacyComponentsProp: false, - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - slots: { - root: { expectedClassName: classes.root }, - input: { expectedClassName: classes.input, testWithElement: 'input' }, - }, - skip: ['componentProp', 'componentsProp'], - })); - - it('should render an <input /> inside the div', () => { - const { container } = render(<InputBase />); - const input = container.querySelector('input'); - expect(input).to.have.attribute('type', 'text'); - expect(input).to.have.class(classes.input); - expect(input).not.to.have.attribute('required'); - }); - - it('should add the right class when size is small', () => { - const { container } = render(<InputBase size="small" />); - expect(container.firstChild).to.have.class(classes.sizeSmall); - }); - - describe('multiline', () => { - describe('warning if multiline related props are passed without specifying the multiline prop', () => { - ['rows', 'minRows', 'maxRows'].forEach((multilineProp) => { - it(`warns if ${multilineProp} is passed without specifying multiline`, () => { - const multilineErrorMessage = `MUI: You have set multiline props on an single-line input.\nSet the \`multiline\` prop if you want to render a multi-line input.\nOtherwise they will be ignored.\nIgnored props: ${multilineProp}`; - expect(() => { - render(<InputBase {...{ [multilineProp]: 1 }} />); - }).toErrorDev([ - multilineErrorMessage, - // React 18 Strict Effects run mount effects twice - React.version.startsWith('18') && multilineErrorMessage, - ]); - }); - }); - }); - - it('should render a `textbox` with `aria-multiline`', () => { - render(<InputBase multiline />); - - const textarea = screen.getByRole('textbox', { hidden: false }); - // implicit `aria-multiline` - expect(textarea).to.have.tagName('textarea'); - }); - - it('should render a `textbox` with `aria-multiline` if `rows` is specified', () => { - render(<InputBase multiline rows={4} />); - - const textarea = screen.getByRole('textbox', { hidden: false }); - // implicit `aria-multiline` - expect(textarea).to.have.tagName('textarea'); - }); - - it('should forward the value to the textarea', () => { - render(<InputBase multiline maxRows={4} value="Hello" />); - - const textarea = screen.getByRole('textbox', { hidden: false }); - expect(textarea).to.have.value('Hello'); - }); - - it('should preserve state when changing rows', () => { - const { setProps } = render(<InputBase multiline />); - const textarea = screen.getByRole('textbox', { hidden: false }); - act(() => { - textarea.focus(); - }); - - setProps({ rows: 4 }); - - expect(textarea).toHaveFocus(); - }); - }); - - describe('prop: disabled', () => { - it('should render a disabled <input />', () => { - const { container } = render(<InputBase disabled />); - const input = container.querySelector('input'); - expect(input).to.have.class(classes.input); - expect(input).to.have.class(classes.disabled); - }); - - it('should reset the focused state if getting disabled', () => { - const handleBlur = spy(); - const handleFocus = spy(); - const { getByRole, setProps } = render( - <InputBase onBlur={handleBlur} onFocus={handleFocus} />, - ); - - act(() => { - getByRole('textbox').focus(); - }); - expect(handleFocus.callCount).to.equal(1); - - setProps({ disabled: true }); - - expect(handleBlur.callCount).to.equal(1); - // check if focus not initiated again - expect(handleFocus.callCount).to.equal(1); - }); - }); - - describe('prop: readonly', () => { - it('should render a readonly <input />', () => { - const { getByRole } = render(<InputBase readOnly />); - const input = getByRole('textbox'); - expect(input).to.have.class(classes.input); - expect(input).to.have.class(classes.readOnly); - expect(input).to.have.property('readOnly'); - }); - }); - - it('should fire event callbacks', () => { - const handleChange = spy(); - const handleFocus = spy(); - const handleBlur = spy(); - const handleKeyUp = spy(); - const handleKeyDown = spy(); - const { getByRole } = render( - <InputBase - onChange={handleChange} - onFocus={handleFocus} - onBlur={handleBlur} - onKeyUp={handleKeyUp} - onKeyDown={handleKeyDown} - />, - ); - const input = getByRole('textbox'); - - // TODO v6: refactor this test with @testing-library/user-event - // simulating user input: gain focus, key input (keydown, (input), change, keyup), blur - act(() => { - input.focus(); - }); - expect(handleFocus.callCount).to.equal(1); - - fireEvent.keyDown(input, { key: 'a' }); - expect(handleKeyDown.callCount).to.equal(1); - - fireEvent.change(input, { target: { value: 'a' } }); - expect(handleChange.callCount).to.equal(1); - - fireEvent.keyUp(input, { key: 'a' }); - expect(handleKeyUp.callCount).to.equal(1); - - act(() => { - input.blur(); - }); - expect(handleBlur.callCount).to.equal(1); - }); - - describe('controlled', () => { - it('should considered [] as controlled', () => { - const { getByRole } = render(<InputBase value={[]} />); - const input = getByRole('textbox'); - - expect(input).to.have.property('value', ''); - fireEvent.change(input, { target: { value: 'do not work' } }); - expect(input).to.have.property('value', ''); - }); - }); - - describe('errors', () => { - it("throws on change if the target isn't mocked", () => { - /** - * This component simulates a custom input component that hides the inner - * input value for security reasons e.g. react-stripe-element. - * - * A ref is exposed to trigger a change event instead of using fireEvent.change - */ - const BadInputComponent = React.forwardRef(function BadInputComponent( - props: { - onChange: (arg: Record<string, unknown>) => void; - }, - ref, - ) { - const { onChange } = props; - - // simulates const handleChange = () => onChange({}) and passing that - // handler to the onChange prop of `input` - React.useImperativeHandle(ref, () => () => onChange({})); - - return <input />; - }); - - BadInputComponent.propTypes = { - onChange: PropTypes.func.isRequired, - }; - - const triggerChangeRef = React.createRef<HTMLInputElement>(); - - expect(() => { - render( - <InputBase - slots={{ input: BadInputComponent }} - slotProps={{ - input: { - ref: triggerChangeRef, - }, - }} - />, - ); - }).toErrorDev([ - 'MUI: You have provided a `slots.input` to the input component\nthat does not correctly handle the `ref` prop.\nMake sure the `ref` prop is called with a HTMLInputElement.', - // React 18 Strict Effects run mount effects twice - React.version.startsWith('18') && - 'MUI: You have provided a `slots.input` to the input component\nthat does not correctly handle the `ref` prop.\nMake sure the `ref` prop is called with a HTMLInputElement.', - ]); - }); - }); - - // for InputBase, the `component` prop is called `inputComponent` so it's skipped - // in describeConformance and manually tested here - describe('prop: inputComponent', () => { - it('should accept any html component', () => { - const { getByTestId } = render( - <InputBase - inputComponent="span" - slotProps={{ - input: { 'data-testid': 'input-component' } as InputBaseInputSlotPropsOverrides, - }} - />, - ); - expect(getByTestId('input-component')).to.have.property('nodeName', 'SPAN'); - }); - - it('should inject onBlur and onFocus', () => { - let injectedProps: Record<string, unknown> = {}; - - const MyInputBase = React.forwardRef(function MyInputBase( - props: { ownerState: InputBaseOwnerState } & Record<string, unknown>, - ref: React.ForwardedRef<HTMLInputElement>, - ) { - injectedProps = props; - const { ownerState, ...other } = props; - return <input ref={ref} {...other} />; - }); - - render(<InputBase inputComponent={MyInputBase} />); - - expect(typeof injectedProps.onBlur).to.equal('function'); - expect(typeof injectedProps.onFocus).to.equal('function'); - }); - - describe('target mock implementations', () => { - it('can just mock the value', () => { - const MockedValue = React.forwardRef(function MockedValue( - props: { - onChange: React.ChangeEventHandler<HTMLInputElement>; - }, - ref: React.ForwardedRef<HTMLInputElement>, - ) { - const { onChange } = props; - - const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { - onChange({ - target: { value: event.target.value }, - } as React.ChangeEvent<HTMLInputElement>); - }; - - return <input ref={ref} onChange={handleChange} />; - }); - MockedValue.propTypes = { onChange: PropTypes.func.isRequired }; - - function FilledState(props: { 'data-testid': string }) { - const formControlContext = useFormControl(); - return <span {...props}>filled: {String(formControlContext?.filled)}</span>; - } - - const { getByRole, getByTestId } = render( - <FormControl> - <FilledState data-testid="filled" /> - <InputBase inputComponent={MockedValue} /> - </FormControl>, - ); - expect(getByTestId('filled')).to.have.text('filled: false'); - - fireEvent.change(getByRole('textbox'), { target: { value: 1 } }); - expect(getByTestId('filled')).to.have.text('filled: true'); - }); - - it("can expose the input component's ref through the inputComponent prop", () => { - const FullTarget = React.forwardRef(function FullTarget( - props: { ownerState: InputBaseOwnerState } & Record<string, unknown>, - ref: React.ForwardedRef<HTMLInputElement>, - ) { - const { ownerState, ...otherProps } = props; - return <input ref={ref} {...otherProps} />; - }); - - function FilledState(props: { 'data-testid': string }) { - const formControlContext = useFormControl(); - return <span {...props}>filled: {String(formControlContext?.filled)}</span>; - } - - const { getByRole, getByTestId } = render( - <FormControl> - <FilledState data-testid="filled" /> - <InputBase inputComponent={FullTarget} /> - </FormControl>, - ); - expect(getByTestId('filled')).to.have.text('filled: false'); - - fireEvent.change(getByRole('textbox'), { target: { value: 1 } }); - expect(getByTestId('filled')).to.have.text('filled: true'); - }); - }); - }); - - describe('with FormControl', () => { - it('should have the formControl class', () => { - const { getByTestId } = render( - <FormControl> - <InputBase data-testid="root" /> - </FormControl>, - ); - expect(getByTestId('root')).to.have.class(classes.formControl); - }); - - describe('callbacks', () => { - it('should fire the onClick prop', () => { - const handleClick = spy(); - const handleFocus = spy(); - const { getByTestId } = render( - <FormControl> - <InputBase data-testid="root" onClick={handleClick} onFocus={handleFocus} /> - </FormControl>, - ); - - fireEvent.click(getByTestId('root')); - expect(handleClick.callCount).to.equal(1); - expect(handleFocus.callCount).to.equal(1); - }); - }); - - describe('error', () => { - it('should be overridden by props', () => { - function InputBaseInErrorForm(props: InputBaseProps) { - return ( - <FormControl error> - <InputBase data-testid="root" {...props} /> - </FormControl> - ); - } - - const { getByTestId, setProps } = render(<InputBaseInErrorForm />); - expect(getByTestId('root')).to.have.class(classes.error); - - setProps({ error: false }); - expect(getByTestId('root')).not.to.have.class(classes.error); - - setProps({ error: true }); - expect(getByTestId('root')).to.have.class(classes.error); - }); - }); - - describe('size', () => { - it('should have the inputSizeSmall class in a dense context', () => { - const { container } = render( - <FormControl size="small"> - <InputBase /> - </FormControl>, - ); - expect(container.querySelector('input')).to.have.class(classes.inputSizeSmall); - }); - - it('should be overridden by props', () => { - function InputBaseInFormWithMargin(props: InputBaseProps) { - return ( - <FormControl size="medium"> - <InputBase {...props} /> - </FormControl> - ); - } - const { container, setProps } = render(<InputBaseInFormWithMargin />); - expect(container.querySelector('input')).not.to.have.class(classes.inputSizeSmall); - - setProps({ size: 'small' }); - expect(container.querySelector('input')).to.have.class(classes.inputSizeSmall); - }); - - it('has an inputHiddenLabel class to further reduce margin', () => { - const { getByRole } = render( - <FormControl hiddenLabel margin="dense"> - <InputBase /> - </FormControl>, - ); - - expect(getByRole('textbox')).to.have.class(classes.inputHiddenLabel); - }); - }); - - describe('required', () => { - it('should have the aria-required prop with value true', () => { - const { container } = render( - <FormControl required> - <InputBase /> - </FormControl>, - ); - const input = container.querySelector('input'); - expect(input).to.have.property('required', true); - }); - }); - - type TestFormController = { - onFocus: () => {}; - onBlur: () => {}; - }; - - describe('focused', () => { - it('prioritizes context focus', () => { - const FormController = React.forwardRef((props, ref) => { - const { onBlur, onFocus } = useFormControl() ?? {}; - - React.useImperativeHandle(ref, () => ({ onBlur, onFocus }), [onBlur, onFocus]); - - return null; - }); - const controlRef = React.createRef<TestFormController>(); - const { getByRole, getByTestId } = render( - <FormControl> - <FormController ref={controlRef} /> - <InputBase data-testid="root" /> - </FormControl>, - ); - - act(() => { - getByRole('textbox').focus(); - }); - expect(getByTestId('root')).to.have.class(classes.focused); - - act(() => { - controlRef.current?.onBlur(); - }); - - expect(getByTestId('root')).not.to.have.class(classes.focused); - - act(() => { - controlRef.current?.onFocus(); - }); - - expect(getByTestId('root')).to.have.class(classes.focused); - }); - - it('propagates focused state', () => { - function FocusedStateLabel(props: { 'data-testid': string; htmlFor: string }) { - const formControlContext = useFormControl(); - return <label {...props}>focused: {String(formControlContext?.focused)}</label>; - } - const { getByRole, getByTestId } = render( - <FormControl> - <FocusedStateLabel data-testid="label" htmlFor="input" /> - <InputBase id="input" /> - </FormControl>, - ); - expect(getByTestId('label')).to.have.text('focused: false'); - - act(() => { - getByRole('textbox').focus(); - }); - expect(getByTestId('label')).to.have.text('focused: true'); - - act(() => { - getByRole('textbox').blur(); - }); - expect(getByTestId('label')).to.have.text('focused: false'); - }); - }); - - it('propagates filled state when uncontrolled', () => { - function FilledStateLabel(props: { 'data-testid': string }) { - const formControlContext = useFormControl(); - return <label {...props}>filled: {String(formControlContext?.filled)}</label>; - } - const { getByRole, getByTestId } = render( - <FormControl> - <FilledStateLabel data-testid="label" /> - <InputBase /> - </FormControl>, - ); - expect(getByTestId('label')).to.have.text('filled: false'); - const textbox = getByRole('textbox'); - - fireEvent.change(textbox, { target: { value: 'material' } }); - expect(getByTestId('label')).to.have.text('filled: true'); - - fireEvent.change(textbox, { target: { value: '0' } }); - expect(getByTestId('label')).to.have.text('filled: true'); - - fireEvent.change(textbox, { target: { value: '' } }); - expect(getByTestId('label')).to.have.text('filled: false'); - }); - - it('propagates filled state when controlled', () => { - function FilledStateLabel(props: { 'data-testid': string }) { - const formControlContext = useFormControl(); - return <label {...props}>filled: {String(formControlContext?.filled)}</label>; - } - function ControlledInputBase(props: InputBaseProps) { - return ( - <FormControl> - <FilledStateLabel data-testid="label" /> - <InputBase {...props} /> - </FormControl> - ); - } - const { getByTestId, setProps } = render(<ControlledInputBase value="" />); - expect(getByTestId('label')).to.have.text('filled: false'); - - setProps({ value: 'material' }); - expect(getByTestId('label')).to.have.text('filled: true'); - - setProps({ value: 0 }); - expect(getByTestId('label')).to.have.text('filled: true'); - - setProps({ value: '' }); - expect(getByTestId('label')).to.have.text('filled: false'); - }); - - describe('registering input', () => { - it("should warn if more than one input is rendered regardless how it's nested", () => { - expect(() => { - render( - <FormControl> - <InputBase /> - <div> - {/* should work regardless how it's nested */} - <InputBase /> - </div> - </FormControl>, - ); - }).toErrorDev([ - 'MUI: There are multiple `InputBase` components inside a FormControl.\nThis creates visual inconsistencies, only use one `InputBase`.', - // React 18 Strict Effects run mount effects twice - React.version.startsWith('18') && - 'MUI: There are multiple `InputBase` components inside a FormControl.\nThis creates visual inconsistencies, only use one `InputBase`.', - ]); - }); - - it('should not warn if only one input is rendered', () => { - expect(() => { - render( - <FormControl> - <InputBase /> - </FormControl>, - ); - }).not.toErrorDev(); - }); - }); - }); - - describe('prop: slotProps', () => { - it('should apply the props on the input', () => { - const { container } = render( - <InputBase slotProps={{ input: { className: 'foo', maxLength: 5 } }} />, - ); - const input = container.querySelector('input'); - expect(input).to.have.class('foo'); - expect(input).to.have.class(classes.input); - expect(input).to.have.property('maxLength', 5); - }); - - it('should be able to get a ref', () => { - const inputRef = React.createRef<HTMLInputElement>(); - const { container } = render(<InputBase slotProps={{ input: { ref: inputRef } }} />); - expect(inputRef.current).to.equal(container.querySelector('input')); - }); - - it('should not repeat the same classname', () => { - const { container } = render(<InputBase slotProps={{ input: { className: 'foo' } }} />); - const input = container.querySelector('input'); - const matches = input?.className.match(/foo/g); - expect(input).to.have.class('foo'); - expect(matches).to.have.length(1); - }); - }); - - describe('prop: slots and slotProps', () => { - it('should call slotProps.input.onChange callback with all params sent from custom inputComponent', () => { - const INPUT_VALUE = 'material'; - const OUTPUT_VALUE = 'test'; - /** - * This component simulates the integration of react-select with Input - * react-select has a custom onChange that is essentially "(string, string) => void" - * https://github.com/mui/material-ui/issues/18130 - */ - const MyInputBase = React.forwardRef(function MyInputBase( - props: { - onChange: (...args: string[]) => void; - }, - ref: React.ForwardedRef<HTMLInputElement>, - ) { - const { onChange, ...other } = props; - - const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { - onChange(e.target.value, OUTPUT_VALUE); - }; - - return <input ref={ref} onChange={handleChange} {...other} />; - }); - - MyInputBase.propTypes = { - onChange: PropTypes.func.isRequired, - }; - - let outputArguments: string[] = []; - function parentHandleChange(...args: string[]) { - outputArguments = args; - } - - const { getByRole } = render( - <InputBase - inputComponent={MyInputBase} - slotProps={{ - input: { - onChange: parentHandleChange as unknown as React.ChangeEventHandler<HTMLInputElement>, - }, - }} - />, - ); - const textbox = getByRole('textbox'); - fireEvent.change(textbox, { target: { value: INPUT_VALUE } }); - - expect(outputArguments.length).to.equal(2); - expect(outputArguments[0]).to.equal(INPUT_VALUE); - expect(outputArguments[1]).to.equal(OUTPUT_VALUE); - }); - }); - - describe('prop: startAdornment, prop: endAdornment', () => { - it('should render adornment before input', () => { - const { getByTestId } = render( - <InputBase - startAdornment={ - <InputAdornment data-testid="adornment" position="start"> - $ - </InputAdornment> - } - />, - ); - - expect(getByTestId('adornment')).not.to.equal(null); - }); - - it('should render adornment after input', () => { - const { getByTestId } = render( - <InputBase - endAdornment={ - <InputAdornment data-testid="adornment" position="end"> - $ - </InputAdornment> - } - />, - ); - - expect(getByTestId('adornment')).not.to.equal(null); - }); - - // TODO v6: use material-next/Select - it('should allow a Select as an adornment', () => { - render( - <InputBase - value="" - name="text" - endAdornment={ - <InputAdornment position="end"> - <Select value="" name="suffix" /> - </InputAdornment> - } - variant="standard" - />, - ); - }); - }); - - describe('prop: inputRef', () => { - it('should be able to access the native input', () => { - const inputRef = React.createRef(); - const { container } = render(<InputBase inputRef={inputRef} />); - expect(inputRef.current).to.equal(container.querySelector('input')); - }); - - it('should be able to access the native textarea', () => { - const inputRef = React.createRef(); - const { container } = render(<InputBase multiline inputRef={inputRef} />); - expect(inputRef.current).to.equal(container.querySelector('textarea')); - }); - }); - - describe('prop: focused', () => { - // TODO v6: requires material-next/TextField - // eslint-disable-next-line mocha/no-skipped-tests - it.skip('should render correct border color with a customized primary color supplied to CssVarsProvider', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - const theme = extendTheme({ - colorSchemes: { - light: { - palette: { - primary: { - main: 'rgb(0, 191, 165)', - }, - }, - }, - }, - }); - const { getByRole } = render( - <CssVarsProvider theme={theme}> - {/* TODO v6: use material-next/TextField */} - <TextField focused label="Your email" /> - </CssVarsProvider>, - ); - - // this `fieldset` is the (internal) NotchedOutline component - const fieldset = getByRole('textbox').nextSibling; - expect(fieldset).toHaveComputedStyle({ - borderTopColor: 'rgb(0, 191, 165)', - borderRightColor: 'rgb(0, 191, 165)', - borderBottomColor: 'rgb(0, 191, 165)', - borderLeftColor: 'rgb(0, 191, 165)', - }); - }); - }); -}); diff --git a/packages/mui-material-next/src/InputBase/InputBase.tsx b/packages/mui-material-next/src/InputBase/InputBase.tsx deleted file mode 100644 index 1d380ef8dcdb1b..00000000000000 --- a/packages/mui-material-next/src/InputBase/InputBase.tsx +++ /dev/null @@ -1,824 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import GlobalStyles from '@mui/material/GlobalStyles'; -import { - EventHandlers, - isHostComponent, - TextareaAutosize, - unstable_composeClasses as composeClasses, - useSlotProps, - WithOptionalOwnerState, -} from '@mui/base'; -import { useInput } from '@mui/base/useInput'; -import { CSSInterpolation } from '@mui/system'; -import { DefaultComponentProps, OverrideProps } from '@mui/types'; -import { - refType, - unstable_capitalize as capitalize, - unstable_useEnhancedEffect as useEnhancedEffect, - unstable_useForkRef as useForkRef, -} from '@mui/utils'; -import FormControlContext from '@mui/material-next/FormControl/FormControlContext'; -import useFormControl from '@mui/material-next/FormControl/useFormControl'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; -import { isFilled } from './utils'; -import { - InputBaseInputSlotProps, - InputBaseOwnerState, - InputBaseProps, - InputBaseRootSlotProps, - InputBaseTypeMap, -} from './InputBase.types'; -import inputBaseClasses, { getInputBaseUtilityClass } from './inputBaseClasses'; - -const useUtilityClasses = (ownerState: InputBaseOwnerState) => { - const { - classes, - color = 'primary', - disabled, - error, - endAdornment, - focused, - formControl, - fullWidth, - hiddenLabel, - multiline, - readOnly, - size, - startAdornment, - type, - } = ownerState; - const slots = { - root: [ - 'root', - `color${capitalize(color)}`, - disabled && 'disabled', - error && 'error', - fullWidth && 'fullWidth', - focused && 'focused', - formControl && 'formControl', - size === 'small' && 'sizeSmall', - multiline && 'multiline', - Boolean(startAdornment) && 'adornedStart', - Boolean(endAdornment) && 'adornedEnd', - hiddenLabel && 'hiddenLabel', - readOnly && 'readOnly', - ], - input: [ - 'input', - disabled && 'disabled', - type === 'search' && 'inputTypeSearch', - multiline && 'inputMultiline', - size === 'small' && 'inputSizeSmall', - hiddenLabel && 'inputHiddenLabel', - Boolean(startAdornment) && 'inputAdornedStart', - Boolean(endAdornment) && 'inputAdornedEnd', - readOnly && 'readOnly', - ], - }; - - return composeClasses(slots, getInputBaseUtilityClass, classes); -}; - -export function rootOverridesResolver( - props: InputBaseRootSlotProps, - styles: Record<string, CSSInterpolation>, -) { - const { ownerState } = props; - - return [ - styles.root, - ownerState.formControl && styles.formControl, - ownerState.startAdornment && styles.adornedStart, - ownerState.endAdornment && styles.adornedEnd, - ownerState.error && styles.error, - ownerState.size === 'small' && styles.sizeSmall, - ownerState.multiline && styles.multiline, - ownerState.color && styles[`color${capitalize(ownerState.color)}`], - ownerState.fullWidth && styles.fullWidth, - ownerState.hiddenLabel && styles.hiddenLabel, - ]; -} - -export const InputBaseRoot = styled('div', { - name: 'MuiInputBase', - slot: 'Root', - overridesResolver: rootOverridesResolver, -})<{ ownerState: InputBaseOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - return { - fontFamily: tokens.sys.typescale.body[ownerState.size || 'medium'].family, - fontWeight: tokens.sys.typescale.body[ownerState.size || 'medium'].weight, - letterSpacing: tokens.sys.typescale.body[ownerState.size || 'medium'].tracking, - color: tokens.palette.text.primary, - lineHeight: '1.4375em', // 23px - boxSizing: 'border-box', // Prevent padding issue with fullWidth. - position: 'relative', - cursor: 'text', - display: 'inline-flex', - alignItems: 'center', - [`&.${inputBaseClasses.disabled}`]: { - color: tokens.palette.text.disabled, - cursor: 'default', - }, - ...(ownerState.multiline && { - padding: '4px 0 5px', - ...(ownerState.size === 'small' && { - paddingTop: 1, - }), - }), - ...(ownerState.fullWidth && { - width: '100%', - }), - }; -}); - -export function inputOverridesResolver( - props: InputBaseInputSlotProps, - styles: Record<string, CSSInterpolation>, -) { - const { ownerState } = props; - - return [ - styles.input, - ownerState.size === 'small' && styles.inputSizeSmall, - ownerState.multiline && styles.inputMultiline, - ownerState.type === 'search' && styles.inputTypeSearch, - ownerState.startAdornment && styles.inputAdornedStart, - ownerState.endAdornment && styles.inputAdornedEnd, - ownerState.hiddenLabel && styles.inputHiddenLabel, - ]; -} - -export const InputBaseInput = styled('input', { - name: 'MuiInputBase', - slot: 'Input', - overridesResolver: inputOverridesResolver, -})<{ ownerState: InputBaseOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - const placeholder = { - color: 'currentColor', - opacity: tokens.opacity.inputPlaceholder, - transition: theme.transitions.create('opacity', { - duration: theme.transitions.duration.shorter, - }), - }; - - const placeholderHidden = { - opacity: '0 !important', - }; - - const placeholderVisible = { - opacity: tokens.opacity.inputPlaceholder, - }; - - return { - font: 'inherit', - letterSpacing: 'inherit', - color: 'currentColor', - padding: '4px 0 5px', - border: 0, - boxSizing: 'content-box', - background: 'none', - height: '1.4375em', // Reset 23pxthe native input line-height - margin: 0, // Reset for Safari - WebkitTapHighlightColor: 'transparent', - display: 'block', - // Make the flex item shrink with Firefox - minWidth: 0, - width: '100%', // Fix IE11 width issue - animationName: 'mui-auto-fill-cancel', - animationDuration: '10ms', - '&::-webkit-input-placeholder': placeholder, - '&::-moz-placeholder': placeholder, // Firefox 19+ - '&:-ms-input-placeholder': placeholder, // IE11 - '&::-ms-input-placeholder': placeholder, // Edge - '&:focus': { - outline: 0, - }, - // Reset Firefox invalid required input style - '&:invalid': { - boxShadow: 'none', - }, - '&::-webkit-search-decoration': { - // Remove the padding when type=search. - WebkitAppearance: 'none', - }, - // Show and hide the placeholder logic - [`label[data-shrink=false] + .${inputBaseClasses.formControl} &`]: { - '&::-webkit-input-placeholder': placeholderHidden, - '&::-moz-placeholder': placeholderHidden, // Firefox 19+ - '&:-ms-input-placeholder': placeholderHidden, // IE11 - '&::-ms-input-placeholder': placeholderHidden, // Edge - '&:focus::-webkit-input-placeholder': placeholderVisible, - '&:focus::-moz-placeholder': placeholderVisible, // Firefox 19+ - '&:focus:-ms-input-placeholder': placeholderVisible, // IE11 - '&:focus::-ms-input-placeholder': placeholderVisible, // Edge - }, - [`&.${inputBaseClasses.disabled}`]: { - opacity: 1, // Reset iOS opacity - WebkitTextFillColor: tokens.palette.text.disabled, // Fix opacity Safari bug - }, - '&:-webkit-autofill': { - animationDuration: '5000s', - animationName: 'mui-auto-fill', - }, - ...(ownerState.size === 'small' && { - paddingTop: 1, - }), - ...(ownerState.multiline && { - height: 'auto', - resize: 'none', - padding: 0, - paddingTop: 0, - }), - ...(ownerState.type === 'search' && { - // Improve type search style. - MozAppearance: 'textfield', - }), - }; -}); - -const inputGlobalStyles = ( - <GlobalStyles - styles={{ - '@keyframes mui-auto-fill': { from: { display: 'block' } }, - '@keyframes mui-auto-fill-cancel': { from: { display: 'block' } }, - }} - /> -); - -/** - * `InputBase` contains as few styles as possible. - * It aims to be a simple building block for creating an input. - * It contains a load of style reset and some state logic. - */ -const InputBase = React.forwardRef(function InputBase< - BaseComponentType extends React.ElementType = InputBaseTypeMap['defaultComponent'], ->(inProps: InputBaseProps<BaseComponentType>, forwardedRef: React.ForwardedRef<any>) { - const props = useThemeProps({ props: inProps, name: 'MuiInputBase' }); - const { - 'aria-describedby': ariaDescribedby, - 'aria-label': ariaLabel, - 'aria-labelledby': ariaLabelledby, - autoComplete, - autoFocus, - className, - color: colorProp, - defaultValue, - disabled: disabledProp, - disableInjectingGlobalStyles, - endAdornment, - error: errorProp, - fullWidth = false, - id, - inputComponent: inputComponentProp = 'input', - inputRef: inputRefProp, - margin, - maxRows, - minRows, - multiline = false, - name, - onBlur, - onChange, - onClick, - onFocus, - onKeyDown, - onKeyUp, - placeholder, - readOnly, - renderSuffix, - required: requiredProp, - rows, - size: sizeProp, - slotProps = {}, - slots = {}, - startAdornment, - type = 'text', - value, - ...other - } = props; - - if (process.env.NODE_ENV !== 'production') { - const definedMultilineProps = (['rows', 'minRows', 'maxRows'] as const).filter( - (multilineProp) => props[multilineProp] !== undefined, - ); - - if (!multiline && definedMultilineProps.length > 0) { - console.error( - [ - 'MUI: You have set multiline props on an single-line input.', - 'Set the `multiline` prop if you want to render a multi-line input.', - 'Otherwise they will be ignored.', - `Ignored props: ${definedMultilineProps.join(', ')}`, - ].join('\n'), - ); - } - } - - const { current: isControlled } = React.useRef(value != null); - - const inputRef = React.useRef<HTMLInputElement>(); - - const muiFormControl = useFormControl(); - - if (process.env.NODE_ENV !== 'production') { - // eslint-disable-next-line react-hooks/rules-of-hooks - React.useEffect(() => { - if (muiFormControl && muiFormControl.registerEffect) { - return muiFormControl.registerEffect(); - } - return undefined; - }, [muiFormControl]); - } - - const onFilled = muiFormControl && muiFormControl.onFilled; - const onEmpty = muiFormControl && muiFormControl.onEmpty; - - // TODO: needs material-next/OutlinedInput - const checkDirty = React.useCallback( - (obj: any) => { - if (isFilled(obj)) { - if (onFilled) { - onFilled(); - } - } else if (onEmpty) { - onEmpty(); - } - }, - [onFilled, onEmpty], - ); - - useEnhancedEffect(() => { - if (isControlled) { - checkDirty({ value }); - } - }, [value, checkDirty, isControlled]); - - const handleFocus = (event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => { - if (onFocus) { - onFocus(event); - } - - if (muiFormControl && muiFormControl.onFocus) { - muiFormControl.onFocus(event); - } - }; - - const handleBlur = (event?: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => { - if (onBlur) { - onBlur(event); - } - - if (muiFormControl && muiFormControl.onBlur) { - muiFormControl.onBlur(event); - } - }; - - const handleChange = ( - event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, - ...args: any[] - ) => { - if (!isControlled && event.target != null) { - checkDirty({ - value: event.target.value, - }); - } - - // Perform in the willUpdate - if (onChange) { - // @ts-ignore - onChange(event, ...args); - } - }; - - const handleClick = (event: React.PointerEvent) => { - if (onClick) { - onClick(event); - } - }; - - React.useEffect(() => { - if (muiFormControl) { - muiFormControl.setAdornedStart(Boolean(startAdornment)); - } - }, [muiFormControl, startAdornment]); - - const required = requiredProp ?? muiFormControl?.required; - - const handleInputRef = useForkRef(inputRefProp, inputRef); - - const { - getRootProps, - getInputProps, - focused: focusedState, - error: errorState, - disabled: disabledState, - // ignore Base UI's formControlContext - } = useInput({ - disabled: disabledProp ?? muiFormControl?.disabled, - defaultValue, - error: errorProp ?? muiFormControl?.error, - onBlur: handleBlur, - onClick: handleClick, - onChange: handleChange, - onFocus: handleFocus, - required, - value, - inputRef: handleInputRef, - }); - - const ownerState = { - ...props, - color: colorProp ?? muiFormControl?.color ?? 'primary', - disabled: disabledState, - endAdornment, - error: errorState, - focused: muiFormControl?.focused ?? focusedState, - formControl: muiFormControl, - fullWidth, - hiddenLabel: muiFormControl?.hiddenLabel ?? false, - multiline, - required, - size: sizeProp ?? muiFormControl?.size, - startAdornment, - type, - }; - - const classes = useUtilityClasses(ownerState); - - const propsToForwardToInputSlot = { - 'aria-describedby': ariaDescribedby, - 'aria-label': ariaLabel, - 'aria-labelledby': ariaLabelledby, - autoComplete, - autoFocus, - id, - onKeyDown, - onKeyUp, - name, - placeholder, - readOnly, - type, - }; - - let InputComponent = inputComponentProp; - if (multiline && InputComponent === 'input') { - InputComponent = TextareaAutosize; - } - - const Root = slots.root || InputBaseRoot; - const rootProps: WithOptionalOwnerState<InputBaseRootSlotProps> = useSlotProps({ - elementType: Root, - getSlotProps: getRootProps, - externalSlotProps: slotProps.root, - externalForwardedProps: other, - additionalProps: { - ref: forwardedRef, - }, - ownerState, - className: [classes.root, className], - }); - - const Input = multiline ? slots.textarea ?? TextareaAutosize : slots.input ?? InputBaseInput; - - const inputProps: WithOptionalOwnerState<InputBaseInputSlotProps> = useSlotProps({ - // TextareaAutosize doesn't support ownerState, we manually change the - // elementType so ownerState is excluded from the return value (this doesn't - // affect other returned props) - elementType: Input === TextareaAutosize ? 'textarea' : Input, - getSlotProps: (otherHandlers: EventHandlers) => { - return getInputProps({ - ...propsToForwardToInputSlot, - ...otherHandlers, - }); - }, - externalSlotProps: slotProps.input, - additionalProps: { - as: InputComponent, - rows: multiline ? rows : undefined, - ...(multiline && - !isHostComponent(Input) && { - minRows: rows || minRows, - maxRows: rows || maxRows, - }), - }, - ownerState, - className: classes.input, - }); - - if (process.env.NODE_ENV !== 'production') { - if (multiline) { - if (rows) { - if (minRows || maxRows) { - console.warn( - 'MUI: You can not use the `minRows` or `maxRows` props when the input `rows` prop is set.', - ); - } - } - } - } - - const handleAutoFill = (event: React.AnimationEvent) => { - // Provide a fake value as Chrome might not let you access it for security reasons. - checkDirty(event.animationName === 'mui-auto-fill-cancel' ? inputRef?.current : { value: 'x' }); - }; - - // Check the input state on mount, in case it was filled by the user - // or auto filled by the browser before the hydration (for SSR). - React.useEffect(() => { - checkDirty(inputRef?.current); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( - <React.Fragment> - {!disableInjectingGlobalStyles && inputGlobalStyles} - <Root {...rootProps}> - {startAdornment} - <FormControlContext.Provider value={undefined}> - <Input onAnimationStart={handleAutoFill} rows={rows} {...inputProps} /> - </FormControlContext.Provider> - {endAdornment} - {renderSuffix - ? renderSuffix({ - // TODO: requires integrating with OutlinedInput - // ...formControlState({ - // props, - // muiFormControl, - // states: ['color', 'disabled', 'error', 'hiddenLabel', 'size', 'required', 'filled'] - // }), - ...muiFormControl, - startAdornment, - }) - : null} - </Root> - </React.Fragment> - ); -}) as InputBaseComponent; - -interface InputBaseComponent { - <C extends React.ElementType>( - props: { - /** - * The component used for the input node. - * Either a string to use a HTML element or a component. - * @default 'input' - */ - inputComponent?: C; - } & OverrideProps<InputBaseTypeMap, C>, - ): JSX.Element | null; - (props: DefaultComponentProps<InputBaseTypeMap>): JSX.Element | null; - propTypes?: any; -} - -InputBase.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 - */ - 'aria-describedby': PropTypes.string, - /** - * Defines a string value that labels the current element. - * @see aria-labelledby. - */ - 'aria-label': PropTypes.string, - /** - * Identifies the element (or elements) that labels the current element. - * @see aria-describedby. - */ - 'aria-labelledby': PropTypes.string, - /** - * This prop helps users to fill forms faster, especially on mobile devices. - * The name can be confusing, as it's more like an autofill. - * You can learn more about it [following the specification](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill). - */ - autoComplete: PropTypes.string, - /** - * If `true`, the `input` element is focused during the first mount. - */ - autoFocus: PropTypes.bool, - /** - * @ignore - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * The prop defaults to the value (`'primary'`) inherited from the parent FormControl component. - */ - color: PropTypes.oneOf([ - 'error', - 'info', - 'primary', - 'secondary', - 'success', - 'tertiary', - 'warning', - ]), - /** - * The default value. Use when the component is not controlled. - */ - defaultValue: PropTypes.any, - /** - * If `true`, the component is disabled. - * The prop defaults to the value (`false`) inherited from the parent FormControl component. - */ - disabled: PropTypes.bool, - /** - * If `true`, GlobalStyles for the auto-fill keyframes will not be injected/removed on mount/unmount. Make sure to inject them at the top of your application. - * This option is intended to help with boosting the initial rendering performance if you are loading a big amount of Input components at once. - * @default false - */ - disableInjectingGlobalStyles: PropTypes.bool, - /** - * End `InputAdornment` for this component. - */ - endAdornment: PropTypes.node, - /** - * If `true`, the `input` will indicate an error. - * The prop defaults to the value (`false`) inherited from the parent FormControl component. - */ - error: PropTypes.bool, - /** - * If `true`, the `input` will take up the full width of its container. - * @default false - */ - fullWidth: PropTypes.bool, - /** - * The id of the `input` element. - */ - id: PropTypes.string, - /** - * The component used for the input node. - * Either a string to use a HTML element or a component. - * @default 'input' - */ - inputComponent: PropTypes.elementType, - /** - * Pass a ref to the `input` element. - */ - inputRef: refType, - /** - * If `dense`, will adjust vertical spacing. This is normally obtained via context from - * FormControl. - * The prop defaults to the value (`'none'`) inherited from the parent FormControl component. - */ - margin: PropTypes.oneOf(['dense', 'none']), - /** - * Maximum number of rows to display when multiline option is set to true. - */ - maxRows: PropTypes.number, - /** - * Minimum number of rows to display when multiline option is set to true. - */ - minRows: PropTypes.number, - /** - * If `true`, a `textarea` element is rendered. - * @default false - */ - multiline: PropTypes.bool, - /** - * Name attribute of the `input` element. - */ - name: PropTypes.string, - /** - * Callback fired when the `input` is blurred. - * - * Notice that the first argument (event) might be undefined. - */ - onBlur: PropTypes.func, - /** - * Callback fired when the value is changed. - * - * @param {React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>} event The event source of the callback. - * You can pull out the new value by accessing `event.target.value` (string). - */ - onChange: PropTypes.func, - /** - * @ignore - */ - onClick: PropTypes.func, - /** - * @ignore - */ - onFocus: PropTypes.func, - /** - * Callback fired when the `input` doesn't satisfy its constraints. - */ - onInvalid: PropTypes.func, - /** - * @ignore - */ - onKeyDown: PropTypes.func, - /** - * @ignore - */ - onKeyUp: PropTypes.func, - /** - * The short hint displayed in the `input` before the user enters a value. - */ - placeholder: PropTypes.string, - /** - * It prevents the user from changing the value of the field - * (not from interacting with the field). - */ - readOnly: PropTypes.bool, - /** - * @ignore - */ - renderSuffix: PropTypes.func, - /** - * If `true`, the `input` element is required. - * The prop defaults to the value (`false`) inherited from the parent FormControl component. - */ - required: PropTypes.bool, - /** - * Number of rows to display when multiline option is set to true. - */ - rows: PropTypes.number, - /** - * The size of the component. - */ - size: PropTypes.oneOf(['small', 'medium']), - /** - * The props used for each slot inside the Input. - * @default {} - */ - slotProps: PropTypes.shape({ - input: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - }), - /** - * The components used for each slot inside the InputBase. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots: PropTypes.shape({ - input: PropTypes.elementType, - root: PropTypes.elementType, - textarea: PropTypes.elementType, - }), - /** - * Start `InputAdornment` for this component. - */ - startAdornment: PropTypes.node, - /** - * 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, - ]), - /** - * Type of the `input` element. It should be [a valid HTML5 input type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Form_%3Cinput%3E_types). - * @default 'text' - */ - type: PropTypes /* @typescript-to-proptypes-ignore */.oneOf([ - 'button', - 'checkbox', - 'color', - 'date', - 'datetime-local', - 'email', - 'file', - 'hidden', - 'image', - 'month', - 'number', - 'password', - 'radio', - 'range', - 'reset', - 'search', - 'submit', - 'tel', - 'text', - 'time', - 'url', - 'week', - ]), - /** - * The value of the `input` element, required for a controlled component. - */ - value: PropTypes.any, -} as any; - -export default InputBase; diff --git a/packages/mui-material-next/src/InputBase/InputBase.types.ts b/packages/mui-material-next/src/InputBase/InputBase.types.ts deleted file mode 100644 index 9d0f0e7b80e061..00000000000000 --- a/packages/mui-material-next/src/InputBase/InputBase.types.ts +++ /dev/null @@ -1,257 +0,0 @@ -import * as React from 'react'; -import { SlotComponentProps } from '@mui/base'; -import { FormControlContextValue } from '@mui/material-next/FormControl/FormControlContext'; -import { UseInputRootSlotProps } from '@mui/base/useInput'; -import { OverridableStringUnion, OverrideProps, Simplify } from '@mui/types'; -import { SxProps } from '../styles'; -import { InputBaseClasses } from './inputBaseClasses'; - -export interface InputBasePropsSizeOverrides {} -export interface InputBasePropsColorOverrides {} -export interface InputBaseRootSlotPropsOverrides {} -export interface InputBaseInputSlotPropsOverrides {} - -export type InputBaseOwnProps = { - 'aria-describedby'?: string; - /** - * This prop helps users to fill forms faster, especially on mobile devices. - * The name can be confusing, as it's more like an autofill. - * You can learn more about it [following the specification](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill). - */ - autoComplete?: string; - /** - * If `true`, the `input` element is focused during the first mount. - */ - autoFocus?: boolean; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<InputBaseClasses>; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * The prop defaults to the value (`'primary'`) inherited from the parent FormControl component. - */ - color?: OverridableStringUnion< - 'primary' | 'secondary' | 'tertiary' | 'error' | 'info' | 'success' | 'warning', - InputBasePropsColorOverrides - >; - /** - * The default value. Use when the component is not controlled. - */ - defaultValue?: unknown; - /** - * If `true`, the component is disabled. - * The prop defaults to the value (`false`) inherited from the parent FormControl component. - */ - disabled?: boolean; - /** - * If `true`, GlobalStyles for the auto-fill keyframes will not be injected/removed on mount/unmount. Make sure to inject them at the top of your application. - * This option is intended to help with boosting the initial rendering performance if you are loading a big amount of Input components at once. - * @default false - */ - disableInjectingGlobalStyles?: boolean; - /** - * End `InputAdornment` for this component. - */ - endAdornment?: React.ReactNode; - /** - * If `true`, the `input` will indicate an error. - * The prop defaults to the value (`false`) inherited from the parent FormControl component. - */ - error?: boolean; - /** - * If `true`, the `input` will take up the full width of its container. - * @default false - */ - fullWidth?: boolean; - /** - * The id of the `input` element. - */ - id?: string; - /** - * Pass a ref to the `input` element. - */ - inputRef?: React.Ref<any>; - /** - * If `dense`, will adjust vertical spacing. This is normally obtained via context from - * FormControl. - * The prop defaults to the value (`'none'`) inherited from the parent FormControl component. - */ - margin?: 'dense' | 'none'; - /** - * Name attribute of the `input` element. - */ - name?: string; - /** - * Callback fired when the `input` is blurred. - * - * Notice that the first argument (event) might be undefined. - */ - onBlur?: (event?: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void; - /** - * Callback fired when the value is changed. - * - * @param {React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>} event The event source of the callback. - * You can pull out the new value by accessing `event.target.value` (string). - */ - onChange?: React.ChangeEventHandler<HTMLTextAreaElement | HTMLInputElement>; - onFocus?: React.FocusEventHandler<HTMLInputElement | HTMLTextAreaElement>; - onKeyDown?: React.KeyboardEventHandler<HTMLInputElement | HTMLTextAreaElement>; - onKeyUp?: React.KeyboardEventHandler<HTMLInputElement | HTMLTextAreaElement>; - /** - * Callback fired when the `input` doesn't satisfy its constraints. - */ - onInvalid?: React.FormEventHandler<HTMLInputElement | HTMLTextAreaElement>; - /** - * The short hint displayed in the `input` before the user enters a value. - */ - placeholder?: string; - /** - * It prevents the user from changing the value of the field - * (not from interacting with the field). - */ - readOnly?: boolean; - /** - * If `true`, the `input` element is required. - * The prop defaults to the value (`false`) inherited from the parent FormControl component. - */ - required?: boolean; - renderSuffix?: (state: { - disabled?: boolean; - error?: boolean; - filled?: boolean; - focused?: boolean; - margin?: 'dense' | 'none' | 'normal'; - required?: boolean; - startAdornment?: React.ReactNode; - }) => React.ReactNode; - /** - * The size of the component. - */ - size?: OverridableStringUnion<'small' | 'medium', InputBasePropsSizeOverrides>; - /** - * The props used for each slot inside the Input. - * @default {} - */ - slotProps?: { - root?: SlotComponentProps<'div', InputBaseRootSlotPropsOverrides, InputBaseOwnerState> & { - sx?: SxProps; - }; - input?: SlotComponentProps<'input', InputBaseInputSlotPropsOverrides, InputBaseOwnerState> & { - sx?: SxProps; - }; - }; - /** - * The components used for each slot inside the InputBase. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots?: InputBaseSlots; - /** - * Start `InputAdornment` for this component. - */ - startAdornment?: React.ReactNode; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; - /** - * The value of the `input` element, required for a controlled component. - */ - value?: unknown; - /** - * Maximum number of rows to display when multiline option is set to true. - */ - maxRows?: number; - /** - * Minimum number of rows to display when multiline option is set to true. - */ - minRows?: number; - /** - * If `true`, a `textarea` element is rendered. - * @default false - */ - multiline?: boolean; - /** - * Number of rows to display when multiline option is set to true. - */ - rows?: number; - /** - * Type of the `input` element. It should be [a valid HTML5 input type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Form_%3Cinput%3E_types). - * @default 'text' - */ - type?: React.HTMLInputTypeAttribute; -}; - -export interface InputBaseSlots { - /** - * The component that renders the root. - * @default 'div' - */ - root?: React.ElementType; - /** - * The component that renders the input. - * @default 'input' - */ - input?: React.ElementType; - /** - * The component that renders the textarea. - * @default 'textarea' - */ - textarea?: React.ElementType; -} - -export interface InputBaseTypeMap< - AdditionalProps = {}, - RootComponentType extends React.ElementType = 'div', -> { - props: InputBaseOwnProps & AdditionalProps; - defaultComponent: RootComponentType; -} - -export type InputBaseProps< - RootComponentType extends React.ElementType = InputBaseTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<InputBaseTypeMap<AdditionalProps, RootComponentType>, RootComponentType> & { - inputComponent?: React.ElementType; -}; - -export type InputBaseOwnerState = Simplify< - InputBaseOwnProps & { - formControl: FormControlContextValue | undefined; - hiddenLabel?: boolean; - focused: boolean; - type: React.InputHTMLAttributes<HTMLInputElement>['type'] | undefined; - } ->; - -export type InputBaseRootSlotProps = Simplify< - UseInputRootSlotProps & { - ownerState: InputBaseOwnerState; - className?: string; - children?: React.ReactNode; - ref?: React.Ref<HTMLDivElement>; - } ->; - -export type InputBaseInputSlotProps = Simplify< - Omit<UseInputRootSlotProps, 'onClick'> & { - 'aria-describedby': React.AriaAttributes['aria-describedby']; - 'aria-label': React.AriaAttributes['aria-label']; - 'aria-labelledby': React.AriaAttributes['aria-labelledby']; - autoComplete: string | undefined; - autoFocus: boolean | undefined; - className?: string; - id: string | undefined; - name: string | undefined; - onKeyDown: React.KeyboardEventHandler<HTMLInputElement> | undefined; - onKeyUp: React.KeyboardEventHandler<HTMLInputElement> | undefined; - ownerState: InputBaseOwnerState; - placeholder: string | undefined; - readOnly: boolean | undefined; - ref: React.Ref<HTMLInputElement>; - type: React.HTMLInputTypeAttribute | undefined; - } ->; diff --git a/packages/mui-material-next/src/InputBase/index.d.ts b/packages/mui-material-next/src/InputBase/index.d.ts deleted file mode 100644 index b000508c465fb1..00000000000000 --- a/packages/mui-material-next/src/InputBase/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './InputBase'; -export * from './InputBase'; - -export { default as inputBaseClasses } from './inputBaseClasses'; -export * from './inputBaseClasses'; diff --git a/packages/mui-material-next/src/InputBase/index.js b/packages/mui-material-next/src/InputBase/index.js deleted file mode 100644 index 305647eb07d08b..00000000000000 --- a/packages/mui-material-next/src/InputBase/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './InputBase'; - -export { default as inputBaseClasses } from './inputBaseClasses'; -export * from './inputBaseClasses'; diff --git a/packages/mui-material-next/src/InputBase/inputBaseClasses.ts b/packages/mui-material-next/src/InputBase/inputBaseClasses.ts deleted file mode 100644 index 6db998b77449e5..00000000000000 --- a/packages/mui-material-next/src/InputBase/inputBaseClasses.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { - unstable_generateUtilityClass as generateUtilityClass, - unstable_generateUtilityClasses as generateUtilityClasses, -} from '@mui/utils'; - -export interface InputBaseClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if the component is a descendant of `FormControl`. */ - formControl: string; - /** Styles applied to the root element if the component is focused. */ - focused: string; - /** Styles applied to the root element if `disabled={true}`. */ - disabled: string; - /** Styles applied to the root element if `startAdornment` is provided. */ - adornedStart: string; - /** Styles applied to the root element if `endAdornment` is provided. */ - adornedEnd: string; - /** State class applied to the root element if `error={true}`. */ - error: string; - /** Styles applied to the input element if `size="small"`. */ - sizeSmall: string; - /** Styles applied to the root element if `multiline={true}`. */ - multiline: string; - /** Styles applied to the root element if the color is secondary. */ - colorSecondary: string; - /** Styles applied to the root element if `fullWidth={true}`. */ - fullWidth: string; - /** Styles applied to the root element if `hiddenLabel={true}`. */ - hiddenLabel: string; - /** State class applied to the root element if `readOnly={true}`. */ - readOnly: string; - /** Styles applied to the input element. */ - input: string; - /** Styles applied to the input element if `size="small"`. */ - inputSizeSmall: string; - /** Styles applied to the input element if `multiline={true}`. */ - inputMultiline: string; - /** Styles applied to the input element if `type="search"`. */ - inputTypeSearch: string; - /** Styles applied to the input element if `startAdornment` is provided. */ - inputAdornedStart: string; - /** Styles applied to the input element if `endAdornment` is provided. */ - inputAdornedEnd: string; - /** Styles applied to the input element if `hiddenLabel={true}`. */ - inputHiddenLabel: string; -} - -export type InputBaseClassKey = keyof InputBaseClasses; - -export function getInputBaseUtilityClass(slot: string): string { - return generateUtilityClass('MuiInputBase', slot); -} - -const inputBaseClasses: InputBaseClasses = generateUtilityClasses('MuiInputBase', [ - 'root', - 'formControl', - 'focused', - 'disabled', - 'adornedStart', - 'adornedEnd', - 'error', - 'sizeSmall', - 'multiline', - 'colorSecondary', - 'fullWidth', - 'hiddenLabel', - 'readOnly', - 'input', - 'inputSizeSmall', - 'inputMultiline', - 'inputTypeSearch', - 'inputAdornedStart', - 'inputAdornedEnd', - 'inputHiddenLabel', -]); - -export default inputBaseClasses; diff --git a/packages/mui-material-next/src/InputBase/utils.test.js b/packages/mui-material-next/src/InputBase/utils.test.js deleted file mode 100644 index 6d1161118fd59a..00000000000000 --- a/packages/mui-material-next/src/InputBase/utils.test.js +++ /dev/null @@ -1,39 +0,0 @@ -import { expect } from 'chai'; -import { hasValue, isFilled } from './utils'; - -describe('Input/utils.js', () => { - describe('hasValue', () => { - ['', 0].forEach((value) => { - it(`is true for ${value}`, () => { - expect(hasValue(value)).to.equal(true); - }); - }); - - [null, undefined].forEach((value) => { - it(`is false for ${value}`, () => { - expect(hasValue(value)).to.equal(false); - }); - }); - }); - - describe('isFilled', () => { - [' ', 0].forEach((value) => { - it(`is true for value ${value}`, () => { - expect(isFilled({ value })).to.equal(true); - }); - - it(`is true for SSR defaultValue ${value}`, () => { - expect(isFilled({ defaultValue: value }, true)).to.equal(true); - }); - }); - [null, undefined, ''].forEach((value) => { - it(`is false for value ${value}`, () => { - expect(isFilled({ value })).to.equal(false); - }); - - it(`is false for SSR defaultValue ${value}`, () => { - expect(isFilled({ defaultValue: value }, true)).to.equal(false); - }); - }); - }); -}); diff --git a/packages/mui-material-next/src/InputBase/utils.ts b/packages/mui-material-next/src/InputBase/utils.ts deleted file mode 100644 index c1d4568c218a25..00000000000000 --- a/packages/mui-material-next/src/InputBase/utils.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Supports determination of isControlled(). -// Controlled input accepts its current value as a prop. -// -// @see https://facebook.github.io/react/docs/forms.html#controlled-components -// @param value -// @returns {boolean} true if string (including '') or number (including zero) - -export function hasValue(value: string | any[] | null | undefined) { - return value != null && !(Array.isArray(value) && value.length === 0); -} - -// Determine if field is empty or filled. -// Response determines if label is presented above field or as placeholder. -// -// @param obj -// @param SSR -// @returns {boolean} False when not present or empty string. -// True when any number or string with length. -export function isFilled(obj: any, SSR = false) { - return ( - obj && - ((hasValue(obj.value) && obj.value !== '') || - (SSR && hasValue(obj.defaultValue) && obj.defaultValue !== '')) - ); -} - -// Determine if an Input is adorned on start. -// It's corresponding to the left with LTR. -// -// @param obj -// @returns {boolean} False when no adornments. -// True when adorned at the start. -export function isAdornedStart(obj: any) { - // TODO: integrate with form control - return obj.startAdornment; -} diff --git a/packages/mui-material-next/src/InputLabel/InputLabel.spec.tsx b/packages/mui-material-next/src/InputLabel/InputLabel.spec.tsx deleted file mode 100644 index e9e6603b0fa0c8..00000000000000 --- a/packages/mui-material-next/src/InputLabel/InputLabel.spec.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import * as React from 'react'; -import { expectType } from '@mui/types'; -import InputLabel from '@mui/material/InputLabel'; - -const CustomComponent: React.FC<{ prop1: string; prop2: number }> = function CustomComponent() { - return <div />; -}; - -const InputLabelTest = () => { - return ( - <div> - <InputLabel /> - <InputLabel component="legend" /> - <InputLabel - component="legend" - onClick={(event) => { - expectType<React.MouseEvent<HTMLLegendElement, MouseEvent>, typeof event>(event); - }} - /> - - {/* @ts-expect-error */} - <InputLabel component="a" incorrectAttribute="url" /> - {/* @ts-expect-error */} - <InputLabel component="div" href="url" /> - <InputLabel component={CustomComponent} prop1="1" prop2={12} /> - {/* @ts-expect-error */} - <InputLabel component={CustomComponent} prop1="1" /> - {/* @ts-expect-error */} - <InputLabel component={CustomComponent} prop1="1" prop2="12" /> - </div> - ); -}; diff --git a/packages/mui-material-next/src/InputLabel/InputLabel.test.tsx b/packages/mui-material-next/src/InputLabel/InputLabel.test.tsx deleted file mode 100644 index f507dc6de95c42..00000000000000 --- a/packages/mui-material-next/src/InputLabel/InputLabel.test.tsx +++ /dev/null @@ -1,225 +0,0 @@ -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { expect } from 'chai'; -import { act, createRenderer } from '@mui-internal/test-utils'; -import { ClassNames } from '@emotion/react'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import FormControl from '@mui/material-next/FormControl'; -import FilledInput from '@mui/material-next/FilledInput'; -import FormLabel from '@mui/material-next/FormLabel'; -import InputLabel, { inputLabelClasses as classes } from '@mui/material-next/InputLabel'; -import describeConformance from '../../test/describeConformance'; - -describe('<InputLabel />', () => { - let originalMatchmedia: typeof window.matchMedia; - - beforeEach(() => { - originalMatchmedia = window.matchMedia; - window.matchMedia = () => - ({ - addListener: () => {}, - removeListener: () => {}, - }) as unknown as MediaQueryList; - }); - - afterEach(() => { - window.matchMedia = originalMatchmedia; - }); - - const { render } = createRenderer(); - - describeConformance(<InputLabel>Foo</InputLabel>, () => ({ - classes, - inheritComponent: FormLabel, - render, - refInstanceof: window.HTMLLabelElement, - muiName: 'MuiInputLabel', - testVariantProps: { size: 'small' }, - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - slots: { - root: { - expectedClassName: classes.root, - }, - }, - skip: ['componentsProp'], - })); - - it('should render a label with text', () => { - const { container } = render(<InputLabel>Foo</InputLabel>); - expect(container.querySelector('label')).to.have.text('Foo'); - }); - - it('should have the animated class by default', () => { - const { container } = render(<InputLabel>Foo</InputLabel>); - expect(container.firstChild).to.have.class(classes.animated); - }); - - it('should not have the animated class when disabled', () => { - const { container } = render(<InputLabel disableAnimation>Foo</InputLabel>); - expect(container.firstChild).not.to.have.class(classes.animated); - }); - - it('should forward the asterisk class to AsteriskComponent when required', () => { - const { container } = render( - <InputLabel classes={{ asterisk: 'my-asterisk' }} required> - Foo - </InputLabel>, - ); - expect(container.querySelector('.my-asterisk')).to.have.class('MuiFormLabel-asterisk'); - expect(container.querySelector('.my-asterisk')).to.have.class('MuiInputLabel-asterisk'); - }); - - describe('with FormControl', () => { - it('should have the formControl class', () => { - const { getByTestId } = render( - <FormControl> - <InputLabel data-testid="root" /> - </FormControl>, - ); - expect(getByTestId('root')).to.have.class(classes.formControl); - }); - - it('should have the small class', () => { - const { getByTestId } = render( - <FormControl size="small"> - <InputLabel data-testid="root" /> - </FormControl>, - ); - - expect(getByTestId('root')).to.have.class(classes.sizeSmall); - }); - - describe('filled', () => { - it('applies a shrink class that can be controlled by props', () => { - function Wrapper({ children }: { children?: React.ReactNode }) { - return ( - <FormControl> - <FilledInput defaultValue="Dave" /> - {children} - </FormControl> - ); - } - Wrapper.propTypes = { children: PropTypes.node }; - const { getByTestId, setProps } = render(<InputLabel data-testid="root">name</InputLabel>, { - wrapper: Wrapper, - }); - - expect(getByTestId('root')).to.have.class(classes.shrink); - - setProps({ shrink: false }); - expect(getByTestId('root')).not.to.have.class(classes.shrink); - - setProps({ shrink: true }); - expect(getByTestId('root')).to.have.class(classes.shrink); - }); - }); - - describe('focused', () => { - it('applies a shrink class that can be controlled by props', () => { - function Wrapper({ children }: { children?: React.ReactNode }) { - return ( - <FormControl> - <FilledInput /> - {children} - </FormControl> - ); - } - Wrapper.propTypes = { children: PropTypes.node }; - - const { getByRole, getByTestId, setProps } = render(<InputLabel data-testid="root" />, { - wrapper: Wrapper, - }); - - const input = getByRole('textbox'); - const label = getByTestId('root'); - - act(() => { - input.focus(); - }); - - expect(label).to.have.class(classes.shrink); - - setProps({ shrink: false }); - expect(label).not.to.have.class(classes.shrink); - - setProps({ shrink: true }); - expect(label).to.have.class(classes.shrink); - }); - - it('provides ownerState.focused in styleOverrides', () => { - const theme = extendTheme({ - components: { - MuiInputLabel: { - styleOverrides: { - root: (props) => { - return { - ...(props.ownerState.focused === true && { - mixBlendMode: 'darken', - }), - }; - }, - }, - }, - }, - }); - - const { getByText } = render( - <CssVarsProvider theme={theme}> - <FormControl focused> - <InputLabel>Test Label</InputLabel> - </FormControl> - </CssVarsProvider>, - ); - - const label = getByText('Test Label'); - - expect(label).to.toHaveComputedStyle({ - mixBlendMode: 'darken', - }); - }); - }); - }); - - describe('Emotion compatibility', () => { - it('classes.root should overwrite built-in styles.', () => { - const text = 'The label'; - - const { getByText } = render( - <ClassNames> - {({ css }) => ( - <FormControl> - <InputLabel classes={{ root: css({ position: 'static' }) }}>{text}</InputLabel> - </FormControl> - )} - </ClassNames>, - ); - const label = getByText(text); - - expect(getComputedStyle(label).position).to.equal('static'); - }); - - it('className should overwrite classes.root and built-in styles.', () => { - const text = 'The label'; - - const { getByText } = render( - <ClassNames> - {({ css }) => ( - <FormControl> - <InputLabel - color="primary" - className={css({ position: 'static' })} - classes={{ root: css({ position: 'sticky' }) }} - > - {text} - </InputLabel> - </FormControl> - )} - </ClassNames>, - ); - const label = getByText(text); - - expect(getComputedStyle(label).position).to.equal('static'); - }); - }); -}); diff --git a/packages/mui-material-next/src/InputLabel/InputLabel.tsx b/packages/mui-material-next/src/InputLabel/InputLabel.tsx deleted file mode 100644 index b612cb87573d66..00000000000000 --- a/packages/mui-material-next/src/InputLabel/InputLabel.tsx +++ /dev/null @@ -1,255 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { unstable_composeClasses as composeClasses, useSlotProps } from '@mui/base'; -import { OverridableComponent } from '@mui/types'; -import { unstable_capitalize as capitalize } from '@mui/utils'; -import useFormControl from '../FormControl/useFormControl'; -import FormLabel, { formLabelClasses } from '../FormLabel'; -import useThemeProps from '../styles/useThemeProps'; -import styled, { rootShouldForwardProp } from '../styles/styled'; -import { getInputLabelUtilityClasses } from './inputLabelClasses'; -import { InputLabelProps, InputLabelTypeMap, InputLabelOwnerState } from './InputLabel.types'; - -const useUtilityClasses = (ownerState: InputLabelOwnerState) => { - const { classes, formControl, size, shrink, disableAnimation, variant, required } = ownerState; - const slots = { - root: [ - 'root', - formControl && 'formControl', - !disableAnimation && 'animated', - shrink && 'shrink', - size && size !== 'normal' && `size${capitalize(size)}`, - variant, - ], - asterisk: [required && 'asterisk'], - }; - - const composedClasses = composeClasses(slots, getInputLabelUtilityClasses, classes); - - return { - ...classes, // forward the focused, disabled, etc. classes to the FormLabel - ...composedClasses, - }; -}; - -const InputLabelRoot = styled(FormLabel, { - shouldForwardProp: (prop) => rootShouldForwardProp(prop) || prop === 'classes', - name: 'MuiInputLabel', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - return [ - { [`& .${formLabelClasses.asterisk}`]: styles.asterisk }, - styles.root, - ownerState.formControl && styles.formControl, - ownerState.size === 'small' && styles.sizeSmall, - ownerState.shrink && styles.shrink, - !ownerState.disableAnimation && styles.animated, - ownerState.focused && styles.focused, - styles[ownerState.variant], - ]; - }, -})<{ ownerState: InputLabelOwnerState }>(({ theme, ownerState }) => ({ - display: 'block', - transformOrigin: 'top left', - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - maxWidth: '100%', - ...(ownerState.formControl && { - position: 'absolute', - left: 0, - top: 0, - // slight alteration to spec spacing to match visual spec result - transform: 'translate(0, 20px) scale(1)', - }), - ...(ownerState.size === 'small' && { - // Compensation for the `Input.inputSizeSmall` style. - transform: 'translate(0, 17px) scale(1)', - }), - ...(ownerState.shrink && { - transform: 'translate(0, -1.5px) scale(0.75)', - transformOrigin: 'top left', - maxWidth: '133%', - }), - ...(!ownerState.disableAnimation && { - transition: theme.transitions.create(['color', 'transform', 'max-width'], { - duration: theme.transitions.duration.shorter, - easing: theme.transitions.easing.easeOut, - }), - }), - ...(ownerState.variant === 'filled' && { - // Chrome's autofill feature gives the input field a yellow background. - // Since the input field is behind the label in the HTML tree, - // the input field is drawn last and hides the label with an opaque background color. - // zIndex: 1 will raise the label above opaque background-colors of input. - zIndex: 1, - pointerEvents: 'none', - transform: 'translate(16px, 16px) scale(1)', - maxWidth: 'calc(100% - 24px)', - ...(ownerState.size === 'small' && { - transform: 'translate(12px, 13px) scale(1)', - }), - ...(ownerState.shrink && { - userSelect: 'none', - pointerEvents: 'auto', - transform: 'translate(16px, 7px) scale(0.75)', - maxWidth: 'calc(133% - 24px)', - ...(ownerState.size === 'small' && { - transform: 'translate(12px, 4px) scale(0.75)', - }), - }), - }), - ...(ownerState.variant === 'outlined' && { - // see comment above on filled.zIndex - zIndex: 1, - pointerEvents: 'none', - transform: 'translate(14px, 16px) scale(1)', - maxWidth: 'calc(100% - 24px)', - ...(ownerState.size === 'small' && { - transform: 'translate(14px, 9px) scale(1)', - }), - ...(ownerState.shrink && { - userSelect: 'none', - pointerEvents: 'auto', - // Theoretically, we should have (8+5)*2/0.75 = 34px - // but it feels a better when it bleeds a bit on the left, so 32px. - maxWidth: 'calc(133% - 32px)', - transform: 'translate(14px, -9px) scale(0.75)', - }), - }), -})); - -const InputLabel = React.forwardRef(function InputLabel< - RootComponentType extends React.ElementType = InputLabelTypeMap['defaultComponent'], ->(inProps: InputLabelProps<RootComponentType>, forwardedRef: React.ForwardedRef<Element>) { - const props = useThemeProps({ name: 'MuiInputLabel', props: inProps }); - const { - disableAnimation = false, - margin, - focused: focusedProp, - required: requiredProp, - shrink: shrinkProp, - size: sizeProp, - variant: variantProp, - slots = {}, - slotProps = {}, - ...other - } = props; - - const muiFormControl = useFormControl(); - - let shrink = shrinkProp; - if (typeof shrink === 'undefined' && muiFormControl) { - shrink = muiFormControl.filled || muiFormControl.focused || muiFormControl.adornedStart; - } - - const ownerState: InputLabelOwnerState = { - ...props, - disableAnimation, - formControl: muiFormControl, - shrink, - size: sizeProp ?? muiFormControl?.size, - variant: variantProp ?? muiFormControl?.variant, - required: requiredProp ?? muiFormControl?.required, - focused: focusedProp ?? muiFormControl?.focused, - }; - - const classes = useUtilityClasses(ownerState); - - const RootSlot = slots?.root ?? InputLabelRoot; - - const rootProps = useSlotProps({ - elementType: RootSlot, - externalSlotProps: slotProps.root, - externalForwardedProps: { ...other, classes }, - additionalProps: { - ref: forwardedRef, - 'data-shrink': shrink, - required: requiredProp, - size: sizeProp, - focused: focusedProp, - }, - ownerState, - className: [classes.root], - }); - - return <RootSlot {...rootProps} />; -}) as OverridableComponent<InputLabelTypeMap>; - -InputLabel.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, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['error', 'info', 'primary', 'secondary', 'success', 'warning']), - PropTypes.string, - ]), - /** - * If `true`, the transition animation is disabled. - * @default false - */ - disableAnimation: PropTypes.bool, - /** - * If `true`, the component is disabled. - */ - disabled: PropTypes.bool, - /** - * If `true`, the label is displayed in an error state. - */ - error: PropTypes.bool, - /** - * If `true`, the `input` of this label is focused. - */ - focused: PropTypes.bool, - /** - * If `dense`, will adjust vertical spacing. This is normally obtained via context from - * FormControl. - */ - margin: PropTypes.oneOf(['dense']), - /** - * if `true`, the label will indicate that the `input` is required. - */ - required: PropTypes.bool, - /** - * If `true`, the label is shrunk. - */ - shrink: PropTypes.bool, - /** - * The size of the component. - * @default 'normal' - */ - size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['normal', 'small']), - PropTypes.string, - ]), - /** - * 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 variant to use. - */ - variant: PropTypes.oneOf(['filled', 'outlined']), -} as any; - -export default InputLabel; diff --git a/packages/mui-material-next/src/InputLabel/InputLabel.types.ts b/packages/mui-material-next/src/InputLabel/InputLabel.types.ts deleted file mode 100644 index 43c654b8f1651a..00000000000000 --- a/packages/mui-material-next/src/InputLabel/InputLabel.types.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverridableStringUnion, OverrideProps } from '@mui/types'; -import { FormControlContextValue } from '../FormControl/FormControlContext'; -import { FormLabelProps, ExtendFormLabelTypeMap } from '../FormLabel/FormLabel.types'; -import { Theme } from '../styles'; -import { InputLabelClasses } from './inputLabelClasses'; - -export interface InputLabelPropsSizeOverrides {} - -export interface InputLabelOwnProps { - /** - * The content of the component. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<InputLabelClasses>; - color?: FormLabelProps['color']; - /** - * If `true`, the transition animation is disabled. - * @default false - */ - disableAnimation?: boolean; - /** - * If `true`, the component is disabled. - */ - disabled?: boolean; - /** - * If `true`, the label is displayed in an error state. - */ - error?: boolean; - /** - * If `true`, the `input` of this label is focused. - */ - focused?: boolean; - /** - * If `dense`, will adjust vertical spacing. This is normally obtained via context from - * FormControl. - */ - margin?: 'dense'; - /** - * if `true`, the label will indicate that the `input` is required. - */ - required?: boolean; - /** - * If `true`, the label is shrunk. - */ - shrink?: boolean; - /** - * The size of the component. - * @default 'normal' - */ - size?: OverridableStringUnion<'small' | 'normal', InputLabelPropsSizeOverrides>; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; - /** - * The variant to use. - */ - variant?: 'outlined' | 'filled'; // TODO v6: standardize to TextFieldProps['variant'] -} - -export type InputLabelTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'label', -> = ExtendFormLabelTypeMap<{ - props: AdditionalProps & InputLabelOwnProps; - defaultComponent: RootComponent; -}>; - -export type InputLabelProps< - RootComponent extends React.ElementType = InputLabelTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<InputLabelTypeMap<AdditionalProps, RootComponent>, RootComponent> & { - component?: React.ElementType; -}; - -export interface InputLabelOwnerState extends InputLabelProps { - formControl: FormControlContextValue | undefined; -} diff --git a/packages/mui-material-next/src/InputLabel/index.ts b/packages/mui-material-next/src/InputLabel/index.ts deleted file mode 100644 index f1050f1d809098..00000000000000 --- a/packages/mui-material-next/src/InputLabel/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './InputLabel'; - -export { default as inputLabelClasses } from './inputLabelClasses'; -export * from './inputLabelClasses'; diff --git a/packages/mui-material-next/src/InputLabel/inputLabelClasses.ts b/packages/mui-material-next/src/InputLabel/inputLabelClasses.ts deleted file mode 100644 index 6ea43d13ce1719..00000000000000 --- a/packages/mui-material-next/src/InputLabel/inputLabelClasses.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { - unstable_generateUtilityClass as generateUtilityClass, - unstable_generateUtilityClasses as generateUtilityClasses, -} from '@mui/utils'; - -export interface InputLabelClasses { - /** Styles applied to the root element. */ - root: string; - /** State class applied to the root element if `focused={true}`. */ - focused: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; - /** State class applied to the root element if `error={true}`. */ - error: string; - /** State class applied to the root element if `required={true}`. */ - required: string; - /** State class applied to the asterisk element. */ - asterisk: string; - /** Styles applied to the root element if the component is a descendant of `FormControl`. */ - formControl: string; - /** Styles applied to the root element if `size="small"`. */ - sizeSmall: string; - /** Styles applied to the input element if `shrink={true}`. */ - shrink: string; - /** Styles applied to the input element unless `disableAnimation={true}`. */ - animated: string; - /** Styles applied to the root element if `variant="filled"`. */ - filled: string; - /** Styles applied to the root element if `variant="outlined"`. */ - outlined: string; - /** Styles applied to the root element if `variant="standard"`. */ - standard: string; -} - -export type InputLabelClassKey = keyof InputLabelClasses; - -export function getInputLabelUtilityClasses(slot: string): string { - return generateUtilityClass('MuiInputLabel', slot); -} - -const inputLabelClasses: InputLabelClasses = generateUtilityClasses('MuiInputLabel', [ - 'root', - 'focused', - 'disabled', - 'error', - 'required', - 'asterisk', - 'formControl', - 'sizeSmall', - 'shrink', - 'animated', - 'standard', - 'filled', - 'outlined', -]); - -export default inputLabelClasses; diff --git a/packages/mui-material-next/src/LinearProgress/LinearProgress.test.tsx b/packages/mui-material-next/src/LinearProgress/LinearProgress.test.tsx deleted file mode 100644 index 65044f08556cf4..00000000000000 --- a/packages/mui-material-next/src/LinearProgress/LinearProgress.test.tsx +++ /dev/null @@ -1,250 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { - createRenderer, - screen, - strictModeDoubleLoggingSuppressed, - MuiRenderResult, -} from '@mui-internal/test-utils'; -import LinearProgress, { - linearProgressClasses as classes, -} from '@mui/material-next/LinearProgress'; -import { CssVarsProvider, extendTheme } from '../styles'; -import describeConformance from '../../test/describeConformance'; - -describe('<LinearProgress />', () => { - const { render } = createRenderer(); - - describeConformance(<LinearProgress />, () => ({ - classes, - inheritComponent: 'span', - render, - muiName: 'MuiLinearProgress', - testDeepOverrides: { slotName: 'bar', slotClassName: classes.bar }, - testVariantProps: { variant: 'determinate', value: 25 }, - refInstanceof: window.HTMLSpanElement, - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - skip: ['componentProp', 'componentsProp'], - })); - - it('should render indeterminate variant by default', () => { - render(<LinearProgress />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar).to.have.class(classes.root); - expect(progressbar).to.have.class(classes.indeterminate); - expect(progressbar.children[0]).to.have.class(classes.bar1); - expect(progressbar.children[1]).to.have.class(classes.bar2); - }); - - it('should render for the primary color by default', () => { - render(<LinearProgress />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar).to.have.class(classes.colorPrimary); - expect(progressbar.children[0]).to.have.class(classes.bar); - expect(progressbar.children[1]).to.have.class(classes.bar); - }); - - it('should render for the secondary color', () => { - render(<LinearProgress color="secondary" />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar).to.have.class(classes.colorSecondary); - expect(progressbar.children[0]).to.have.class(classes.bar); - expect(progressbar.children[1]).to.have.class(classes.bar); - }); - - it('should render for the tertiary color', () => { - render(<LinearProgress color="tertiary" />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar).to.have.class(classes.colorTertiary); - expect(progressbar.children[0]).to.have.class(classes.bar); - expect(progressbar.children[1]).to.have.class(classes.bar); - }); - - it('should render with determinate classes for the primary color by default', () => { - render(<LinearProgress value={1} variant="determinate" />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar).to.have.class(classes.determinate); - expect(progressbar).to.have.class(classes.colorPrimary); - expect(progressbar.children[0]).to.have.class(classes.bar); - expect(progressbar.children[0]).to.have.class(classes.bar1); - }); - - it('should render with determinate classes for the primary color', () => { - render(<LinearProgress color="primary" value={1} variant="determinate" />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar).to.have.class(classes.determinate); - expect(progressbar).to.have.class(classes.colorPrimary); - expect(progressbar.children[0]).to.have.class(classes.bar); - expect(progressbar.children[0]).to.have.class(classes.bar1); - }); - - it('should render with determinate classes for the secondary color', () => { - render(<LinearProgress color="secondary" value={1} variant="determinate" />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar).to.have.class(classes.determinate); - expect(progressbar).to.have.class(classes.colorSecondary); - expect(progressbar.children[0]).to.have.class(classes.bar); - expect(progressbar.children[0]).to.have.class(classes.bar1); - }); - - it('should render with determinate classes for the tertiary color', () => { - render(<LinearProgress color="tertiary" value={1} variant="determinate" />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar).to.have.class(classes.determinate); - expect(progressbar).to.have.class(classes.colorTertiary); - expect(progressbar.children[0]).to.have.class(classes.bar); - expect(progressbar.children[0]).to.have.class(classes.bar1); - }); - - it('should set width of bar1 on determinate variant', () => { - render(<LinearProgress variant="determinate" value={77} />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar.children[0]).to.have.nested.property('style.transform', 'translateX(-23%)'); - }); - - it('should render with buffer classes for the primary color by default', () => { - render(<LinearProgress value={1} valueBuffer={1} variant="buffer" />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar).to.have.class(classes.colorPrimary); - expect(progressbar).to.have.class(classes.buffer); - expect(progressbar.children[0]).to.have.class(classes.dashed); - expect(progressbar.children[1]).to.have.class(classes.bar); - expect(progressbar.children[1]).to.have.class(classes.bar1); - expect(progressbar.children[2]).to.have.class(classes.bar2); - }); - - it('should render with buffer classes for the primary color', () => { - render(<LinearProgress value={1} valueBuffer={1} color="primary" variant="buffer" />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar).to.have.class(classes.colorPrimary); - expect(progressbar).to.have.class(classes.buffer); - expect(progressbar.children[0]).to.have.class(classes.dashed); - expect(progressbar.children[1]).to.have.class(classes.bar); - expect(progressbar.children[1]).to.have.class(classes.bar1); - expect(progressbar.children[2]).to.have.class(classes.bar2); - }); - - it('should render with buffer classes for the secondary color', () => { - render(<LinearProgress value={1} valueBuffer={1} color="secondary" variant="buffer" />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar).to.have.class(classes.colorSecondary); - expect(progressbar).to.have.class(classes.buffer); - expect(progressbar.children[0]).to.have.class(classes.dashed); - expect(progressbar.children[1]).to.have.class(classes.bar); - expect(progressbar.children[1]).to.have.class(classes.bar1); - expect(progressbar.children[2]).to.have.class(classes.bar2); - }); - - it('should render with buffer classes for the tertiary color', () => { - render(<LinearProgress value={1} valueBuffer={1} color="tertiary" variant="buffer" />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar).to.have.class(classes.colorTertiary); - expect(progressbar).to.have.class(classes.buffer); - expect(progressbar.children[0]).to.have.class(classes.dashed); - expect(progressbar.children[1]).to.have.class(classes.bar); - expect(progressbar.children[1]).to.have.class(classes.bar1); - expect(progressbar.children[2]).to.have.class(classes.bar2); - }); - - it('should set width of bar1 and bar2 on buffer variant', () => { - render(<LinearProgress variant="buffer" value={77} valueBuffer={85} />); - - expect(document.querySelector(`.${classes.bar1}`)).to.have.nested.property( - 'style.transform', - 'translateX(-23%)', - ); - expect(document.querySelector(`.${classes.bar2}`)).to.have.nested.property( - 'style.transform', - 'translateX(-15%)', - ); - }); - - it('should render with query classes', () => { - render(<LinearProgress variant="query" />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar).to.have.class(classes.query); - expect(progressbar).to.have.class(classes.colorPrimary); - expect(progressbar.children[0]).to.have.class(classes.bar); - expect(progressbar.children[0]).to.have.class(classes.bar1); - expect(progressbar.children[1]).to.have.class(classes.bar); - expect(progressbar.children[1]).to.have.class(classes.bar2); - }); - - it('exposes the current, min and max value to screen readers when determinate', () => { - render(<LinearProgress variant="determinate" value={77} />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar).to.have.attribute('aria-valuenow', '77'); - expect(progressbar).to.have.attribute('aria-valuemin', '0'); - expect(progressbar).to.have.attribute('aria-valuemax', '100'); - }); - - describe('prop: value', () => { - it('should warn when not used as expected', () => { - let rerender: MuiRenderResult['rerender']; - - expect(() => { - ({ rerender } = render(<LinearProgress variant="determinate" value={undefined} />)); - }).toErrorDev([ - 'MUI: You need to provide a value prop', - !strictModeDoubleLoggingSuppressed && 'MUI: You need to provide a value prop', - ]); - - expect(() => { - rerender(<LinearProgress variant="buffer" value={undefined} />); - }).toErrorDev([ - 'MUI: You need to provide a value prop', - 'MUI: You need to provide a valueBuffer prop', - !strictModeDoubleLoggingSuppressed && 'MUI: You need to provide a value prop', - !strictModeDoubleLoggingSuppressed && 'MUI: You need to provide a valueBuffer prop', - ]); - }); - }); - - describe('prop: fourColor ', () => { - it('should default to false', () => { - render(<LinearProgress variant="indeterminate" />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar).to.have.class(classes.root); - expect(progressbar).not.to.have.class(classes.fourColor); - expect(progressbar.children[0]).to.have.class(classes.bar1); - expect(progressbar.children[1]).to.have.class(classes.bar2); - }); - - it('should render without fourColor class when set to false', () => { - render(<LinearProgress variant="indeterminate" fourColor={false} />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar).to.have.class(classes.root); - expect(progressbar).not.to.have.class(classes.fourColor); - expect(progressbar.children[0]).to.have.class(classes.bar1); - expect(progressbar.children[1]).to.have.class(classes.bar2); - }); - - it('should render with fourColor class when set to true', () => { - render(<LinearProgress variant="indeterminate" fourColor />); - const progressbar = screen.getByRole('progressbar'); - - expect(progressbar).to.have.class(classes.root); - expect(progressbar).to.have.class(classes.fourColor); - expect(progressbar.children[0]).to.have.class(classes.bar1); - expect(progressbar.children[1]).to.have.class(classes.bar2); - }); - }); -}); diff --git a/packages/mui-material-next/src/LinearProgress/LinearProgress.tsx b/packages/mui-material-next/src/LinearProgress/LinearProgress.tsx deleted file mode 100644 index d7593d5094d221..00000000000000 --- a/packages/mui-material-next/src/LinearProgress/LinearProgress.tsx +++ /dev/null @@ -1,432 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { keyframes, css } from '@mui/system'; -import { unstable_capitalize as capitalize, chainPropTypes } from '@mui/utils'; -import { OverridableComponent } from '@mui/types'; -import useTheme from '../styles/useTheme'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; -import { getLinearProgressUtilityClass } from './linearProgressClasses'; -import { - LinearProgressOwnerState, - LinearProgressProps, - LinearProgressTypeMap, -} from './LinearProgress.types'; - -const TRANSITION_DURATION = 4; // seconds -const indeterminate1Keyframe = keyframes` - 0% { - left: -35%; - right: 100%; - } - - 60% { - left: 100%; - right: -90%; - } - - 100% { - left: 100%; - right: -90%; - } -`; - -const indeterminate2Keyframe = keyframes` - 0% { - left: -200%; - right: 100%; - } - - 60% { - left: 107%; - right: -8%; - } - - 100% { - left: 107%; - right: -8%; - } -`; - -const bufferKeyframe = keyframes` - 0% { - transform: translateX(calc(var(--md-comp-linear-progress-indicator-active-indicator-height) / 2 * 5)); - } -`; - -const fourColorKeyframe = keyframes` - 0% { - background: var(--md-comp-linear-progress-indicator-four-color-active-indicator-one-color); - } - - 15% { - background: var(--md-comp-linear-progress-indicator-four-color-active-indicator-one-color); - } - 25% { - background: var(--md-comp-linear-progress-indicator-four-color-active-indicator-two-color); - } - 40% { - background: var(--md-comp-linear-progress-indicator-four-color-active-indicator-two-color); - } - - 50% { - background: var(--md-comp-linear-progress-indicator-four-color-active-indicator-three-color); - } - 65% { - background: var(--md-comp-linear-progress-indicator-four-color-active-indicator-three-color); - } - 75% { - background: var(--md-comp-linear-progress-indicator-four-color-active-indicator-four-color); - } - 90% { - background: var(--md-comp-linear-progress-indicator-four-color-active-indicator-four-color); - } - 100% { - background: var(--md-comp-linear-progress-indicator-four-color-active-indicator-one-color); - } -`; - -const useUtilityClasses = (ownerState: LinearProgressOwnerState) => { - const { classes, variant, color, fourColor } = ownerState; - - const slots = { - root: ['root', `color${capitalize(color)}`, variant, fourColor && 'fourColor'], - dashed: ['dashed'], - bar1: ['bar', `bar1`], - bar2: ['bar', `bar2`], - }; - - return composeClasses(slots, getLinearProgressUtilityClass, classes); -}; - -const LinearProgressRoot = styled('span', { - name: 'MuiLinearProgress', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - styles[`color${capitalize(ownerState.color)}`], - styles[ownerState.variant], - ]; - }, -})<{ ownerState: LinearProgressOwnerState }>(({ ownerState, theme: { vars: tokens } }) => ({ - '--md-comp-linear-progress-indicator-track-color': tokens.sys.color.surfaceContainerHighest, - '--md-comp-linear-progress-indicator-active-indicator-height': '4px', - '--md-comp-linear-progress-indicator-active-indicator-color': - ownerState.color !== 'inherit' ? tokens.sys.color[ownerState.color] : 'currentColor', - '--md-comp-linear-progress-indicator-four-color-active-indicator-one-color': - tokens.sys.color.primary, - '--md-comp-linear-progress-indicator-four-color-active-indicator-two-color': - tokens.sys.color.onPrimaryContainer, - '--md-comp-linear-progress-indicator-four-color-active-indicator-three-color': - tokens.sys.color.tertiary, - '--md-comp-linear-progress-indicator-four-color-active-indicator-four-color': - tokens.sys.color.onTertiaryContainer, - position: 'relative', - overflow: 'hidden', - display: 'block', - height: 'var(--md-comp-linear-progress-indicator-active-indicator-height)', - zIndex: 0, // Fix Safari's bug during composition of different paint. - '@media print': { - colorAdjust: 'exact', - }, - backgroundColor: 'var(--md-comp-linear-progress-indicator-track-color)', - ...(ownerState.color === 'inherit' && - ownerState.variant !== 'buffer' && { - backgroundColor: 'none', - '&::before': { - content: '""', - position: 'absolute', - left: 0, - top: 0, - right: 0, - bottom: 0, - backgroundColor: 'currentColor', - opacity: 0.3, - }, - }), - ...(ownerState.variant === 'buffer' && { backgroundColor: 'transparent' }), - ...(ownerState.variant === 'query' && { transform: 'rotate(180deg)' }), -})); - -const LinearProgressDashed = styled('span', { - name: 'MuiLinearProgress', - slot: 'Dashed', - overridesResolver: (props, styles) => styles.dashed, -})<{ ownerState: LinearProgressOwnerState }>( - ({ ownerState }) => ({ - position: 'absolute', - marginTop: 0, - height: '100%', - width: '100%', - inset: '0px', - backgroundColor: 'var(--md-comp-linear-progress-indicator-track-color)', - ...(ownerState.color === 'inherit' && { - opacity: 0.3, - }), - maskImage: `url("data:image/svg+xml,%3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 5 2' preserveAspectRatio='xMinYMin slice'%3E%3Ccircle cx='1' cy='1' r='1'/%3E%3C/svg%3E")`, - backgroundRepeat: 'repeat-x', - }), - css` - animation: 250ms ${bufferKeyframe} infinite linear; - `, -); - -const LinearProgressBar1 = styled('span', { - name: 'MuiLinearProgress', - slot: 'Bar1', - overridesResolver: (props, styles) => { - return [styles.bar, styles.bar1]; - }, -})<{ ownerState: LinearProgressOwnerState }>(({ ownerState }) => ({ - width: '100%', - position: 'absolute', - left: 0, - bottom: 0, - top: 0, - transition: 'transform 0.2s linear', - transformOrigin: 'left', - backgroundColor: 'var(--md-comp-linear-progress-indicator-active-indicator-color)', - ...(ownerState.variant === 'determinate' && { - transition: `transform .${TRANSITION_DURATION}s linear`, - }), - ...(ownerState.variant === 'buffer' && { - zIndex: 1, - transition: `transform .${TRANSITION_DURATION}s linear`, - }), - ...((ownerState.variant === 'indeterminate' || ownerState.variant === 'query') && { - width: 'auto', - animation: `${indeterminate1Keyframe} 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite`, - ...(ownerState.fourColor && { - animationName: `${indeterminate1Keyframe}, ${fourColorKeyframe}`, - animationDuration: '2.1s, 4.2s', - animationTimingFunction: 'cubic-bezier(0.65, 0.815, 0.735, 0.395), linear', - }), - }), -})); - -const LinearProgressBar2 = styled('span', { - name: 'MuiLinearProgress', - slot: 'Bar2', - overridesResolver: (props, styles) => { - return [styles.bar, styles.bar2]; - }, -})<{ ownerState: LinearProgressOwnerState }>(({ ownerState }) => ({ - width: '100%', - position: 'absolute', - left: 0, - bottom: 0, - top: 0, - transition: 'transform 0.2s linear', - transformOrigin: 'left', - backgroundColor: 'var(--md-comp-linear-progress-indicator-active-indicator-color)', - ...(ownerState.color === 'inherit' && { - opacity: 0.3, - }), - ...(ownerState.variant === 'buffer' && { - backgroundColor: 'var(--md-comp-linear-progress-indicator-track-color)', - transition: `transform .${TRANSITION_DURATION}s linear`, - }), - ...((ownerState.variant === 'indeterminate' || ownerState.variant === 'query') && { - width: 'auto', - animation: `${indeterminate2Keyframe} 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) 1.15s infinite`, - ...(ownerState.fourColor && { - animationName: `${indeterminate2Keyframe}, ${fourColorKeyframe}`, - animationDuration: '2.1s, 4.2s', - animationDelay: '1.15s, 0s', - animationTimingFunction: 'cubic-bezier(0.165, 0.84, 0.44, 1), linear', - }), - }), -})); - -/** - * ## ARIA - * - * If the progress bar is describing the loading progress of a particular region of a page, - * you should use `aria-describedby` to point to the progress bar, and set the `aria-busy` - * attribute to `true` on that region until it has finished loading. - * - * Demos: - * - * - [Progress](https://mui.com/material-ui/react-progress/) - * - * API: - * - * - [LinearProgress API](https://mui.com/material-ui/api/linear-progress/) - */ -const LinearProgress = React.forwardRef(function LinearProgress< - BaseComponentType extends React.ElementType = LinearProgressTypeMap['defaultComponent'], ->(inProps: LinearProgressProps<BaseComponentType>, ref: React.ForwardedRef<HTMLSpanElement>) { - const props = useThemeProps({ props: inProps, name: 'MuiLinearProgress' }); - const { - className, - color = 'primary', - fourColor = false, - value, - valueBuffer, - variant = 'indeterminate', - ...other - } = props; - const ownerState = { - ...props, - color, - fourColor, - variant, - }; - - const classes = useUtilityClasses(ownerState); - const theme = useTheme(); - - const rootProps: React.DetailedHTMLProps< - React.HTMLAttributes<HTMLSpanElement>, - HTMLSpanElement - > = {}; - const inlineStyles: { bar1: React.CSSProperties; bar2: React.CSSProperties } = { - bar1: {}, - bar2: {}, - }; - - if (variant === 'determinate' || variant === 'buffer') { - if (value !== undefined) { - rootProps['aria-valuenow'] = Math.round(value); - rootProps['aria-valuemin'] = 0; - rootProps['aria-valuemax'] = 100; - let transform = value - 100; - if (theme.direction === 'rtl') { - transform = -transform; - } - inlineStyles.bar1.transform = `translateX(${transform}%)`; - } else if (process.env.NODE_ENV !== 'production') { - console.error( - 'MUI: You need to provide a value prop ' + - 'when using the determinate or buffer variant of LinearProgress .', - ); - } - } - if (variant === 'buffer') { - if (valueBuffer !== undefined) { - let transform = (valueBuffer || 0) - 100; - if (theme.direction === 'rtl') { - transform = -transform; - } - inlineStyles.bar2.transform = `translateX(${transform}%)`; - } else if (process.env.NODE_ENV !== 'production') { - console.error( - 'MUI: You need to provide a valueBuffer prop ' + - 'when using the buffer variant of LinearProgress.', - ); - } - } - - return ( - <LinearProgressRoot - className={clsx(classes.root, className)} - ownerState={ownerState} - role="progressbar" - {...rootProps} - ref={ref} - {...other} - > - {variant === 'buffer' ? ( - <LinearProgressDashed className={classes.dashed} ownerState={ownerState} /> - ) : null} - <LinearProgressBar1 - className={classes.bar1} - ownerState={ownerState} - style={inlineStyles.bar1} - /> - {variant === 'determinate' ? null : ( - <LinearProgressBar2 - className={classes.bar2} - ownerState={ownerState} - style={inlineStyles.bar2} - /> - )} - </LinearProgressRoot> - ); -}) as OverridableComponent<LinearProgressTypeMap>; - -LinearProgress.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, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color: PropTypes.oneOf([ - 'error', - 'info', - 'inherit', - 'primary', - 'secondary', - 'success', - 'tertiary', - 'warning', - ]), - /** - * If `true`, the component render indeterminate or query mode using four colors instead of one. - * This only works if variant is `indeterminate` or `query`. - * @default false - */ - fourColor: chainPropTypes(PropTypes.bool, (props) => { - if ( - props.fourColor && - props.variant && - props.variant !== 'indeterminate' && - props.variant !== 'query' - ) { - return new Error( - 'MUI: You have provided the `fourColor` prop ' + - 'with a variant other than `indeterminate` or `query`. This will have no effect.', - ); - } - return null; - }), - /** - * 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 value of the progress indicator for the determinate and buffer variants. - * Value between 0 and 100. - */ - value: PropTypes.number, - /** - * The value for the buffer variant. - * Value between 0 and 100. - */ - valueBuffer: PropTypes.number, - /** - * The variant to use. - * Use indeterminate or query when there is no progress value. - * @default 'indeterminate' - */ - variant: PropTypes.oneOf(['buffer', 'determinate', 'indeterminate', 'query']), -} as any; - -export default LinearProgress; diff --git a/packages/mui-material-next/src/LinearProgress/LinearProgress.types.ts b/packages/mui-material-next/src/LinearProgress/LinearProgress.types.ts deleted file mode 100644 index 56117cfa93d6e5..00000000000000 --- a/packages/mui-material-next/src/LinearProgress/LinearProgress.types.ts +++ /dev/null @@ -1,66 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverridableStringUnion, OverrideProps, PartiallyRequired } from '@mui/types'; -import { Theme } from '../styles'; -import { LinearProgressClasses } from './linearProgressClasses'; - -export interface LinearProgressPropsColorOverrides {} - -export interface LinearProgressOwnProps { - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<LinearProgressClasses>; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color?: OverridableStringUnion< - 'primary' | 'secondary' | 'tertiary' | 'error' | 'info' | 'success' | 'warning' | 'inherit', - LinearProgressPropsColorOverrides - >; - /** - * If `true`, the component render indeterminate or query mode using four colors instead of one. - * This only works if variant is `indeterminate` or `query`. - * @default false - */ - fourColor?: boolean; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; - /** - * The value of the progress indicator for the determinate and buffer variants. - * Value between 0 and 100. - */ - value?: number; - /** - * The value for the buffer variant. - * Value between 0 and 100. - */ - valueBuffer?: number; - /** - * The variant to use. - * Use indeterminate or query when there is no progress value. - * @default 'indeterminate' - */ - variant?: 'determinate' | 'indeterminate' | 'buffer' | 'query'; -} - -export interface LinearProgressTypeMap< - AdditionalProps = {}, - RootComponentType extends React.ElementType = 'span', -> { - props: LinearProgressOwnProps & AdditionalProps; - defaultComponent: RootComponentType; -} - -export type LinearProgressProps< - RootComponentType extends React.ElementType = LinearProgressTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<LinearProgressTypeMap<AdditionalProps, RootComponentType>, RootComponentType>; - -export interface LinearProgressOwnerState - extends PartiallyRequired<LinearProgressProps, 'color' | 'variant'> {} diff --git a/packages/mui-material-next/src/LinearProgress/index.ts b/packages/mui-material-next/src/LinearProgress/index.ts deleted file mode 100644 index afb73ef3020a9c..00000000000000 --- a/packages/mui-material-next/src/LinearProgress/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './LinearProgress'; - -export { default as linearProgressClasses } from './linearProgressClasses'; -export * from './linearProgressClasses'; diff --git a/packages/mui-material-next/src/LinearProgress/linearProgressClasses.ts b/packages/mui-material-next/src/LinearProgress/linearProgressClasses.ts deleted file mode 100644 index 52948771006b28..00000000000000 --- a/packages/mui-material-next/src/LinearProgress/linearProgressClasses.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface LinearProgressClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root and bar2 element if `color="primary"`; bar2 if `variant="buffer"`. */ - colorPrimary: string; - /** Styles applied to the root and bar2 elements if `color="secondary"`; bar2 if `variant="buffer"`. */ - colorSecondary: string; - /** Styles applied to the root and bar2 elements if `color="tertiary"`; bar2 if `variant="buffer"`. */ - colorTertiary: string; - /** Styles applied to the root element if `variant="determinate"`. */ - determinate: string; - /** Styles applied to the root element if `variant="indeterminate"`. */ - indeterminate: string; - /** Styles applied to the root element if `variant="buffer"`. */ - buffer: string; - /** Styles applied to the root element if `variant="query"`. */ - query: string; - /** Styles applied to the root element if `fourColor={true}`. */ - fourColor: string; - /** Styles applied to the additional bar element if `variant="buffer"`. */ - dashed: string; - /** Styles applied to the layered bar1 and bar2 elements. */ - bar: string; - /** Styles applied to the bar1 element. */ - bar1: string; - /** Styles applied to the bar2 element. */ - bar2: string; -} - -export type LinearProgressClassKey = keyof LinearProgressClasses; - -export function getLinearProgressUtilityClass(slot: string): string { - return generateUtilityClass('MuiLinearProgress', slot); -} - -const linearProgressClasses: LinearProgressClasses = generateUtilityClasses('MuiLinearProgress', [ - 'root', - 'colorPrimary', - 'colorSecondary', - 'colorTertiary', - 'determinate', - 'indeterminate', - 'buffer', - 'query', - 'fourColor', - 'dashed', - 'bar', - 'bar1', - 'bar2', -]); - -export default linearProgressClasses; diff --git a/packages/mui-material-next/src/List/List.d.ts b/packages/mui-material-next/src/List/List.d.ts deleted file mode 100644 index 4ba4a61c1d8f6a..00000000000000 --- a/packages/mui-material-next/src/List/List.d.ts +++ /dev/null @@ -1,75 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverridableComponent, OverridableTypeMap, OverrideProps } from '@mui/types'; -import { Theme } from '..'; -import { ListClasses } from './listClasses'; - -export interface ListOwnProps { - /** - * The content of the component. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<ListClasses>; - /** - * If `true`, compact vertical padding designed for keyboard and mouse input is used for - * the list and list items. - * The prop is available to descendant components as the `dense` context. - * @default false - */ - dense?: boolean; - /** - * If `true`, vertical padding is removed from the list. - * @default false - */ - disablePadding?: boolean; - /** - * The content of the subheader, normally `ListSubheader`. - */ - subheader?: React.ReactNode; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -export interface ListTypeMap<AdditionalProps = {}, RootComponent extends React.ElementType = 'ul'> { - props: AdditionalProps & ListOwnProps; - defaultComponent: RootComponent; -} - -/** - * utility to create component types that inherit props from List. - */ -export interface ExtendListTypeMap<TypeMap extends OverridableTypeMap> { - props: TypeMap['props'] & ListTypeMap['props']; - defaultComponent: TypeMap['defaultComponent']; -} - -export type ExtendList<TypeMap extends OverridableTypeMap> = OverridableComponent< - ExtendListTypeMap<TypeMap> ->; - -/** - * - * Demos: - * - * - [Lists](https://mui.com/material-ui/react-list/) - * - [Transfer List](https://mui.com/material-ui/react-transfer-list/) - * - * API: - * - * - [List API](https://mui.com/material-ui/api/list/) - */ -declare const List: ExtendList<ListTypeMap>; - -export type ListProps< - RootComponent extends React.ElementType = ListTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<ListTypeMap<AdditionalProps, RootComponent>, RootComponent> & { - component?: React.ElementType; -}; - -export default List; diff --git a/packages/mui-material-next/src/List/List.js b/packages/mui-material-next/src/List/List.js deleted file mode 100644 index a8c6037163f5db..00000000000000 --- a/packages/mui-material-next/src/List/List.js +++ /dev/null @@ -1,135 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; -import ListContext from './ListContext'; -import { getListUtilityClass } from './listClasses'; - -const useUtilityClasses = (ownerState) => { - const { classes, disablePadding, dense, subheader } = ownerState; - - const slots = { - root: ['root', !disablePadding && 'padding', dense && 'dense', subheader && 'subheader'], - }; - - return composeClasses(slots, getListUtilityClass, classes); -}; - -const ListRoot = styled('ul', { - name: 'MuiList', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - !ownerState.disablePadding && styles.padding, - ownerState.dense && styles.dense, - ownerState.subheader && styles.subheader, - ]; - }, -})(({ ownerState }) => ({ - listStyle: 'none', - margin: 0, - padding: 0, - position: 'relative', - ...(!ownerState.disablePadding && { - paddingTop: 8, - paddingBottom: 8, - }), - ...(ownerState.subheader && { - paddingTop: 0, - }), -})); - -const List = React.forwardRef(function List(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiList' }); - const { - children, - className, - component = 'ul', - dense = false, - disablePadding = false, - subheader, - ...other - } = props; - - const context = React.useMemo(() => ({ dense }), [dense]); - - const ownerState = { - ...props, - component, - dense, - disablePadding, - }; - - const classes = useUtilityClasses(ownerState); - - return ( - <ListContext.Provider value={context}> - <ListRoot - as={component} - className={clsx(classes.root, className)} - ref={ref} - ownerState={ownerState} - {...other} - > - {subheader} - {children} - </ListRoot> - </ListContext.Provider> - ); -}); - -List.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The content of the component. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * If `true`, compact vertical padding designed for keyboard and mouse input is used for - * the list and list items. - * The prop is available to descendant components as the `dense` context. - * @default false - */ - dense: PropTypes.bool, - /** - * If `true`, vertical padding is removed from the list. - * @default false - */ - disablePadding: PropTypes.bool, - /** - * The content of the subheader, normally `ListSubheader`. - */ - subheader: PropTypes.node, - /** - * 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, - ]), -}; - -export default List; diff --git a/packages/mui-material-next/src/List/List.spec.tsx b/packages/mui-material-next/src/List/List.spec.tsx deleted file mode 100644 index 7d813edca758a7..00000000000000 --- a/packages/mui-material-next/src/List/List.spec.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import * as React from 'react'; -import List from '@mui/material-next/List'; - -// custom host -// https://github.com/mui/material-ui/issues/13746 -{ - <List component="div" />; - <List - component="div" - onChange={(event: React.FormEvent<HTMLDivElement>) => { - event.currentTarget; - }} - />; -} diff --git a/packages/mui-material-next/src/List/List.test.js b/packages/mui-material-next/src/List/List.test.js deleted file mode 100644 index bd5e5843f1e2b6..00000000000000 --- a/packages/mui-material-next/src/List/List.test.js +++ /dev/null @@ -1,82 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -import ListSubheader, { listSubheaderClasses } from '@mui/material-next/ListSubheader'; -import ListItem, { listItemClasses } from '@mui/material-next/ListItem'; -import List, { listClasses as classes } from '@mui/material-next/List'; -import describeConformance from '../../test/describeConformance'; - -describe('<List />', () => { - const { render } = createRenderer(); - - describeConformance(<List />, () => ({ - classes, - inheritComponent: 'ul', - render, - muiName: 'MuiList', - refInstanceof: window.HTMLUListElement, - testVariantProps: { disablePadding: true }, - skip: ['componentsProp'], - })); - - it('should render with padding classes', () => { - const { container } = render(<List className="woofList" />); - - expect(container.firstChild).to.have.class(classes.padding); - }); - - it('can disable the padding', () => { - const { container } = render(<List disablePadding />); - - expect(container.firstChild).not.to.have.class(classes.padding); - }); - - describe('prop: subheader', () => { - it('should render with subheader class', () => { - const { container } = render(<List subheader={<ListSubheader>Title</ListSubheader>} />); - - expect(container.firstChild).to.have.class(classes.subheader); - }); - - it('should render ListSubheader', () => { - const { container } = render(<List subheader={<ListSubheader>Title</ListSubheader>} />); - const item = container.querySelector('li'); - - expect(item).to.have.class(listSubheaderClasses.root); - }); - }); - - describe('prop: dense', () => { - it('is disabled by default', () => { - const { container } = render(<List />); - - expect(container.firstChild).not.to.have.class(classes.dense); - }); - - it('adds a dense class', () => { - const { container } = render(<List dense />); - - expect(container.firstChild).to.have.class(classes.dense); - }); - - it('sets dense on deep nested ListItem', () => { - // mocking a tooltip - const Tooltip = React.Fragment; - - const { container } = render( - <List dense> - <Tooltip> - <ListItem>Inbox</ListItem> - </Tooltip> - <ListItem>Drafts</ListItem> - <ListItem /> - </List>, - ); - - const liItems = container.querySelectorAll('li'); - for (let i = 0; i < liItems.length; i += 1) { - expect(liItems[i]).to.have.class(listItemClasses.dense); - } - }); - }); -}); diff --git a/packages/mui-material-next/src/List/ListContext.d.ts b/packages/mui-material-next/src/List/ListContext.d.ts deleted file mode 100644 index f120852fc45ad4..00000000000000 --- a/packages/mui-material-next/src/List/ListContext.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import * as React from 'react'; - -declare const ListContext: React.Context<{ dense?: boolean }>; -export default ListContext; diff --git a/packages/mui-material-next/src/List/ListContext.js b/packages/mui-material-next/src/List/ListContext.js deleted file mode 100644 index fe430f60ecb62a..00000000000000 --- a/packages/mui-material-next/src/List/ListContext.js +++ /dev/null @@ -1,13 +0,0 @@ -'use client'; -import * as React from 'react'; - -/** - * @ignore - internal component. - */ -const ListContext = React.createContext({}); - -if (process.env.NODE_ENV !== 'production') { - ListContext.displayName = 'ListContext'; -} - -export default ListContext; diff --git a/packages/mui-material-next/src/List/index.d.ts b/packages/mui-material-next/src/List/index.d.ts deleted file mode 100644 index 6d6f8d6cfa0d68..00000000000000 --- a/packages/mui-material-next/src/List/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './List'; -export * from './List'; - -export { default as listClasses } from './listClasses'; -export * from './listClasses'; diff --git a/packages/mui-material-next/src/List/index.js b/packages/mui-material-next/src/List/index.js deleted file mode 100644 index b8789a9b10e3ce..00000000000000 --- a/packages/mui-material-next/src/List/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './List'; - -export { default as listClasses } from './listClasses'; -export * from './listClasses'; diff --git a/packages/mui-material-next/src/List/listClasses.ts b/packages/mui-material-next/src/List/listClasses.ts deleted file mode 100644 index 02728bdf6f07de..00000000000000 --- a/packages/mui-material-next/src/List/listClasses.ts +++ /dev/null @@ -1,28 +0,0 @@ -import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; -import generateUtilityClass from '@mui/utils/generateUtilityClass'; - -export interface ListClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element unless `disablePadding={true}`. */ - padding: string; - /** Styles applied to the root element if dense. */ - dense: string; - /** Styles applied to the root element if a `subheader` is provided. */ - subheader: string; -} - -export type ListClassKey = keyof ListClasses; - -export function getListUtilityClass(slot: string): string { - return generateUtilityClass('MuiList', slot); -} - -const listClasses: ListClasses = generateUtilityClasses('MuiList', [ - 'root', - 'padding', - 'dense', - 'subheader', -]); - -export default listClasses; diff --git a/packages/mui-material-next/src/ListItem/ListItem.d.ts b/packages/mui-material-next/src/ListItem/ListItem.d.ts deleted file mode 100644 index 15153a502ca2cf..00000000000000 --- a/packages/mui-material-next/src/ListItem/ListItem.d.ts +++ /dev/null @@ -1,191 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverridableComponent, OverrideProps } from '@mui/types'; -import { Theme } from '../styles'; -import { ExtendButtonBase } from '../ButtonBase/ButtonBase.types'; -import { ListItemClasses } from './listItemClasses'; - -export interface ListItemComponentsPropsOverrides {} - -/** - * This type is kept for compatibility. Use `ListItemOwnProps` instead. - */ -export interface ListItemBaseProps { - /** - * Defines the `align-items` style property. - * @default 'center' - */ - alignItems?: 'flex-start' | 'center'; - /** - * If `true`, the list item is focused during the first mount. - * Focus will also be triggered if the value changes from false to true. - * @default false - * @deprecated checkout [ListItemButton](/material-ui/api/list-item-button/) instead - */ - autoFocus?: boolean; - /** - * The content of the component if a `ListItemSecondaryAction` is used it must - * be the last child. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<ListItemClasses>; - /** - * The container component used when a `ListItemSecondaryAction` is the last child. - * @default 'li' - * @deprecated - */ - ContainerComponent?: React.ElementType<React.HTMLAttributes<HTMLDivElement>>; - /** - * Props applied to the container component if used. - * @default {} - * @deprecated - */ - ContainerProps?: React.HTMLAttributes<HTMLDivElement>; - /** - * If `true`, compact vertical padding designed for keyboard and mouse input is used. - * The prop defaults to the value inherited from the parent List component. - * @default false - */ - dense?: boolean; - /** - * If `true`, the component is disabled. - * @default false - * @deprecated checkout [ListItemButton](/material-ui/api/list-item-button/) instead - */ - disabled?: boolean; - /** - * If `true`, the left and right padding is removed. - * @default false - */ - disableGutters?: boolean; - /** - * If `true`, all padding is removed. - * @default false - */ - disablePadding?: boolean; - /** - * If `true`, a 1px light border is added to the bottom of the list item. - * @default false - */ - divider?: boolean; - /** - * The element to display at the end of ListItem. - */ - secondaryAction?: React.ReactNode; - /** - * Use to apply selected styling. - * @default false - * @deprecated checkout [ListItemButton](/material-ui/api/list-item-button/) instead - */ - selected?: boolean; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -export interface ListItemOwnProps extends ListItemBaseProps { - /** - * The components used for each slot inside. - * - * This prop is an alias for the `slots` prop. - * It's recommended to use the `slots` prop instead. - * - * @default {} - */ - components?: { - Root?: React.ElementType; - }; - /** - * The extra props for the slot components. - * You can override the existing props or add new ones. - * - * This prop is an alias for the `slotProps` prop. - * It's recommended to use the `slotProps` prop instead, as `componentsProps` will be deprecated in the future. - * - * @default {} - */ - componentsProps?: { - root?: React.HTMLAttributes<HTMLDivElement> & ListItemComponentsPropsOverrides; - }; - /** - * The extra props for the slot components. - * You can override the existing props or add new ones. - * - * This prop is an alias for the `componentsProps` prop, which will be deprecated in the future. - * - * @default {} - */ - slotProps?: { - root?: React.HTMLAttributes<HTMLDivElement> & ListItemComponentsPropsOverrides; - }; - /** - * The components used for each slot inside. - * - * This prop is an alias for the `components` prop, which will be deprecated in the future. - * - * @default {} - */ - slots?: { - root?: React.ElementType; - }; -} - -export interface ListItemTypeMap<AdditionalProps, RootComponent extends React.ElementType> { - props: AdditionalProps & ListItemOwnProps; - defaultComponent: RootComponent; -} - -/** - * Uses an additional container component if `ListItemSecondaryAction` is the last child. - * - * Demos: - * - * - [Lists](https://mui.com/material-ui/react-list/) - * - [Transfer List](https://mui.com/material-ui/react-transfer-list/) - * - * API: - * - * - [ListItem API](https://mui.com/material-ui/api/list-item/) - */ -declare const ListItem: ExtendButtonBase< - ListItemTypeMap< - { - /** - * If `true`, the list item is a button (using `ButtonBase`). Props intended - * for `ButtonBase` can then be applied to `ListItem`. - * @default false - * @deprecated checkout [ListItemButton](/material-ui/api/list-item-button/) instead - * - */ - button: true; - }, - 'div' - > -> & - OverridableComponent< - ListItemTypeMap< - { - /** - * If `true`, the list item is a button (using `ButtonBase`). Props intended - * for `ButtonBase` can then be applied to `ListItem`. - * @default false - * @deprecated checkout [ListItemButton](/material-ui/api/list-item-button/) instead - */ - button?: false; - }, - 'li' - > - >; - -export type ListItemProps< - RootComponent extends React.ElementType = 'li', - AdditionalProps = {}, -> = OverrideProps<ListItemTypeMap<AdditionalProps, RootComponent>, RootComponent> & { - component?: React.ElementType; -}; - -export default ListItem; diff --git a/packages/mui-material-next/src/ListItem/ListItem.js b/packages/mui-material-next/src/ListItem/ListItem.js deleted file mode 100644 index dc41c6e9bcd6e7..00000000000000 --- a/packages/mui-material-next/src/ListItem/ListItem.js +++ /dev/null @@ -1,496 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses, isHostComponent } from '@mui/base'; -import { chainPropTypes, elementTypeAcceptingRef } from '@mui/utils'; -import { alpha } from '@mui/system'; -import isMuiElement from '@mui/utils/isMuiElement'; -import useEnhancedEffect from '@mui/utils/useEnhancedEffect'; -import useForkRef from '@mui/utils/useForkRef'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; -import ButtonBase from '../ButtonBase'; -import ListContext from '../List/ListContext'; -import listItemClasses, { getListItemUtilityClass } from './listItemClasses'; -import { listItemButtonClasses } from '../ListItemButton'; -import ListItemSecondaryAction from '../ListItemSecondaryAction'; - -export const overridesResolver = (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - ownerState.dense && styles.dense, - ownerState.alignItems === 'flex-start' && styles.alignItemsFlexStart, - ownerState.divider && styles.divider, - !ownerState.disableGutters && styles.gutters, - !ownerState.disablePadding && styles.padding, - ownerState.button && styles.button, - ownerState.hasSecondaryAction && styles.secondaryAction, - ]; -}; - -const useUtilityClasses = (ownerState) => { - const { - alignItems, - button, - classes, - dense, - disabled, - disableGutters, - disablePadding, - divider, - hasSecondaryAction, - selected, - } = ownerState; - - const slots = { - root: [ - 'root', - dense && 'dense', - !disableGutters && 'gutters', - !disablePadding && 'padding', - divider && 'divider', - disabled && 'disabled', - button && 'button', - alignItems === 'flex-start' && 'alignItemsFlexStart', - hasSecondaryAction && 'secondaryAction', - selected && 'selected', - ], - container: ['container'], - }; - - return composeClasses(slots, getListItemUtilityClass, classes); -}; - -export const ListItemRoot = styled('div', { - name: 'MuiListItem', - slot: 'Root', - overridesResolver, -})(({ theme, ownerState }) => ({ - display: 'flex', - justifyContent: 'flex-start', - alignItems: 'center', - position: 'relative', - textDecoration: 'none', - width: '100%', - boxSizing: 'border-box', - textAlign: 'left', - ...(!ownerState.disablePadding && { - paddingTop: 8, - paddingBottom: 8, - ...(ownerState.dense && { - paddingTop: 4, - paddingBottom: 4, - }), - ...(!ownerState.disableGutters && { - paddingLeft: 16, - paddingRight: 16, - }), - ...(!!ownerState.secondaryAction && { - // Add some space to avoid collision as `ListItemSecondaryAction` - // is absolutely positioned. - paddingRight: 48, - }), - }), - ...(!!ownerState.secondaryAction && { - [`& > .${listItemButtonClasses.root}`]: { - paddingRight: 48, - }, - }), - [`&.${listItemClasses.focusVisible}`]: { - backgroundColor: (theme.vars || theme).palette.action.focus, - }, - [`&.${listItemClasses.selected}`]: { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})` - : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity), - [`&.${listItemClasses.focusVisible}`]: { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.focusOpacity}))` - : alpha( - theme.palette.primary.main, - theme.palette.action.selectedOpacity + theme.palette.action.focusOpacity, - ), - }, - }, - [`&.${listItemClasses.disabled}`]: { - opacity: (theme.vars || theme).palette.action.disabledOpacity, - }, - ...(ownerState.alignItems === 'flex-start' && { - alignItems: 'flex-start', - }), - ...(ownerState.divider && { - borderBottom: `1px solid ${(theme.vars || theme).palette.divider}`, - backgroundClip: 'padding-box', - }), - ...(ownerState.button && { - transition: theme.transitions.create('background-color', { - duration: theme.transitions.duration.shortest, - }), - '&:hover': { - textDecoration: 'none', - backgroundColor: (theme.vars || theme).palette.action.hover, - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - }, - [`&.${listItemClasses.selected}:hover`]: { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.hoverOpacity}))` - : alpha( - theme.palette.primary.main, - theme.palette.action.selectedOpacity + theme.palette.action.hoverOpacity, - ), - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})` - : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity), - }, - }, - }), - ...(ownerState.hasSecondaryAction && { - // Add some space to avoid collision as `ListItemSecondaryAction` - // is absolutely positioned. - paddingRight: 48, - }), -})); - -const ListItemContainer = styled('li', { - name: 'MuiListItem', - slot: 'Container', - overridesResolver: (props, styles) => styles.container, -})({ - position: 'relative', -}); - -/** - * Uses an additional container component if `ListItemSecondaryAction` is the last child. - */ -const ListItem = React.forwardRef(function ListItem(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiListItem' }); - const { - alignItems = 'center', - autoFocus = false, - button = false, - children: childrenProp, - className, - component: componentProp, - components = {}, - componentsProps = {}, - ContainerComponent = 'li', - ContainerProps: { className: ContainerClassName, ...ContainerProps } = {}, - dense = false, - disabled = false, - disableGutters = false, - disablePadding = false, - divider = false, - focusVisibleClassName, - secondaryAction, - selected = false, - slotProps = {}, - slots = {}, - ...other - } = props; - - const context = React.useContext(ListContext); - const childContext = React.useMemo( - () => ({ - dense: dense || context.dense || false, - alignItems, - disableGutters, - }), - [alignItems, context.dense, dense, disableGutters], - ); - - const listItemRef = React.useRef(null); - useEnhancedEffect(() => { - if (autoFocus) { - if (listItemRef.current) { - listItemRef.current.focus(); - } else if (process.env.NODE_ENV !== 'production') { - console.error( - 'MUI: Unable to set focus to a ListItem whose component has not been rendered.', - ); - } - } - }, [autoFocus]); - - const children = React.Children.toArray(childrenProp); - - // v4 implementation, deprecated in v5, will be removed in v6 - const hasSecondaryAction = - children.length && isMuiElement(children[children.length - 1], ['ListItemSecondaryAction']); - - const ownerState = { - ...props, - alignItems, - autoFocus, - button, - dense: childContext.dense, - disabled, - disableGutters, - disablePadding, - divider, - hasSecondaryAction, - selected, - }; - - const classes = useUtilityClasses(ownerState); - - const handleRef = useForkRef(listItemRef, ref); - - const Root = slots.root || components.Root || ListItemRoot; - const rootProps = slotProps.root || componentsProps.root || {}; - - const componentProps = { - className: clsx(classes.root, rootProps.className, className), - disabled, - ...other, - }; - - let Component = componentProp || 'li'; - - if (button) { - componentProps.component = componentProp || 'div'; - componentProps.focusVisibleClassName = clsx( - listItemClasses.focusVisible, - focusVisibleClassName, - ); - - Component = ButtonBase; - } - - // v4 implementation, deprecated in v5, will be removed in v6 - if (hasSecondaryAction) { - // Use div by default. - Component = !componentProps.component && !componentProp ? 'div' : Component; - - // Avoid nesting of li > li. - if (ContainerComponent === 'li') { - if (Component === 'li') { - Component = 'div'; - } else if (componentProps.component === 'li') { - componentProps.component = 'div'; - } - } - - return ( - <ListContext.Provider value={childContext}> - <ListItemContainer - as={ContainerComponent} - className={clsx(classes.container, ContainerClassName)} - ref={handleRef} - ownerState={ownerState} - {...ContainerProps} - > - <Root - {...rootProps} - {...(!isHostComponent(Root) && { - as: Component, - ownerState: { ...ownerState, ...rootProps.ownerState }, - })} - {...componentProps} - > - {children} - </Root> - {children.pop()} - </ListItemContainer> - </ListContext.Provider> - ); - } - - return ( - <ListContext.Provider value={childContext}> - <Root - {...rootProps} - as={Component} - ref={handleRef} - {...(!isHostComponent(Root) && { - ownerState: { ...ownerState, ...rootProps.ownerState }, - })} - {...componentProps} - > - {children} - {secondaryAction && <ListItemSecondaryAction>{secondaryAction}</ListItemSecondaryAction>} - </Root> - </ListContext.Provider> - ); -}); - -ListItem.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * Defines the `align-items` style property. - * @default 'center' - */ - alignItems: PropTypes.oneOf(['center', 'flex-start']), - /** - * If `true`, the list item is focused during the first mount. - * Focus will also be triggered if the value changes from false to true. - * @default false - * @deprecated checkout [ListItemButton](/material-ui/api/list-item-button/) instead - */ - autoFocus: PropTypes.bool, - /** - * If `true`, the list item is a button (using `ButtonBase`). Props intended - * for `ButtonBase` can then be applied to `ListItem`. - * @default false - * @deprecated checkout [ListItemButton](/material-ui/api/list-item-button/) instead - */ - button: PropTypes.bool, - /** - * The content of the component if a `ListItemSecondaryAction` is used it must - * be the last child. - */ - children: chainPropTypes(PropTypes.node, (props) => { - const children = React.Children.toArray(props.children); - - // React.Children.toArray(props.children).findLastIndex(isListItemSecondaryAction) - let secondaryActionIndex = -1; - for (let i = children.length - 1; i >= 0; i -= 1) { - const child = children[i]; - if (isMuiElement(child, ['ListItemSecondaryAction'])) { - secondaryActionIndex = i; - break; - } - } - - // is ListItemSecondaryAction the last child of ListItem - if (secondaryActionIndex !== -1 && secondaryActionIndex !== children.length - 1) { - return new Error( - 'MUI: You used an element after ListItemSecondaryAction. ' + - 'For ListItem to detect that it has a secondary action ' + - 'you must pass it as the last child to ListItem.', - ); - } - - return null; - }), - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * The components used for each slot inside. - * - * This prop is an alias for the `slots` prop. - * It's recommended to use the `slots` prop instead. - * - * @default {} - */ - components: PropTypes.shape({ - Root: PropTypes.elementType, - }), - /** - * The extra props for the slot components. - * You can override the existing props or add new ones. - * - * This prop is an alias for the `slotProps` prop. - * It's recommended to use the `slotProps` prop instead, as `componentsProps` will be deprecated in the future. - * - * @default {} - */ - componentsProps: PropTypes.shape({ - root: PropTypes.object, - }), - /** - * The container component used when a `ListItemSecondaryAction` is the last child. - * @default 'li' - * @deprecated - */ - ContainerComponent: elementTypeAcceptingRef, - /** - * Props applied to the container component if used. - * @default {} - * @deprecated - */ - ContainerProps: PropTypes.object, - /** - * If `true`, compact vertical padding designed for keyboard and mouse input is used. - * The prop defaults to the value inherited from the parent List component. - * @default false - */ - dense: PropTypes.bool, - /** - * If `true`, the component is disabled. - * @default false - * @deprecated checkout [ListItemButton](/material-ui/api/list-item-button/) instead - */ - disabled: PropTypes.bool, - /** - * If `true`, the left and right padding is removed. - * @default false - */ - disableGutters: PropTypes.bool, - /** - * If `true`, all padding is removed. - * @default false - */ - disablePadding: PropTypes.bool, - /** - * If `true`, a 1px light border is added to the bottom of the list item. - * @default false - */ - divider: PropTypes.bool, - /** - * @ignore - */ - focusVisibleClassName: PropTypes.string, - /** - * The element to display at the end of ListItem. - */ - secondaryAction: PropTypes.node, - /** - * Use to apply selected styling. - * @default false - * @deprecated checkout [ListItemButton](/material-ui/api/list-item-button/) instead - */ - selected: PropTypes.bool, - /** - * The extra props for the slot components. - * You can override the existing props or add new ones. - * - * This prop is an alias for the `componentsProps` prop, which will be deprecated in the future. - * - * @default {} - */ - slotProps: PropTypes.shape({ - root: PropTypes.object, - }), - /** - * The components used for each slot inside. - * - * This prop is an alias for the `components` prop, which will be deprecated in the future. - * - * @default {} - */ - slots: PropTypes.shape({ - root: PropTypes.elementType, - }), - /** - * 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, - ]), -}; - -export default ListItem; diff --git a/packages/mui-material-next/src/ListItem/ListItem.spec.tsx b/packages/mui-material-next/src/ListItem/ListItem.spec.tsx deleted file mode 100644 index 0e575d99fe1a98..00000000000000 --- a/packages/mui-material-next/src/ListItem/ListItem.spec.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import * as React from 'react'; -import ListItem from '@mui/material-next/ListItem'; -import { styled } from '@mui/material/styles'; - -// button: boolean -function BooleanButtonTest() { - // https://github.com/mui/material-ui/issues/14971 - - function EditableItemFail(props: { editable: boolean }) { - const { editable } = props; - // @ts-expect-error 'boolean' is not assignable to type 'true' - return <ListItem button={editable}>Editable? {editable}</ListItem>; - } - - function EditableItemValid(props: { editable: boolean }) { - const { editable } = props; - if (editable) { - <ListItem button>Editable? Yes</ListItem>; - } - return <ListItem>Editable? No</ListItem>; - } -} - -// verify that https://github.com/mui/material-ui/issues/19756 already worked. -function MouseEnterTest() { - function handleMouseEnter(event: React.MouseEvent<HTMLLIElement>) {} - <ListItem onMouseEnter={handleMouseEnter} />; - - function handleMouseEnterButton(event: React.MouseEvent<HTMLDivElement>) {} - // @ts-expect-error - <ListItem onMouseEnter={handleMouseEnterButton} />; // desired: missing property button - <ListItem button onMouseEnter={handleMouseEnterButton} />; -} - -// https://github.com/mui/material-ui/issues/26469 -const StyledListItem = styled(ListItem)({}); -function StyledTest() { - <StyledListItem dense />; - - // @ts-expect-error - <StyledListItem button />; // `button` is deprecated in v5, can be removed in v6 -} diff --git a/packages/mui-material-next/src/ListItem/ListItem.test.js b/packages/mui-material-next/src/ListItem/ListItem.test.js deleted file mode 100644 index abe5fb73890ecb..00000000000000 --- a/packages/mui-material-next/src/ListItem/ListItem.test.js +++ /dev/null @@ -1,249 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import PropTypes from 'prop-types'; -import { act, createRenderer, fireEvent, queries } from '@mui-internal/test-utils'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; -import ListItemText from '@mui/material-next/ListItemText'; -import ListItemSecondaryAction from '@mui/material-next/ListItemSecondaryAction'; -import ListItem, { listItemClasses as classes } from '@mui/material-next/ListItem'; -import ListContext from '../List/ListContext'; -import describeConformance from '../../test/describeConformance'; - -const NoContent = React.forwardRef(() => { - return null; -}); - -describe('<ListItem />', () => { - const { render } = createRenderer(); - - describeConformance(<ListItem />, () => ({ - classes, - inheritComponent: 'li', - render, - refInstanceof: window.HTMLLIElement, - muiName: 'MuiListItem', - testVariantProps: { dense: true }, - testLegacyComponentsProp: true, - slots: { - root: {}, - }, - skip: [ - 'componentsProp', - 'slotPropsCallback', // not supported yet - ], - })); - - it('should render with gutters classes', () => { - const { getByRole } = render(<ListItem />); - expect(getByRole('listitem')).to.have.class(classes.gutters); - }); - - it('should render with the selected class', () => { - const { getByRole } = render(<ListItem selected />); - expect(getByRole('listitem')).to.have.class(classes.selected); - }); - - it('should disable the gutters', () => { - const { getByRole } = render(<ListItem disableGutters />); - expect(getByRole('listitem')).not.to.have.class(classes.gutters); - }); - - describe('prop: button', () => { - it('renders a div', () => { - const { container } = render(<ListItem button />); - expect(container.firstChild).to.have.property('nodeName', 'DIV'); - }); - }); - - describe('context: dense', () => { - it('should forward the context', () => { - let context = null; - const { setProps } = render( - <ListItem> - <ListContext.Consumer> - {(options) => { - context = options; - }} - </ListContext.Consumer> - </ListItem>, - ); - expect(context).to.have.property('dense', false); - setProps({ dense: true }); - expect(context).to.have.property('dense', true); - }); - }); - - describe('action', () => { - it('should show action if provided', () => { - const { getByText } = render(<ListItem secondaryAction="foo" />); - expect(getByText('foo')).toBeVisible(); - }); - }); - - // TODO remove in v6 in favor of ListItemButton - describe('secondary action', () => { - it('should wrap with a container', () => { - const { getByRole } = render( - <ListItem> - <ListItemText primary="primary" /> - <ListItemSecondaryAction /> - </ListItem>, - ); - const listItem = getByRole('listitem'); - - expect(listItem).to.have.class(classes.container); - expect(listItem.querySelector(`div.${classes.root}`)).not.to.equal(null); - }); - - it('should accept a component property', () => { - const { getByRole } = render( - <ListItem component="span"> - <ListItemText primary="primary" /> - <ListItemSecondaryAction /> - </ListItem>, - ); - const listItem = getByRole('listitem'); - - expect(listItem).to.have.class(classes.container); - expect(listItem.querySelector(`span.${classes.root}`)).not.to.equal(null); - }); - - it('should accept a button property', () => { - const { getByRole } = render( - <ListItem button> - <ListItemText primary="primary" /> - <ListItemSecondaryAction /> - </ListItem>, - ); - const listItem = getByRole('listitem'); - - expect(listItem).to.have.class(classes.container); - expect(queries.getByRole(listItem, 'button')).not.to.equal(null); - }); - - it('should accept a ContainerComponent property', () => { - const { getByRole } = render( - <ListItem ContainerComponent="div" ContainerProps={{ role: 'listitem' }}> - <ListItemText primary="primary" /> - <ListItemSecondaryAction /> - </ListItem>, - ); - const listItem = getByRole('listitem'); - - expect(listItem).to.have.property('nodeName', 'DIV'); - expect(listItem).to.have.class(classes.container); - expect(listItem.querySelector(`div.${classes.root}`)).not.to.equal(null); - }); - - it('can autofocus a custom ContainerComponent', () => { - const { getByRole } = render( - <ListItem - autoFocus - ContainerComponent="div" - ContainerProps={{ role: 'listitem', tabIndex: -1 }} - > - <ListItemText primary="primary" /> - <ListItemSecondaryAction /> - </ListItem>, - ); - - expect(getByRole('listitem')).toHaveFocus(); - }); - - it('should allow customization of the wrapper', () => { - const { getByRole } = render( - <ListItem ContainerProps={{ className: 'bubu', role: 'listitem' }}> - <ListItemText primary="primary" /> - <ListItemSecondaryAction /> - </ListItem>, - ); - const listItem = getByRole('listitem'); - - expect(listItem).to.have.class(classes.container); - expect(listItem).to.have.class('bubu'); - }); - - describe('warnings', () => { - beforeEach(() => { - PropTypes.resetWarningCache(); - }); - - it('warns if it cant detect the secondary action properly', () => { - expect(() => { - PropTypes.checkPropTypes( - ListItem.propTypes, - { - classes: {}, - children: [ - <ListItemSecondaryAction>I should have come last :(</ListItemSecondaryAction>, - <ListItemText>My position does not matter.</ListItemText>, - ], - }, - 'prop', - 'MockedName', - ); - }).toErrorDev('Warning: Failed prop type: MUI: You used an element'); - }); - - it('should warn (but not error) with autoFocus with a function component with no content', () => { - expect(() => { - render(<ListItem component={NoContent} autoFocus />); - }).toErrorDev([ - 'MUI: Unable to set focus to a ListItem whose component has not been rendered.', - // React 18 Strict Effects run mount effects twice - React.version.startsWith('18') && - 'MUI: Unable to set focus to a ListItem whose component has not been rendered.', - ]); - }); - }); - }); - - // TODO remove in v6 in favor of ListItemButton - describe('prop: focusVisibleClassName', () => { - it('should merge the class names', () => { - const { getByRole } = render( - <ListItem button focusVisibleClassName="focusVisibleClassName" />, - ); - const button = getByRole('button'); - - act(() => { - fireEvent.keyDown(document.activeElement || document.body, { key: 'Tab' }); - button.focus(); - }); - - expect(button).to.have.class('focusVisibleClassName'); - expect(button).to.have.class(classes.focusVisible); - }); - }); - - it('container overrides should work', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const testStyle = { - marginTop: '13px', - }; - - const theme = createTheme({ - components: { - MuiListItem: { - styleOverrides: { - container: testStyle, - }, - }, - }, - }); - - const { container } = render( - <ThemeProvider theme={theme}> - <ListItem> - Test<ListItemSecondaryAction>SecondaryAction</ListItemSecondaryAction> - </ListItem> - </ThemeProvider>, - ); - - const listItemContainer = container.getElementsByClassName(classes.container)[0]; - expect(listItemContainer).to.toHaveComputedStyle(testStyle); - }); -}); diff --git a/packages/mui-material-next/src/ListItem/index.d.ts b/packages/mui-material-next/src/ListItem/index.d.ts deleted file mode 100644 index e897bf17fca806..00000000000000 --- a/packages/mui-material-next/src/ListItem/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './ListItem'; -export * from './ListItem'; - -export { default as listItemClasses } from './listItemClasses'; -export * from './listItemClasses'; diff --git a/packages/mui-material-next/src/ListItem/index.js b/packages/mui-material-next/src/ListItem/index.js deleted file mode 100644 index 911c835b6b5887..00000000000000 --- a/packages/mui-material-next/src/ListItem/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './ListItem'; - -export { default as listItemClasses } from './listItemClasses'; -export * from './listItemClasses'; diff --git a/packages/mui-material-next/src/ListItem/listItemClasses.ts b/packages/mui-material-next/src/ListItem/listItemClasses.ts deleted file mode 100644 index 98a317a8fe4d69..00000000000000 --- a/packages/mui-material-next/src/ListItem/listItemClasses.ts +++ /dev/null @@ -1,52 +0,0 @@ -import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; -import generateUtilityClass from '@mui/utils/generateUtilityClass'; - -export interface ListItemClasses { - /** Styles applied to the (normally root) `component` element. May be wrapped by a `container`. */ - root: string; - /** Styles applied to the container element if `children` includes `ListItemSecondaryAction`. */ - container: string; - /** State class applied to the `component`'s `focusVisibleClassName` prop if `button={true}`. */ - focusVisible: string; - /** Styles applied to the component element if dense. */ - dense: string; - /** Styles applied to the component element if `alignItems="flex-start"`. */ - alignItemsFlexStart: string; - /** State class applied to the inner `component` element if `disabled={true}`. */ - disabled: string; - /** Styles applied to the inner `component` element if `divider={true}`. */ - divider: string; - /** Styles applied to the inner `component` element unless `disableGutters={true}`. */ - gutters: string; - /** Styles applied to the root element unless `disablePadding={true}`. */ - padding: string; - /** Styles applied to the inner `component` element if `button={true}`. */ - button: string; - /** Styles applied to the component element if `children` includes `ListItemSecondaryAction`. */ - secondaryAction: string; - /** State class applied to the root element if `selected={true}`. */ - selected: string; -} - -export type ListItemClassKey = keyof ListItemClasses; - -export function getListItemUtilityClass(slot: string): string { - return generateUtilityClass('MuiListItem', slot); -} - -const listItemClasses: ListItemClasses = generateUtilityClasses('MuiListItem', [ - 'root', - 'container', - 'focusVisible', - 'dense', - 'alignItemsFlexStart', - 'disabled', - 'divider', - 'gutters', - 'padding', - 'button', - 'secondaryAction', - 'selected', -]); - -export default listItemClasses; diff --git a/packages/mui-material-next/src/ListItemAvatar/ListItemAvatar.d.ts b/packages/mui-material-next/src/ListItemAvatar/ListItemAvatar.d.ts deleted file mode 100644 index b8ba0741ff4dd7..00000000000000 --- a/packages/mui-material-next/src/ListItemAvatar/ListItemAvatar.d.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { SxProps } from '@mui/system'; -import { InternalStandardProps as StandardProps, Theme } from '@mui/material'; -import { ListItemAvatarClasses } from './listItemAvatarClasses'; - -export interface ListItemAvatarProps extends StandardProps<React.HTMLAttributes<HTMLDivElement>> { - /** - * The content of the component, normally an `Avatar`. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<ListItemAvatarClasses>; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -/** - * A simple wrapper to apply `List` styles to an `Avatar`. - * - * Demos: - * - * - [Lists](https://mui.com/material-ui/react-list/) - * - * API: - * - * - [ListItemAvatar API](https://mui.com/material-ui/api/list-item-avatar/) - */ -export default function ListItemAvatar(props: ListItemAvatarProps): JSX.Element; diff --git a/packages/mui-material-next/src/ListItemAvatar/ListItemAvatar.js b/packages/mui-material-next/src/ListItemAvatar/ListItemAvatar.js deleted file mode 100644 index 6bb8339ba8eec7..00000000000000 --- a/packages/mui-material-next/src/ListItemAvatar/ListItemAvatar.js +++ /dev/null @@ -1,88 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import ListContext from '../List/ListContext'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; -import { getListItemAvatarUtilityClass } from './listItemAvatarClasses'; - -const useUtilityClasses = (ownerState) => { - const { alignItems, classes } = ownerState; - - const slots = { - root: ['root', alignItems === 'flex-start' && 'alignItemsFlexStart'], - }; - - return composeClasses(slots, getListItemAvatarUtilityClass, classes); -}; - -const ListItemAvatarRoot = styled('div', { - name: 'MuiListItemAvatar', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [styles.root, ownerState.alignItems === 'flex-start' && styles.alignItemsFlexStart]; - }, -})(({ ownerState }) => ({ - minWidth: 56, - flexShrink: 0, - ...(ownerState.alignItems === 'flex-start' && { - marginTop: 8, - }), -})); - -/** - * A simple wrapper to apply `List` styles to an `Avatar`. - */ -const ListItemAvatar = React.forwardRef(function ListItemAvatar(inProps, ref) { - const props = useThemeProps({ - props: inProps, - name: 'MuiListItemAvatar', - }); - - const { className, ...other } = props; - const context = React.useContext(ListContext); - const ownerState = { ...props, alignItems: context.alignItems }; - const classes = useUtilityClasses(ownerState); - - return ( - <ListItemAvatarRoot - className={clsx(classes.root, className)} - ownerState={ownerState} - ref={ref} - {...other} - /> - ); -}); - -ListItemAvatar.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The content of the component, normally an `Avatar`. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * 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, - ]), -}; - -export default ListItemAvatar; diff --git a/packages/mui-material-next/src/ListItemAvatar/ListItemAvatar.test.js b/packages/mui-material-next/src/ListItemAvatar/ListItemAvatar.test.js deleted file mode 100644 index 605fd1128c3587..00000000000000 --- a/packages/mui-material-next/src/ListItemAvatar/ListItemAvatar.test.js +++ /dev/null @@ -1,24 +0,0 @@ -import * as React from 'react'; -import { createRenderer } from '@mui-internal/test-utils'; -import ListItemAvatar, { - listItemAvatarClasses as classes, -} from '@mui/material-next/ListItemAvatar'; -import describeConformance from '../../test/describeConformance'; - -describe('<ListItemAvatar />', () => { - const { render } = createRenderer(); - - describeConformance( - <ListItemAvatar> - <div /> - </ListItemAvatar>, - () => ({ - classes, - inheritComponent: 'div', - render, - muiName: 'MuiListItemAvatar', - refInstanceof: window.HTMLDivElement, - skip: ['componentProp', 'componentsProp', 'themeVariants'], - }), - ); -}); diff --git a/packages/mui-material-next/src/ListItemAvatar/index.d.ts b/packages/mui-material-next/src/ListItemAvatar/index.d.ts deleted file mode 100644 index 70accfc1af3b70..00000000000000 --- a/packages/mui-material-next/src/ListItemAvatar/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './ListItemAvatar'; -export * from './ListItemAvatar'; - -export { default as listItemAvatarClasses } from './listItemAvatarClasses'; -export * from './listItemAvatarClasses'; diff --git a/packages/mui-material-next/src/ListItemAvatar/index.js b/packages/mui-material-next/src/ListItemAvatar/index.js deleted file mode 100644 index 7193ec97998512..00000000000000 --- a/packages/mui-material-next/src/ListItemAvatar/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './ListItemAvatar'; - -export { default as listItemAvatarClasses } from './listItemAvatarClasses'; -export * from './listItemAvatarClasses'; diff --git a/packages/mui-material-next/src/ListItemAvatar/listItemAvatarClasses.ts b/packages/mui-material-next/src/ListItemAvatar/listItemAvatarClasses.ts deleted file mode 100644 index dca9d9a65d38c9..00000000000000 --- a/packages/mui-material-next/src/ListItemAvatar/listItemAvatarClasses.ts +++ /dev/null @@ -1,22 +0,0 @@ -import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; -import generateUtilityClass from '@mui/utils/generateUtilityClass'; - -export interface ListItemAvatarClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element when the parent `ListItem` uses `alignItems="flex-start"`. */ - alignItemsFlexStart: string; -} - -export type ListItemAvatarClassKey = keyof ListItemAvatarClasses; - -export function getListItemAvatarUtilityClass(slot: string): string { - return generateUtilityClass('MuiListItemAvatar', slot); -} - -const listItemAvatarClasses: ListItemAvatarClasses = generateUtilityClasses('MuiListItemAvatar', [ - 'root', - 'alignItemsFlexStart', -]); - -export default listItemAvatarClasses; diff --git a/packages/mui-material-next/src/ListItemButton/ListItemButton.d.ts b/packages/mui-material-next/src/ListItemButton/ListItemButton.d.ts deleted file mode 100644 index 3547ee871270d9..00000000000000 --- a/packages/mui-material-next/src/ListItemButton/ListItemButton.d.ts +++ /dev/null @@ -1,95 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverrideProps } from '@mui/types'; -import { Theme } from '../styles'; -import { ExtendButtonBase, ExtendButtonBaseTypeMap } from '../ButtonBase/ButtonBase.types'; -import { ListItemButtonClasses } from './listItemButtonClasses'; - -/** - * This interface is kept for backward compatibility. To extend `LitItemButton` - * props through module augmentation, use `ListItemButtonOwnProps`. - */ -export interface ListItemButtonBaseProps { - /** - * Defines the `align-items` style property. - * @default 'center' - */ - alignItems?: 'flex-start' | 'center'; - /** - * If `true`, the list item is focused during the first mount. - * Focus will also be triggered if the value changes from false to true. - * @default false - */ - autoFocus?: boolean; - /** - * The content of the component if a `ListItemSecondaryAction` is used it must - * be the last child. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<ListItemButtonClasses>; - /** - * If `true`, compact vertical padding designed for keyboard and mouse input is used. - * The prop defaults to the value inherited from the parent List component. - * @default false - */ - dense?: boolean; - /** - * If `true`, the component is disabled. - * @default false - */ - disabled?: boolean; - /** - * If `true`, the left and right padding is removed. - * @default false - */ - disableGutters?: boolean; - /** - * If `true`, a 1px light border is added to the bottom of the list item. - * @default false - */ - divider?: boolean; - /** - * Use to apply selected styling. - * @default false - */ - selected?: boolean; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -export interface ListItemButtonOwnProps extends ListItemButtonBaseProps {} - -export type ListItemButtonTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'div', -> = ExtendButtonBaseTypeMap<{ - props: AdditionalProps & ListItemButtonOwnProps; - defaultComponent: RootComponent; -}>; - -/** - * - * Demos: - * - * - [Lists](https://mui.com/material-ui/react-list/) - * - * API: - * - * - [ListItemButton API](https://mui.com/material-ui/api/list-item-button/) - * - inherits [ButtonBase API](https://mui.com/material-ui/api/button-base/) - */ -declare const ListItemButton: ExtendButtonBase<ListItemButtonTypeMap>; - -export type ListItemButtonProps< - RootComponent extends React.ElementType = ListItemButtonTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<ListItemButtonTypeMap<AdditionalProps, RootComponent>, RootComponent> & { - component?: React.ElementType; -}; - -export default ListItemButton; diff --git a/packages/mui-material-next/src/ListItemButton/ListItemButton.js b/packages/mui-material-next/src/ListItemButton/ListItemButton.js deleted file mode 100644 index 39253fcd6b5f9e..00000000000000 --- a/packages/mui-material-next/src/ListItemButton/ListItemButton.js +++ /dev/null @@ -1,283 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { alpha } from '@mui/system'; -import useEnhancedEffect from '@mui/utils/useEnhancedEffect'; -import useForkRef from '@mui/utils/useForkRef'; -import styled, { rootShouldForwardProp } from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; -import ButtonBase from '../ButtonBase'; -import ListContext from '../List/ListContext'; -import listItemButtonClasses, { getListItemButtonUtilityClass } from './listItemButtonClasses'; - -export const overridesResolver = (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - ownerState.dense && styles.dense, - ownerState.alignItems === 'flex-start' && styles.alignItemsFlexStart, - ownerState.divider && styles.divider, - !ownerState.disableGutters && styles.gutters, - ]; -}; - -const useUtilityClasses = (ownerState) => { - const { alignItems, classes, dense, disabled, disableGutters, divider, selected } = ownerState; - - const slots = { - root: [ - 'root', - dense && 'dense', - !disableGutters && 'gutters', - divider && 'divider', - disabled && 'disabled', - alignItems === 'flex-start' && 'alignItemsFlexStart', - selected && 'selected', - ], - }; - - const composedClasses = composeClasses(slots, getListItemButtonUtilityClass, classes); - - return { - ...classes, - ...composedClasses, - }; -}; - -const ListItemButtonRoot = styled(ButtonBase, { - shouldForwardProp: (prop) => rootShouldForwardProp(prop) || prop === 'classes', - name: 'MuiListItemButton', - slot: 'Root', - overridesResolver, -})(({ theme, ownerState }) => ({ - display: 'flex', - flexGrow: 1, - justifyContent: 'flex-start', - alignItems: 'center', - position: 'relative', - textDecoration: 'none', - minWidth: 0, - boxSizing: 'border-box', - textAlign: 'left', - paddingTop: 8, - paddingBottom: 8, - transition: theme.transitions.create('background-color', { - duration: theme.transitions.duration.shortest, - }), - '&:hover': { - textDecoration: 'none', - backgroundColor: (theme.vars || theme).palette.action.hover, - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - }, - [`&.${listItemButtonClasses.selected}`]: { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})` - : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity), - [`&.${listItemButtonClasses.focusVisible}`]: { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.focusOpacity}))` - : alpha( - theme.palette.primary.main, - theme.palette.action.selectedOpacity + theme.palette.action.focusOpacity, - ), - }, - }, - [`&.${listItemButtonClasses.selected}:hover`]: { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.hoverOpacity}))` - : alpha( - theme.palette.primary.main, - theme.palette.action.selectedOpacity + theme.palette.action.hoverOpacity, - ), - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})` - : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity), - }, - }, - [`&.${listItemButtonClasses.focusVisible}`]: { - backgroundColor: (theme.vars || theme).palette.action.focus, - }, - [`&.${listItemButtonClasses.disabled}`]: { - opacity: (theme.vars || theme).palette.action.disabledOpacity, - }, - ...(ownerState.divider && { - borderBottom: `1px solid ${(theme.vars || theme).palette.divider}`, - backgroundClip: 'padding-box', - }), - ...(ownerState.alignItems === 'flex-start' && { - alignItems: 'flex-start', - }), - ...(!ownerState.disableGutters && { - paddingLeft: 16, - paddingRight: 16, - }), - ...(ownerState.dense && { - paddingTop: 4, - paddingBottom: 4, - }), -})); - -const ListItemButton = React.forwardRef(function ListItemButton(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiListItemButton' }); - const { - alignItems = 'center', - autoFocus = false, - component = 'div', - children, - dense = false, - disableGutters = false, - divider = false, - focusVisibleClassName, - selected = false, - className, - ...other - } = props; - - const context = React.useContext(ListContext); - const childContext = React.useMemo( - () => ({ - dense: dense || context.dense || false, - alignItems, - disableGutters, - }), - [alignItems, context.dense, dense, disableGutters], - ); - - const listItemRef = React.useRef(null); - useEnhancedEffect(() => { - if (autoFocus) { - if (listItemRef.current) { - listItemRef.current.focus(); - } else if (process.env.NODE_ENV !== 'production') { - console.error( - 'MUI: Unable to set focus to a ListItemButton whose component has not been rendered.', - ); - } - } - }, [autoFocus]); - - const ownerState = { - ...props, - alignItems, - dense: childContext.dense, - disableGutters, - divider, - selected, - }; - - const classes = useUtilityClasses(ownerState); - - const handleRef = useForkRef(listItemRef, ref); - - return ( - <ListContext.Provider value={childContext}> - <ListItemButtonRoot - ref={handleRef} - href={other.href || other.to} - // `ButtonBase` processes `href` or `to` if `component` is set to 'button' - component={(other.href || other.to) && component === 'div' ? 'button' : component} - focusVisibleClassName={clsx(classes.focusVisible, focusVisibleClassName)} - ownerState={ownerState} - className={clsx(classes.root, className)} - {...other} - classes={classes} - > - {children} - </ListItemButtonRoot> - </ListContext.Provider> - ); -}); - -ListItemButton.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * Defines the `align-items` style property. - * @default 'center' - */ - alignItems: PropTypes.oneOf(['center', 'flex-start']), - /** - * If `true`, the list item is focused during the first mount. - * Focus will also be triggered if the value changes from false to true. - * @default false - */ - autoFocus: PropTypes.bool, - /** - * The content of the component if a `ListItemSecondaryAction` is used it must - * be the last child. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * If `true`, compact vertical padding designed for keyboard and mouse input is used. - * The prop defaults to the value inherited from the parent List component. - * @default false - */ - dense: PropTypes.bool, - /** - * If `true`, the component is disabled. - * @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 list item. - * @default false - */ - divider: PropTypes.bool, - /** - * This prop can help identify which element has keyboard focus. - * The class name will be applied when the element gains the focus through keyboard interaction. - * It's a polyfill for the [CSS :focus-visible selector](https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo). - * The rationale for using this feature [is explained here](https://github.com/WICG/focus-visible/blob/HEAD/explainer.md). - * A [polyfill can be used](https://github.com/WICG/focus-visible) to apply a `focus-visible` class to other components - * if needed. - */ - focusVisibleClassName: PropTypes.string, - /** - * The URL to link to when the button is clicked. - * If defined, an `a` element will be used as the root node. - */ - href: PropTypes.string, - /** - * Use to apply selected styling. - * @default false - */ - selected: PropTypes.bool, - /** - * 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, - ]), -}; - -export default ListItemButton; diff --git a/packages/mui-material-next/src/ListItemButton/ListItemButton.test.js b/packages/mui-material-next/src/ListItemButton/ListItemButton.test.js deleted file mode 100644 index 5d317f01f10270..00000000000000 --- a/packages/mui-material-next/src/ListItemButton/ListItemButton.test.js +++ /dev/null @@ -1,260 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { act, createRenderer, fireEvent } from '@mui-internal/test-utils'; -import ListItemButton, { - listItemButtonClasses as classes, -} from '@mui/material-next/ListItemButton'; -import ButtonBase from '@mui/material-next/ButtonBase'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; -import ListContext from '../List/ListContext'; -import describeConformance from '../../test/describeConformance'; - -describe('<ListItemButton />', () => { - const { render } = createRenderer(); - - describeConformance(<ListItemButton />, () => ({ - classes, - inheritComponent: ButtonBase, - render, - refInstanceof: window.HTMLDivElement, - testComponentPropWith: 'a', - muiName: 'MuiListItemButton', - testVariantProps: { dense: true }, - skip: ['componentsProp'], - })); - - it('should render with gutters classes', () => { - const { getByRole } = render(<ListItemButton />); - expect(getByRole('button')).to.have.class(classes.gutters); - }); - - it('should render with the selected class', () => { - const { getByRole } = render(<ListItemButton selected />); - expect(getByRole('button')).to.have.class(classes.selected); - }); - - it('should disable the gutters', () => { - const { getByRole } = render(<ListItemButton disableGutters />); - expect(getByRole('button')).not.to.have.class(classes.gutters); - }); - - describe('context: dense', () => { - it('should forward the context', () => { - let context = null; - const { setProps } = render( - <ListItemButton> - <ListContext.Consumer> - {(options) => { - context = options; - }} - </ListContext.Consumer> - </ListItemButton>, - ); - expect(context).to.have.property('dense', false); - setProps({ dense: true }); - expect(context).to.have.property('dense', true); - }); - }); - - describe('prop: focusVisibleClassName', () => { - it('should merge the class names', () => { - const { getByRole } = render( - <ListItemButton focusVisibleClassName="focusVisibleClassName" />, - ); - const button = getByRole('button'); - - act(() => { - fireEvent.keyDown(document.activeElement || document.body, { key: 'Tab' }); - button.focus(); - }); - - expect(button).to.have.class('focusVisibleClassName'); - expect(button).to.have.class(classes.focusVisible); - }); - }); - - describe('prop: href', () => { - const href = 'example.com'; - - it('should rendered as link without specifying component="a"', () => { - const { getByRole } = render(<ListItemButton href={href} />); - - const link = getByRole('link'); - - expect(!!link).to.equal(true); - }); - - it('should rendered as link when specifying component="div"', () => { - const { getByRole } = render(<ListItemButton href={href} component="div" />); - - const link = getByRole('link'); - - expect(!!link).to.equal(true); - }); - - it('should rendered as link when specifying component="a"', () => { - const { getByRole } = render(<ListItemButton href={href} component="a" />); - - const link = getByRole('link'); - - expect(!!link).to.equal(true); - }); - - // TODO v7: support hostElementName prop - // eslint-disable-next-line mocha/no-skipped-tests - it.skip('should be rendered as the specifed component', () => { - const { getByRole } = render(<ListItemButton href={href} component="h1" />); - - const heading = getByRole('heading'); - - expect(!!heading).to.equal(true); - }); - }); - - describe('prop: to', () => { - const to = 'example.com'; - - it('should rendered as link without specifying component="a"', () => { - const { getByRole } = render(<ListItemButton to={to} />); - - const link = getByRole('link'); - - expect(!!link).to.equal(true); - }); - - it('should rendered as link when specifying component="div"', () => { - const { getByRole } = render(<ListItemButton to={to} component="div" />); - - const link = getByRole('link'); - - expect(!!link).to.equal(true); - }); - - it('should rendered as link when specifying component="a"', () => { - const { getByRole } = render(<ListItemButton to={to} component="a" />); - - const link = getByRole('link'); - - expect(!!link).to.equal(true); - }); - - // TODO v7: support hostElementName prop - // eslint-disable-next-line mocha/no-skipped-tests - it.skip('should be rendered as the specifed component', () => { - const { getByRole } = render(<ListItemButton to={to} component="h1" />); - - const heading = getByRole('heading'); - - expect(!!heading).to.equal(true); - }); - }); - - describe('prop: LinkComponent', () => { - const href = 'example.com'; - const customLinkId = 'customLink'; - const CustomLink = React.forwardRef((props, ref) => { - // eslint-disable-next-line jsx-a11y/anchor-has-content - return <a data-testid={customLinkId} ref={ref} {...props} />; - }); - - it('should render as LinkComponent when href is provided', () => { - const { container, getByTestId } = render( - <ListItemButton href={href} LinkComponent={CustomLink} />, - ); - const button = container.firstChild; - - expect(getByTestId(customLinkId)).not.to.equal(null); - expect(button).to.have.property('nodeName', 'A'); - expect(button).to.have.attribute('href', href); - }); - - // TODO v7: support hostElementName prop - // eslint-disable-next-line mocha/no-skipped-tests - it.skip('should ignore LinkComponent if component is provided', () => { - const { container, queryByTestId } = render( - <ListItemButton href={href} LinkComponent={CustomLink} component="h1" />, - ); - const button = container.firstChild; - - expect(queryByTestId(customLinkId)).to.equal(null); - expect(button).to.have.property('nodeName', 'H1'); - expect(button).to.have.attribute('href', href); - }); - - it('should render as LinkComponent (from theme) when href is provided', () => { - const theme = createTheme({ - components: { - MuiListItemButton: { - defaultProps: { - LinkComponent: CustomLink, - }, - }, - }, - }); - const { container, getByTestId } = render( - <ThemeProvider theme={theme}> - <ListItemButton href={href} />, - </ThemeProvider>, - ); - const button = container.firstChild; - - expect(getByTestId(customLinkId)).not.to.equal(null); - expect(button).to.have.property('nodeName', 'A'); - expect(button).to.have.attribute('href', href); - }); - - it('should render as LinkComponent (from theme MuiButtonBase) when href is provided', () => { - const theme = createTheme({ - components: { - MuiButtonBase: { - defaultProps: { - LinkComponent: CustomLink, - }, - }, - }, - }); - const { container, getByTestId } = render( - <ThemeProvider theme={theme}> - <ListItemButton href={href} />, - </ThemeProvider>, - ); - const button = container.firstChild; - - expect(getByTestId(customLinkId)).not.to.equal(null); - expect(button).to.have.property('nodeName', 'A'); - expect(button).to.have.attribute('href', href); - }); - - it('should prefer LinkComponent from MuiListItemButton over MuiButtonBase', () => { - const WrongCustomLink = React.forwardRef((props, ref) => { - // eslint-disable-next-line jsx-a11y/anchor-has-content - return <a data-testid="wrong-link" ref={ref} {...props} />; - }); - - const theme = createTheme({ - components: { - MuiListItemButton: { - defaultProps: { - LinkComponent: CustomLink, - }, - }, - MuiButtonBase: { - defaultProps: { - LinkComponent: WrongCustomLink, - }, - }, - }, - }); - const { container, getByTestId } = render( - <ThemeProvider theme={theme}> - <ListItemButton href={href} />, - </ThemeProvider>, - ); - const button = container.firstChild; - - expect(getByTestId(customLinkId)).not.to.equal(null); - expect(button).to.have.property('nodeName', 'A'); - expect(button).to.have.attribute('href', href); - }); - }); -}); diff --git a/packages/mui-material-next/src/ListItemButton/index.d.ts b/packages/mui-material-next/src/ListItemButton/index.d.ts deleted file mode 100644 index 57fdf7f3962ea2..00000000000000 --- a/packages/mui-material-next/src/ListItemButton/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './ListItemButton'; -export * from './ListItemButton'; - -export { default as listItemButtonClasses } from './listItemButtonClasses'; -export * from './listItemButtonClasses'; diff --git a/packages/mui-material-next/src/ListItemButton/index.js b/packages/mui-material-next/src/ListItemButton/index.js deleted file mode 100644 index 5af1d8f49e1c43..00000000000000 --- a/packages/mui-material-next/src/ListItemButton/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './ListItemButton'; - -export { default as listItemButtonClasses } from './listItemButtonClasses'; -export * from './listItemButtonClasses'; diff --git a/packages/mui-material-next/src/ListItemButton/listItemButtonClasses.ts b/packages/mui-material-next/src/ListItemButton/listItemButtonClasses.ts deleted file mode 100644 index 22ed9b1d60c524..00000000000000 --- a/packages/mui-material-next/src/ListItemButton/listItemButtonClasses.ts +++ /dev/null @@ -1,40 +0,0 @@ -import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; -import generateUtilityClass from '@mui/utils/generateUtilityClass'; - -export interface ListItemButtonClasses { - /** Styles applied to the root element. */ - root: string; - /** State class applied to the `component`'s `focusVisibleClassName` prop. */ - focusVisible: string; - /** Styles applied to the component element if dense. */ - dense: string; - /** Styles applied to the component element if `alignItems="flex-start"`. */ - alignItemsFlexStart: string; - /** State class applied to the inner `component` element if `disabled={true}`. */ - disabled: string; - /** Styles applied to the inner `component` element if `divider={true}`. */ - divider: string; - /** Styles applied to the inner `component` element unless `disableGutters={true}`. */ - gutters: string; - /** State class applied to the root element if `selected={true}`. */ - selected: string; -} - -export type ListItemButtonClassKey = keyof ListItemButtonClasses; - -export function getListItemButtonUtilityClass(slot: string): string { - return generateUtilityClass('MuiListItemButton', slot); -} - -const listItemButtonClasses: ListItemButtonClasses = generateUtilityClasses('MuiListItemButton', [ - 'root', - 'focusVisible', - 'dense', - 'alignItemsFlexStart', - 'disabled', - 'divider', - 'gutters', - 'selected', -]); - -export default listItemButtonClasses; diff --git a/packages/mui-material-next/src/ListItemIcon/ListItemIcon.d.ts b/packages/mui-material-next/src/ListItemIcon/ListItemIcon.d.ts deleted file mode 100644 index efe7ba7c3eeb28..00000000000000 --- a/packages/mui-material-next/src/ListItemIcon/ListItemIcon.d.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { InternalStandardProps as StandardProps, Theme } from '@mui/material'; -import { ListItemIconClasses } from './listItemIconClasses'; - -export interface ListItemIconProps extends StandardProps<React.HTMLAttributes<HTMLDivElement>> { - /** - * The content of the component, normally `Icon`, `SvgIcon`, - * or a `@mui/icons-material` SVG icon element. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<ListItemIconClasses>; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -/** - * A simple wrapper to apply `List` styles to an `Icon` or `SvgIcon`. - * - * Demos: - * - * - [Lists](https://mui.com/material-ui/react-list/) - * - * API: - * - * - [ListItemIcon API](https://mui.com/material-ui/api/list-item-icon/) - */ -export default function ListItemIcon(props: ListItemIconProps): JSX.Element; diff --git a/packages/mui-material-next/src/ListItemIcon/ListItemIcon.js b/packages/mui-material-next/src/ListItemIcon/ListItemIcon.js deleted file mode 100644 index 1b0ff52ff8ed5a..00000000000000 --- a/packages/mui-material-next/src/ListItemIcon/ListItemIcon.js +++ /dev/null @@ -1,91 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; -import { getListItemIconUtilityClass } from './listItemIconClasses'; -import ListContext from '../List/ListContext'; - -const useUtilityClasses = (ownerState) => { - const { alignItems, classes } = ownerState; - - const slots = { - root: ['root', alignItems === 'flex-start' && 'alignItemsFlexStart'], - }; - - return composeClasses(slots, getListItemIconUtilityClass, classes); -}; - -const ListItemIconRoot = styled('div', { - name: 'MuiListItemIcon', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [styles.root, ownerState.alignItems === 'flex-start' && styles.alignItemsFlexStart]; - }, -})(({ theme, ownerState }) => ({ - minWidth: 56, - color: (theme.vars || theme).palette.action.active, - flexShrink: 0, - display: 'inline-flex', - ...(ownerState.alignItems === 'flex-start' && { - marginTop: 8, - }), -})); - -/** - * A simple wrapper to apply `List` styles to an `Icon` or `SvgIcon`. - */ -const ListItemIcon = React.forwardRef(function ListItemIcon(inProps, ref) { - const props = useThemeProps({ - props: inProps, - name: 'MuiListItemIcon', - }); - - const { className, ...other } = props; - const context = React.useContext(ListContext); - const ownerState = { ...props, alignItems: context.alignItems }; - const classes = useUtilityClasses(ownerState); - - return ( - <ListItemIconRoot - className={clsx(classes.root, className)} - ownerState={ownerState} - ref={ref} - {...other} - /> - ); -}); - -ListItemIcon.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The content of the component, normally `Icon`, `SvgIcon`, - * or a `@mui/icons-material` SVG icon element. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * 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, - ]), -}; - -export default ListItemIcon; diff --git a/packages/mui-material-next/src/ListItemIcon/ListItemIcon.test.js b/packages/mui-material-next/src/ListItemIcon/ListItemIcon.test.js deleted file mode 100644 index 41106f2aa645f4..00000000000000 --- a/packages/mui-material-next/src/ListItemIcon/ListItemIcon.test.js +++ /dev/null @@ -1,22 +0,0 @@ -import * as React from 'react'; -import { createRenderer } from '@mui-internal/test-utils'; -import ListItemIcon, { listItemIconClasses as classes } from '@mui/material-next/ListItemIcon'; -import describeConformance from '../../test/describeConformance'; - -describe('<ListItemIcon />', () => { - const { render } = createRenderer(); - - describeConformance( - <ListItemIcon> - <div /> - </ListItemIcon>, - () => ({ - classes, - inheritComponent: 'div', - render, - muiName: 'MuiListItemIcon', - refInstanceof: window.HTMLDivElement, - skip: ['componentProp', 'componentsProp', 'themeVariants'], - }), - ); -}); diff --git a/packages/mui-material-next/src/ListItemIcon/index.d.ts b/packages/mui-material-next/src/ListItemIcon/index.d.ts deleted file mode 100644 index 71ca165ef66f1a..00000000000000 --- a/packages/mui-material-next/src/ListItemIcon/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './ListItemIcon'; -export * from './ListItemIcon'; - -export { default as listItemIconClasses } from './listItemIconClasses'; -export * from './listItemIconClasses'; diff --git a/packages/mui-material-next/src/ListItemIcon/index.js b/packages/mui-material-next/src/ListItemIcon/index.js deleted file mode 100644 index 62c8390c58ff7c..00000000000000 --- a/packages/mui-material-next/src/ListItemIcon/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './ListItemIcon'; - -export { default as listItemIconClasses } from './listItemIconClasses'; -export * from './listItemIconClasses'; diff --git a/packages/mui-material-next/src/ListItemIcon/listItemIconClasses.ts b/packages/mui-material-next/src/ListItemIcon/listItemIconClasses.ts deleted file mode 100644 index 93d14311f7b5e4..00000000000000 --- a/packages/mui-material-next/src/ListItemIcon/listItemIconClasses.ts +++ /dev/null @@ -1,22 +0,0 @@ -import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; -import generateUtilityClass from '@mui/utils/generateUtilityClass'; - -export interface ListItemIconClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element when the parent `ListItem` uses `alignItems="flex-start"`. */ - alignItemsFlexStart: string; -} - -export type ListItemIconClassKey = keyof ListItemIconClasses; - -export function getListItemIconUtilityClass(slot: string): string { - return generateUtilityClass('MuiListItemIcon', slot); -} - -const listItemIconClasses: ListItemIconClasses = generateUtilityClasses('MuiListItemIcon', [ - 'root', - 'alignItemsFlexStart', -]); - -export default listItemIconClasses; diff --git a/packages/mui-material-next/src/ListItemSecondaryAction/ListItemSecondaryAction.d.ts b/packages/mui-material-next/src/ListItemSecondaryAction/ListItemSecondaryAction.d.ts deleted file mode 100644 index 755aafdf96d5d1..00000000000000 --- a/packages/mui-material-next/src/ListItemSecondaryAction/ListItemSecondaryAction.d.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { InternalStandardProps as StandardProps, Theme } from '@mui/material'; -import { ListItemSecondaryActionClasses } from './listItemSecondaryActionClasses'; - -export interface ListItemSecondaryActionProps - extends StandardProps<React.HTMLAttributes<HTMLDivElement>> { - /** - * The content of the component, normally an `IconButton` or selection control. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<ListItemSecondaryActionClasses>; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -/** - * Must be used as the last child of ListItem to function properly. - * - * Demos: - * - * - [Lists](https://mui.com/material-ui/react-list/) - * - * API: - * - * - [ListItemSecondaryAction API](https://mui.com/material-ui/api/list-item-secondary-action/) - */ -declare const ListItemSecondaryAction: ((props: ListItemSecondaryActionProps) => JSX.Element) & { - muiName: string; -}; - -export default ListItemSecondaryAction; diff --git a/packages/mui-material-next/src/ListItemSecondaryAction/ListItemSecondaryAction.js b/packages/mui-material-next/src/ListItemSecondaryAction/ListItemSecondaryAction.js deleted file mode 100644 index cf9d7acfceefb0..00000000000000 --- a/packages/mui-material-next/src/ListItemSecondaryAction/ListItemSecondaryAction.js +++ /dev/null @@ -1,88 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; -import ListContext from '../List/ListContext'; -import { getListItemSecondaryActionClassesUtilityClass } from './listItemSecondaryActionClasses'; - -const useUtilityClasses = (ownerState) => { - const { disableGutters, classes } = ownerState; - - const slots = { - root: ['root', disableGutters && 'disableGutters'], - }; - - return composeClasses(slots, getListItemSecondaryActionClassesUtilityClass, classes); -}; - -const ListItemSecondaryActionRoot = styled('div', { - name: 'MuiListItemSecondaryAction', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [styles.root, ownerState.disableGutters && styles.disableGutters]; - }, -})(({ ownerState }) => ({ - position: 'absolute', - right: 16, - top: '50%', - transform: 'translateY(-50%)', - ...(ownerState.disableGutters && { - right: 0, - }), -})); - -/** - * Must be used as the last child of ListItem to function properly. - */ -const ListItemSecondaryAction = React.forwardRef(function ListItemSecondaryAction(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiListItemSecondaryAction' }); - const { className, ...other } = props; - const context = React.useContext(ListContext); - const ownerState = { ...props, disableGutters: context.disableGutters }; - const classes = useUtilityClasses(ownerState); - - return ( - <ListItemSecondaryActionRoot - className={clsx(classes.root, className)} - ownerState={ownerState} - ref={ref} - {...other} - /> - ); -}); - -ListItemSecondaryAction.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The content of the component, normally an `IconButton` or selection control. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * 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, - ]), -}; - -ListItemSecondaryAction.muiName = 'ListItemSecondaryAction'; - -export default ListItemSecondaryAction; diff --git a/packages/mui-material-next/src/ListItemSecondaryAction/ListItemSecondaryAction.test.js b/packages/mui-material-next/src/ListItemSecondaryAction/ListItemSecondaryAction.test.js deleted file mode 100644 index e98e603f109e5f..00000000000000 --- a/packages/mui-material-next/src/ListItemSecondaryAction/ListItemSecondaryAction.test.js +++ /dev/null @@ -1,39 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -import ListItem from '@mui/material-next/ListItem'; -import ListItemSecondaryAction, { - listItemSecondaryActionClasses as classes, -} from '@mui/material-next/ListItemSecondaryAction'; -import describeConformance from '../../test/describeConformance'; - -describe('<ListItemSecondaryAction />', () => { - const { render } = createRenderer(); - - describeConformance(<ListItemSecondaryAction />, () => ({ - classes, - inheritComponent: 'div', - render, - refInstanceof: window.HTMLDivElement, - muiName: 'MuiListItemSecondaryAction', - skip: ['componentProp', 'componentsProp', 'themeVariants'], - })); - - it('should render without classes that disable gutters', () => { - const { getByTestId } = render( - <ListItem> - <ListItemSecondaryAction data-testid="secondary-action" /> - </ListItem>, - ); - expect(getByTestId('secondary-action')).not.to.have.class(classes.disableGutters); - }); - - it('should disable the gutters', () => { - const { getByTestId } = render( - <ListItem disableGutters> - <ListItemSecondaryAction data-testid="secondary-action" /> - </ListItem>, - ); - expect(getByTestId('secondary-action')).to.have.class(classes.disableGutters); - }); -}); diff --git a/packages/mui-material-next/src/ListItemSecondaryAction/index.d.ts b/packages/mui-material-next/src/ListItemSecondaryAction/index.d.ts deleted file mode 100644 index e6e2d06c1bba6c..00000000000000 --- a/packages/mui-material-next/src/ListItemSecondaryAction/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './ListItemSecondaryAction'; -export * from './ListItemSecondaryAction'; - -export { default as listItemSecondaryActionClasses } from './listItemSecondaryActionClasses'; -export * from './listItemSecondaryActionClasses'; diff --git a/packages/mui-material-next/src/ListItemSecondaryAction/index.js b/packages/mui-material-next/src/ListItemSecondaryAction/index.js deleted file mode 100644 index 97f6c675e650e7..00000000000000 --- a/packages/mui-material-next/src/ListItemSecondaryAction/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './ListItemSecondaryAction'; - -export { default as listItemSecondaryActionClasses } from './listItemSecondaryActionClasses'; -export * from './listItemSecondaryActionClasses'; diff --git a/packages/mui-material-next/src/ListItemSecondaryAction/listItemSecondaryActionClasses.ts b/packages/mui-material-next/src/ListItemSecondaryAction/listItemSecondaryActionClasses.ts deleted file mode 100644 index 6f3c9ef5631eab..00000000000000 --- a/packages/mui-material-next/src/ListItemSecondaryAction/listItemSecondaryActionClasses.ts +++ /dev/null @@ -1,22 +0,0 @@ -import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; -import generateUtilityClass from '@mui/utils/generateUtilityClass'; - -export interface ListItemSecondaryActionClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element when the parent `ListItem` has `disableGutters={true}`. */ - disableGutters: string; -} - -export type ListItemSecondaryActionClassKey = keyof ListItemSecondaryActionClasses; - -export function getListItemSecondaryActionClassesUtilityClass(slot: string): string { - return generateUtilityClass('MuiListItemSecondaryAction', slot); -} - -const listItemSecondaryActionClasses: ListItemSecondaryActionClasses = generateUtilityClasses( - 'MuiListItemSecondaryAction', - ['root', 'disableGutters'], -); - -export default listItemSecondaryActionClasses; diff --git a/packages/mui-material-next/src/ListItemText/ListItemText.d.ts b/packages/mui-material-next/src/ListItemText/ListItemText.d.ts deleted file mode 100644 index 05e0c49ba3f2c7..00000000000000 --- a/packages/mui-material-next/src/ListItemText/ListItemText.d.ts +++ /dev/null @@ -1,77 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { InternalStandardProps as StandardProps, Theme } from '@mui/material'; -/* TODO: change @mui/material/Typography to @mui/material-next/Typography once Typograpghy is available in @mui/material-next */ -import { TypographyProps } from '@mui/material/Typography'; -import { ListItemTextClasses } from './listItemTextClasses'; - -export interface ListItemTextProps< - PrimaryTypographyComponent extends React.ElementType = 'span', - SecondaryTypographyComponent extends React.ElementType = 'p', -> extends StandardProps<React.HTMLAttributes<HTMLDivElement>> { - /** - * Alias for the `primary` prop. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<ListItemTextClasses>; - /** - * If `true`, the children won't be wrapped by a Typography component. - * This can be useful to render an alternative Typography variant by wrapping - * the `children` (or `primary`) text, and optional `secondary` text - * with the Typography component. - * @default false - */ - disableTypography?: boolean; - /** - * If `true`, the children are indented. - * This should be used if there is no left avatar or left icon. - * @default false - */ - inset?: boolean; - /** - * The main content element. - */ - primary?: React.ReactNode; - /** - * These props will be forwarded to the primary typography component - * (as long as disableTypography is not `true`). - */ - primaryTypographyProps?: TypographyProps< - PrimaryTypographyComponent, - { component?: PrimaryTypographyComponent } - >; - /** - * The secondary content element. - */ - secondary?: React.ReactNode; - /** - * These props will be forwarded to the secondary typography component - * (as long as disableTypography is not `true`). - */ - secondaryTypographyProps?: TypographyProps< - SecondaryTypographyComponent, - { component?: SecondaryTypographyComponent } - >; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -/** - * - * Demos: - * - * - [Lists](https://mui.com/material-ui/react-list/) - * - * API: - * - * - [ListItemText API](https://mui.com/material-ui/api/list-item-text/) - */ -export default function ListItemText< - PrimaryTypographyComponent extends React.ElementType = 'span', - SecondaryTypographyComponent extends React.ElementType = 'p', ->(props: ListItemTextProps<PrimaryTypographyComponent, SecondaryTypographyComponent>): JSX.Element; diff --git a/packages/mui-material-next/src/ListItemText/ListItemText.js b/packages/mui-material-next/src/ListItemText/ListItemText.js deleted file mode 100644 index ba24d2e670a003..00000000000000 --- a/packages/mui-material-next/src/ListItemText/ListItemText.js +++ /dev/null @@ -1,184 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -/* TODO: change @mui/material/Typography to @mui/material-next/Typography once Typograpghy is available in @mui/material-next */ -import Typography from '@mui/material/Typography'; -import ListContext from '../List/ListContext'; -import useThemeProps from '../styles/useThemeProps'; -import styled from '../styles/styled'; -import listItemTextClasses, { getListItemTextUtilityClass } from './listItemTextClasses'; - -const useUtilityClasses = (ownerState) => { - const { classes, inset, primary, secondary, dense } = ownerState; - - const slots = { - root: ['root', inset && 'inset', dense && 'dense', primary && secondary && 'multiline'], - primary: ['primary'], - secondary: ['secondary'], - }; - - return composeClasses(slots, getListItemTextUtilityClass, classes); -}; - -const ListItemTextRoot = styled('div', { - name: 'MuiListItemText', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - { [`& .${listItemTextClasses.primary}`]: styles.primary }, - { [`& .${listItemTextClasses.secondary}`]: styles.secondary }, - styles.root, - ownerState.inset && styles.inset, - ownerState.primary && ownerState.secondary && styles.multiline, - ownerState.dense && styles.dense, - ]; - }, -})(({ ownerState }) => ({ - flex: '1 1 auto', - minWidth: 0, - marginTop: 4, - marginBottom: 4, - ...(ownerState.primary && - ownerState.secondary && { - marginTop: 6, - marginBottom: 6, - }), - ...(ownerState.inset && { - paddingLeft: 56, - }), -})); - -const ListItemText = React.forwardRef(function ListItemText(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiListItemText' }); - const { - children, - className, - disableTypography = false, - inset = false, - primary: primaryProp, - primaryTypographyProps, - secondary: secondaryProp, - secondaryTypographyProps, - ...other - } = props; - const { dense } = React.useContext(ListContext); - - let primary = primaryProp != null ? primaryProp : children; - let secondary = secondaryProp; - - const ownerState = { - ...props, - disableTypography, - inset, - primary: !!primary, - secondary: !!secondary, - dense, - }; - - const classes = useUtilityClasses(ownerState); - - if (primary != null && primary.type !== Typography && !disableTypography) { - primary = ( - <Typography - variant={dense ? 'body2' : 'body1'} - className={classes.primary} - component={primaryTypographyProps?.variant ? undefined : 'span'} - display="block" - {...primaryTypographyProps} - > - {primary} - </Typography> - ); - } - - if (secondary != null && secondary.type !== Typography && !disableTypography) { - secondary = ( - <Typography - variant="body2" - className={classes.secondary} - color="text.secondary" - display="block" - {...secondaryTypographyProps} - > - {secondary} - </Typography> - ); - } - - return ( - <ListItemTextRoot - className={clsx(classes.root, className)} - ownerState={ownerState} - ref={ref} - {...other} - > - {primary} - {secondary} - </ListItemTextRoot> - ); -}); - -ListItemText.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * Alias for the `primary` prop. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * If `true`, the children won't be wrapped by a Typography component. - * This can be useful to render an alternative Typography variant by wrapping - * the `children` (or `primary`) text, and optional `secondary` text - * with the Typography component. - * @default false - */ - disableTypography: PropTypes.bool, - /** - * If `true`, the children are indented. - * This should be used if there is no left avatar or left icon. - * @default false - */ - inset: PropTypes.bool, - /** - * The main content element. - */ - primary: PropTypes.node, - /** - * These props will be forwarded to the primary typography component - * (as long as disableTypography is not `true`). - */ - primaryTypographyProps: PropTypes.object, - /** - * The secondary content element. - */ - secondary: PropTypes.node, - /** - * These props will be forwarded to the secondary typography component - * (as long as disableTypography is not `true`). - */ - secondaryTypographyProps: 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, - ]), -}; - -export default ListItemText; diff --git a/packages/mui-material-next/src/ListItemText/ListItemText.spec.tsx b/packages/mui-material-next/src/ListItemText/ListItemText.spec.tsx deleted file mode 100644 index a2be2d28ae4eda..00000000000000 --- a/packages/mui-material-next/src/ListItemText/ListItemText.spec.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import * as React from 'react'; -import { ListItemText } from '@mui/material-next'; - -const CustomComponent: React.FC<{ prop1: string; prop2: number }> = function CustomComponent() { - return <div />; -}; - -function typographyPropsTest() { - // @ts-expect-error - <ListItemText primaryTypographyProps={{ component: 'incorrectComponent' }} />; - <ListItemText primaryTypographyProps={{ component: 'a', href: 'href' }} />; - <ListItemText - primaryTypographyProps={{ - component: 'a', - // @ts-expect-error - htmlFor: 'nonexistent-attribute', - }} - />; - <ListItemText - primaryTypographyProps={{ component: CustomComponent, prop1: 'prop1', prop2: 2 }} - />; - // @ts-expect-error - <ListItemText primaryTypographyProps={{ component: CustomComponent, prop2: 2 }} />; - <ListItemText primaryTypographyProps={{ variant: 'h1' }} />; - <ListItemText primaryTypographyProps={{ align: 'left' }} />; - <ListItemText - primaryTypographyProps={{ - color: 'primary', - display: 'block', - gutterBottom: true, - noWrap: true, - variantMapping: { h1: 'h1' }, - }} - />; -} - -function secondaryTypographyPropsTest() { - // @ts-expect-error - <ListItemText secondaryTypographyProps={{ component: 'incorrectComponent' }} />; - <ListItemText secondaryTypographyProps={{ component: 'a', href: 'href' }} />; - <ListItemText - secondaryTypographyProps={{ - component: 'a', - // @ts-expect-error - htmlFor: 'nonexistent-attribute', - }} - />; - <ListItemText - secondaryTypographyProps={{ component: CustomComponent, prop1: 'prop1', prop2: 2 }} - />; - // @ts-expect-error - <ListItemText secondaryTypographyProps={{ component: CustomComponent, prop2: 2 }} />; - <ListItemText secondaryTypographyProps={{ variant: 'h1' }} />; - <ListItemText secondaryTypographyProps={{ align: 'left' }} />; - <ListItemText - secondaryTypographyProps={{ - color: 'primary', - display: 'block', - gutterBottom: true, - noWrap: true, - variantMapping: { h1: 'h1' }, - }} - />; -} - -function mixedTypographyPropsTest() { - <ListItemText - // @ts-expect-error - primaryTypographyProps={{ component: 'incorrectComponent' }} - // @ts-expect-error - secondaryTypographyProps={{ component: 'incorrectComponent' }} - />; - <ListItemText - primaryTypographyProps={{ component: 'a', href: 'href' }} - secondaryTypographyProps={{ component: 'a', href: 'href' }} - />; - <ListItemText - primaryTypographyProps={{ - component: 'a', - // @ts-expect-error - htmlFor: 'nonexistent-attribute', - }} - secondaryTypographyProps={{ - component: 'a', - // @ts-expect-error - htmlFor: 'nonexistent-attribute', - }} - />; - <ListItemText - primaryTypographyProps={{ component: CustomComponent, prop1: 'prop1', prop2: 2 }} - secondaryTypographyProps={{ component: CustomComponent, prop1: 'prop1', prop2: 2 }} - />; - <ListItemText - // @ts-expect-error - primaryTypographyProps={{ component: CustomComponent, prop2: 2 }} - // @ts-expect-error - secondaryTypographyProps={{ component: CustomComponent, prop2: 2 }} - />; -} diff --git a/packages/mui-material-next/src/ListItemText/ListItemText.test.js b/packages/mui-material-next/src/ListItemText/ListItemText.test.js deleted file mode 100644 index e102c87adae328..00000000000000 --- a/packages/mui-material-next/src/ListItemText/ListItemText.test.js +++ /dev/null @@ -1,201 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -/* TODO: change @mui/material/Typography to @mui/material-next/Typography once Typograpghy is available in @mui/material-next */ -import Typography, { typographyClasses } from '@mui/material/Typography'; -import ListItemText, { listItemTextClasses as classes } from '@mui/material-next/ListItemText'; -import describeConformance from '../../test/describeConformance'; - -describe('<ListItemText />', () => { - const { render } = createRenderer(); - - describeConformance(<ListItemText>Conformance?</ListItemText>, () => ({ - classes, - inheritComponent: 'div', - render, - muiName: 'MuiListItemText', - testVariantProps: { inset: true }, - refInstanceof: window.HTMLDivElement, - skip: ['componentProp', 'componentsProp'], - })); - - it('should render with inset class', () => { - const { container } = render(<ListItemText inset />); - expect(container.querySelector('div')).to.have.class(classes.inset); - expect(container.querySelector('div')).to.have.class(classes.root); - }); - - it('should render with no children', () => { - const { container } = render(<ListItemText />); - expect(container.querySelector('div').querySelectorAll('*')).to.have.length(0); - }); - - describe('prop: primary', () => { - it('should render primary text', () => { - const ref = React.createRef(); - const text = () => ref.current.textContent; - const { container } = render(<ListItemText primary="This is the primary text" ref={ref} />); - expect(container.querySelectorAll('span.MuiTypography-root')).to.have.length(1); - expect(container.querySelector('span.MuiTypography-root')).to.have.class( - typographyClasses.body1, - ); - expect(text()).to.equal('This is the primary text'); - }); - - it('should use the primary node', () => { - const primaryRef = React.createRef(); - const primary = <span ref={primaryRef} />; - const { container } = render(<ListItemText primary={primary} />); - expect(container.querySelector('div')).to.contain(primaryRef.current); - }); - - it('should use the children prop as primary node', () => { - const primaryRef = React.createRef(); - const primary = <span ref={primaryRef} />; - const { container } = render(<ListItemText>{primary}</ListItemText>); - expect(container.querySelector('div')).to.contain(primaryRef.current); - }); - - it('should read 0 as primary', () => { - const { container } = render(<ListItemText primary={0} />); - expect(container.querySelector('span.MuiTypography-root')).to.have.text('0'); - }); - }); - - describe('prop: secondary', () => { - it('should render secondary text', () => { - const ref = React.createRef(); - const text = () => ref.current.textContent; - const { container } = render( - <ListItemText secondary="This is the secondary text" ref={ref} />, - ); - expect(container.querySelectorAll('p.MuiTypography-root')).to.have.length(1); - expect(container.querySelector('p.MuiTypography-root')).to.have.class( - typographyClasses.body2, - ); - expect(text()).to.equal('This is the secondary text'); - }); - - it('should use the secondary node', () => { - const secondaryRef = React.createRef(); - const secondary = <span ref={secondaryRef} />; - const { container } = render(<ListItemText secondary={secondary} />); - expect(container.querySelector('div')).to.contain(secondaryRef.current); - }); - - it('should read 0 as secondary', () => { - const { container } = render(<ListItemText secondary={0} />); - expect(container.querySelector('p.MuiTypography-root')).to.have.text('0'); - }); - }); - - describe('prop: disableTypography', () => { - it('should wrap children in `<Typography/>` by default', () => { - const { container } = render( - <ListItemText primary="This is the primary text" secondary="This is the secondary text" />, - ); - - const texts = container.querySelectorAll('.MuiTypography-root'); - expect(texts).to.have.length(2); - - const primaryText = texts[0]; - expect(primaryText).to.have.class(typographyClasses.body1); - expect(primaryText).to.have.text('This is the primary text'); - - const secondaryText = texts[1]; - expect(secondaryText).to.have.class(typographyClasses.body2); - expect(secondaryText).to.have.text('This is the secondary text'); - }); - - it('should render JSX children', () => { - const primaryRef = React.createRef(); - const primaryChild = ( - <p className="test" ref={primaryRef}> - This is the primary text - </p> - ); - const secondaryRef = React.createRef(); - const secondaryChild = ( - <p className="test" ref={secondaryRef}> - This is the secondary text - </p> - ); - const { container } = render( - <ListItemText primary={primaryChild} secondary={secondaryChild} disableTypography />, - ); - const texts = container.querySelectorAll('div > p:not(.MuiTypography-root)'); - expect(texts[0]).to.equal(primaryRef.current); - expect(texts[1]).to.equal(secondaryRef.current); - }); - }); - - it('should render primary and secondary text with customisable classes', () => { - const textClasses = { - primary: 'GeneralText', - secondary: 'SecondaryText', - }; - const { container } = render( - <ListItemText - primary="This is the primary text" - secondary="This is the secondary text" - classes={textClasses} - />, - ); - const texts = container.querySelector('div').querySelectorAll('*'); - - expect(texts[0]).to.have.class('GeneralText'); - expect(texts[1]).to.have.class('SecondaryText'); - }); - - it('should not re-wrap the <Typography> element', () => { - const primary = <Typography>This is the primary text</Typography>; - const secondary = <Typography>This is the secondary text</Typography>; - const { container } = render(<ListItemText primary={primary} secondary={secondary} />); - const texts = container.querySelectorAll('.MuiTypography-root'); - expect(texts).to.have.length(2); - expect(texts[0]).to.have.text('This is the primary text'); - expect(texts[1]).have.text('This is the secondary text'); - }); - - it('should use variant if provided', () => { - const { getByText } = render( - <ListItemText - primary="This is the primary text" - primaryTypographyProps={{ variant: 'h3' }} - secondary="This is the secondary text" - secondaryTypographyProps={{ variant: 'h4' }} - />, - ); - expect(getByText('This is the primary text')).to.have.tagName('h3'); - expect(getByText('This is the secondary text')).to.have.tagName('h4'); - }); - - it('should fall back to the default tag name if no variant provided', () => { - const { getByText } = render( - <ListItemText primary="This is the primary text" secondary="This is the secondary text" />, - ); - expect(getByText('This is the primary text')).to.have.tagName('span'); - expect(getByText('This is the secondary text')).to.have.tagName('p'); - }); - - it('should pass primaryTypographyProps to primary Typography component', () => { - const { container } = render( - <ListItemText - primary="This is the primary text" - primaryTypographyProps={{ 'data-test': 'foo' }} - />, - ); - expect(container.querySelector('span')).to.have.attribute('data-test'); - }); - - it('should pass secondaryTypographyProps to secondary Typography component', () => { - const { container } = render( - <ListItemText - primary="This is the primary text" - secondary="This is the secondary text" - secondaryTypographyProps={{ 'data-test': 'foo' }} - />, - ); - expect(container.querySelector('p')).to.have.attribute('data-test'); - }); -}); diff --git a/packages/mui-material-next/src/ListItemText/index.d.ts b/packages/mui-material-next/src/ListItemText/index.d.ts deleted file mode 100644 index 64a6dbeaae28a3..00000000000000 --- a/packages/mui-material-next/src/ListItemText/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './ListItemText'; -export * from './ListItemText'; - -export { default as listItemTextClasses } from './listItemTextClasses'; -export * from './listItemTextClasses'; diff --git a/packages/mui-material-next/src/ListItemText/index.js b/packages/mui-material-next/src/ListItemText/index.js deleted file mode 100644 index f64e13990f8777..00000000000000 --- a/packages/mui-material-next/src/ListItemText/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './ListItemText'; - -export { default as listItemTextClasses } from './listItemTextClasses'; -export * from './listItemTextClasses'; diff --git a/packages/mui-material-next/src/ListItemText/listItemTextClasses.ts b/packages/mui-material-next/src/ListItemText/listItemTextClasses.ts deleted file mode 100644 index dddc15e7546565..00000000000000 --- a/packages/mui-material-next/src/ListItemText/listItemTextClasses.ts +++ /dev/null @@ -1,34 +0,0 @@ -import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; -import generateUtilityClass from '@mui/utils/generateUtilityClass'; - -export interface ListItemTextClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the Typography component if primary and secondary are set. */ - multiline: string; - /** Styles applied to the Typography component if dense. */ - dense: string; - /** Styles applied to the root element if `inset={true}`. */ - inset: string; - /** Styles applied to the primary `Typography` component. */ - primary: string; - /** Styles applied to the secondary `Typography` component. */ - secondary: string; -} - -export type ListItemTextClassKey = keyof ListItemTextClasses; - -export function getListItemTextUtilityClass(slot: string): string { - return generateUtilityClass('MuiListItemText', slot); -} - -const listItemTextClasses: ListItemTextClasses = generateUtilityClasses('MuiListItemText', [ - 'root', - 'multiline', - 'dense', - 'inset', - 'primary', - 'secondary', -]); - -export default listItemTextClasses; diff --git a/packages/mui-material-next/src/ListSubheader/ListSubheader.d.ts b/packages/mui-material-next/src/ListSubheader/ListSubheader.d.ts deleted file mode 100644 index 3260cab7b2ada4..00000000000000 --- a/packages/mui-material-next/src/ListSubheader/ListSubheader.d.ts +++ /dev/null @@ -1,69 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverridableComponent, OverrideProps } from '@mui/types'; -import { Theme } from '..'; -import { ListSubheaderClasses } from './listSubheaderClasses'; - -export interface ListSubheaderOwnProps { - /** - * The content of the component. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<ListSubheaderClasses>; - /** - * The color of the component. It supports those theme colors that make sense for this component. - * @default 'default' - */ - color?: 'default' | 'primary' | 'inherit'; - /** - * If `true`, the List Subheader will not have gutters. - * @default false - */ - disableGutters?: boolean; - /** - * If `true`, the List Subheader will not stick to the top during scroll. - * @default false - */ - disableSticky?: boolean; - /** - * If `true`, the List Subheader is indented. - * @default false - */ - inset?: boolean; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -export interface ListSubheaderTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'li', -> { - props: AdditionalProps & ListSubheaderOwnProps; - defaultComponent: RootComponent; -} - -/** - * - * Demos: - * - * - [Lists](https://mui.com/material-ui/react-list/) - * - * API: - * - * - [ListSubheader API](https://mui.com/material-ui/api/list-subheader/) - */ -declare const ListSubheader: OverridableComponent<ListSubheaderTypeMap>; - -export type ListSubheaderProps< - RootComponent extends React.ElementType = ListSubheaderTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<ListSubheaderTypeMap<AdditionalProps, RootComponent>, RootComponent> & { - component?: React.ElementType; -}; - -export default ListSubheader; diff --git a/packages/mui-material-next/src/ListSubheader/ListSubheader.js b/packages/mui-material-next/src/ListSubheader/ListSubheader.js deleted file mode 100644 index faab04070d7290..00000000000000 --- a/packages/mui-material-next/src/ListSubheader/ListSubheader.js +++ /dev/null @@ -1,158 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import capitalize from '@mui/utils/capitalize'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; -import { getListSubheaderUtilityClass } from './listSubheaderClasses'; - -const useUtilityClasses = (ownerState) => { - const { classes, color, disableGutters, inset, disableSticky } = ownerState; - - const slots = { - root: [ - 'root', - color !== 'default' && `color${capitalize(color)}`, - !disableGutters && 'gutters', - inset && 'inset', - !disableSticky && 'sticky', - ], - }; - - return composeClasses(slots, getListSubheaderUtilityClass, classes); -}; - -const ListSubheaderRoot = styled('li', { - name: 'MuiListSubheader', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - ownerState.color !== 'default' && styles[`color${capitalize(ownerState.color)}`], - !ownerState.disableGutters && styles.gutters, - ownerState.inset && styles.inset, - !ownerState.disableSticky && styles.sticky, - ]; - }, -})(({ theme, ownerState }) => ({ - boxSizing: 'border-box', - lineHeight: '48px', - listStyle: 'none', - color: (theme.vars || theme).palette.text.secondary, - fontFamily: theme.typography.fontFamily, - fontWeight: theme.typography.fontWeightMedium, - fontSize: theme.typography.pxToRem(14), - ...(ownerState.color === 'primary' && { - color: (theme.vars || theme).palette.primary.main, - }), - ...(ownerState.color === 'inherit' && { - color: 'inherit', - }), - ...(!ownerState.disableGutters && { - paddingLeft: 16, - paddingRight: 16, - }), - ...(ownerState.inset && { - paddingLeft: 72, - }), - ...(!ownerState.disableSticky && { - position: 'sticky', - top: 0, - zIndex: 1, - backgroundColor: (theme.vars || theme).palette.background.paper, - }), -})); - -const ListSubheader = React.forwardRef(function ListSubheader(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiListSubheader' }); - const { - className, - color = 'default', - component = 'li', - disableGutters = false, - disableSticky = false, - inset = false, - ...other - } = props; - - const ownerState = { - ...props, - color, - component, - disableGutters, - disableSticky, - inset, - }; - - const classes = useUtilityClasses(ownerState); - - return ( - <ListSubheaderRoot - as={component} - className={clsx(classes.root, className)} - ref={ref} - ownerState={ownerState} - {...other} - /> - ); -}); - -ListSubheader.muiSkipListHighlight = true; - -ListSubheader.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The content of the component. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The color of the component. It supports those theme colors that make sense for this component. - * @default 'default' - */ - color: PropTypes.oneOf(['default', 'inherit', 'primary']), - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * If `true`, the List Subheader will not have gutters. - * @default false - */ - disableGutters: PropTypes.bool, - /** - * If `true`, the List Subheader will not stick to the top during scroll. - * @default false - */ - disableSticky: PropTypes.bool, - /** - * If `true`, the List Subheader is indented. - * @default false - */ - inset: PropTypes.bool, - /** - * 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, - ]), -}; - -export default ListSubheader; diff --git a/packages/mui-material-next/src/ListSubheader/ListSubheader.test.js b/packages/mui-material-next/src/ListSubheader/ListSubheader.test.js deleted file mode 100644 index 6e3d4ebc774621..00000000000000 --- a/packages/mui-material-next/src/ListSubheader/ListSubheader.test.js +++ /dev/null @@ -1,61 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -import ListSubheader, { listSubheaderClasses as classes } from '@mui/material-next/ListSubheader'; -import describeConformance from '../../test/describeConformance'; - -describe('<ListSubheader />', () => { - const { render } = createRenderer(); - - describeConformance(<ListSubheader />, () => ({ - classes, - inheritComponent: 'li', - render, - muiName: 'MuiListSubheader', - refInstanceof: window.HTMLLIElement, - testVariantProps: { disableGutters: true }, - skip: ['componentsProp'], - })); - - it('should display primary color', () => { - const { container } = render(<ListSubheader color="primary" />); - - expect(container.firstChild).to.have.class(classes.colorPrimary); - expect(container.firstChild).to.have.class(classes.root); - }); - - it('should display inset class', () => { - const { container } = render(<ListSubheader inset />); - - expect(container.firstChild).to.have.class(classes.inset); - expect(container.firstChild).to.have.class(classes.root); - }); - - describe('prop: disableSticky', () => { - it('should display sticky class', () => { - const { container } = render(<ListSubheader />); - - expect(container.firstChild).to.have.class(classes.sticky); - }); - - it('should not display sticky class', () => { - const { container } = render(<ListSubheader disableSticky />); - - expect(container.firstChild).not.to.have.class(classes.sticky); - }); - }); - - describe('prop: disableGutters', () => { - it('should not display gutters class', () => { - const { container } = render(<ListSubheader disableGutters />); - - expect(container.firstChild).not.to.have.class(classes.gutters); - }); - - it('should display gutters class', () => { - const { container } = render(<ListSubheader />); - - expect(container.firstChild).to.have.class(classes.gutters); - }); - }); -}); diff --git a/packages/mui-material-next/src/ListSubheader/index.d.ts b/packages/mui-material-next/src/ListSubheader/index.d.ts deleted file mode 100644 index a38eca969f9cc7..00000000000000 --- a/packages/mui-material-next/src/ListSubheader/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './ListSubheader'; -export * from './ListSubheader'; - -export { default as listSubheaderClasses } from './listSubheaderClasses'; -export * from './listSubheaderClasses'; diff --git a/packages/mui-material-next/src/ListSubheader/index.js b/packages/mui-material-next/src/ListSubheader/index.js deleted file mode 100644 index 4f23484d86d1c6..00000000000000 --- a/packages/mui-material-next/src/ListSubheader/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './ListSubheader'; - -export { default as listSubheaderClasses } from './listSubheaderClasses'; -export * from './listSubheaderClasses'; diff --git a/packages/mui-material-next/src/ListSubheader/listSubheaderClasses.ts b/packages/mui-material-next/src/ListSubheader/listSubheaderClasses.ts deleted file mode 100644 index a2fd6f25809d85..00000000000000 --- a/packages/mui-material-next/src/ListSubheader/listSubheaderClasses.ts +++ /dev/null @@ -1,34 +0,0 @@ -import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; -import generateUtilityClass from '@mui/utils/generateUtilityClass'; - -export interface ListSubheaderClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `color="primary"`. */ - colorPrimary: string; - /** Styles applied to the root element if `color="inherit"`. */ - colorInherit: string; - /** Styles applied to the inner `component` element unless `disableGutters={true}`. */ - gutters: string; - /** Styles applied to the root element if `inset={true}`. */ - inset: string; - /** Styles applied to the root element unless `disableSticky={true}`. */ - sticky: string; -} - -export type ListSubheaderClassKey = keyof ListSubheaderClasses; - -export function getListSubheaderUtilityClass(slot: string): string { - return generateUtilityClass('MuiListSubheader', slot); -} - -const listSubheaderClasses: ListSubheaderClasses = generateUtilityClasses('MuiListSubheader', [ - 'root', - 'colorPrimary', - 'colorInherit', - 'gutters', - 'inset', - 'sticky', -]); - -export default listSubheaderClasses; diff --git a/packages/mui-material-next/src/Menu/Menu.test.tsx b/packages/mui-material-next/src/Menu/Menu.test.tsx deleted file mode 100644 index 4ea9d4ec28e1af..00000000000000 --- a/packages/mui-material-next/src/Menu/Menu.test.tsx +++ /dev/null @@ -1,801 +0,0 @@ -import * as React from 'react'; -import { spy } from 'sinon'; -import { expect } from 'chai'; -import { - act, - createRenderer, - createMount, - screen, - fireEvent, - strictModeDoubleLoggingSuppressed, -} from '@mui-internal/test-utils'; -import { useDropdown, DropdownContext } from '@mui/base/useDropdown'; -import { useMenuButton } from '@mui/base/useMenuButton'; -import Button from '@mui/material/Button'; -import Menu, { menuClasses as classes, MenuProps } from '@mui/material-next/Menu'; -import Popover from '@mui/material/Popover'; -import { extendTheme, CssVarsProvider } from '@mui/material-next/styles'; -import { MenuPaper } from './Menu'; -import describeConformance from '../../test/describeConformance'; - -describe('<Menu />', () => { - const { render } = createRenderer({ clock: 'fake' }); - const mount = createMount(); - - describeConformance(<Menu anchorEl={() => document.createElement('div')} open />, () => ({ - classes, - inheritComponent: Popover, - render, - muiName: 'MuiMenu', - refInstanceof: window.HTMLDivElement, - slots: { - root: { - expectedClassName: classes.root, - }, - // TODO v6: Enable this API - // paper: { - // expectedClassName: classes.paper, - // }, - }, - testDeepOverrides: { slotName: 'list', slotClassName: classes.list }, - testRootOverrides: { slotName: 'root', slotClassName: classes.root }, - testVariantProps: { variant: 'menu' }, - skip: [ - 'rootClass', // portal, can't determine the root - 'componentProp', - 'componentsProp', - 'reactTestRenderer', // react-transition-group issue - 'themeDefaultProps', // portal, can't determine the root - ], - })); - - describe('event callbacks', () => { - describe('entering', () => { - it('should fire callbacks', () => { - const handleEnter = spy(); - const handleEntering = spy(); - render( - <Menu - anchorEl={document.createElement('div')} - open - TransitionProps={{ - onEnter: handleEnter, - onEntering: handleEntering, - }} - />, - ); - - expect(handleEnter.callCount).to.equal( - // onEnter is called on mount which is run twice with Strict Effects - React.version.startsWith('18') ? 2 : 1, - ); - expect(handleEnter.args[0].length).to.equal(2); - expect(handleEntering.callCount).to.equal(1); - expect(handleEntering.args[0].length).to.equal(2); - }); - }); - - describe('exiting', () => { - it('should fire callbacks', () => { - const handleExit = spy(); - const handleExiting = spy(); - - const { setProps } = render( - <Menu - TransitionProps={{ - onExit: handleExit, - onExiting: handleExiting, - }} - anchorEl={document.createElement('div')} - open - />, - ); - - setProps({ - open: false, - }); - - expect(handleExit.callCount).to.equal(1); - expect(handleExit.args[0].length).to.equal(1); - expect(handleExiting.callCount).to.equal(1); - expect(handleExiting.args[0].length).to.equal(1); - }); - }); - }); - - it('should pass `classes.paper` to the Paper', () => { - render( - <Menu - anchorEl={document.createElement('div')} - open - PaperProps={{ 'data-testid': 'paper' }} - />, - ); - - expect(screen.getByTestId('paper')).to.have.class(classes.paper); - }); - - describe('prop: PopoverClasses', () => { - it('should be able to change the Popover style', () => { - render( - <Menu - anchorEl={document.createElement('div')} - open - PaperProps={{ 'data-testid': 'paper' }} - PopoverClasses={{ paper: 'bar' }} - />, - ); - - expect(screen.getByTestId('paper')).to.have.class('bar'); - }); - - it('should be able to change the Popover root element style when Menu classes prop is also provided', () => { - render( - <Menu - anchorEl={document.createElement('div')} - open - data-testid="popover" - classes={{ paper: 'bar' }} - PopoverClasses={{ root: 'foo' }} - />, - ); - expect(screen.getByTestId('popover')).to.have.class('foo'); - }); - }); - - describe('prop: PaperProps', () => { - it('should be passed to the paper component', () => { - const customElevation = 12; - const customClasses = { rounded: 'custom-rounded-class' }; - const wrapper = mount( - <Menu - anchorEl={document.createElement('div')} - open - PaperProps={{ - 'data-testid': 'paper', - elevation: customElevation, - classes: customClasses, - }} - />, - ); - - expect(wrapper.find(MenuPaper).props().elevation).to.equal(customElevation); - expect(wrapper.find(MenuPaper).props().classes).to.contain(customClasses); - }); - }); - - it('should pass onClose prop to Popover', () => { - const handleClose = spy(); - render(<Menu anchorEl={document.createElement('div')} open onClose={handleClose} />); - - act(() => { - screen.getByRole('menu').focus(); - }); - fireEvent.keyDown(screen.getByRole('menu'), { key: 'Escape' }); - - expect(handleClose.callCount).to.equal(1); - }); - - it('renders its children only when open', () => { - const { setProps } = render( - <Menu anchorEl={document.createElement('div')} open={false}> - <div data-testid="children" /> - </Menu>, - ); - - expect(screen.queryByTestId('children')).to.equal(null); - - setProps({ open: true }); - - expect(screen.getByTestId('children')).not.to.equal(null); - }); - - describe('list node', () => { - it('should render a menu inside the Popover', () => { - render(<Menu anchorEl={document.createElement('div')} open data-testid="popover" />); - - expect(screen.getByTestId('popover').querySelector('[role="menu"]')).not.to.equal(null); - }); - }); - - it('should open during the initial mount', () => { - function MenuItem(props: { children?: React.ReactNode }) { - const { children } = props; - return ( - <div role="menuitem" tabIndex={-1}> - {children} - </div> - ); - } - render( - <Menu anchorEl={document.createElement('div')} open> - <MenuItem>one</MenuItem> - </Menu>, - ); - - expect(screen.getByRole('menuitem')).to.not.equal(null); - }); - - it('should not focus list if autoFocus=false', () => { - const { setProps } = render( - <Menu anchorEl={document.createElement('div')} autoFocus={false} open={false}> - <div tabIndex={-1} /> - </Menu>, - ); - - act(() => { - setProps({ open: true }); - }); - - expect(screen.getByRole('menu')).not.toHaveFocus(); - }); - - it('should call TransitionProps.onEntering', () => { - const onEnteringSpy = spy(); - render( - <Menu - anchorEl={document.createElement('div')} - open - TransitionProps={{ onEntering: onEnteringSpy }} - />, - ); - - expect(onEnteringSpy.callCount).to.equal(1); - }); - - it('should call TransitionProps.onEntering, disableAutoFocusItem', () => { - const onEnteringSpy = spy(); - render( - <Menu - anchorEl={document.createElement('div')} - disableAutoFocusItem - open - TransitionProps={{ onEntering: onEnteringSpy }} - />, - ); - - expect(onEnteringSpy.callCount).to.equal(1); - }); - - it('should call onClose on tab', () => { - function MenuItem(props: { autoFocus?: boolean; children?: React.ReactNode }) { - const { autoFocus, children } = props; - - const ref = React.useRef<HTMLDivElement | null>(null); - React.useEffect(() => { - if (autoFocus) { - ref?.current?.focus(); - } - }, [autoFocus]); - - return ( - <div ref={ref} role="menuitem" tabIndex={-1}> - {children} - </div> - ); - } - const onCloseSpy = spy(); - render( - <Menu anchorEl={document.createElement('div')} open onClose={onCloseSpy}> - <MenuItem>hello</MenuItem> - </Menu>, - ); - - act(() => { - screen.getByRole('menuitem').focus(); - }); - fireEvent.keyDown(screen.getByRole('menuitem'), { key: 'Tab' }); - - expect(onCloseSpy.callCount).to.equal(1); - expect(onCloseSpy.args[0][1]).to.equal('tabKeyDown'); - }); - - it('ignores invalid children', () => { - render( - <Menu anchorEl={document.createElement('div')} open> - {null} - <span role="menuitem">hello</span> - {/* testing conditional rendering */} - {false && <span role="menuitem">hello</span>} - {undefined} - foo - </Menu>, - ); - - expect(screen.getAllByRole('menuitem')).to.have.length(1); - }); - - describe('warnings', () => { - it('warns a Fragment is passed as a child', () => { - expect(() => { - render( - <Menu anchorEl={document.createElement('div')} open={false}> - {/* eslint-disable-next-line react/jsx-no-useless-fragment */} - <React.Fragment /> - </Menu>, - ); - }).toErrorDev([ - "MUI: The Menu component doesn't accept a Fragment as a child.", - !strictModeDoubleLoggingSuppressed && - "MUI: The Menu component doesn't accept a Fragment as a child.", - "MUI: The Menu component doesn't accept a Fragment as a child.", - !strictModeDoubleLoggingSuppressed && - "MUI: The Menu component doesn't accept a Fragment as a child.", - ]); - }); - }); - - describe('theme customization', () => { - it('should override Menu Paper styles following correct precedence', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const menuPaperOverrides = { borderRadius: 4 }; - const popoverPaperOverrides = { borderRadius: 8, height: 100 }; - const rootPaperOverrides = { borderRadius: 16, height: 200, width: 200 }; - - const theme = extendTheme({ - components: { - MuiMenu: { styleOverrides: { paper: menuPaperOverrides } }, - MuiPopover: { styleOverrides: { paper: popoverPaperOverrides } }, - MuiPaper: { styleOverrides: { root: rootPaperOverrides } }, - }, - }); - - render( - <CssVarsProvider theme={theme}> - <Menu - anchorEl={document.createElement('div')} - open - PaperProps={{ - 'data-testid': 'paper', - }} - /> - </CssVarsProvider>, - ); - - const paper = screen.getByTestId('paper'); - expect(paper).toHaveComputedStyle({ - borderTopLeftRadius: `${menuPaperOverrides.borderRadius}px`, - borderBottomLeftRadius: `${menuPaperOverrides.borderRadius}px`, - borderTopRightRadius: `${menuPaperOverrides.borderRadius}px`, - borderBottomRightRadius: `${menuPaperOverrides.borderRadius}px`, - height: `${popoverPaperOverrides.height}px`, - width: `${rootPaperOverrides.width}px`, - }); - }); - - it('should override Menu Paper styles using styles in MuiPaper slot', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const theme = extendTheme({ - components: { - MuiPaper: { styleOverrides: { rounded: { borderRadius: 90 } } }, - }, - }); - - render( - <CssVarsProvider theme={theme}> - <Menu - anchorEl={document.createElement('div')} - open - PaperProps={{ - 'data-testid': 'paper', - }} - /> - </CssVarsProvider>, - ); - - const paper = screen.getByTestId('paper'); - expect(paper).toHaveComputedStyle({ - borderTopLeftRadius: '90px', - borderBottomLeftRadius: '90px', - borderTopRightRadius: '90px', - borderBottomRightRadius: '90px', - }); - }); - }); - - describe('paper', () => { - it('should use MenuPaper component', () => { - const wrapper = mount( - <Menu anchorEl={document.createElement('div')} open> - <div /> - </Menu>, - ); - - expect(wrapper.find(MenuPaper)).to.have.length(1); - }); - }); - - describe('slots', () => { - it('should merge slots with existing values', () => { - const wrapper = mount( - <Menu slots={{ root: 'span' }} anchorEl={document.createElement('div')} open> - <div /> - </Menu>, - ); - - expect(wrapper.find(MenuPaper)).to.have.length(1); - }); - }); - - const MenuButton = React.forwardRef< - HTMLButtonElement, - { id?: string; children?: React.ReactNode } - >(function MenuButton(props, forwardedRef) { - const { getRootProps: getButtonProps } = useMenuButton({ rootRef: forwardedRef }); - - return <Button type="button" {...props} {...getButtonProps()} />; - }); - - function ContextMenu({ open, ...menuProps }: MenuProps) { - const { contextValue: dropdownContextValue } = useDropdown({ defaultOpen: open }); - - return ( - <DropdownContext.Provider value={dropdownContextValue}> - <MenuButton id="basic-button" aria-haspopup="true" data-testid="menu-button"> - Dashboard - </MenuButton> - <Menu {...menuProps} /> - </DropdownContext.Provider> - ); - } - - describe('using context', () => { - describe('event callbacks', () => { - describe('entering', () => { - it('should fire callbacks', () => { - const handleEnter = spy(); - const handleEntering = spy(); - render( - <ContextMenu - open - anchorEl={document.createElement('div')} - TransitionProps={{ - onEnter: handleEnter, - onEntering: handleEntering, - }} - />, - ); - - expect(handleEnter.callCount).to.equal( - // onEnter is called on mount which is run twice with Strict Effects - React.version.startsWith('18') ? 2 : 1, - ); - expect(handleEnter.args[0].length).to.equal(2); - expect(handleEntering.callCount).to.equal(1); - expect(handleEntering.args[0].length).to.equal(2); - }); - }); - - describe('exiting', () => { - it('should fire callbacks', () => { - const handleExit = spy(); - const handleExiting = spy(); - - const { getByTestId } = render( - <ContextMenu - TransitionProps={{ - onExit: handleExit, - onExiting: handleExiting, - }} - anchorEl={document.createElement('div')} - open - />, - ); - - act(() => { - getByTestId('menu-button').click(); - }); - - expect(handleExit.callCount).to.equal(1); - expect(handleExit.args[0].length).to.equal(1); - expect(handleExiting.callCount).to.equal(1); - expect(handleExiting.args[0].length).to.equal(1); - }); - }); - }); - - it('should pass `classes.paper` to the Paper', () => { - render( - <ContextMenu - anchorEl={document.createElement('div')} - open - PaperProps={{ 'data-testid': 'paper' }} - />, - ); - - expect(screen.getByTestId('paper')).to.have.class(classes.paper); - }); - - describe('prop: PopoverClasses', () => { - it('should be able to change the Popover style', () => { - render( - <ContextMenu - anchorEl={document.createElement('div')} - open - PaperProps={{ 'data-testid': 'paper' }} - PopoverClasses={{ paper: 'bar' }} - />, - ); - - expect(screen.getByTestId('paper')).to.have.class('bar'); - }); - - it('should be able to change the Popover root element style when Menu classes prop is also provided', () => { - render( - <ContextMenu - anchorEl={document.createElement('div')} - open - data-testid="popover" - classes={{ paper: 'bar' }} - PopoverClasses={{ root: 'foo' }} - />, - ); - expect(screen.getByTestId('popover')).to.have.class('foo'); - }); - }); - - describe('prop: PaperProps', () => { - it('should be passed to the paper component', () => { - const customElevation = 12; - const customClasses = { rounded: 'custom-rounded-class' }; - const wrapper = mount( - <ContextMenu - anchorEl={document.createElement('div')} - open - PaperProps={{ - 'data-testid': 'paper', - elevation: customElevation, - classes: customClasses, - }} - />, - ); - - expect(wrapper.find(MenuPaper).props().elevation).to.equal(customElevation); - expect(wrapper.find(MenuPaper).props().classes).to.contain(customClasses); - }); - }); - - it('should pass onClose prop to Popover', () => { - const handleClose = spy(); - render(<ContextMenu anchorEl={document.createElement('div')} open onClose={handleClose} />); - - act(() => { - screen.getByRole('menu').focus(); - }); - fireEvent.keyDown(screen.getByRole('menu'), { key: 'Escape' }); - - expect(handleClose.callCount).to.equal(1); - }); - - it('renders its children only when open', () => { - const { getByTestId } = render( - <ContextMenu anchorEl={document.createElement('div')} open={false}> - <div data-testid="children" /> - </ContextMenu>, - ); - - expect(screen.queryByTestId('children')).to.equal(null); - - act(() => { - getByTestId('menu-button').click(); - }); - - expect(screen.getByTestId('children')).not.to.equal(null); - }); - - describe('list node', () => { - it('should render a menu inside the Popover', () => { - render(<ContextMenu anchorEl={document.createElement('div')} open data-testid="popover" />); - - expect(screen.getByTestId('popover').querySelector('[role="menu"]')).not.to.equal(null); - }); - }); - - it('should open during the initial mount', () => { - function MenuItem(props: { children?: React.ReactNode }) { - const { children } = props; - return ( - <div role="menuitem" tabIndex={-1}> - {children} - </div> - ); - } - render( - <ContextMenu anchorEl={document.createElement('div')} open> - <MenuItem>one</MenuItem> - </ContextMenu>, - ); - - expect(screen.getByRole('menuitem')).to.not.equal(null); - }); - - it('should not focus list if autoFocus=false', () => { - render( - <ContextMenu anchorEl={document.createElement('div')} autoFocus={false} open> - <div tabIndex={-1} /> - </ContextMenu>, - ); - - expect(screen.getByRole('menu')).not.toHaveFocus(); - }); - - it('should call TransitionProps.onEntering', () => { - const onEnteringSpy = spy(); - render( - <ContextMenu - anchorEl={document.createElement('div')} - open - TransitionProps={{ onEntering: onEnteringSpy }} - />, - ); - - expect(onEnteringSpy.callCount).to.equal(1); - }); - - it('should call TransitionProps.onEntering, disableAutoFocusItem', () => { - const onEnteringSpy = spy(); - render( - <ContextMenu - anchorEl={document.createElement('div')} - disableAutoFocusItem - open - TransitionProps={{ onEntering: onEnteringSpy }} - />, - ); - - expect(onEnteringSpy.callCount).to.equal(1); - }); - - it('should call onClose on tab', () => { - function MenuItem(props: { autoFocus?: boolean; children?: React.ReactNode }) { - const { autoFocus, children } = props; - - const ref = React.useRef<HTMLDivElement | null>(null); - React.useEffect(() => { - if (autoFocus) { - ref?.current?.focus(); - } - }, [autoFocus]); - - return ( - <div ref={ref} role="menuitem" tabIndex={-1}> - {children} - </div> - ); - } - const onCloseSpy = spy(); - render( - <ContextMenu anchorEl={document.createElement('div')} open onClose={onCloseSpy}> - <MenuItem>hello</MenuItem> - </ContextMenu>, - ); - - act(() => { - screen.getByRole('menuitem').focus(); - }); - fireEvent.keyDown(screen.getByRole('menuitem'), { key: 'Tab' }); - - expect(onCloseSpy.callCount).to.equal(1); - expect(onCloseSpy.args[0][1]).to.equal('tabKeyDown'); - }); - - it('ignores invalid children', () => { - render( - <ContextMenu anchorEl={document.createElement('div')} open> - {null} - <span role="menuitem">hello</span> - {/* testing conditional rendering */} - {false && <span role="menuitem">hello</span>} - {undefined} - foo - </ContextMenu>, - ); - - expect(screen.getAllByRole('menuitem')).to.have.length(1); - }); - - describe('theme customization', () => { - it('should override Menu Paper styles following correct precedence', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const menuPaperOverrides = { borderRadius: 4 }; - const popoverPaperOverrides = { borderRadius: 8, height: 100 }; - const rootPaperOverrides = { borderRadius: 16, height: 200, width: 200 }; - - const theme = extendTheme({ - components: { - MuiMenu: { styleOverrides: { paper: menuPaperOverrides } }, - MuiPopover: { styleOverrides: { paper: popoverPaperOverrides } }, - MuiPaper: { styleOverrides: { root: rootPaperOverrides } }, - }, - }); - - render( - <CssVarsProvider theme={theme}> - <ContextMenu - anchorEl={document.createElement('div')} - open - PaperProps={{ - 'data-testid': 'paper', - }} - /> - </CssVarsProvider>, - ); - - const paper = screen.getByTestId('paper'); - expect(paper).toHaveComputedStyle({ - borderTopLeftRadius: `${menuPaperOverrides.borderRadius}px`, - borderBottomLeftRadius: `${menuPaperOverrides.borderRadius}px`, - borderTopRightRadius: `${menuPaperOverrides.borderRadius}px`, - borderBottomRightRadius: `${menuPaperOverrides.borderRadius}px`, - height: `${popoverPaperOverrides.height}px`, - width: `${rootPaperOverrides.width}px`, - }); - }); - - it('should override Menu Paper styles using styles in MuiPaper slot', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const theme = extendTheme({ - components: { - MuiPaper: { styleOverrides: { rounded: { borderRadius: 90 } } }, - }, - }); - - render( - <CssVarsProvider theme={theme}> - <ContextMenu - anchorEl={document.createElement('div')} - open - PaperProps={{ - 'data-testid': 'paper', - }} - /> - </CssVarsProvider>, - ); - - const paper = screen.getByTestId('paper'); - expect(paper).toHaveComputedStyle({ - borderTopLeftRadius: '90px', - borderBottomLeftRadius: '90px', - borderTopRightRadius: '90px', - borderBottomRightRadius: '90px', - }); - }); - }); - - describe('paper', () => { - it('should use MenuPaper component', () => { - const wrapper = mount( - <ContextMenu anchorEl={document.createElement('div')} open> - <div /> - </ContextMenu>, - ); - - expect(wrapper.find(MenuPaper)).to.have.length(1); - }); - }); - - describe('slots', () => { - it('should merge slots with existing values', () => { - const wrapper = mount( - <ContextMenu slots={{ root: 'span' }} anchorEl={document.createElement('div')}> - <div /> - </ContextMenu>, - ); - - expect(wrapper.find(MenuPaper)).to.have.length(1); - }); - }); - }); -}); diff --git a/packages/mui-material-next/src/Menu/Menu.tsx b/packages/mui-material-next/src/Menu/Menu.tsx deleted file mode 100644 index 6719041fa5668e..00000000000000 --- a/packages/mui-material-next/src/Menu/Menu.tsx +++ /dev/null @@ -1,442 +0,0 @@ -'use client'; -import * as React from 'react'; -import { isFragment } from 'react-is'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { OverridableComponent } from '@mui/types'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { useMenu, MenuProvider } from '@mui/base/useMenu'; -import { useDropdown, DropdownContext } from '@mui/base/useDropdown'; -import { useSlotProps } from '@mui/base/utils'; -import { ListActionTypes } from '@mui/base/useList'; -import { - HTMLElementType, - unstable_getScrollbarSize as getScrollbarSize, - unstable_ownerDocument as ownerDocument, -} from '@mui/utils'; -// TODO v6: Replace with @mui/material-next when the Popover component is available -import Popover, { PopoverPaper, PopoverOrigin } from '@mui/material/Popover'; -import { styled, useTheme, useThemeProps, rootShouldForwardProp } from '../styles'; -import { MenuTypeMap, MenuOwnerState } from './Menu.types'; -import { getMenuUtilityClass } from './menuClasses'; - -const RTL_ORIGIN: PopoverOrigin = { - vertical: 'top', - horizontal: 'right', -}; - -const LTR_ORIGIN: PopoverOrigin = { - vertical: 'top', - horizontal: 'left', -}; - -const useUtilityClasses = (ownerState: MenuOwnerState) => { - const { classes } = ownerState; - - const slots = { - root: ['root'], - paper: ['paper'], - list: ['list'], - }; - - return composeClasses(slots, getMenuUtilityClass, classes); -}; - -const MenuRoot = styled(Popover, { - shouldForwardProp: (prop: string) => rootShouldForwardProp(prop) || prop === 'classes', - name: 'MuiMenu', - slot: 'Root', - overridesResolver: (props, styles) => styles.root, -})({}); - -export const MenuPaper = styled(PopoverPaper, { - name: 'MuiMenu', - slot: 'Paper', - overridesResolver: (props, styles) => styles.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', -}); - -const MenuListbox = styled('ul', { - name: 'MuiMenu', - slot: 'List', - overridesResolver: (props, styles) => styles.list, -})({ - // reset the default padding-inline-start - paddingInlineStart: 0, - // We disable the focus ring for mouse, touch and keyboard users. - outline: 0, -}); - -const MenuInner = React.forwardRef(function Menu(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiMenu' }); - - const { - anchorEl, - autoFocus = true, - children, - className, - disableAutoFocusItem = false, - MenuListProps = {}, - onClose, - PaperProps = {}, - PopoverClasses, - transitionDuration = 'auto', - TransitionProps: { onEntering, ...TransitionProps } = {}, - variant = 'selectedMenu', - slots = {}, - slotProps = {}, - actions, - ...other - } = props; - - const theme = useTheme(); - const isRtl = theme.direction === 'rtl'; - - const listRef = React.useRef<HTMLElement>(null); - - // TODO v6: Handle the rest of the props from the MenuListProps prop - const { - // varaint - className: menuListPropsClassName, - disabledItemsFocusable, - disableListWrap, - ...otherMenuListProps - } = MenuListProps; - - const { contextValue, getListboxProps, dispatch, open, triggerElement } = useMenu({ - // onItemsChange, - disabledItemsFocusable: Boolean(disabledItemsFocusable), - disableListWrap: Boolean(disableListWrap), - autoFocus, - componentName: 'Menu', - }); - - const ownerState = { - ...props, - open, - autoFocus, - disableAutoFocusItem, - MenuListProps, - onEntering, - PaperProps, - transitionDuration, - TransitionProps, - variant, - }; - - React.useImperativeHandle( - actions, - () => ({ - dispatch, - resetHighlight: () => dispatch({ type: ListActionTypes.resetHighlight, event: null }), - }), - [dispatch], - ); - - const classes = useUtilityClasses(ownerState); - - const handleEntering = (element: HTMLElement, isAppearing: boolean) => { - // adjust styles for scrollbar - if (element && listRef.current) { - // Let's ignore that piece of logic if users are already overriding the width - // of the menu. - const containerElement = element; - const noExplicitWidth = !listRef.current.style.width; - if (containerElement.clientHeight < listRef?.current?.clientHeight && noExplicitWidth) { - const scrollbarSize = `${getScrollbarSize(ownerDocument(containerElement))}px`; - listRef.current.style[theme.direction === 'rtl' ? 'paddingLeft' : 'paddingRight'] = - scrollbarSize; - listRef.current.style.width = `calc(100% + ${scrollbarSize})`; - } - } - - if (onEntering) { - onEntering(element, isAppearing); - } - }; - - const handleListKeyDown = (event: React.KeyboardEvent) => { - if (event.key === 'Tab') { - event.preventDefault(); - - if (onClose) { - onClose(event, 'tabKeyDown'); - } - } - }; - - /** - * the index of the item should receive focus - * in a `variant="selectedMenu"` it's the first `selected` item - * otherwise it's the very first item. - */ - let activeItemIndex = -1; - // since we inject focus related props into children we have to do a lookahead - // to check if there is a `selected` item. We're looking for the last `selected` - // item and use the first valid item as a fallback - React.Children.map(children, (child, index) => { - if (!React.isValidElement(child)) { - return; - } - - if (process.env.NODE_ENV !== 'production') { - if (isFragment(child)) { - console.error( - [ - "MUI: The Menu component doesn't accept a Fragment as a child.", - 'Consider providing an array instead.', - ].join('\n'), - ); - } - } - - if (!child.props.disabled) { - if (variant === 'selectedMenu' && child.props.selected) { - activeItemIndex = index; - } else if (activeItemIndex === -1) { - activeItemIndex = index; - } - } - }); - - const PaperSlot = slots.paper ?? MenuPaper; - const paperExternalSlotProps = slotProps.paper ?? PaperProps; - - const rootSlotProps = useSlotProps({ - elementType: slots.root, - externalSlotProps: slotProps.root, - additionalProps: { - role: undefined, - }, - ownerState, - className: [classes.root, className], - }); - - const paperSlotProps = useSlotProps({ - elementType: PaperSlot, - externalSlotProps: paperExternalSlotProps, - ownerState, - className: classes.paper, - }); - - // TODO v6: Setting autoFocus = false is missing feature at this point - // const autoFocusList = autoFocus && (activeItemIndex === -1 || disableAutoFocusItem); - - const Listbox = slots.listbox ?? MenuListbox; - const listboxProps = useSlotProps({ - elementType: Listbox, - getSlotProps: (otherHandlers) => { - return getListboxProps({ - onKeyDown: handleListKeyDown, - ...otherHandlers, - }); - }, - externalSlotProps: (args) => ({ - ...(typeof slotProps.listbox === 'function' ? slotProps.listbox(args) : slotProps.listbox), - ...otherMenuListProps, - }), - additionalProps: { - ref: listRef, - // TODO v6: These need to be handled on the Menu component level, now that the MenuListbox is simply ul - // variant, - // autoFocusItem, - // autoFocus: autoFocus && (activeItemIndex === -1 || disableAutoFocusItem), - }, - className: clsx(classes.list, menuListPropsClassName), - ownerState, - }); - - return ( - <MenuRoot - onClose={onClose} - anchorOrigin={{ - vertical: 'bottom', - horizontal: isRtl ? 'right' : 'left', - }} - transformOrigin={isRtl ? RTL_ORIGIN : LTR_ORIGIN} - slots={{ - paper: PaperSlot, - root: slots.root, - }} - slotProps={{ - root: rootSlotProps, - paper: paperSlotProps, - }} - open={open} - ref={ref} - transitionDuration={transitionDuration} - TransitionProps={{ onEntering: handleEntering, ...TransitionProps }} - // @ts-ignore internal usage - ownerState={ownerState} - anchorEl={anchorEl ?? triggerElement} - {...other} - classes={PopoverClasses} - > - <MenuProvider value={contextValue}> - <Listbox {...listboxProps}>{children}</Listbox> - </MenuProvider> - </MenuRoot> - ); -}) as OverridableComponent<MenuTypeMap>; - -/** - * - * Demos: - * - * - [App Bar](https://mui.com/material-ui/react-app-bar/) - * - [Menu](https://mui.com/material-ui/react-menu/) - * - * API: - * - * - [Menu API](https://mui.com/material-ui/api/menu/) - * - inherits [Popover API](https://mui.com/material-ui/api/popover/) - */ -const Menu = React.forwardRef(function Menu(inProps, ref) { - const { open } = inProps; - const upperDropdownContext = React.useContext(DropdownContext); - - const { contextValue: dropdownContextValue } = useDropdown({ - open, - componentName: 'Menu', - }); - - return !upperDropdownContext ? ( - <DropdownContext.Provider value={dropdownContextValue}> - <MenuInner ref={ref} {...inProps} /> - </DropdownContext.Provider> - ) : ( - <MenuInner ref={ref} {...inProps} /> - ); -}) as OverridableComponent<MenuTypeMap>; - -Menu.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * A ref with imperative actions that can be performed on the menu. - */ - actions: PropTypes.oneOfType([ - PropTypes.func, - PropTypes.shape({ - current: PropTypes.shape({ - dispatch: PropTypes.func.isRequired, - resetHighlight: PropTypes.func.isRequired, - }), - }), - ]), - /** - * An HTML element, [PopoverVirtualElement](/material-ui/react-popover/#virtual-element), - * or a function that returns either. - * It's used to set the position of the popover. - */ - anchorEl: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - HTMLElementType, - PropTypes.func, - ]), - /** - * If `true` (Default) will focus the `[role="menu"]` if no focusable child is found. Disabled - * children are not focusable. If you set this prop to `false` focus will be placed - * on the parent modal container. This has severe accessibility implications - * and should only be considered if you manage focus otherwise. - */ - autoFocus: PropTypes.bool, - /** - * Menu contents, normally `MenuItem`s. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * When opening the menu will not focus the active item but the `[role="menu"]` - * unless `autoFocus` is also set to `false`. Not using the default means not - * following WAI-ARIA authoring practices. Please be considerate about possible - * accessibility implications. - * @default false - */ - disableAutoFocusItem: PropTypes.bool, - /** - * Props applied to the [`MenuList`](/material-ui/api/menu-list/) element. - * @default {} - */ - MenuListProps: PropTypes.object, - /** - * Callback fired when the component requests to be closed. - * - * @param {object} event The event source of the callback. - * @param {string} reason Can be: `"escapeKeyDown"`, `"backdropClick"`, `"tabKeyDown"`. - */ - onClose: PropTypes.func, - /** - * If `true`, the component is shown. - */ - open: PropTypes.bool, - /** - * `classes` prop applied to the [`Popover`](/material-ui/api/popover/) element. - */ - PopoverClasses: PropTypes.object, - /** - * The extra props for the slot components. - * You can override the existing props or add new ones. - * - * @default {} - */ - slotProps: PropTypes.shape({ - listbox: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - paper: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - }), - /** - * The components used for each slot inside. - * - * @default {} - */ - slots: PropTypes.shape({ - listbox: PropTypes.elementType, - paper: PropTypes.elementType, - root: PropTypes.elementType, - }), - /** - * 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, - ]), - /** - * Set to 'auto' to automatically calculate transition time based on height. - * @default 'auto' - */ - transitionDuration: PropTypes.oneOfType([ - PropTypes.oneOf(['auto']), - PropTypes.number, - PropTypes.shape({ - appear: PropTypes.number, - enter: PropTypes.number, - exit: PropTypes.number, - }), - ]), - /** - * Props applied to the transition element. - * By default, the element is based on this [`Transition`](https://reactcommunity.org/react-transition-group/transition/) component. - * @default {} - */ - TransitionProps: PropTypes.object, - /** - * The variant to use. Use `menu` to prevent selected items from impacting the initial focus. - * @default 'selectedMenu' - */ - variant: PropTypes.oneOf(['menu', 'selectedMenu']), -} as any; - -export default Menu; diff --git a/packages/mui-material-next/src/Menu/Menu.types.ts b/packages/mui-material-next/src/Menu/Menu.types.ts deleted file mode 100644 index 49ddbfabbe9a27..00000000000000 --- a/packages/mui-material-next/src/Menu/Menu.types.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* eslint-disable no-restricted-imports */ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverrideProps } from '@mui/types'; -import { SlotComponentProps, MenuActions } from '@mui/base'; -import { InternalStandardProps as StandardProps } from '@mui/material'; -import Paper from '@mui/material/Paper'; -import Popover, { PopoverProps } from '@mui/material/Popover'; -import { MenuListProps } from '@mui/material/MenuList'; -import { Theme } from '@mui/material/styles'; -import { TransitionProps } from '@mui/material/transitions/transition'; -import { MenuClasses } from './menuClasses'; - -export interface MenuTypeMap<AdditionalProps = {}, RootComponent extends React.ElementType = 'ul'> { - props: AdditionalProps & - StandardProps<Omit<PopoverProps, 'slots' | 'slotProps' | 'open' | 'onClose'>, 'children'> & { - /** - * A ref with imperative actions that can be performed on the menu. - */ - actions?: React.Ref<MenuActions>; - /** - * An HTML element, or a function that returns one. - * It's used to set the position of the menu. - */ - anchorEl?: PopoverProps['anchorEl']; - /** - * If `true` (Default) will focus the `[role="menu"]` if no focusable child is found. Disabled - * children are not focusable. If you set this prop to `false` focus will be placed - * on the parent modal container. This has severe accessibility implications - * and should only be considered if you manage focus otherwise. - * @default true - */ - autoFocus?: boolean; - /** - * Menu contents, normally `MenuItem`s. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<MenuClasses>; - /** - * When opening the menu will not focus the active item but the `[role="menu"]` - * unless `autoFocus` is also set to `false`. Not using the default means not - * following WAI-ARIA authoring practices. Please be considerate about possible - * accessibility implications. - * @default false - */ - disableAutoFocusItem?: boolean; - /** - * Props applied to the [`MenuList`](/material-ui/api/menu-list/) element. - * @default {} - */ - MenuListProps?: Partial<MenuListProps>; - /** - * Callback fired when the component requests to be closed. - * - * @param {object} event The event source of the callback. - * @param {string} reason Can be: `"escapeKeyDown"`, `"backdropClick"`, `"tabKeyDown"`. - */ - onClose?: { - bivarianceHack(event: {}, reason: 'backdropClick' | 'escapeKeyDown' | 'tabKeyDown'): void; - }['bivarianceHack']; - /** - * If `true`, the component is shown. - */ - open?: boolean; - /** - * `classes` prop applied to the [`Popover`](/material-ui/api/popover/) element. - */ - PopoverClasses?: PopoverProps['classes']; - /** - * The components used for each slot inside. - * - * @default {} - */ - slots?: { - root?: React.ElementType; - listbox?: React.ElementType; - paper?: React.ElementType; - }; - /** - * The extra props for the slot components. - * You can override the existing props or add new ones. - * - * @default {} - */ - slotProps?: { - root?: SlotComponentProps<typeof Popover, {}, MenuOwnerState>; - listbox?: SlotComponentProps<'ul', {}, MenuOwnerState>; - paper?: SlotComponentProps<typeof Paper, {}, {}>; - }; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; - /** - * The length of the transition in `ms`, or 'auto' - * @default 'auto' - */ - transitionDuration?: TransitionProps['timeout'] | 'auto'; - /** - * Props applied to the transition element. - * By default, the element is based on this [`Transition`](https://reactcommunity.org/react-transition-group/transition/) component. - * @default {} - */ - TransitionProps?: TransitionProps; - /** - * The variant to use. Use `menu` to prevent selected items from impacting the initial focus. - * @default 'selectedMenu' - */ - variant?: 'menu' | 'selectedMenu'; - }; - defaultComponent: RootComponent; -} - -export type MenuProps< - RootComponent extends React.ElementType = MenuTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<MenuTypeMap<AdditionalProps, RootComponent>, RootComponent> & { - component?: React.ElementType; -}; - -export interface MenuOwnerState extends Omit<MenuProps, 'open'> { - open: boolean; -} diff --git a/packages/mui-material-next/src/Menu/index.ts b/packages/mui-material-next/src/Menu/index.ts deleted file mode 100644 index 902a3ba8e61b04..00000000000000 --- a/packages/mui-material-next/src/Menu/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -'use client'; -export { default } from './Menu'; -export * from './Menu.types'; - -export { default as menuClasses } from './menuClasses'; -export * from './menuClasses'; diff --git a/packages/mui-material-next/src/Menu/menuClasses.ts b/packages/mui-material-next/src/Menu/menuClasses.ts deleted file mode 100644 index 47cebc2a222148..00000000000000 --- a/packages/mui-material-next/src/Menu/menuClasses.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface MenuClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the Paper component. */ - paper: string; - /** Styles applied to the List component via `MenuList`. */ - list: string; -} - -export type MenuClassKey = keyof MenuClasses; - -export function getMenuUtilityClass(slot: string): string { - return generateUtilityClass('MuiMenu', slot); -} - -const menuClasses: MenuClasses = generateUtilityClasses('MuiMenu', ['root', 'paper', 'list']); - -export default menuClasses; diff --git a/packages/mui-material-next/src/MenuItem/MenuItem.spec.tsx b/packages/mui-material-next/src/MenuItem/MenuItem.spec.tsx deleted file mode 100644 index d91914e1273a66..00000000000000 --- a/packages/mui-material-next/src/MenuItem/MenuItem.spec.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import * as React from 'react'; -import { expectType } from '@mui/types'; -import MenuItem, { MenuItemProps } from '@mui/material-next/MenuItem'; -import Link from '@mui/material/Link'; - -const CustomComponent: React.FC<{ stringProp: string; numberProp: number }> = - function CustomComponent() { - return <div />; - }; - -const props1: MenuItemProps<'div'> = { - component: 'div', - onChange: (event) => { - expectType<React.FormEvent<HTMLDivElement>, typeof event>(event); - }, -}; - -const props2: MenuItemProps = { - onChange: (event) => { - expectType<React.FormEvent<HTMLLIElement>, typeof event>(event); - }, -}; - -const props3: MenuItemProps<typeof CustomComponent> = { - component: CustomComponent, - stringProp: '2', - numberProp: 2, -}; - -const props4: MenuItemProps<typeof CustomComponent> = { - component: CustomComponent, - stringProp: '2', - numberProp: 2, - // @ts-expect-error CustomComponent does not accept incorrectProp - incorrectProp: 3, -}; - -// @ts-expect-error missing props -const props5: MenuItemProps<typeof CustomComponent> = { - component: CustomComponent, -}; - -const TestComponent = () => { - return ( - <React.Fragment> - <MenuItem /> - <MenuItem component={'a'} href="/test" /> - - <MenuItem component={CustomComponent} stringProp="s" numberProp={1} /> - { - // @ts-expect-error missing props - <MenuItem component={CustomComponent} /> - } - <MenuItem - onChange={(event) => { - expectType<React.FormEvent<HTMLLIElement>, typeof event>(event); - }} - /> - <MenuItem - component="span" - onChange={(event) => { - expectType<React.FormEvent<HTMLSpanElement>, typeof event>(event); - }} - /> - <MenuItem component={Link} /> - </React.Fragment> - ); -}; diff --git a/packages/mui-material-next/src/MenuItem/MenuItem.test.tsx b/packages/mui-material-next/src/MenuItem/MenuItem.test.tsx deleted file mode 100644 index c10c1751576a67..00000000000000 --- a/packages/mui-material-next/src/MenuItem/MenuItem.test.tsx +++ /dev/null @@ -1,219 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { spy } from 'sinon'; -import { act, createRenderer, fireEvent, screen } from '@mui-internal/test-utils'; -import { MenuProvider } from '@mui/base/useMenu'; -import MenuItem, { menuItemClasses as classes } from '@mui/material-next/MenuItem'; -import Menu from '@mui/material-next/Menu'; -import ButtonBase from '@mui/material-next/ButtonBase'; -import describeConformance from '../../test/describeConformance'; - -const dummyGetItemState = () => ({ - disabled: false, - highlighted: false, - selected: false, - index: 0, - focusable: true, -}); - -const testContext = { - dispatch: () => {}, - getItemIndex: () => 0, - getItemProps: () => ({}), - getItemState: dummyGetItemState, - open: false, - registerHighlightChangeHandler: () => () => {}, - registerItem: () => ({ id: '', deregister: () => {} }), - registerSelectionChangeHandler: () => () => {}, - totalSubitemCount: 0, -}; - -describe('<MenuItem />', () => { - const { render } = createRenderer({ clock: 'fake' }); - - // afterEach(() => { - // document.getElementsByTagName('html')[0].innerHTML = ''; - // }); - - describeConformance(<MenuItem data-testid="menuitem">1</MenuItem>, () => ({ - render: (node) => { - return render(<MenuProvider value={testContext}>{node}</MenuProvider>); - }, - wrapMount: (mount) => (node) => mount(<MenuProvider value={testContext}>{node}</MenuProvider>), - classes, - inheritComponent: ButtonBase, - refInstanceof: window.HTMLLIElement, - testComponentPropWith: 'a', - muiName: 'MuiMenuItem', - testVariantProps: { dense: true }, - skip: ['componentsProp', 'reactTestRenderer'], - })); - - const renderWithMenu = (node: React.ReactNode) => { - function Test() { - return ( - <Menu anchorEl={document.createElement('div')} open> - {node} - </Menu> - ); - } - - return render(<Test />); - }; - - it('should render a focusable menuitem', () => { - renderWithMenu(<MenuItem />); - const menuitem = screen.getByRole('menuitem'); - - expect(menuitem).to.have.property('tabIndex', 0); - }); - - it('has a ripple when clicked', () => { - renderWithMenu( - <MenuItem TouchRippleProps={{ classes: { rippleVisible: 'ripple-visible' } }} />, - ); - const menuitem = screen.getByRole('menuitem'); - - // ripple starts on mousedown - fireEvent.mouseDown(menuitem); - - expect(menuitem.querySelectorAll('.ripple-visible')).to.have.length(1); - }); - - it('should render with the selected class but not aria-selected when `selected`', () => { - renderWithMenu(<MenuItem selected />); - const menuitem = screen.getByRole('menuitem'); - - expect(menuitem).to.have.class(classes.selected); - expect(menuitem).not.to.have.attribute('aria-selected'); - }); - - it('can have a role of option', () => { - renderWithMenu(<MenuItem role="option" aria-selected={false} />); - - expect(screen.queryByRole('option')).not.to.equal(null); - }); - - describe('event callbacks', () => { - const events: Array<keyof typeof fireEvent> = [ - 'click', - 'mouseDown', - 'mouseEnter', - 'mouseLeave', - 'mouseUp', - 'touchEnd', - ]; - - events.forEach((eventName) => { - it(`should fire ${eventName}`, () => { - const handlerName = `on${eventName[0].toUpperCase()}${eventName.slice(1)}`; - const handler = spy(); - renderWithMenu(<MenuItem {...{ [handlerName]: handler }} />); - - fireEvent[eventName](screen.getByRole('menuitem')); - - expect(handler.callCount).to.equal(1); - }); - }); - - it(`should fire focus, keydown, keyup and blur`, () => { - const handleFocus = spy(); - const handleKeyDown = spy(); - const handleKeyUp = spy(); - const handleBlur = spy(); - renderWithMenu( - <MenuItem - onFocus={handleFocus} - onKeyDown={handleKeyDown} - onKeyUp={handleKeyUp} - onBlur={handleBlur} - />, - ); - const menuitem = screen.getByRole('menuitem'); - - act(() => { - menuitem.focus(); - }); - - expect(handleFocus.callCount).to.equal(1); - - fireEvent.keyDown(menuitem); - - expect(handleKeyDown.callCount).to.equal(1); - - fireEvent.keyUp(menuitem); - - expect(handleKeyUp.callCount).to.equal(1); - - expect(handleKeyDown.callCount).to.equal(1); - }); - - it('should fire onTouchStart', function touchStartTest() { - // only run in supported browsers - if (typeof Touch === 'undefined') { - this.skip(); - } - - const handleTouchStart = spy(); - renderWithMenu(<MenuItem onTouchStart={handleTouchStart} />); - const menuitem = screen.getByRole('menuitem'); - - const touch = new Touch({ identifier: 0, target: menuitem, clientX: 0, clientY: 0 }); - fireEvent.touchStart(menuitem, { touches: [touch] }); - - expect(handleTouchStart.callCount).to.equal(1); - }); - }); - - it('can be disabled', () => { - renderWithMenu(<MenuItem disabled />); - const menuitem = screen.getByRole('menuitem'); - - expect(menuitem).to.have.attribute('aria-disabled', 'true'); - }); - - it('can be selected', () => { - renderWithMenu(<MenuItem selected />); - const menuitem = screen.getByRole('menuitem'); - - expect(menuitem).to.have.class(classes.selected); - }); - - it('prop: disableGutters', () => { - const { rerender } = render( - <Menu anchorEl={document.createElement('div')} open> - <MenuItem /> - </Menu>, - ); - const menuitem = screen.getByRole('menuitem'); - - expect(menuitem).to.have.class(classes.gutters); - - rerender( - <Menu anchorEl={document.createElement('div')} open> - <MenuItem disableGutters /> - </Menu>, - ); - - expect(menuitem).not.to.have.class(classes.gutters); - }); - - // TODO v6: Need to be re-structured now that we don't use the List component internally - // describe('context: dense', () => { - // it.skip('should forward the context', () => { - // let context = null; - // const { setProps } = render( - // <MenuItem> - // <ListContext.Consumer> - // {(options) => { - // context = options; - // }} - // </ListContext.Consumer> - // </MenuItem>, - // ); - // expect(context).to.have.property('dense', false); - // setProps({ dense: true }); - // expect(context).to.have.property('dense', true); - // }); - // }); -}); diff --git a/packages/mui-material-next/src/MenuItem/MenuItem.tsx b/packages/mui-material-next/src/MenuItem/MenuItem.tsx deleted file mode 100644 index 1967b236d42247..00000000000000 --- a/packages/mui-material-next/src/MenuItem/MenuItem.tsx +++ /dev/null @@ -1,323 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { OverridableComponent } from '@mui/types'; -import { alpha } from '@mui/system'; -import { - unstable_useEnhancedEffect as useEnhancedEffect, - unstable_useForkRef as useForkRef, -} from '@mui/utils'; -import { useSlotProps } from '@mui/base/utils'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { useMenuItem } from '@mui/base/useMenuItem'; -// TODO v6: Replace with @mui/material-next when the List components are available -import ListContext from '@mui/material/List/ListContext'; -import { listItemIconClasses } from '@mui/material/ListItemIcon'; -import { listItemTextClasses } from '@mui/material/ListItemText'; -import { styled, useThemeProps, rootShouldForwardProp } from '../styles'; -import ButtonBase from '../ButtonBase'; -import { dividerClasses } from '../Divider'; -import { MenuItemProps, MenuItemOwnerState, MenuItemTypeMap } from './MenuItem.types'; -import menuItemClasses, { getMenuItemUtilityClass } from './menuItemClasses'; - -export const overridesResolver = ( - props: MenuItemProps & { ownerState: MenuItemOwnerState }, - styles: any, -) => { - const { ownerState } = props; - - return [ - styles.root, - ownerState.dense && styles.dense, - ownerState.divider && styles.divider, - !ownerState.disableGutters && styles.gutters, - ]; -}; - -const useUtilityClasses = (ownerState: MenuItemOwnerState) => { - const { disabled, dense, divider, disableGutters, selected, classes } = ownerState; - const slots = { - root: [ - 'root', - dense && 'dense', - disabled && 'disabled', - !disableGutters && 'gutters', - divider && 'divider', - selected && 'selected', - ], - }; - - const composedClasses = composeClasses(slots, getMenuItemUtilityClass, classes); - - return { - ...classes, - ...composedClasses, - }; -}; - -const MenuItemRoot = styled(ButtonBase, { - shouldForwardProp: (prop: string) => rootShouldForwardProp(prop) || prop === 'classes', - name: 'MuiMenuItem', - slot: 'Root', - overridesResolver, -})<{ ownerState: MenuItemOwnerState }>(({ theme, ownerState }) => ({ - ...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', - ...(!ownerState.disableGutters && { - paddingLeft: 16, - paddingRight: 16, - }), - ...(ownerState.divider && { - borderBottom: `1px solid ${(theme.vars || theme).palette.divider}`, - backgroundClip: 'padding-box', - }), - '&: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.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})` - : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity), - [`&.${menuItemClasses.focusVisible}`]: { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.focusOpacity}))` - : alpha( - theme.palette.primary.main, - theme.palette.action.selectedOpacity + theme.palette.action.focusOpacity, - ), - }, - }, - [`&.${menuItemClasses.selected}:hover`]: { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.hoverOpacity}))` - : alpha( - theme.palette.primary.main, - theme.palette.action.selectedOpacity + theme.palette.action.hoverOpacity, - ), - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})` - : alpha(theme.palette.primary.main, 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, - }, - ...(!ownerState.dense && { - [theme.breakpoints.up('sm')]: { - minHeight: 'auto', - }, - }), - ...(ownerState.dense && { - minHeight: 32, // https://m2.material.io/components/menus#specs > Dense - paddingTop: 4, - paddingBottom: 4, - ...theme.typography.body2, - [`& .${listItemIconClasses.root} svg`]: { - fontSize: '1.25rem', - }, - }), -})); - -const MenuItem = React.forwardRef(function MenuItem<RootComponentType extends React.ElementType>( - inProps: MenuItemProps<RootComponentType>, - ref: React.ForwardedRef<Element>, -) { - const props = useThemeProps({ props: inProps, name: 'MuiMenuItem' }); - const { - autoFocus = false, - component = 'li', - dense = false, - divider = false, - disableGutters = false, - focusVisibleClassName, - role = 'menuitem', - className, - disabled: disabledProp, - label: labelProp, - ...other - } = props; - - const context = React.useContext(ListContext); - const childContext = React.useMemo( - () => ({ - dense: dense || context.dense || false, - disableGutters, - }), - [context.dense, dense, disableGutters], - ); - - const menuItemRef = React.useRef<HTMLElement>(null); - const handleRef = useForkRef(menuItemRef, ref); - - const { getRootProps, disabled, focusVisible, highlighted } = useMenuItem({ - disabled: disabledProp, - rootRef: handleRef, - label: labelProp, - }); - - useEnhancedEffect(() => { - if (autoFocus) { - if (menuItemRef.current) { - menuItemRef.current.focus(); - } else if (process.env.NODE_ENV !== 'production') { - console.error( - 'MUI: Unable to set focus to a MenuItem whose component has not been rendered.', - ); - } - } - }, [autoFocus]); - - const ownerState = { - ...props, - dense: childContext.dense, - divider, - disableGutters, - disabled, - focusVisible, - highlighted, - }; - - const classes = useUtilityClasses(props); - - const Root = /* slots.root ?? */ MenuItemRoot; - const rootProps = useSlotProps({ - elementType: Root, - getSlotProps: getRootProps, - // TODO v6: Add support for slotProps.root - externalSlotProps: {}, - externalForwardedProps: other, - additionalProps: { - role, - component, - focusVisibleClassName: clsx(classes.focusVisible, focusVisibleClassName), - classes, - }, - className: clsx(classes.root, className), - ownerState, - }); - return ( - <ListContext.Provider value={childContext}> - <MenuItemRoot {...rootProps} /> - </ListContext.Provider> - ); -}) as OverridableComponent<MenuItemTypeMap>; - -MenuItem.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * If `true`, the list item is focused during the first mount. - * Focus will also be triggered if the value changes from false to true. - * @default false - */ - autoFocus: PropTypes.bool, - /** - * The content of the component. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * If `true`, compact vertical padding designed for keyboard and mouse input is used. - * The prop defaults to the value inherited from the parent Menu component. - * @default false - */ - dense: PropTypes.bool, - /** - * @ignore - */ - 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, - /** - * This prop can help identify which element has keyboard focus. - * The class name will be applied when the element gains the focus through keyboard interaction. - * It's a polyfill for the [CSS :focus-visible selector](https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo). - * The rationale for using this feature [is explained here](https://github.com/WICG/focus-visible/blob/HEAD/explainer.md). - * A [polyfill can be used](https://github.com/WICG/focus-visible) to apply a `focus-visible` class to other components - * if needed. - */ - focusVisibleClassName: PropTypes.string, - /** - * A text representation of the menu item's content. - * Used for keyboard text navigation matching. - */ - label: PropTypes.string, - /** - * @ignore - */ - role: PropTypes /* @typescript-to-proptypes-ignore */.string, - /** - * If `true`, the component is selected. - * @default false - */ - selected: PropTypes.bool, - /** - * 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 MenuItem; diff --git a/packages/mui-material-next/src/MenuItem/MenuItem.types.ts b/packages/mui-material-next/src/MenuItem/MenuItem.types.ts deleted file mode 100644 index 9adc9311704417..00000000000000 --- a/packages/mui-material-next/src/MenuItem/MenuItem.types.ts +++ /dev/null @@ -1,71 +0,0 @@ -import * as React from 'react'; -import { OverrideProps } from '@mui/types'; -import { SxProps } from '@mui/system'; -import { ExtendButtonBaseTypeMap } from '@mui/material/ButtonBase'; -import { Theme } from '@mui/material/styles'; -import { MenuItemClasses } from './menuItemClasses'; - -export interface MenuItemOwnProps { - /** - * If `true`, the list item is focused during the first mount. - * Focus will also be triggered if the value changes from false to true. - * @default false - */ - autoFocus?: boolean; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<MenuItemClasses>; - /** - * If `true`, compact vertical padding designed for keyboard and mouse input is used. - * The prop defaults to the value inherited from the parent Menu component. - * @default false - */ - dense?: boolean; - /** - * If `true`, the component is disabled. - * @default false - */ - disabled?: boolean; - /** - * If `true`, the left and right padding is removed. - * @default false - */ - disableGutters?: boolean; - /** - * If `true`, a 1px light border is added to the bottom of the menu item. - * @default false - */ - divider?: boolean; - /** - * A text representation of the menu item's content. - * Used for keyboard text navigation matching. - */ - label?: string; - /** - * If `true`, the component is selected. - * @default false - */ - selected?: boolean; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -export type MenuItemTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'li', -> = ExtendButtonBaseTypeMap<{ - props: AdditionalProps & MenuItemOwnProps; - defaultComponent: RootComponent; -}>; - -export type MenuItemProps< - RootComponent extends React.ElementType = MenuItemTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<MenuItemTypeMap<AdditionalProps, RootComponent>, RootComponent> & { - component?: React.ElementType; -}; - -export interface MenuItemOwnerState extends MenuItemProps {} diff --git a/packages/mui-material-next/src/MenuItem/index.ts b/packages/mui-material-next/src/MenuItem/index.ts deleted file mode 100644 index e182efe257b7f5..00000000000000 --- a/packages/mui-material-next/src/MenuItem/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -'use client'; -export { default } from './MenuItem'; -export * from './MenuItem'; - -export * from './MenuItem.types'; - -export * from './menuItemClasses'; -export { default as menuItemClasses } from './menuItemClasses'; diff --git a/packages/mui-material-next/src/MenuItem/menuItemClasses.ts b/packages/mui-material-next/src/MenuItem/menuItemClasses.ts deleted file mode 100644 index b4a3acd84088cc..00000000000000 --- a/packages/mui-material-next/src/MenuItem/menuItemClasses.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface MenuItemClasses { - /** Styles applied to the root element. */ - root: string; - /** State class applied to the root element if keyboard focused. */ - focusVisible: string; - /** Styles applied to the root element if dense. */ - dense: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; - /** Styles applied to the root element if `divider={true}`. */ - divider: string; - /** Styles applied to the inner `component` element unless `disableGutters={true}`. */ - gutters: string; - /** State class applied to the root element if `selected={true}`. */ - selected: string; -} - -export type MenuItemClassKey = keyof MenuItemClasses; - -export function getMenuItemUtilityClass(slot: string): string { - return generateUtilityClass('MuiMenuItem', slot); -} - -const menuItemClasses: MenuItemClasses = generateUtilityClasses('MuiMenuItem', [ - 'root', - 'focusVisible', - 'dense', - 'disabled', - 'divider', - 'gutters', - 'selected', -]); - -export default menuItemClasses; diff --git a/packages/mui-material-next/src/Option/Option.spec.tsx b/packages/mui-material-next/src/Option/Option.spec.tsx deleted file mode 100644 index 479f440f605670..00000000000000 --- a/packages/mui-material-next/src/Option/Option.spec.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import * as React from 'react'; -import { expectType } from '@mui/types'; -import Option, { OptionProps } from '@mui/material-next/Option'; -import Link from '@mui/material/Link'; - -const CustomComponent: React.FC<{ stringProp: string; numberProp: number }> = - function CustomComponent() { - return <div />; - }; - -const props1: OptionProps<'div'> = { - component: 'div', - onChange: (event) => { - expectType<React.FormEvent<HTMLDivElement>, typeof event>(event); - }, -}; - -const props2: OptionProps = { - onChange: (event) => { - expectType<React.FormEvent<HTMLLIElement>, typeof event>(event); - }, -}; - -const props3: OptionProps<typeof CustomComponent> = { - component: CustomComponent, - stringProp: '2', - numberProp: 2, -}; - -const props4: OptionProps<typeof CustomComponent> = { - component: CustomComponent, - stringProp: '2', - numberProp: 2, - // @ts-expect-error CustomComponent does not accept incorrectProp - incorrectProp: 3, -}; - -// @ts-expect-error missing props -const props5: OptionProps<typeof CustomComponent> = { - component: CustomComponent, -}; - -const TestComponent = () => { - return ( - <React.Fragment> - <Option /> - <Option component={'a'} href="/test" /> - - <Option component={CustomComponent} stringProp="s" numberProp={1} /> - { - // @ts-expect-error missing props - <Option component={CustomComponent} /> - } - <Option - onChange={(event) => { - expectType<React.FormEvent<HTMLLIElement>, typeof event>(event); - }} - /> - <Option - component="span" - onChange={(event) => { - expectType<React.FormEvent<HTMLSpanElement>, typeof event>(event); - }} - /> - <Option component={Link} /> - </React.Fragment> - ); -}; diff --git a/packages/mui-material-next/src/Option/Option.test.tsx b/packages/mui-material-next/src/Option/Option.test.tsx deleted file mode 100644 index 30db120b976d9d..00000000000000 --- a/packages/mui-material-next/src/Option/Option.test.tsx +++ /dev/null @@ -1,217 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { spy } from 'sinon'; -import { act, createRenderer, fireEvent, screen } from '@mui-internal/test-utils'; -import { MenuProvider } from '@mui/base/useMenu'; -import Option, { optionClasses as classes } from '@mui/material-next/Option'; -import Menu from '@mui/material-next/Menu'; -import ButtonBase from '@mui/material-next/ButtonBase'; -import describeConformance from '../../test/describeConformance'; - -const dummyGetItemState = () => ({ - disabled: false, - highlighted: false, - selected: false, - index: 0, - focusable: true, -}); - -const testContext = { - dispatch: () => {}, - getItemIndex: () => 0, - getItemProps: () => ({}), - getItemState: dummyGetItemState, - open: false, - registerHighlightChangeHandler: () => () => {}, - registerItem: () => ({ id: '', deregister: () => {} }), - registerSelectionChangeHandler: () => () => {}, - totalSubitemCount: 0, -}; - -describe('<Option />', () => { - const { render } = createRenderer({ clock: 'fake' }); - - // afterEach(() => { - // document.getElementsByTagName('html')[0].innerHTML = ''; - // }); - - describeConformance(<Option data-testid="option">1</Option>, () => ({ - render: (node) => { - return render(<MenuProvider value={testContext}>{node}</MenuProvider>); - }, - wrapMount: (mount) => (node) => mount(<MenuProvider value={testContext}>{node}</MenuProvider>), - classes, - inheritComponent: ButtonBase, - refInstanceof: window.HTMLLIElement, - testComponentPropWith: 'a', - muiName: 'MuiOption', - testVariantProps: { dense: true }, - skip: ['componentsProp', 'reactTestRenderer'], - })); - - const renderWithMenu = (node: React.ReactNode) => { - function Test() { - return ( - <Menu anchorEl={document.createElement('div')} open> - {node} - </Menu> - ); - } - - return render(<Test />); - }; - - it('should render a focusable option', () => { - renderWithMenu(<Option />); - const option = screen.getByRole('option'); - - expect(option).to.have.property('tabIndex', 0); - }); - - it('has a ripple when clicked', () => { - renderWithMenu(<Option TouchRippleProps={{ classes: { rippleVisible: 'ripple-visible' } }} />); - const option = screen.getByRole('option'); - - // ripple starts on mousedown - fireEvent.mouseDown(option); - - expect(option.querySelectorAll('.ripple-visible')).to.have.length(1); - }); - - it('should render with the selected class but not aria-selected when `selected`', () => { - renderWithMenu(<Option selected />); - const option = screen.getByRole('option'); - - expect(option).to.have.class(classes.selected); - expect(option).not.to.have.attribute('aria-selected'); - }); - - it('can have a role of option', () => { - renderWithMenu(<Option role="option" aria-selected={false} />); - - expect(screen.queryByRole('option')).not.to.equal(null); - }); - - describe('event callbacks', () => { - const events: Array<keyof typeof fireEvent> = [ - 'click', - 'mouseDown', - 'mouseEnter', - 'mouseLeave', - 'mouseUp', - 'touchEnd', - ]; - - events.forEach((eventName) => { - it(`should fire ${eventName}`, () => { - const handlerName = `on${eventName[0].toUpperCase()}${eventName.slice(1)}`; - const handler = spy(); - renderWithMenu(<Option {...{ [handlerName]: handler }} />); - - fireEvent[eventName](screen.getByRole('option')); - - expect(handler.callCount).to.equal(1); - }); - }); - - it(`should fire focus, keydown, keyup and blur`, () => { - const handleFocus = spy(); - const handleKeyDown = spy(); - const handleKeyUp = spy(); - const handleBlur = spy(); - renderWithMenu( - <Option - onFocus={handleFocus} - onKeyDown={handleKeyDown} - onKeyUp={handleKeyUp} - onBlur={handleBlur} - />, - ); - const option = screen.getByRole('option'); - - act(() => { - option.focus(); - }); - - expect(handleFocus.callCount).to.equal(1); - - fireEvent.keyDown(option); - - expect(handleKeyDown.callCount).to.equal(1); - - fireEvent.keyUp(option); - - expect(handleKeyUp.callCount).to.equal(1); - - expect(handleKeyDown.callCount).to.equal(1); - }); - - it('should fire onTouchStart', function touchStartTest() { - // only run in supported browsers - if (typeof Touch === 'undefined') { - this.skip(); - } - - const handleTouchStart = spy(); - renderWithMenu(<Option onTouchStart={handleTouchStart} />); - const option = screen.getByRole('option'); - - const touch = new Touch({ identifier: 0, target: option, clientX: 0, clientY: 0 }); - fireEvent.touchStart(option, { touches: [touch] }); - - expect(handleTouchStart.callCount).to.equal(1); - }); - }); - - it('can be disabled', () => { - renderWithMenu(<Option disabled />); - const option = screen.getByRole('option'); - - expect(option).to.have.attribute('aria-disabled', 'true'); - }); - - it('can be selected', () => { - renderWithMenu(<Option selected />); - const option = screen.getByRole('option'); - - expect(option).to.have.class(classes.selected); - }); - - it('prop: disableGutters', () => { - const { rerender } = render( - <Menu anchorEl={document.createElement('div')} open> - <Option /> - </Menu>, - ); - const option = screen.getByRole('option'); - - expect(option).to.have.class(classes.gutters); - - rerender( - <Menu anchorEl={document.createElement('div')} open> - <Option disableGutters /> - </Menu>, - ); - - expect(option).not.to.have.class(classes.gutters); - }); - - // TODO v6: Need to be re-structured now that we don't use the List component internally - // describe('context: dense', () => { - // it.skip('should forward the context', () => { - // let context = null; - // const { setProps } = render( - // <Option> - // <ListContext.Consumer> - // {(options) => { - // context = options; - // }} - // </ListContext.Consumer> - // </Option>, - // ); - // expect(context).to.have.property('dense', false); - // setProps({ dense: true }); - // expect(context).to.have.property('dense', true); - // }); - // }); -}); diff --git a/packages/mui-material-next/src/Option/Option.tsx b/packages/mui-material-next/src/Option/Option.tsx deleted file mode 100644 index e6217ffe012b81..00000000000000 --- a/packages/mui-material-next/src/Option/Option.tsx +++ /dev/null @@ -1,321 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { OverridableComponent } from '@mui/types'; -import { alpha } from '@mui/system'; -import { - unstable_useEnhancedEffect as useEnhancedEffect, - unstable_useForkRef as useForkRef, -} from '@mui/utils'; -import { useSlotProps } from '@mui/base/utils'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { useMenuItem } from '@mui/base/useMenuItem'; -// TODO v6: Replace with @mui/material-next when the List components are available -import ListContext from '@mui/material/List/ListContext'; -import { listItemIconClasses } from '@mui/material/ListItemIcon'; -import { listItemTextClasses } from '@mui/material/ListItemText'; -import { styled, useThemeProps, rootShouldForwardProp } from '../styles'; -import ButtonBase from '../ButtonBase'; -import { dividerClasses } from '../Divider'; -import { OptionProps, OptionOwnerState, OptionTypeMap } from './Option.types'; -import optionClasses, { getOptionUtilityClass } from './optionClasses'; - -const overridesResolver = (props: OptionProps & { ownerState: OptionOwnerState }, styles: any) => { - const { ownerState } = props; - - return [ - styles.root, - ownerState.dense && styles.dense, - ownerState.divider && styles.divider, - !ownerState.disableGutters && styles.gutters, - ]; -}; - -const useUtilityClasses = (ownerState: OptionOwnerState) => { - const { disabled, dense, divider, disableGutters, selected, classes } = ownerState; - const slots = { - root: [ - 'root', - dense && 'dense', - disabled && 'disabled', - !disableGutters && 'gutters', - divider && 'divider', - selected && 'selected', - ], - }; - - const composedClasses = composeClasses(slots, getOptionUtilityClass, classes); - - return { - ...classes, - ...composedClasses, - }; -}; - -const OptionRoot = styled(ButtonBase, { - shouldForwardProp: (prop: string) => rootShouldForwardProp(prop) || prop === 'classes', - name: 'MuiOption', - slot: 'Root', - overridesResolver, -})<{ ownerState: OptionOwnerState }>(({ theme, ownerState }) => ({ - ...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', - ...(!ownerState.disableGutters && { - paddingLeft: 16, - paddingRight: 16, - }), - ...(ownerState.divider && { - borderBottom: `1px solid ${(theme.vars || theme).palette.divider}`, - backgroundClip: 'padding-box', - }), - '&:hover': { - textDecoration: 'none', - backgroundColor: (theme.vars || theme).palette.action.hover, - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - }, - [`&.${optionClasses.selected}`]: { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})` - : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity), - [`&.${optionClasses.focusVisible}`]: { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.focusOpacity}))` - : alpha( - theme.palette.primary.main, - theme.palette.action.selectedOpacity + theme.palette.action.focusOpacity, - ), - }, - }, - [`&.${optionClasses.selected}:hover`]: { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.hoverOpacity}))` - : alpha( - theme.palette.primary.main, - theme.palette.action.selectedOpacity + theme.palette.action.hoverOpacity, - ), - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})` - : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity), - }, - }, - [`&.${optionClasses.focusVisible}`]: { - backgroundColor: (theme.vars || theme).palette.action.focus, - }, - [`&.${optionClasses.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, - }, - ...(!ownerState.dense && { - [theme.breakpoints.up('sm')]: { - minHeight: 'auto', - }, - }), - ...(ownerState.dense && { - minHeight: 32, // https://m2.material.io/components/menus#specs > Dense - paddingTop: 4, - paddingBottom: 4, - ...theme.typography.body2, - [`& .${listItemIconClasses.root} svg`]: { - fontSize: '1.25rem', - }, - }), -})); - -const Option = React.forwardRef(function Option<RootComponentType extends React.ElementType>( - inProps: OptionProps<RootComponentType>, - ref: React.ForwardedRef<Element>, -) { - const props = useThemeProps({ props: inProps, name: 'MuiOption' }); - const { - autoFocus = false, - component = 'li', - dense = false, - divider = false, - disableGutters = false, - focusVisibleClassName, - role = 'option', - className, - disabled: disabledProp, - label: labelProp, - ...other - } = props; - - const context = React.useContext(ListContext); - const childContext = React.useMemo( - () => ({ - dense: dense || context.dense || false, - disableGutters, - }), - [context.dense, dense, disableGutters], - ); - - const optionRef = React.useRef<HTMLElement>(null); - const handleRef = useForkRef(optionRef, ref); - - const { getRootProps, disabled, focusVisible, highlighted } = useMenuItem({ - disabled: disabledProp, - rootRef: handleRef, - label: labelProp, - }); - - useEnhancedEffect(() => { - if (autoFocus) { - if (optionRef.current) { - optionRef.current.focus(); - } else if (process.env.NODE_ENV !== 'production') { - console.error( - 'MUI: Unable to set focus to an Option whose component has not been rendered.', - ); - } - } - }, [autoFocus]); - - const ownerState = { - ...props, - dense: childContext.dense, - divider, - disableGutters, - disabled, - focusVisible, - highlighted, - }; - - const classes = useUtilityClasses(props); - - const Root = /* slots.root ?? */ OptionRoot; - const rootProps = useSlotProps({ - elementType: Root, - getSlotProps: getRootProps, - // TODO v6: Add support for slotProps.root - externalSlotProps: {}, - externalForwardedProps: other, - additionalProps: { - role, - component, - focusVisibleClassName: clsx(classes.focusVisible, focusVisibleClassName), - classes, - }, - className: clsx(classes.root, className), - ownerState, - }); - return ( - <ListContext.Provider value={childContext}> - <OptionRoot {...rootProps} /> - </ListContext.Provider> - ); -}) as OverridableComponent<OptionTypeMap>; - -Option.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * If `true`, the list item is focused during the first mount. - * Focus will also be triggered if the value changes from false to true. - * @default false - */ - autoFocus: PropTypes.bool, - /** - * The content of the component. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * If `true`, compact vertical padding designed for keyboard and mouse input is used. - * The prop defaults to the value inherited from the parent Select component. - * @default false - */ - dense: PropTypes.bool, - /** - * If `true`, the component is disabled. - * @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 option. - * @default false - */ - divider: PropTypes.bool, - /** - * This prop can help identify which element has keyboard focus. - * The class name will be applied when the element gains the focus through keyboard interaction. - * It's a polyfill for the [CSS :focus-visible selector](https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo). - * The rationale for using this feature [is explained here](https://github.com/WICG/focus-visible/blob/HEAD/explainer.md). - * A [polyfill can be used](https://github.com/WICG/focus-visible) to apply a `focus-visible` class to other components - * if needed. - */ - focusVisibleClassName: PropTypes.string, - /** - * A text representation of the option's content. - * Used for keyboard text navigation matching. - */ - label: PropTypes.string, - /** - * @ignore - */ - role: PropTypes /* @typescript-to-proptypes-ignore */.string, - /** - * If `true`, the component is selected. - * @default false - */ - selected: PropTypes.bool, - /** - * 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 Option; diff --git a/packages/mui-material-next/src/Option/Option.types.ts b/packages/mui-material-next/src/Option/Option.types.ts deleted file mode 100644 index 8d5ea7c7bffabd..00000000000000 --- a/packages/mui-material-next/src/Option/Option.types.ts +++ /dev/null @@ -1,71 +0,0 @@ -import * as React from 'react'; -import { OverrideProps } from '@mui/types'; -import { SxProps } from '@mui/system'; -import { ExtendButtonBaseTypeMap } from '@mui/material/ButtonBase'; -import { Theme } from '@mui/material/styles'; -import { OptionClasses } from './optionClasses'; - -export interface OptionOwnProps { - /** - * If `true`, the list item is focused during the first mount. - * Focus will also be triggered if the value changes from false to true. - * @default false - */ - autoFocus?: boolean; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<OptionClasses>; - /** - * If `true`, compact vertical padding designed for keyboard and mouse input is used. - * The prop defaults to the value inherited from the parent Select component. - * @default false - */ - dense?: boolean; - /** - * If `true`, the component is disabled. - * @default false - */ - disabled?: boolean; - /** - * If `true`, the left and right padding is removed. - * @default false - */ - disableGutters?: boolean; - /** - * If `true`, a 1px light border is added to the bottom of the option. - * @default false - */ - divider?: boolean; - /** - * A text representation of the option's content. - * Used for keyboard text navigation matching. - */ - label?: string; - /** - * If `true`, the component is selected. - * @default false - */ - selected?: boolean; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -export type OptionTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'li', -> = ExtendButtonBaseTypeMap<{ - props: AdditionalProps & OptionOwnProps; - defaultComponent: RootComponent; -}>; - -export type OptionProps< - RootComponent extends React.ElementType = OptionTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<OptionTypeMap<AdditionalProps, RootComponent>, RootComponent> & { - component?: React.ElementType; -}; - -export interface OptionOwnerState extends OptionProps {} diff --git a/packages/mui-material-next/src/Option/index.ts b/packages/mui-material-next/src/Option/index.ts deleted file mode 100644 index 2cad5ee88dcd5d..00000000000000 --- a/packages/mui-material-next/src/Option/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -'use client'; -export { default } from './Option'; - -export * from './Option.types'; - -export * from './optionClasses'; -export { default as optionClasses } from './optionClasses'; diff --git a/packages/mui-material-next/src/Option/optionClasses.ts b/packages/mui-material-next/src/Option/optionClasses.ts deleted file mode 100644 index 6b72b86f280b41..00000000000000 --- a/packages/mui-material-next/src/Option/optionClasses.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface OptionClasses { - /** Styles applied to the root element. */ - root: string; - /** State class applied to the root element if keyboard focused. */ - focusVisible: string; - /** Styles applied to the root element if dense. */ - dense: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; - /** Styles applied to the root element if `divider={true}`. */ - divider: string; - /** Styles applied to the inner `component` element unless `disableGutters={true}`. */ - gutters: string; - /** State class applied to the root element if `selected={true}`. */ - selected: string; -} - -export type OptionClassKey = keyof OptionClasses; - -export function getOptionUtilityClass(slot: string): string { - return generateUtilityClass('MuiOption', slot); -} - -const optionClasses: OptionClasses = generateUtilityClasses('MuiOption', [ - 'root', - 'focusVisible', - 'dense', - 'disabled', - 'divider', - 'gutters', - 'selected', -]); - -export default optionClasses; diff --git a/packages/mui-material-next/src/OutlinedInput/NotchedOutline.d.ts b/packages/mui-material-next/src/OutlinedInput/NotchedOutline.d.ts deleted file mode 100644 index 3d571c00c8259f..00000000000000 --- a/packages/mui-material-next/src/OutlinedInput/NotchedOutline.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as React from 'react'; -// TODO v6: port to material-next -import { InternalStandardProps as StandardProps } from '@mui/material'; - -export interface NotchedOutlineProps - extends StandardProps<React.FieldsetHTMLAttributes<HTMLFieldSetElement>> { - disabled?: boolean; - error?: boolean; - focused?: boolean; - label?: React.ReactNode; - notched: boolean; -} - -export type NotchedOutlineClassKey = keyof NonNullable<NotchedOutlineProps['classes']>; - -declare const NotchedOutline: React.JSXElementConstructor<NotchedOutlineProps>; - -export default NotchedOutline; diff --git a/packages/mui-material-next/src/OutlinedInput/NotchedOutline.js b/packages/mui-material-next/src/OutlinedInput/NotchedOutline.js deleted file mode 100644 index 58223da4479c8e..00000000000000 --- a/packages/mui-material-next/src/OutlinedInput/NotchedOutline.js +++ /dev/null @@ -1,118 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import styled, { rootShouldForwardProp } from '../styles/styled'; - -const NotchedOutlineRoot = styled('fieldset', { shouldForwardProp: rootShouldForwardProp })({ - textAlign: 'left', - position: 'absolute', - bottom: 0, - right: 0, - top: -5, - left: 0, - margin: 0, - padding: '0 8px', - pointerEvents: 'none', - borderRadius: 'inherit', - borderStyle: 'solid', - borderWidth: 1, - overflow: 'hidden', - minWidth: '0%', -}); - -const NotchedOutlineLegend = styled('legend', { shouldForwardProp: rootShouldForwardProp })( - ({ ownerState, theme }) => ({ - float: 'unset', // Fix conflict with bootstrap - width: 'auto', // Fix conflict with bootstrap - overflow: 'hidden', // Fix Horizontal scroll when label too long - ...(!ownerState.withLabel && { - padding: 0, - lineHeight: '11px', // sync with `height` in `legend` styles - transition: theme.transitions.create('width', { - duration: 150, - easing: theme.transitions.easing.easeOut, - }), - }), - ...(ownerState.withLabel && { - display: 'block', // Fix conflict with normalize.css and sanitize.css - padding: 0, - height: 11, // sync with `lineHeight` in `legend` styles - fontSize: '0.75em', - visibility: 'hidden', - maxWidth: 0.01, - transition: theme.transitions.create('max-width', { - duration: 50, - easing: theme.transitions.easing.easeOut, - }), - whiteSpace: 'nowrap', - '& > span': { - paddingLeft: 5, - paddingRight: 5, - display: 'inline-block', - opacity: 0, - visibility: 'visible', - }, - ...(ownerState.notched && { - maxWidth: '100%', - transition: theme.transitions.create('max-width', { - duration: 100, - easing: theme.transitions.easing.easeOut, - delay: 50, - }), - }), - }), - }), -); - -/** - * @ignore - internal component. - */ -export default function NotchedOutline(props) { - const { children, classes, className, label, notched, ...other } = props; - const withLabel = label != null && label !== ''; - const ownerState = { - ...props, - notched, - withLabel, - }; - return ( - <NotchedOutlineRoot aria-hidden className={className} ownerState={ownerState} {...other}> - <NotchedOutlineLegend ownerState={ownerState}> - {/* Use the nominal use case of the legend, avoid rendering artefacts. */} - {withLabel ? ( - <span>{label}</span> - ) : ( - // notranslate needed while Google Translate will not fix zero-width space issue - <span className="notranslate">​</span> - )} - </NotchedOutlineLegend> - </NotchedOutlineRoot> - ); -} - -NotchedOutline.propTypes = { - /** - * The content of the component. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The label. - */ - label: PropTypes.node, - /** - * If `true`, the outline is notched to accommodate the label. - */ - notched: PropTypes.bool.isRequired, - /** - * @ignore - */ - style: PropTypes.object, -}; diff --git a/packages/mui-material-next/src/OutlinedInput/NotchedOutline.test.js b/packages/mui-material-next/src/OutlinedInput/NotchedOutline.test.js deleted file mode 100644 index b27d8e928cbe75..00000000000000 --- a/packages/mui-material-next/src/OutlinedInput/NotchedOutline.test.js +++ /dev/null @@ -1,68 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; -import NotchedOutline from './NotchedOutline'; - -describe('<NotchedOutline />', () => { - const { render } = createRenderer(); - - const defaultProps = { - notched: true, - label: 'My label', - }; - - it('should pass props', () => { - const { container } = render( - <NotchedOutline - {...defaultProps} - className="notched-outline" - style={{ - width: 17, - }} - />, - ); - - expect(container.querySelector('fieldset')).to.have.class('notched-outline'); - expect(container.querySelector('fieldset').style.width).to.equal('17px'); - }); - - it('should set alignment rtl', () => { - const { container: container1 } = render( - <ThemeProvider - theme={createTheme({ - direction: 'ltr', - })} - > - <NotchedOutline {...defaultProps} /> - </ThemeProvider>, - ); - expect(container1.querySelector('fieldset')).toHaveComputedStyle({ - paddingLeft: '8px', - }); - - const { container: container2 } = render( - <ThemeProvider - theme={createTheme({ - direction: 'rtl', - })} - > - <NotchedOutline {...defaultProps} /> - </ThemeProvider>, - ); - expect(container2.querySelector('fieldset')).toHaveComputedStyle({ - paddingRight: '8px', - }); - }); - - it('should not set padding (notch) for empty, null or undefined label props', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - const spanStyle = { paddingLeft: '0px', paddingRight: '0px' }; - ['', undefined, null].forEach((prop) => { - const { container: container1 } = render(<NotchedOutline {...defaultProps} label={prop} />); - expect(container1.querySelector('span')).toHaveComputedStyle(spanStyle); - }); - }); -}); diff --git a/packages/mui-material-next/src/OutlinedInput/OutlinedInput.d.ts b/packages/mui-material-next/src/OutlinedInput/OutlinedInput.d.ts deleted file mode 100644 index 64e42f02211288..00000000000000 --- a/packages/mui-material-next/src/OutlinedInput/OutlinedInput.d.ts +++ /dev/null @@ -1,41 +0,0 @@ -import * as React from 'react'; -import { SxProps, Theme } from '@mui/system'; -// TODO v6: port to material-next -import { InternalStandardProps as StandardProps } from '@mui/material'; -import { InputBaseProps } from '@mui/material/InputBase'; -import { OutlinedInputClasses } from './outlinedInputClasses'; - -export interface OutlinedInputProps extends StandardProps<InputBaseProps> { - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<OutlinedInputClasses>; - /** - * The label of the `input`. It is only used for layout. The actual labelling - * is handled by `InputLabel`. - */ - label?: React.ReactNode; - /** - * If `true`, the outline is notched to accommodate the label. - */ - notched?: boolean; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -/** - * - * Demos: - * - * - [Text Field](https://mui.com/material-ui/react-text-field/) - * - * API: - * - * - [OutlinedInput API](https://mui.com/material-ui/api/outlined-input/) - * - inherits [InputBase API](https://mui.com/material-ui/api/input-base/) - */ -declare const OutlinedInput: ((props: OutlinedInputProps) => JSX.Element) & { muiName: string }; - -export default OutlinedInput; diff --git a/packages/mui-material-next/src/OutlinedInput/OutlinedInput.js b/packages/mui-material-next/src/OutlinedInput/OutlinedInput.js deleted file mode 100644 index 0f9c57d50b9506..00000000000000 --- a/packages/mui-material-next/src/OutlinedInput/OutlinedInput.js +++ /dev/null @@ -1,414 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { refType } from '@mui/utils'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import NotchedOutline from './NotchedOutline'; -import useFormControl from '../FormControl/useFormControl'; -import formControlState from '../FormControl/formControlState'; -import styled, { rootShouldForwardProp } from '../styles/styled'; -import outlinedInputClasses, { getOutlinedInputUtilityClass } from './outlinedInputClasses'; -import InputBase, { - rootOverridesResolver as inputBaseRootOverridesResolver, - inputOverridesResolver as inputBaseInputOverridesResolver, - InputBaseRoot, - InputBaseInput, -} from '../InputBase/InputBase'; -import useThemeProps from '../styles/useThemeProps'; - -const useUtilityClasses = (ownerState) => { - const { classes } = ownerState; - - const slots = { - root: ['root'], - notchedOutline: ['notchedOutline'], - input: ['input'], - }; - - const composedClasses = composeClasses(slots, getOutlinedInputUtilityClass, classes); - - return { - ...classes, // forward classes to the InputBase - ...composedClasses, - }; -}; - -const OutlinedInputRoot = styled(InputBaseRoot, { - shouldForwardProp: (prop) => rootShouldForwardProp(prop) || prop === 'classes', - name: 'MuiOutlinedInput', - slot: 'Root', - overridesResolver: inputBaseRootOverridesResolver, -})(({ theme, ownerState }) => { - const borderColor = - theme.palette.mode === 'light' ? 'rgba(0, 0, 0, 0.23)' : 'rgba(255, 255, 255, 0.23)'; - return { - position: 'relative', - borderRadius: (theme.vars || theme).shape.borderRadius, - [`&:hover .${outlinedInputClasses.notchedOutline}`]: { - borderColor: (theme.vars || theme).palette.text.primary, - }, - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - [`&:hover .${outlinedInputClasses.notchedOutline}`]: { - borderColor: theme.vars - ? `rgba(${theme.vars.palette.common.onBackgroundChannel} / 0.23)` - : borderColor, - }, - }, - [`&.${outlinedInputClasses.focused} .${outlinedInputClasses.notchedOutline}`]: { - borderColor: (theme.vars || theme).palette[ownerState.color].main, - borderWidth: 2, - }, - [`&.${outlinedInputClasses.error} .${outlinedInputClasses.notchedOutline}`]: { - borderColor: (theme.vars || theme).palette.error.main, - }, - [`&.${outlinedInputClasses.disabled} .${outlinedInputClasses.notchedOutline}`]: { - borderColor: (theme.vars || theme).palette.action.disabled, - }, - ...(ownerState.startAdornment && { - paddingLeft: 14, - }), - ...(ownerState.endAdornment && { - paddingRight: 14, - }), - ...(ownerState.multiline && { - padding: '16.5px 14px', - ...(ownerState.size === 'small' && { - padding: '8.5px 14px', - }), - }), - }; -}); - -const NotchedOutlineRoot = styled(NotchedOutline, { - name: 'MuiOutlinedInput', - slot: 'NotchedOutline', - overridesResolver: (props, styles) => styles.notchedOutline, -})(({ theme }) => { - const borderColor = - theme.palette.mode === 'light' ? 'rgba(0, 0, 0, 0.23)' : 'rgba(255, 255, 255, 0.23)'; - return { - borderColor: theme.vars - ? `rgba(${theme.vars.palette.common.onBackgroundChannel} / 0.23)` - : borderColor, - }; -}); - -const OutlinedInputInput = styled(InputBaseInput, { - name: 'MuiOutlinedInput', - slot: 'Input', - overridesResolver: inputBaseInputOverridesResolver, -})(({ theme, ownerState }) => ({ - padding: '16.5px 14px', - ...(!theme.vars && { - '&:-webkit-autofill': { - WebkitBoxShadow: theme.palette.mode === 'light' ? null : '0 0 0 100px #266798 inset', - WebkitTextFillColor: theme.palette.mode === 'light' ? null : '#fff', - caretColor: theme.palette.mode === 'light' ? null : '#fff', - borderRadius: 'inherit', - }, - }), - ...(theme.vars && { - '&:-webkit-autofill': { - borderRadius: 'inherit', - }, - [theme.getColorSchemeSelector('dark')]: { - '&:-webkit-autofill': { - WebkitBoxShadow: '0 0 0 100px #266798 inset', - WebkitTextFillColor: '#fff', - caretColor: '#fff', - }, - }, - }), - ...(ownerState.size === 'small' && { - padding: '8.5px 14px', - }), - ...(ownerState.multiline && { - padding: 0, - }), - ...(ownerState.startAdornment && { - paddingLeft: 0, - }), - ...(ownerState.endAdornment && { - paddingRight: 0, - }), -})); - -const OutlinedInput = React.forwardRef(function OutlinedInput(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiOutlinedInput' }); - const { - components = {}, - fullWidth = false, - inputComponent = 'input', - label, - multiline = false, - notched, - slots = {}, - type = 'text', - ...other - } = props; - - const classes = useUtilityClasses(props); - - const muiFormControl = useFormControl(); - const fcs = formControlState({ - props, - muiFormControl, - states: ['color', 'disabled', 'error', 'focused', 'hiddenLabel', 'size', 'required'], - }); - - const ownerState = { - ...props, - color: fcs.color || 'primary', - disabled: fcs.disabled, - error: fcs.error, - focused: fcs.focused, - formControl: muiFormControl, - fullWidth, - hiddenLabel: fcs.hiddenLabel, - multiline, - size: fcs.size, - type, - }; - - const RootSlot = slots.root ?? components.Root ?? OutlinedInputRoot; - const InputSlot = slots.input ?? components.Input ?? OutlinedInputInput; - - return ( - <InputBase - slots={{ root: RootSlot, input: InputSlot }} - renderSuffix={(state) => ( - <NotchedOutlineRoot - ownerState={ownerState} - className={classes.notchedOutline} - label={ - label != null && label !== '' && fcs.required ? ( - <React.Fragment> - {label} -  {'*'} - </React.Fragment> - ) : ( - label - ) - } - notched={ - typeof notched !== 'undefined' - ? notched - : Boolean(state.startAdornment || state.filled || state.focused) - } - /> - )} - fullWidth={fullWidth} - inputComponent={inputComponent} - multiline={multiline} - ref={ref} - type={type} - {...other} - classes={{ - ...classes, - notchedOutline: null, - }} - /> - ); -}); - -OutlinedInput.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * This prop helps users to fill forms faster, especially on mobile devices. - * The name can be confusing, as it's more like an autofill. - * You can learn more about it [following the specification](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill). - */ - autoComplete: PropTypes.string, - /** - * If `true`, the `input` element is focused during the first mount. - */ - autoFocus: PropTypes.bool, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * The prop defaults to the value (`'primary'`) inherited from the parent FormControl component. - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['primary', 'secondary']), - PropTypes.string, - ]), - /** - * The components used for each slot inside. - * - * This prop is an alias for the `slots` prop. - * It's recommended to use the `slots` prop instead. - * - * @default {} - */ - components: PropTypes.shape({ - Input: PropTypes.elementType, - Root: PropTypes.elementType, - }), - /** - * The default value. Use when the component is not controlled. - */ - defaultValue: PropTypes.any, - /** - * If `true`, the component is disabled. - * The prop defaults to the value (`false`) inherited from the parent FormControl component. - */ - disabled: PropTypes.bool, - /** - * End `InputAdornment` for this component. - */ - endAdornment: PropTypes.node, - /** - * If `true`, the `input` will indicate an error. - * The prop defaults to the value (`false`) inherited from the parent FormControl component. - */ - error: PropTypes.bool, - /** - * If `true`, the `input` will take up the full width of its container. - * @default false - */ - fullWidth: PropTypes.bool, - /** - * The id of the `input` element. - */ - id: PropTypes.string, - /** - * The component used for the `input` element. - * Either a string to use a HTML element or a component. - * @default 'input' - */ - inputComponent: PropTypes.elementType, - /** - * [Attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Attributes) applied to the `input` element. - * @default {} - */ - inputProps: PropTypes.object, - /** - * Pass a ref to the `input` element. - */ - inputRef: refType, - /** - * The label of the `input`. It is only used for layout. The actual labelling - * is handled by `InputLabel`. - */ - label: PropTypes.node, - /** - * If `dense`, will adjust vertical spacing. This is normally obtained via context from - * FormControl. - * The prop defaults to the value (`'none'`) inherited from the parent FormControl component. - */ - margin: PropTypes.oneOf(['dense', 'none']), - /** - * Maximum number of rows to display when multiline option is set to true. - */ - maxRows: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - /** - * Minimum number of rows to display when multiline option is set to true. - */ - minRows: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - /** - * If `true`, a [TextareaAutosize](/material-ui/react-textarea-autosize/) element is rendered. - * @default false - */ - multiline: PropTypes.bool, - /** - * Name attribute of the `input` element. - */ - name: PropTypes.string, - /** - * If `true`, the outline is notched to accommodate the label. - */ - notched: PropTypes.bool, - /** - * Callback fired when the value is changed. - * - * @param {React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>} event The event source of the callback. - * You can pull out the new value by accessing `event.target.value` (string). - */ - onChange: PropTypes.func, - /** - * The short hint displayed in the `input` before the user enters a value. - */ - placeholder: PropTypes.string, - /** - * It prevents the user from changing the value of the field - * (not from interacting with the field). - */ - readOnly: PropTypes.bool, - /** - * If `true`, the `input` element is required. - * The prop defaults to the value (`false`) inherited from the parent FormControl component. - */ - required: PropTypes.bool, - /** - * Number of rows to display when multiline option is set to true. - */ - rows: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - /** - * The components used for each slot inside. - * - * This prop is an alias for the `components` prop, which will be deprecated in the future. - * - * @default {} - */ - slots: PropTypes.shape({ - input: PropTypes.elementType, - root: PropTypes.elementType, - }), - /** - * Start `InputAdornment` for this component. - */ - startAdornment: PropTypes.node, - /** - * 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, - ]), - /** - * Type of the `input` element. It should be [a valid HTML5 input type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Form_%3Cinput%3E_types). - * @default 'text' - */ - type: PropTypes /* @typescript-to-proptypes-ignore */.oneOf([ - 'button', - 'checkbox', - 'color', - 'date', - 'datetime-local', - 'email', - 'file', - 'hidden', - 'image', - 'month', - 'number', - 'password', - 'radio', - 'range', - 'reset', - 'search', - 'submit', - 'tel', - 'text', - 'time', - 'url', - 'week', - ]), - /** - * The value of the `input` element, required for a controlled component. - */ - value: PropTypes.any, -}; - -OutlinedInput.muiName = 'Input'; - -export default OutlinedInput; diff --git a/packages/mui-material-next/src/OutlinedInput/OutlinedInput.test.js b/packages/mui-material-next/src/OutlinedInput/OutlinedInput.test.js deleted file mode 100644 index b45720d6055341..00000000000000 --- a/packages/mui-material-next/src/OutlinedInput/OutlinedInput.test.js +++ /dev/null @@ -1,97 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; -import OutlinedInput, { outlinedInputClasses as classes } from '@mui/material/OutlinedInput'; -import InputBase from '@mui/material/InputBase'; -import describeConformance from '../../test/describeConformance'; - -describe('<OutlinedInput />', () => { - const { render } = createRenderer(); - - describeConformance(<OutlinedInput />, () => ({ - classes, - inheritComponent: InputBase, - render, - refInstanceof: window.HTMLDivElement, - muiName: 'MuiOutlinedInput', - testDeepOverrides: { slotName: 'input', slotClassName: classes.input }, - testVariantProps: { variant: 'contained', fullWidth: true }, - testStateOverrides: { prop: 'size', value: 'small', styleKey: 'sizeSmall' }, - testLegacyComponentsProp: true, - slots: { - // can't test with DOM element as InputBase places an ownerState prop on it unconditionally. - root: { expectedClassName: classes.root, testWithElement: null }, - input: { expectedClassName: classes.input, testWithElement: null }, - }, - skip: [ - 'componentProp', - 'componentsProp', - 'slotPropsCallback', // not supported yet - ], - })); - - it('should render a NotchedOutline', () => { - const { container } = render( - <OutlinedInput classes={{ notchedOutline: 'notched-outlined' }} />, - ); - - expect(container.querySelector('.notched-outlined')).not.to.equal(null); - }); - - it('should set correct label prop on outline', () => { - const { container } = render( - <OutlinedInput - classes={{ notchedOutline: 'notched-outlined' }} - label={<div data-testid="label">label</div>} - required - />, - ); - const notchOutlined = container.querySelector('.notched-outlined legend'); - expect(notchOutlined).to.have.text('label\u2009*'); - }); - - it('should forward classes to InputBase', () => { - render(<OutlinedInput error classes={{ error: 'error' }} />); - expect(document.querySelector('.error')).not.to.equal(null); - }); - - it('should respects the componentsProps if passed', () => { - render(<OutlinedInput componentsProps={{ root: { 'data-test': 'test' } }} />); - expect(document.querySelector('[data-test=test]')).not.to.equal(null); - }); - - it('should respect the classes coming from InputBase', () => { - render( - <OutlinedInput - data-test="test" - multiline - sx={{ [`&.${classes.multiline}`]: { mt: '10px' } }} - />, - ); - expect(document.querySelector('[data-test=test]')).toHaveComputedStyle({ marginTop: '10px' }); - }); - - it('should have ownerState in the theme style overrides', () => { - expect(() => - render( - <ThemeProvider - theme={createTheme({ - components: { - MuiOutlinedInput: { - styleOverrides: { - root: ({ ownerState }) => ({ - // test that ownerState is not undefined - ...(ownerState.disabled && {}), - }), - }, - }, - }, - })} - > - <OutlinedInput /> - </ThemeProvider>, - ), - ).not.to.throw(); - }); -}); diff --git a/packages/mui-material-next/src/OutlinedInput/index.d.ts b/packages/mui-material-next/src/OutlinedInput/index.d.ts deleted file mode 100644 index 70e67f7c67f200..00000000000000 --- a/packages/mui-material-next/src/OutlinedInput/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './OutlinedInput'; -export * from './OutlinedInput'; - -export { default as outlinedInputClasses } from './outlinedInputClasses'; -export * from './outlinedInputClasses'; diff --git a/packages/mui-material-next/src/OutlinedInput/index.js b/packages/mui-material-next/src/OutlinedInput/index.js deleted file mode 100644 index ba19d9b1b14008..00000000000000 --- a/packages/mui-material-next/src/OutlinedInput/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './OutlinedInput'; - -export { default as outlinedInputClasses } from './outlinedInputClasses'; -export * from './outlinedInputClasses'; diff --git a/packages/mui-material-next/src/OutlinedInput/outlinedInputClasses.ts b/packages/mui-material-next/src/OutlinedInput/outlinedInputClasses.ts deleted file mode 100644 index d9001c541ecf85..00000000000000 --- a/packages/mui-material-next/src/OutlinedInput/outlinedInputClasses.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { - unstable_generateUtilityClass as generateUtilityClass, - unstable_generateUtilityClasses as generateUtilityClasses, -} from '@mui/utils'; -import { inputBaseClasses } from '../InputBase'; - -export interface OutlinedInputClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if the color is secondary. */ - colorSecondary: string; - /** Styles applied to the root element if the component is focused. */ - focused: string; - /** Styles applied to the root element if `disabled={true}`. */ - disabled: string; - /** Styles applied to the root element if `startAdornment` is provided. */ - adornedStart: string; - /** Styles applied to the root element if `endAdornment` is provided. */ - adornedEnd: string; - /** State class applied to the root element if `error={true}`. */ - error: string; - /** Styles applied to the input element if `size="small"`. */ - sizeSmall: string; - /** Styles applied to the root element if `multiline={true}`. */ - multiline: string; - /** Styles applied to the NotchedOutline element. */ - notchedOutline: string; - /** Styles applied to the input element. */ - input: string; - /** Styles applied to the input element if `size="small"`. */ - inputSizeSmall: string; - /** Styles applied to the input element if `multiline={true}`. */ - inputMultiline: string; - /** Styles applied to the input element if `startAdornment` is provided. */ - inputAdornedStart: string; - /** Styles applied to the input element if `endAdornment` is provided. */ - inputAdornedEnd: string; - /** Styles applied to the input element if `type="search"`. */ - inputTypeSearch: string; -} - -export type OutlinedInputClassKey = keyof OutlinedInputClasses; - -export function getOutlinedInputUtilityClass(slot: string): string { - return generateUtilityClass('MuiOutlinedInput', slot); -} - -const outlinedInputClasses: OutlinedInputClasses = { - ...inputBaseClasses, - ...generateUtilityClasses('MuiOutlinedInput', ['root', 'notchedOutline', 'input']), -}; - -export default outlinedInputClasses; diff --git a/packages/mui-material-next/src/Select/Select.d.ts b/packages/mui-material-next/src/Select/Select.d.ts deleted file mode 100644 index d562bab14eaeba..00000000000000 --- a/packages/mui-material-next/src/Select/Select.d.ts +++ /dev/null @@ -1,173 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -// TODO v6: replace material Theme with material-next Theme when Material Design 3 is implemented -import { InternalStandardProps as StandardProps, Theme } from '@mui/material'; -// TODO v6: replace with material-next Input components props https://github.com/mui/material-ui/pull/39188#discussion_r1339645381 -import { InputProps } from '@mui/material/Input'; -// TODO v6: replace with material-next Menu https://github.com/mui/material-ui/pull/38934 -import { MenuProps } from '@mui/material/Menu'; -// TODO v6: replace with material-next OutlinedInput when available -import { OutlinedInputProps } from '@mui/material/OutlinedInput'; -import { SelectChangeEvent, SelectInputProps } from './SelectInput'; -import { SelectClasses } from './selectClasses'; - -export { SelectChangeEvent }; - -export interface SelectProps<Value = unknown> - extends StandardProps<InputProps, 'value' | 'onChange'>, - Omit<OutlinedInputProps, 'value' | 'onChange'> { - /** - * If `true`, the width of the popover will automatically be set according to the items inside the - * menu, otherwise it will be at least the width of the select input. - * @default false - */ - autoWidth?: boolean; - /** - * The option elements to populate the select with. - * Can be some `MenuItem` when `native` is false and `option` when `native` is true. - * - * ⚠️The `MenuItem` elements **must** be direct descendants when `native` is false. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - * @default {} - */ - classes?: Partial<SelectClasses>; - /** - * If `true`, the component is initially open. Use when the component open state is not controlled (i.e. the `open` prop is not defined). - * You can only use it when the `native` prop is `false` (default). - * @default false - */ - defaultOpen?: boolean; - /** - * The default value. Use when the component is not controlled. - */ - defaultValue?: Value; - /** - * If `true`, a value is displayed even if no items are selected. - * - * In order to display a meaningful value, a function can be passed to the `renderValue` prop which - * returns the value to be displayed when no items are selected. - * - * ⚠️ When using this prop, make sure the label doesn't overlap with the empty displayed value. - * The label should either be hidden or forced to a shrunk state. - * @default false - */ - displayEmpty?: boolean; - /** - * The icon that displays the arrow. - * @default ArrowDropDownIcon - */ - IconComponent?: React.ElementType; - /** - * The `id` of the wrapper element or the `select` element when `native`. - */ - id?: string; - /** - * An `Input` element; does not have to be a material-ui specific `Input`. - */ - input?: React.ReactElement<any, any>; - /** - * [Attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Attributes) applied to the `input` element. - * When `native` is `true`, the attributes are applied on the `select` element. - */ - inputProps?: InputProps['inputProps']; - /** - * See [OutlinedInput#label](/material-ui/api/outlined-input/#props) - */ - label?: React.ReactNode; - /** - * The ID of an element that acts as an additional label. The Select will - * be labelled by the additional label and the selected value. - */ - labelId?: string; - /** - * Props applied to the [`Menu`](/material-ui/api/menu/) element. - */ - MenuProps?: Partial<MenuProps>; - /** - * If `true`, `value` must be an array and the menu will support multiple selections. - * @default false - */ - multiple?: boolean; - /** - * If `true`, the component uses a native `select` element. - * @default false - */ - native?: boolean; - /** - * Callback fired when a menu item is selected. - * - * @param {SelectChangeEvent<Value>} event The event source of the callback. - * You can pull out the new value by accessing `event.target.value` (any). - * **Warning**: This is a generic event, not a change event, unless the change event is caused by browser autofill. - * @param {object} [child] The react element that was selected when `native` is `false` (default). - */ - onChange?: SelectInputProps<Value>['onChange']; - /** - * Callback fired when the component requests to be closed. - * Use it in either controlled (see the `open` prop), or uncontrolled mode (to detect when the Select collapses). - * - * @param {object} event The event source of the callback. - */ - onClose?: (event: React.SyntheticEvent) => void; - /** - * Callback fired when the component requests to be opened. - * Use it in either controlled (see the `open` prop), or uncontrolled mode (to detect when the Select expands). - * - * @param {object} event The event source of the callback. - */ - onOpen?: (event: React.SyntheticEvent) => void; - /** - * If `true`, the component is shown. - * You can only use it when the `native` prop is `false` (default). - */ - open?: boolean; - /** - * Render the selected value. - * You can only use it when the `native` prop is `false` (default). - * - * @param {any} value The `value` provided to the component. - * @returns {ReactNode} - */ - renderValue?: (value: Value) => React.ReactNode; - /** - * Props applied to the clickable div element. - */ - SelectDisplayProps?: React.HTMLAttributes<HTMLDivElement>; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; - /** - * The `input` value. Providing an empty string will select no options. - * Set to an empty string `''` if you don't want any of the available options to be selected. - * - * If the value is an object it must have reference equality with the option in order to be selected. - * If the value is not an object, the string representation must match with the string representation of the option in order to be selected. - */ - value?: Value | ''; - /** - * The variant to use. - * @default 'outlined' - */ - variant?: 'standard' | 'outlined' | 'filled'; -} - -/** - * - * Demos: - * - * - [Select](https://mui.com/material-ui/react-select/) - * - * API: - * - * - [Select API](https://mui.com/material-ui/api/select/) - * - inherits [OutlinedInput API](https://mui.com/material-ui/api/outlined-input/) - */ -declare const Select: (<Value>(props: SelectProps<Value>) => JSX.Element) & { - muiName: string; -}; - -export default Select; diff --git a/packages/mui-material-next/src/Select/Select.js b/packages/mui-material-next/src/Select/Select.js deleted file mode 100644 index 4c637ca8942744..00000000000000 --- a/packages/mui-material-next/src/Select/Select.js +++ /dev/null @@ -1,287 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { deepmerge, unstable_useForkRef as useForkRef } from '@mui/utils'; -import NativeSelectInput from '@mui/material/NativeSelect/NativeSelectInput'; -// TODO v6: Remove Input component after implementing Material Design 3 -import Input from '@mui/material/Input'; -// TODO v6: replace with material-next FilledInput when available https://github.com/mui/material-ui/issues/39052 -import FilledInput from '@mui/material/FilledInput'; -// TODO v6: replace with material-next OutlinedInput when available -import OutlinedInput from '@mui/material/OutlinedInput'; -import SelectInput from './SelectInput'; -import formControlState from '../FormControl/formControlState'; -import useFormControl from '../FormControl/useFormControl'; -import ArrowDropDownIcon from '../internal/svg-icons/ArrowDropDown'; -import useThemeProps from '../styles/useThemeProps'; -import styled, { rootShouldForwardProp } from '../styles/styled'; - -const useUtilityClasses = (ownerState) => { - const { classes } = ownerState; - - return classes; -}; - -const styledRootConfig = { - name: 'MuiSelect', - overridesResolver: (props, styles) => styles.root, - shouldForwardProp: (prop) => rootShouldForwardProp(prop) && prop !== 'variant', - slot: 'Root', -}; - -const StyledInput = styled(Input, styledRootConfig)(''); - -const StyledOutlinedInput = styled(OutlinedInput, styledRootConfig)(''); - -const StyledFilledInput = styled(FilledInput, styledRootConfig)(''); - -const Select = React.forwardRef(function Select(inProps, ref) { - const props = useThemeProps({ name: 'MuiSelect', props: inProps }); - const { - autoWidth = false, - children, - classes: classesProp = {}, - className, - defaultOpen = false, - displayEmpty = false, - IconComponent = ArrowDropDownIcon, - id, - input, - inputProps, - label, - labelId, - MenuProps, - multiple = false, - native = false, - onClose, - onOpen, - open, - renderValue, - SelectDisplayProps, - variant: variantProp = 'outlined', - ...other - } = props; - - const inputComponent = native ? NativeSelectInput : SelectInput; - - const muiFormControl = useFormControl(); - const fcs = formControlState({ - props, - muiFormControl, - states: ['variant', 'error'], - }); - - const variant = fcs.variant || variantProp; - - const ownerState = { ...props, variant, classes: classesProp }; - const classes = useUtilityClasses(ownerState); - const { root, ...restOfClasses } = classes; - - const InputComponent = - input || - { - standard: <StyledInput ownerState={ownerState} />, - outlined: <StyledOutlinedInput label={label} ownerState={ownerState} />, - filled: <StyledFilledInput ownerState={ownerState} />, - }[variant]; - - const inputComponentRef = useForkRef(ref, InputComponent.ref); - - return ( - <React.Fragment> - {React.cloneElement(InputComponent, { - // Most of the logic is implemented in `SelectInput`. - // The `Select` component is a simple API wrapper to expose something better to play with. - inputComponent, - inputProps: { - children, - error: fcs.error, - IconComponent, - variant, - type: undefined, // We render a select. We can ignore the type provided by the `Input`. - multiple, - ...(native - ? { id } - : { - autoWidth, - defaultOpen, - displayEmpty, - labelId, - MenuProps, - onClose, - onOpen, - open, - renderValue, - SelectDisplayProps: { id, ...SelectDisplayProps }, - }), - ...inputProps, - classes: inputProps ? deepmerge(restOfClasses, inputProps.classes) : restOfClasses, - ...(input ? input.props.inputProps : {}), - }, - ...(multiple && native && variant === 'outlined' ? { notched: true } : {}), - ref: inputComponentRef, - className: clsx(InputComponent.props.className, className, classes.root), - // If a custom input is provided via 'input' prop, do not allow 'variant' to be propagated to it's root element. See https://github.com/mui/material-ui/issues/33894. - ...(!input && { variant }), - ...other, - })} - </React.Fragment> - ); -}); - -Select.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * If `true`, the width of the popover will automatically be set according to the items inside the - * menu, otherwise it will be at least the width of the select input. - * @default false - */ - autoWidth: PropTypes.bool, - /** - * The option elements to populate the select with. - * Can be some `MenuItem` when `native` is false and `option` when `native` is true. - * - * ⚠️The `MenuItem` elements **must** be direct descendants when `native` is false. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - * @default {} - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * If `true`, the component is initially open. Use when the component open state is not controlled (i.e. the `open` prop is not defined). - * You can only use it when the `native` prop is `false` (default). - * @default false - */ - defaultOpen: PropTypes.bool, - /** - * The default value. Use when the component is not controlled. - */ - defaultValue: PropTypes.any, - /** - * If `true`, a value is displayed even if no items are selected. - * - * In order to display a meaningful value, a function can be passed to the `renderValue` prop which - * returns the value to be displayed when no items are selected. - * - * ⚠️ When using this prop, make sure the label doesn't overlap with the empty displayed value. - * The label should either be hidden or forced to a shrunk state. - * @default false - */ - displayEmpty: PropTypes.bool, - /** - * The icon that displays the arrow. - * @default ArrowDropDownIcon - */ - IconComponent: PropTypes.elementType, - /** - * The `id` of the wrapper element or the `select` element when `native`. - */ - id: PropTypes.string, - /** - * An `Input` element; does not have to be a material-ui specific `Input`. - */ - input: PropTypes.element, - /** - * [Attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Attributes) applied to the `input` element. - * When `native` is `true`, the attributes are applied on the `select` element. - */ - inputProps: PropTypes.object, - /** - * See [OutlinedInput#label](/material-ui/api/outlined-input/#props) - */ - label: PropTypes.node, - /** - * The ID of an element that acts as an additional label. The Select will - * be labelled by the additional label and the selected value. - */ - labelId: PropTypes.string, - /** - * Props applied to the [`Menu`](/material-ui/api/menu/) element. - */ - MenuProps: PropTypes.object, - /** - * If `true`, `value` must be an array and the menu will support multiple selections. - * @default false - */ - multiple: PropTypes.bool, - /** - * If `true`, the component uses a native `select` element. - * @default false - */ - native: PropTypes.bool, - /** - * Callback fired when a menu item is selected. - * - * @param {SelectChangeEvent<Value>} event The event source of the callback. - * You can pull out the new value by accessing `event.target.value` (any). - * **Warning**: This is a generic event, not a change event, unless the change event is caused by browser autofill. - * @param {object} [child] The react element that was selected when `native` is `false` (default). - */ - onChange: PropTypes.func, - /** - * Callback fired when the component requests to be closed. - * Use it in either controlled (see the `open` prop), or uncontrolled mode (to detect when the Select collapses). - * - * @param {object} event The event source of the callback. - */ - onClose: PropTypes.func, - /** - * Callback fired when the component requests to be opened. - * Use it in either controlled (see the `open` prop), or uncontrolled mode (to detect when the Select expands). - * - * @param {object} event The event source of the callback. - */ - onOpen: PropTypes.func, - /** - * If `true`, the component is shown. - * You can only use it when the `native` prop is `false` (default). - */ - open: PropTypes.bool, - /** - * Render the selected value. - * You can only use it when the `native` prop is `false` (default). - * - * @param {any} value The `value` provided to the component. - * @returns {ReactNode} - */ - renderValue: PropTypes.func, - /** - * Props applied to the clickable div element. - */ - SelectDisplayProps: 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 `input` value. Providing an empty string will select no options. - * Set to an empty string `''` if you don't want any of the available options to be selected. - * - * If the value is an object it must have reference equality with the option in order to be selected. - * If the value is not an object, the string representation must match with the string representation of the option in order to be selected. - */ - value: PropTypes.oneOfType([PropTypes.oneOf(['']), PropTypes.any]), - /** - * The variant to use. - * @default 'outlined' - */ - variant: PropTypes.oneOf(['filled', 'outlined', 'standard']), -}; - -Select.muiName = 'Select'; - -export default Select; diff --git a/packages/mui-material-next/src/Select/Select.spec.tsx b/packages/mui-material-next/src/Select/Select.spec.tsx deleted file mode 100644 index 80bc65da3c3936..00000000000000 --- a/packages/mui-material-next/src/Select/Select.spec.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import * as React from 'react'; -// TODO v6: replace with material-next Menu when available https://github.com/mui/material-ui/pull/38934 -import MenuItem from '@mui/material/MenuItem'; -// TODO v6: replace with material-next's extendTheme when implementing Material Design 3 -import { createTheme } from '@mui/material/styles'; -import Select, { SelectChangeEvent } from '@mui/material-next/Select'; - -function genericValueTest() { - function handleChangeWithSameTypeAsSelect(event: SelectChangeEvent<number>) {} - <Select<number> onChange={handleChangeWithSameTypeAsSelect} />; - - function handleChangeWithDifferentTypeFromSelect( - event: React.ChangeEvent<{ name?: string; value: string }>, - ) {} - <Select<number> - // @ts-expect-error - onChange={handleChangeWithDifferentTypeFromSelect} - />; - - <Select<string> - // @ts-expect-error defaultValue should be a string - defaultValue={1} - // @ts-expect-error Value should be a string - value={10} - />; - - <Select - onChange={(event) => { - function testString(value: string) {} - function testNumber(value: number) {} - - testString(event.target.value); - // @ts-expect-error - testNumber(event.target.value); - }} - value="1" - />; - - <Select onChange={(event) => console.log(event.target.value)} value="1"> - <MenuItem value="1" /> - {/* Whoops. The value in onChange won't be a string */} - <MenuItem value={2} /> - </Select>; - - // notched prop should be available (inherited from OutlinedInputProps) and NOT throw typescript error - <Select notched />; - - // disabledUnderline prop should be available (inherited from InputProps) and NOT throw typescript error - <Select disableUnderline />; - - // Tests presence of `root` class in SelectClasses - const theme = createTheme({ - components: { - MuiSelect: { - styleOverrides: { - root: { - borderRadius: '8px', - }, - }, - }, - }, - }); - - // tests deep slot prop forwarding up to the modal backdrop - <Select - MenuProps={{ - slotProps: { - root: { - slotProps: { - backdrop: { - style: { - backgroundColor: 'transparent', - }, - }, - }, - }, - }, - }} - />; -} diff --git a/packages/mui-material-next/src/Select/Select.test.js b/packages/mui-material-next/src/Select/Select.test.js deleted file mode 100644 index 437ca7a1f23816..00000000000000 --- a/packages/mui-material-next/src/Select/Select.test.js +++ /dev/null @@ -1,1708 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { spy, stub } from 'sinon'; -import { ErrorBoundary, act, createRenderer, fireEvent, screen } from '@mui-internal/test-utils'; -import { nativeSelectClasses } from '@mui/material/NativeSelect'; -// TODO v6: replace with material-next's extendTheme and provider when implementing Material Design 3 -import { createTheme, ThemeProvider } from '@mui/material/styles'; -// TODO v6: replace with material-next Menu components when available https://github.com/mui/material-ui/pull/38934 -import MenuItem, { menuItemClasses } from '@mui/material/MenuItem'; -// TODO v6: replace with material-next ListSubheader when available -import ListSubheader from '@mui/material/ListSubheader'; -// TODO v6: replace with material-next OutlinedInput when available -import OutlinedInput from '@mui/material/OutlinedInput'; -// TODO v6: replace with material-next InputLabel when available -import InputLabel from '@mui/material/InputLabel'; -// TODO v6: replace with material-next Divider when available -import Divider from '@mui/material/Divider'; -import Select from '@mui/material-next/Select'; -import classes from './selectClasses'; -import describeConformance from '../../test/describeConformance'; - -describe('<Select />', () => { - const { clock, render } = createRenderer({ clock: 'fake' }); - - describeConformance(<Select value="" />, () => ({ - classes, - inheritComponent: OutlinedInput, - render, - refInstanceof: window.HTMLDivElement, - muiName: 'MuiSelect', - skip: ['componentProp', 'componentsProp', 'themeVariants', 'themeStyleOverrides'], - })); - - describe('prop: inputProps', () => { - it('should be able to provide a custom classes property', () => { - render( - <Select - inputProps={{ - classes: { select: 'select' }, - }} - value="" - />, - ); - expect(document.querySelector(`.${classes.select}`)).to.have.class('select'); - }); - }); - - it('should be able to mount the component', () => { - const { container } = render( - <Select value={10}> - <MenuItem value=""> - <em>None</em> - </MenuItem> - <MenuItem value={10}>Ten</MenuItem> - <MenuItem value={20}>Twenty</MenuItem> - <MenuItem value={30}>Thirty</MenuItem> - </Select>, - ); - - expect(container.querySelector('input')).to.have.property('value', '10'); - }); - - specify('the trigger is in tab order', () => { - const { getByRole } = render( - <Select value=""> - <MenuItem value="">None</MenuItem> - </Select>, - ); - - expect(getByRole('combobox')).to.have.property('tabIndex', 0); - }); - - it('should accept null child', () => { - render( - <Select open value={10}> - {null} - <MenuItem value={10}>Ten</MenuItem> - </Select>, - ); - }); - - ['', 0, false, undefined, NaN].forEach((value) => - it(`should support conditional rendering with "${value}"`, () => { - render( - <Select open value={2}> - {value && <MenuItem value={1}>One</MenuItem>} - <MenuItem value={2}>Two</MenuItem> - </Select>, - ); - }), - ); - - it('should have an input with [aria-hidden] by default', () => { - const { container } = render( - <Select value="10"> - <MenuItem value="10">Ten</MenuItem> - </Select>, - ); - - expect(container.querySelector('input')).to.have.attribute('aria-hidden', 'true'); - }); - - it('should ignore onBlur when the menu opens', () => { - // mousedown calls focus while click opens moving the focus to an item - // this means the trigger is blurred immediately - const handleBlur = spy(); - const { getByRole, getAllByRole, queryByRole } = render( - <Select - onBlur={handleBlur} - value="" - onMouseDown={(event) => { - // simulating certain platforms that focus on mousedown - if (event.defaultPrevented === false) { - event.currentTarget.focus(); - } - }} - > - <MenuItem value="">none</MenuItem> - <MenuItem value={10}>Ten</MenuItem> - </Select>, - ); - const trigger = getByRole('combobox'); - - fireEvent.mouseDown(trigger); - - expect(handleBlur.callCount).to.equal(0); - expect(getByRole('listbox')).not.to.equal(null); - - act(() => { - const options = getAllByRole('option'); - fireEvent.mouseDown(options[0]); - options[0].click(); - }); - - expect(handleBlur.callCount).to.equal(0); - expect(queryByRole('listbox', { hidden: false })).to.equal(null); - }); - - it('options should have a data-value attribute', () => { - render( - <Select open value={10}> - <MenuItem value={10}>Ten</MenuItem> - <MenuItem value={20}>Twenty</MenuItem> - </Select>, - ); - const options = screen.getAllByRole('option'); - - expect(options[0]).to.have.attribute('data-value', '10'); - expect(options[1]).to.have.attribute('data-value', '20'); - }); - - [' ', 'ArrowUp', 'ArrowDown', 'Enter'].forEach((key) => { - it(`should open menu when pressed ${key} key on select`, () => { - render( - <Select value=""> - <MenuItem value="">none</MenuItem> - </Select>, - ); - const trigger = screen.getByRole('combobox'); - act(() => { - trigger.focus(); - }); - - fireEvent.keyDown(trigger, { key }); - expect(screen.getByRole('listbox', { hidden: false })).not.to.equal(null); - - fireEvent.keyUp(screen.getAllByRole('option')[0], { key }); - expect(screen.getByRole('listbox', { hidden: false })).not.to.equal(null); - }); - }); - - it('should pass "name" as part of the event.target for onBlur', () => { - const handleBlur = stub().callsFake((event) => event.target.name); - const { getByRole } = render( - <Select onBlur={handleBlur} name="blur-testing" value=""> - <MenuItem value="">none</MenuItem> - </Select>, - ); - const button = getByRole('combobox'); - act(() => { - button.focus(); - }); - - act(() => { - button.blur(); - }); - - expect(handleBlur.callCount).to.equal(1); - expect(handleBlur.firstCall.returnValue).to.equal('blur-testing'); - }); - - it('should call onClose when the backdrop is clicked', () => { - const handleClose = spy(); - const { getByTestId } = render( - <Select - MenuProps={{ BackdropProps: { 'data-testid': 'backdrop' } }} - onClose={handleClose} - open - value="" - > - <MenuItem value="">none</MenuItem> - </Select>, - ); - - act(() => { - getByTestId('backdrop').click(); - }); - - expect(handleClose.callCount).to.equal(1); - }); - - it('should call onClose when the same option is selected', () => { - const handleChange = spy(); - const handleClose = spy(); - render( - <Select open onChange={handleChange} onClose={handleClose} value="second"> - <MenuItem value="first" /> - <MenuItem value="second" /> - </Select>, - ); - - screen.getByRole('option', { selected: true }).click(); - - expect(handleChange.callCount).to.equal(0); - expect(handleClose.callCount).to.equal(1); - }); - - it('should focus select when its label is clicked', () => { - const { getByRole, getByTestId } = render( - <React.Fragment> - <InputLabel id="my$label" data-testid="label" /> - <Select value="" labelId="my$label" /> - </React.Fragment>, - ); - - fireEvent.click(getByTestId('label')); - - expect(getByRole('combobox')).toHaveFocus(); - }); - - it('should focus list if no selection', () => { - const { getByRole } = render(<Select value="" autoFocus />); - - fireEvent.mouseDown(getByRole('combobox')); - - // TODO not matching WAI-ARIA authoring practices. It should focus the first (or selected) item. - expect(getByRole('listbox')).toHaveFocus(); - }); - - describe('prop: onChange', () => { - it('should get selected element from arguments', () => { - const onChangeHandler = spy(); - const { getAllByRole, getByRole } = render( - <Select onChange={onChangeHandler} value="0"> - <MenuItem value="0" /> - <MenuItem value="1" /> - <MenuItem value="2" /> - </Select>, - ); - fireEvent.mouseDown(getByRole('combobox')); - act(() => { - getAllByRole('option')[1].click(); - }); - - expect(onChangeHandler.calledOnce).to.equal(true); - const selected = onChangeHandler.args[0][1]; - expect(React.isValidElement(selected)).to.equal(true); - }); - - it('should call onChange before onClose', () => { - const eventLog = []; - const onChangeHandler = spy(() => eventLog.push('CHANGE_EVENT')); - const onCloseHandler = spy(() => eventLog.push('CLOSE_EVENT')); - const { getAllByRole, getByRole } = render( - <Select onChange={onChangeHandler} onClose={onCloseHandler} value="0"> - <MenuItem value="0" /> - <MenuItem value="1" /> - </Select>, - ); - - fireEvent.mouseDown(getByRole('combobox')); - act(() => { - getAllByRole('option')[1].click(); - }); - - expect(eventLog).to.deep.equal(['CHANGE_EVENT', 'CLOSE_EVENT']); - }); - - it('should not be called if selected element has the current value (value did not change)', () => { - const onChangeHandler = spy(); - const { getAllByRole, getByRole } = render( - <Select onChange={onChangeHandler} value="1"> - <MenuItem value="0" /> - <MenuItem value="1" /> - <MenuItem value="2" /> - </Select>, - ); - fireEvent.mouseDown(getByRole('combobox')); - act(() => { - getAllByRole('option')[1].click(); - }); - - expect(onChangeHandler.callCount).to.equal(0); - }); - }); - - describe('prop: defaultOpen', () => { - it('should be open on mount', () => { - const { getByRole } = render(<Select defaultOpen value="" />); - expect(getByRole('combobox', { hidden: true })).to.have.attribute('aria-expanded', 'true'); - }); - }); - - describe('prop: value', () => { - it('should select the option based on the number value', () => { - render( - <Select open value={20}> - <MenuItem value={10}>Ten</MenuItem> - <MenuItem value={20}>Twenty</MenuItem> - <MenuItem value={30}>Thirty</MenuItem> - </Select>, - ); - const options = screen.getAllByRole('option'); - - expect(options[0]).not.to.have.attribute('aria-selected', 'true'); - expect(options[1]).to.have.attribute('aria-selected', 'true'); - expect(options[2]).not.to.have.attribute('aria-selected', 'true'); - }); - - it('should select the option based on the string value', () => { - render( - <Select open value="20"> - <MenuItem value={10}>Ten</MenuItem> - <MenuItem value={20}>Twenty</MenuItem> - <MenuItem value={30}>Thirty</MenuItem> - </Select>, - ); - const options = screen.getAllByRole('option'); - - expect(options[0]).not.to.have.attribute('aria-selected', 'true'); - expect(options[1]).to.have.attribute('aria-selected', 'true'); - expect(options[2]).not.to.have.attribute('aria-selected', 'true'); - }); - - it('should select only the option that matches the object', () => { - const obj1 = { id: 1 }; - const obj2 = { id: 2 }; - render( - <Select open value={obj1}> - <MenuItem value={obj1}>1</MenuItem> - <MenuItem value={obj2}>2</MenuItem> - </Select>, - ); - const options = screen.getAllByRole('option'); - - expect(options[0]).to.have.attribute('aria-selected', 'true'); - expect(options[1]).not.to.have.attribute('aria-selected', 'true'); - }); - - it('should be able to use an object', () => { - const value = {}; - const { getByRole } = render( - <Select value={value}> - <MenuItem value=""> - <em>None</em> - </MenuItem> - <MenuItem value={10}>Ten</MenuItem> - <MenuItem value={value}>Twenty</MenuItem> - <MenuItem value={30}>Thirty</MenuItem> - </Select>, - ); - - expect(getByRole('combobox')).to.have.text('Twenty'); - }); - - describe('warnings', () => { - it('warns when the value is not present in any option', () => { - expect(() => - render( - <Select value={20}> - <MenuItem value={10}>Ten</MenuItem> - <MenuItem value={30}>Thirty</MenuItem> - </Select>, - ), - ).toWarnDev([ - 'MUI: You have provided an out-of-range value `20` for the select component.', - // React 18 Strict Effects run mount effects twice - React.version.startsWith('18') && - 'MUI: You have provided an out-of-range value `20` for the select component.', - 'MUI: You have provided an out-of-range value `20` for the select component.', - ]); - }); - }); - }); - - it('should not have the selectable option selected when inital value provided is empty string on Select with ListSubHeader item', () => { - render( - <Select open value=""> - <ListSubheader>Category 1</ListSubheader> - <MenuItem value={10}>Ten</MenuItem> - <ListSubheader>Category 2</ListSubheader> - <MenuItem value={20}>Twenty</MenuItem> - <MenuItem value={30}>Thirty</MenuItem> - </Select>, - ); - - const options = screen.getAllByRole('option'); - expect(options[1]).not.to.have.class(menuItemClasses.selected); - }); - - describe('SVG icon', () => { - it('should not present an SVG icon when native and multiple are specified', () => { - const { container } = render( - <Select native multiple value={[0, 1]}> - <option value={0}>Zero</option> - <option value={1}>One</option> - <option value={2}>Two</option> - </Select>, - ); - expect(container.querySelector('svg')).to.equal(null); - }); - - it('should present an SVG icon', () => { - const { container } = render( - <Select native value={1}> - <option value={0}>Zero</option> - <option value={1}>One</option> - <option value={2}>Two</option> - </Select>, - ); - expect(container.querySelector('svg')).toBeVisible(); - }); - }); - - describe('accessibility', () => { - it('sets aria-expanded="true" when the listbox is displayed', () => { - // since we make the rest of the UI inaccessible when open this doesn't - // technically matter. This is only here in case we keep the rest accessible - const { getByRole } = render(<Select open value="" />); - - expect(getByRole('combobox', { hidden: true })).to.have.attribute('aria-expanded', 'true'); - }); - - specify('ARIA 1.2: aria-expanded="false" if the listbox isn\'t displayed', () => { - const { getByRole } = render(<Select value="" />); - - expect(getByRole('combobox')).to.have.attribute('aria-expanded', 'false'); - }); - - it('sets aria-disabled="true" when component is disabled', () => { - const { getByRole } = render(<Select disabled value="" />); - - expect(getByRole('combobox')).to.have.attribute('aria-disabled', 'true'); - }); - - it('sets disabled attribute in input when component is disabled', () => { - const { container } = render(<Select disabled value="" />); - - expect(container.querySelector('input')).to.have.property('disabled', true); - }); - - specify('aria-disabled is not present if component is not disabled', () => { - const { getByRole } = render(<Select disabled={false} value="" />); - - expect(getByRole('combobox')).not.to.have.attribute('aria-disabled'); - }); - - it('indicates that activating the button displays a listbox', () => { - const { getByRole } = render(<Select value="" />); - - expect(getByRole('combobox')).to.have.attribute('aria-haspopup', 'listbox'); - }); - - it('renders an element with listbox behavior', () => { - const { getByRole } = render(<Select open value="" />); - - expect(getByRole('listbox')).toBeVisible(); - }); - - it('indicates that input element has combobox role and aria-controls set to id of listbox', () => { - const { getByRole } = render(<Select open value="" />); - const listboxId = getByRole('listbox').id; - - expect(getByRole('combobox', { hidden: true })).to.have.attribute('aria-controls', listboxId); - }); - - specify('the listbox is focusable', () => { - const { getByRole } = render(<Select open value="" />); - - act(() => { - getByRole('listbox').focus(); - }); - - expect(getByRole('listbox')).toHaveFocus(); - }); - - it('identifies each selectable element containing an option', () => { - const { getAllByRole } = render( - <Select open value=""> - <MenuItem value="1">First</MenuItem> - <MenuItem value="2">Second</MenuItem> - </Select>, - ); - - const options = getAllByRole('option'); - expect(options[0]).to.have.text('First'); - expect(options[1]).to.have.text('Second'); - }); - - it('indicates the selected option', () => { - const { getAllByRole } = render( - <Select open value="2"> - <MenuItem value="1">First</MenuItem> - <MenuItem value="2">Second</MenuItem> - </Select>, - ); - - expect(getAllByRole('option')[1]).to.have.attribute('aria-selected', 'true'); - }); - - describe('when the first child is a ListSubheader', () => { - it('first selectable option is focused to use the arrow', () => { - const { getAllByRole } = render( - <Select defaultValue="" open> - <ListSubheader>Category 1</ListSubheader> - <MenuItem value={1}>Option 1</MenuItem> - <MenuItem value={2}>Option 2</MenuItem> - <ListSubheader>Category 2</ListSubheader> - <MenuItem value={3}>Option 3</MenuItem> - <MenuItem value={4}>Option 4</MenuItem> - </Select>, - ); - - const options = getAllByRole('option'); - expect(options[1]).to.have.attribute('tabindex', '0'); - - act(() => { - fireEvent.keyDown(options[1], { key: 'ArrowDown' }); - fireEvent.keyDown(options[2], { key: 'ArrowDown' }); - fireEvent.keyDown(options[4], { key: 'Enter' }); - }); - - expect(options[4]).to.have.attribute('aria-selected', 'true'); - }); - - describe('when also the second child is a ListSubheader', () => { - it('first selectable option is focused to use the arrow', () => { - const { getAllByRole } = render( - <Select defaultValue="" open> - <ListSubheader>Empty category</ListSubheader> - <ListSubheader>Category 1</ListSubheader> - <MenuItem value={1}>Option 1</MenuItem> - <MenuItem value={2}>Option 2</MenuItem> - <ListSubheader>Category 2</ListSubheader> - <MenuItem value={3}>Option 3</MenuItem> - <MenuItem value={4}>Option 4</MenuItem> - </Select>, - ); - - const options = getAllByRole('option'); - expect(options[2]).to.have.attribute('tabindex', '0'); - - act(() => { - fireEvent.keyDown(options[2], { key: 'ArrowDown' }); - fireEvent.keyDown(options[3], { key: 'ArrowDown' }); - fireEvent.keyDown(options[5], { key: 'Enter' }); - }); - - expect(options[5]).to.have.attribute('aria-selected', 'true'); - }); - }); - - describe('when the second child is null', () => { - it('first selectable option is focused to use the arrow', () => { - const { getAllByRole } = render( - <Select defaultValue="" open> - <ListSubheader>Category 1</ListSubheader> - {null} - <MenuItem value={1}>Option 1</MenuItem> - <MenuItem value={2}>Option 2</MenuItem> - <ListSubheader>Category 2</ListSubheader> - <MenuItem value={3}>Option 3</MenuItem> - <MenuItem value={4}>Option 4</MenuItem> - </Select>, - ); - - const options = getAllByRole('option'); - expect(options[1]).to.have.attribute('tabindex', '0'); - - act(() => { - fireEvent.keyDown(options[1], { key: 'ArrowDown' }); - fireEvent.keyDown(options[2], { key: 'ArrowDown' }); - fireEvent.keyDown(options[4], { key: 'Enter' }); - }); - - expect(options[4]).to.have.attribute('aria-selected', 'true'); - }); - }); - - ['', 0, false, undefined, NaN].forEach((value) => - describe(`when the second child is conditionally rendering with "${value}"`, () => { - it('first selectable option is focused to use the arrow', () => { - const { getAllByRole } = render( - <Select defaultValue="" open> - <ListSubheader>Category 1</ListSubheader> - {value && <MenuItem value={1}>One</MenuItem>} - <MenuItem value={1}>Option 1</MenuItem> - <MenuItem value={2}>Option 2</MenuItem> - <ListSubheader>Category 2</ListSubheader> - <MenuItem value={3}>Option 3</MenuItem> - <MenuItem value={4}>Option 4</MenuItem> - </Select>, - ); - - const options = getAllByRole('option'); - expect(options[1]).to.have.attribute('tabindex', '0'); - - act(() => { - fireEvent.keyDown(options[1], { key: 'ArrowDown' }); - fireEvent.keyDown(options[2], { key: 'ArrowDown' }); - fireEvent.keyDown(options[4], { key: 'Enter' }); - }); - - expect(options[4]).to.have.attribute('aria-selected', 'true'); - }); - }), - ); - }); - - describe('when the first child is a ListSubheader wrapped in a custom component', () => { - describe('with the `muiSkipListHighlight` static field', () => { - function WrappedListSubheader(props) { - return <ListSubheader {...props} />; - } - - WrappedListSubheader.muiSkipListHighlight = true; - - it('highlights the first selectable option below the header', () => { - const { getByText } = render( - <Select defaultValue="" open> - <WrappedListSubheader>Category 1</WrappedListSubheader> - <MenuItem value={1}>Option 1</MenuItem> - <MenuItem value={2}>Option 2</MenuItem> - <WrappedListSubheader>Category 2</WrappedListSubheader> - <MenuItem value={3}>Option 3</MenuItem> - <MenuItem value={4}>Option 4</MenuItem> - </Select>, - ); - - const expectedHighlightedOption = getByText('Option 1'); - expect(expectedHighlightedOption).to.have.attribute('tabindex', '0'); - }); - }); - - describe('with the `muiSkipListHighlight` prop', () => { - function WrappedListSubheader(props) { - const { muiSkipListHighlight, ...other } = props; - return <ListSubheader {...other} />; - } - - it('highlights the first selectable option below the header', () => { - const { getByText } = render( - <Select defaultValue="" open> - <WrappedListSubheader muiSkipListHighlight>Category 1</WrappedListSubheader> - <MenuItem value={1}>Option 1</MenuItem> - <MenuItem value={2}>Option 2</MenuItem> - <WrappedListSubheader muiSkipListHighlight>Category 2</WrappedListSubheader> - <MenuItem value={3}>Option 3</MenuItem> - <MenuItem value={4}>Option 4</MenuItem> - </Select>, - ); - - const expectedHighlightedOption = getByText('Option 1'); - expect(expectedHighlightedOption).to.have.attribute('tabindex', '0'); - }); - }); - }); - - describe('when the first child is a MenuItem disabled', () => { - it('highlights the first selectable option below the header', () => { - const { getAllByRole } = render( - <Select defaultValue="" open> - <MenuItem value="" disabled> - <em>None</em> - </MenuItem> - <ListSubheader>Category 1</ListSubheader> - <MenuItem value={1}>Option 1</MenuItem> - <MenuItem value={2}>Option 2</MenuItem> - <ListSubheader>Category 2</ListSubheader> - <MenuItem value={3}>Option 3</MenuItem> - <MenuItem value={4}>Option 4</MenuItem> - </Select>, - ); - - const options = getAllByRole('option'); - expect(options[2]).to.have.attribute('tabindex', '0'); - - act(() => { - fireEvent.keyDown(options[2], { key: 'ArrowDown' }); - fireEvent.keyDown(options[3], { key: 'ArrowDown' }); - fireEvent.keyDown(options[5], { key: 'Enter' }); - }); - - expect(options[5]).to.have.attribute('aria-selected', 'true'); - }); - }); - - it('it will fallback to its content for the accessible name when it has no name', () => { - const { getByRole } = render(<Select value="" />); - - // TODO what is the accessible name actually? - expect(getByRole('combobox')).not.to.have.attribute('aria-labelledby'); - }); - - it('is labelled by itself when it has a name', () => { - const { getByRole } = render(<Select name="select" value="" />); - - expect(getByRole('combobox')).to.have.attribute( - 'aria-labelledby', - getByRole('combobox').getAttribute('id'), - ); - }); - - it('is labelled by itself when it has an id which is preferred over name', () => { - const { getAllByRole } = render( - <React.Fragment> - <span id="select-1-label">Chose first option:</span> - <Select id="select-1" labelId="select-1-label" name="select" value="" /> - <span id="select-2-label">Chose second option:</span> - <Select id="select-2" labelId="select-2-label" name="select" value="" /> - </React.Fragment>, - ); - - const triggers = getAllByRole('combobox'); - - expect(triggers[0]).to.have.attribute( - 'aria-labelledby', - `select-1-label ${triggers[0].getAttribute('id')}`, - ); - expect(triggers[1]).to.have.attribute( - 'aria-labelledby', - `select-2-label ${triggers[1].getAttribute('id')}`, - ); - }); - - it('can be labelled by an additional element if its id is provided in `labelId`', () => { - const { getByRole } = render( - <React.Fragment> - <span id="select-label">Choose one:</span> - <Select labelId="select-label" name="select" value="" /> - </React.Fragment>, - ); - - expect(getByRole('combobox')).to.have.attribute( - 'aria-labelledby', - `select-label ${getByRole('combobox').getAttribute('id')}`, - ); - }); - - specify('the list of options is not labelled by default', () => { - const { getByRole } = render(<Select open value="" />); - - expect(getByRole('listbox')).not.to.have.attribute('aria-labelledby'); - }); - - specify('the list of options can be labelled by providing `labelId`', () => { - const { getByRole } = render( - <React.Fragment> - <span id="select-label">Choose one:</span> - <Select labelId="select-label" open value="" /> - </React.Fragment>, - ); - - expect(getByRole('listbox')).to.have.attribute('aria-labelledby', 'select-label'); - }); - - it('should have appropriate accessible description when provided in props', () => { - const { getByRole } = render( - <React.Fragment> - <Select aria-describedby="select-helper-text" value="" /> - <span id="select-helper-text">Helper text content</span> - </React.Fragment>, - ); - - const target = getByRole('combobox'); - expect(target).to.have.attribute('aria-describedby', 'select-helper-text'); - expect(target).toHaveAccessibleDescription('Helper text content'); - }); - }); - - describe('prop: readOnly', () => { - it('should not trigger any event with readOnly', () => { - render( - <Select readOnly value="10"> - <MenuItem value={10}>Ten</MenuItem> - <MenuItem value={20}>Twenty</MenuItem> - </Select>, - ); - const trigger = screen.getByRole('combobox'); - act(() => { - trigger.focus(); - }); - - fireEvent.keyDown(trigger, { key: 'ArrowDown' }); - expect(screen.queryByRole('listbox')).to.equal(null); - - fireEvent.keyUp(trigger, { key: 'ArrowDown' }); - expect(screen.queryByRole('listbox')).to.equal(null); - }); - }); - - describe('prop: MenuProps', () => { - it('should apply additional props to the Menu component', () => { - const onEntered = spy(); - const { getByRole } = render( - <Select MenuProps={{ TransitionProps: { onEntered }, transitionDuration: 100 }} value="10"> - <MenuItem value="10">Ten</MenuItem> - </Select>, - ); - - fireEvent.mouseDown(getByRole('combobox')); - clock.tick(99); - - expect(onEntered.callCount).to.equal(0); - - clock.tick(1); - - expect(onEntered.callCount).to.equal(1); - }); - - it('should be able to override PaperProps minWidth', () => { - const { getByTestId } = render( - <Select - MenuProps={{ PaperProps: { 'data-testid': 'paper', style: { minWidth: 12 } } }} - open - value="10" - > - <MenuItem value="10">Ten</MenuItem> - </Select>, - ); - - expect(getByTestId('paper').style).to.have.property('minWidth', '12px'); - }); - - // https://github.com/mui/material-ui/issues/38700 - it('should merge `slotProps.paper` with the default Paper props', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const { getByTestId, getByRole } = render( - <Select MenuProps={{ slotProps: { paper: { 'data-testid': 'paper' } } }} open value="10"> - <MenuItem value="10">Ten</MenuItem> - </Select>, - ); - - const paper = getByTestId('paper'); - const selectButton = getByRole('combobox', { hidden: true }); - - expect(paper.style).to.have.property('minWidth', `${selectButton.clientWidth}px`); - }); - - // https://github.com/mui/material-ui/issues/38949 - it('should forward `slotProps` to menu', function test() { - const { getByTestId } = render( - <Select - MenuProps={{ - slotProps: { - root: { - slotProps: { - backdrop: { 'data-testid': 'backdrop', style: { backgroundColor: 'red' } }, - }, - }, - }, - }} - open - value="10" - > - <MenuItem value="10">Ten</MenuItem> - </Select>, - ); - - const backdrop = getByTestId('backdrop'); - - expect(backdrop.style).to.have.property('backgroundColor', 'red'); - }); - }); - - describe('prop: SelectDisplayProps', () => { - it('should apply additional props to trigger element', () => { - const { getByRole } = render( - <Select SelectDisplayProps={{ 'data-test': 'SelectDisplay' }} value="10"> - <MenuItem value="10">Ten</MenuItem> - </Select>, - ); - - expect(getByRole('combobox')).to.have.attribute('data-test', 'SelectDisplay'); - }); - }); - - describe('prop: displayEmpty', () => { - it('should display the selected item even if its value is empty', () => { - const { getByRole } = render( - <Select value="" displayEmpty> - <MenuItem value="">Ten</MenuItem> - <MenuItem value={20}>Twenty</MenuItem> - <MenuItem value={30}>Thirty</MenuItem> - </Select>, - ); - - expect(getByRole('combobox')).to.have.text('Ten'); - }); - }); - - describe('prop: renderValue', () => { - it('should use the prop to render the value', () => { - const renderValue = (x) => `0b${x.toString(2)}`; - const { getByRole } = render( - <Select renderValue={renderValue} value={4}> - <MenuItem value={2}>2</MenuItem> - <MenuItem value={4}>4</MenuItem> - </Select>, - ); - - expect(getByRole('combobox')).to.have.text('0b100'); - }); - }); - - describe('prop: open (controlled)', () => { - it('should not focus on close controlled select', () => { - function ControlledWrapper() { - const [open, setOpen] = React.useState(false); - - return ( - <div> - <button type="button" id="open-select" onClick={() => setOpen(true)}> - Open select - </button> - <Select - MenuProps={{ transitionDuration: 0 }} - open={open} - onClose={() => setOpen(false)} - value="" - > - <MenuItem onClick={() => setOpen(false)}>close</MenuItem> - </Select> - </div> - ); - } - const { container, getByRole } = render(<ControlledWrapper />); - const openSelect = container.querySelector('#open-select'); - act(() => { - openSelect.focus(); - }); - fireEvent.click(openSelect); - - const option = getByRole('option'); - expect(option).toHaveFocus(); - fireEvent.click(option); - - expect(container.querySelectorAll(classes.focused).length).to.equal(0); - expect(openSelect).toHaveFocus(); - }); - - it('should allow to control closing by passing onClose props', () => { - function ControlledWrapper() { - const [open, setOpen] = React.useState(false); - - return ( - <Select - MenuProps={{ transitionDuration: 0 }} - open={open} - onClose={() => setOpen(false)} - onOpen={() => setOpen(true)} - value="" - > - <MenuItem onClick={() => setOpen(false)}>close</MenuItem> - </Select> - ); - } - const { getByRole, queryByRole } = render(<ControlledWrapper />); - - fireEvent.mouseDown(getByRole('combobox')); - expect(getByRole('listbox')).not.to.equal(null); - - act(() => { - getByRole('option').click(); - }); - // react-transition-group uses one extra commit for exit to completely remove - // it from the DOM. but it's at least immediately inaccessible. - // It's desired that this fails one day. The additional tick required to remove - // this from the DOM is not a feature - expect(getByRole('listbox', { hidden: true })).toBeInaccessible(); - clock.tick(0); - - expect(queryByRole('listbox', { hidden: true })).to.equal(null); - }); - - it('should be open when initially true', () => { - const { getByRole } = render( - <Select open value=""> - <MenuItem>Hello</MenuItem> - </Select>, - ); - - expect(getByRole('listbox')).not.to.equal(null); - }); - - it('open only with the left mouse button click', () => { - // Test for https://github.com/mui/material-ui/issues/19250#issuecomment-578620934 - // Right/middle mouse click shouldn't open the Select - const { getByRole, queryByRole } = render( - <Select value=""> - <MenuItem value=""> - <em>None</em> - </MenuItem> - <MenuItem value={10}>Ten</MenuItem> - <MenuItem value={20}>Twenty</MenuItem> - <MenuItem value={30}>Thirty</MenuItem> - </Select>, - ); - - const trigger = getByRole('combobox'); - - // If clicked by the right/middle mouse button, no options list should be opened - fireEvent.mouseDown(trigger, { button: 1 }); - expect(queryByRole('listbox')).to.equal(null); - - fireEvent.mouseDown(trigger, { button: 2 }); - expect(queryByRole('listbox')).to.equal(null); - }); - }); - - describe('prop: autoWidth', () => { - it('should take the trigger parent element width into account by default', () => { - const { container, getByRole, getByTestId } = render( - <Select MenuProps={{ PaperProps: { 'data-testid': 'paper' } }} value=""> - <MenuItem>Only</MenuItem> - </Select>, - ); - const parentEl = container.querySelector('.MuiInputBase-root'); - const button = getByRole('combobox'); - stub(parentEl, 'clientWidth').get(() => 14); - - fireEvent.mouseDown(button); - expect(getByTestId('paper').style).to.have.property('minWidth', '14px'); - }); - - it('should not take the trigger parent element width into account when autoWidth is true', () => { - const { container, getByRole, getByTestId } = render( - <Select autoWidth MenuProps={{ PaperProps: { 'data-testid': 'paper' } }} value=""> - <MenuItem>Only</MenuItem> - </Select>, - ); - const parentEl = container.querySelector('.MuiInputBase-root'); - const button = getByRole('combobox'); - stub(parentEl, 'clientWidth').get(() => 14); - - fireEvent.mouseDown(button); - expect(getByTestId('paper').style).to.have.property('minWidth', ''); - }); - }); - - describe('prop: multiple', () => { - it('should serialize multiple select value', () => { - const { container, getAllByRole } = render( - <Select multiple open value={[10, 30]}> - <MenuItem value={10}>Ten</MenuItem> - <MenuItem value={20}>Twenty</MenuItem> - <MenuItem value={30}>Thirty</MenuItem> - </Select>, - ); - const options = getAllByRole('option'); - - expect(container.querySelector('input')).to.have.property('value', '10,30'); - expect(options[0]).to.have.attribute('aria-selected', 'true'); - expect(options[1]).not.to.have.attribute('aria-selected', 'true'); - expect(options[2]).to.have.attribute('aria-selected', 'true'); - }); - - it('should have aria-multiselectable=true when multiple is true', () => { - const { getByRole } = render( - <Select multiple value={[10, 30]}> - <MenuItem value={10}>Ten</MenuItem> - <MenuItem value={20}>Twenty</MenuItem> - <MenuItem value={30}>Thirty</MenuItem> - </Select>, - ); - - fireEvent.mouseDown(getByRole('combobox')); - - expect(getByRole('listbox')).to.have.attribute('aria-multiselectable', 'true'); - }); - - it('should serialize multiple select display value', () => { - const { getByRole } = render( - <Select multiple value={[10, 20, 30]}> - <MenuItem value={10}>Ten</MenuItem> - <MenuItem value={20}> - <strong>Twenty</strong> - </MenuItem> - <MenuItem value={30}>Thirty</MenuItem> - </Select>, - ); - - expect(getByRole('combobox')).to.have.text('Ten, Twenty, Thirty'); - }); - - it('should not throw an error if `value` is an empty array', () => { - expect(() => { - render(<Select multiple value={[]} />); - }).not.to.throw(); - }); - - it('should not throw an error if `value` is not an empty array', () => { - expect(() => { - render(<Select multiple value={['foo']} />); - }).not.to.throw(); - }); - - it("selects value based on their stringified equality when they're not objects", () => { - const { getAllByRole } = render( - <Select multiple open value={['10', '20']}> - <MenuItem value={10}>Ten</MenuItem> - <MenuItem value={20}>Twenty</MenuItem> - <MenuItem value={30}>Thirty</MenuItem> - </Select>, - ); - const options = getAllByRole('option'); - - expect(options[0]).to.have.attribute('aria-selected', 'true'); - expect(options[1]).to.have.attribute('aria-selected', 'true'); - expect(options[2]).not.to.have.attribute('aria-selected', 'true'); - }); - - it("selects values based on strict equality if they're objects", () => { - const obj1 = { id: 1 }; - const obj2 = { id: 2 }; - const obj3 = { id: 3 }; - const { getAllByRole } = render( - <Select multiple open value={[obj1, obj3]}> - <MenuItem value={obj1}>ID: 1</MenuItem> - <MenuItem value={obj2}>ID: 2</MenuItem> - <MenuItem value={obj3}>ID: 3</MenuItem> - </Select>, - ); - const options = getAllByRole('option'); - - expect(options[0]).to.have.attribute('aria-selected', 'true'); - expect(options[1]).not.to.have.attribute('aria-selected', 'true'); - expect(options[2]).to.have.attribute('aria-selected', 'true'); - }); - - describe('errors', () => { - it('should throw if non array', function test() { - // TODO is this fixed? - if (!/jsdom/.test(window.navigator.userAgent)) { - // can't catch render errors in the browser for unknown reason - // tried try-catch + error boundary + window onError preventDefault - this.skip(); - } - - const errorRef = React.createRef(); - expect(() => { - render( - <ErrorBoundary ref={errorRef}> - <Select multiple value="10,20"> - <MenuItem value="10">Ten</MenuItem> - <MenuItem value="20">Twenty</MenuItem> - <MenuItem value="30">Thirty</MenuItem> - </Select> - </ErrorBoundary>, - ); - }).toErrorDev([ - 'MUI: The `value` prop must be an array', - // React 18 Strict Effects run mount effects twice - React.version.startsWith('18') && 'MUI: The `value` prop must be an array', - 'The above error occurred in the <ForwardRef(SelectInput)> component', - ]); - const { - current: { errors }, - } = errorRef; - expect(errors).to.have.length(1); - expect(errors[0].toString()).to.include('MUI: The `value` prop must be an array'); - }); - }); - - describe('prop: onChange', () => { - it('should call onChange when clicking an item', () => { - function ControlledSelectInput(props) { - const { onChange } = props; - const [values, clickedValue] = React.useReducer((currentValues, valueClicked) => { - if (currentValues.indexOf(valueClicked) === -1) { - return currentValues.concat(valueClicked); - } - return currentValues.filter((value) => { - return value !== valueClicked; - }); - }, []); - - const handleChange = (event) => { - onChange(event); - clickedValue(event.target.value); - }; - - return ( - <Select multiple name="age" onChange={handleChange} value={values}> - <MenuItem value={10}>Ten</MenuItem> - <MenuItem value={20}>Ten</MenuItem> - <MenuItem value={30}>Ten</MenuItem> - </Select> - ); - } - const onChange = stub().callsFake((event) => { - return { - name: event.target.name, - value: event.target.value, - }; - }); - const { getByRole, getAllByRole } = render(<ControlledSelectInput onChange={onChange} />); - - fireEvent.mouseDown(getByRole('combobox')); - const options = getAllByRole('option'); - fireEvent.click(options[2]); - - expect(onChange.callCount).to.equal(1); - expect(onChange.firstCall.returnValue).to.deep.equal({ name: 'age', value: [30] }); - - act(() => { - options[0].click(); - }); - - expect(onChange.callCount).to.equal(2); - expect(onChange.secondCall.returnValue).to.deep.equal({ name: 'age', value: [30, 10] }); - }); - }); - - it('should apply multiple class to `select` slot', () => { - const { container } = render( - <Select multiple open value={[10, 30]}> - <MenuItem value={10}>Ten</MenuItem> - <MenuItem value={20}>Twenty</MenuItem> - <MenuItem value={30}>Thirty</MenuItem> - </Select>, - ); - - expect(container.querySelector(`.${classes.select}`)).to.have.class(classes.multiple); - }); - - it('should be able to override `multiple` rule name in `select` slot', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const selectStyle = { - marginLeft: '10px', - marginTop: '10px', - }; - - const multipleStyle = { - marginTop: '14px', - }; - - const theme = createTheme({ - components: { - MuiSelect: { - styleOverrides: { - select: selectStyle, - multiple: multipleStyle, - }, - }, - }, - }); - - const { container } = render( - <ThemeProvider theme={theme}> - <Select open value={['first']} multiple> - <MenuItem value="first" /> - <MenuItem value="second" /> - </Select> - </ThemeProvider>, - ); - - const combinedStyle = { ...selectStyle, ...multipleStyle }; - - expect(container.getElementsByClassName(classes.select)[0]).to.toHaveComputedStyle( - combinedStyle, - ); - }); - }); - - describe('prop: autoFocus', () => { - it('should focus select after Select did mount', () => { - const { getByRole } = render(<Select value="" autoFocus />); - - expect(getByRole('combobox')).toHaveFocus(); - }); - }); - - it('should be able to return the input node via a ref object', () => { - const ref = React.createRef(); - const { setProps } = render(<Select inputProps={{ ref }} value="" />); - - expect(ref.current.node).to.have.tagName('input'); - - setProps({ - value: '', - }); - expect(ref.current.node).to.have.tagName('input'); - }); - - describe('prop: inputRef', () => { - it('should be able to return the input node via a ref object', () => { - const ref = React.createRef(); - render(<Select inputRef={ref} value="" />); - - expect(ref.current.node).to.have.tagName('input'); - }); - - // TODO: This might be confusing a prop called input!Ref can imperatively - // focus a button. This implies <input type="button" /> is still used. - it('should be able focus the trigger imperatively', () => { - const ref = React.createRef(); - const { getByRole } = render(<Select inputRef={ref} value="" />); - - act(() => { - ref.current.focus(); - }); - - expect(getByRole('combobox')).toHaveFocus(); - }); - }); - - describe('prop: name', () => { - it('should have no id when name is not provided', () => { - const { getByRole } = render(<Select value="" />); - - expect(getByRole('combobox')).not.to.have.attribute('id'); - }); - - it('should have select-`name` id when name is provided', () => { - const { getByRole } = render(<Select name="foo" value="" />); - - expect(getByRole('combobox')).to.have.attribute('id', 'mui-component-select-foo'); - }); - }); - - describe('prop: native', () => { - it('renders a <select />', () => { - const { container } = render(<Select native />); - - expect(container.querySelector('select')).not.to.equal(null); - }); - - it('can be labelled with a <label />', () => { - const { getByRole } = render( - <React.Fragment> - <label htmlFor="select">A select</label> - <Select id="select" native /> - </React.Fragment>, - ); - - expect(getByRole('combobox', { name: 'A select' })).to.have.property('tagName', 'SELECT'); - }); - }); - - it('prevents the default when releasing Space on the children', () => { - const keyUpSpy = spy(); - render( - <Select value="one" open> - <MenuItem onKeyUp={keyUpSpy} value="one"> - One - </MenuItem> - </Select>, - ); - - fireEvent.keyUp(screen.getAllByRole('option')[0], { key: ' ' }); - - expect(keyUpSpy.callCount).to.equal(1); - expect(keyUpSpy.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('should pass onClick prop to MenuItem', () => { - const onClick = spy(); - const { getAllByRole } = render( - <Select open value="30"> - <MenuItem onClick={onClick} value={30}> - Thirty - </MenuItem> - </Select>, - ); - - const options = getAllByRole('option'); - fireEvent.click(options[0]); - - expect(onClick.callCount).to.equal(1); - }); - - // https://github.com/testing-library/react-testing-library/issues/322 - // https://twitter.com/devongovett/status/1248306411508916224 - it('should handle the browser autofill event and simple testing-library API', () => { - const onChangeHandler = spy(); - const { container, getByRole } = render( - <Select onChange={onChangeHandler} defaultValue="germany" name="country"> - <MenuItem value="france">France</MenuItem> - <MenuItem value="germany">Germany</MenuItem> - <MenuItem value="china">China</MenuItem> - </Select>, - ); - fireEvent.change(container.querySelector('input[name="country"]'), { - target: { - value: 'france', - }, - }); - - expect(onChangeHandler.calledOnce).to.equal(true); - expect(getByRole('combobox')).to.have.text('France'); - }); - - it('should support native form validation', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - // see https://github.com/jsdom/jsdom/issues/123 - this.skip(); - } - - const handleSubmit = spy((event) => { - // avoid karma reload. - event.preventDefault(); - }); - function Form(props) { - return ( - <form onSubmit={handleSubmit}> - <Select required name="country" {...props}> - <MenuItem value="" /> - <MenuItem value="france">France</MenuItem> - <MenuItem value="germany">Germany</MenuItem> - <MenuItem value="china">China</MenuItem> - </Select> - <button type="submit" /> - </form> - ); - } - const { container, setProps } = render(<Form value="" />); - - fireEvent.click(container.querySelector('button[type=submit]')); - expect(handleSubmit.callCount).to.equal(0, 'the select is empty it should disallow submit'); - - setProps({ value: 'france' }); - fireEvent.click(container.querySelector('button[type=submit]')); - expect(handleSubmit.callCount).to.equal(1); - }); - - it('should programmatically focus the select', () => { - const { getByRole } = render( - <Select - value={1} - inputRef={(input) => { - if (input !== null) { - input.focus(); - } - }} - > - <MenuItem value={1}>1</MenuItem> - <MenuItem value={2}>2</MenuItem> - </Select>, - ); - expect(document.activeElement).to.equal(getByRole('combobox')); - }); - - it('should not override the event.target on mouse events', () => { - const handleChange = spy(); - const handleClick = spy(); - render( - <div onClick={handleClick}> - <Select open onChange={handleChange} value="second"> - <MenuItem value="first" /> - <MenuItem value="second" /> - </Select> - </div>, - ); - - const options = screen.getAllByRole('option'); - options[0].click(); - - expect(handleChange.callCount).to.equal(1); - expect(handleClick.callCount).to.equal(1); - expect(handleClick.firstCall.args[0]).to.have.property('target', options[0]); - }); - - it('should only select options', () => { - const handleChange = spy(); - render( - <Select open onChange={handleChange} value="second"> - <MenuItem value="first" /> - <Divider /> - <MenuItem value="second" /> - </Select>, - ); - - const divider = document.querySelector('hr'); - divider.click(); - expect(handleChange.callCount).to.equal(0); - }); - - it('slots overrides should work', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const rootStyle = { - marginTop: '15px', - }; - - const iconStyle = { - marginTop: '13px', - }; - - const nativeInputStyle = { - marginTop: '10px', - }; - - const selectStyle = { - marginLeft: '10px', - marginTop: '12px', - }; - - const multipleStyle = { - marginTop: '14px', - }; - - const theme = createTheme({ - components: { - MuiSelect: { - styleOverrides: { - root: rootStyle, - select: selectStyle, - icon: iconStyle, - nativeInput: nativeInputStyle, - multiple: multipleStyle, - }, - }, - }, - }); - - const { container, getByTestId } = render( - <ThemeProvider theme={theme}> - <Select open value="first" data-testid="select"> - <MenuItem value="first" /> - <MenuItem value="second" /> - </Select> - </ThemeProvider>, - ); - - expect(getByTestId('select')).toHaveComputedStyle(rootStyle); - expect(container.getElementsByClassName(classes.icon)[0]).to.toHaveComputedStyle(iconStyle); - expect(container.getElementsByClassName(classes.nativeInput)[0]).to.toHaveComputedStyle( - nativeInputStyle, - ); - expect(container.getElementsByClassName(classes.select)[0]).to.toHaveComputedStyle(selectStyle); - }); - - describe('theme styleOverrides:', () => { - it('should override with error style when `native select` has `error` state', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const iconStyle = { color: 'rgb(255, 0, 0)' }; - - const theme = createTheme({ - components: { - MuiNativeSelect: { - styleOverrides: { - icon: (props) => ({ - ...(props.ownerState.error && iconStyle), - }), - }, - }, - }, - }); - - const { container } = render( - <ThemeProvider theme={theme}> - <Select value="first" error IconComponent="div" native> - <option value="first">first</option> - </Select> - </ThemeProvider>, - ); - - expect(container.querySelector(`.${nativeSelectClasses.icon}`)).toHaveComputedStyle( - iconStyle, - ); - }); - - it('should override with error style when `select` has `error` state', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const iconStyle = { color: 'rgb(255, 0, 0)' }; - const selectStyle = { color: 'rgb(255, 192, 203)' }; - - const theme = createTheme({ - components: { - MuiSelect: { - styleOverrides: { - icon: (props) => ({ - ...(props.ownerState.error && iconStyle), - }), - select: (props) => ({ - ...(props.ownerState.error && selectStyle), - }), - }, - }, - }, - }); - - const { container } = render( - <ThemeProvider theme={theme}> - <Select value="" error IconComponent="div" /> - </ThemeProvider>, - ); - expect(container.querySelector(`.${classes.select}`)).toHaveComputedStyle(selectStyle); - expect(container.querySelector(`.${classes.icon}`)).toHaveComputedStyle(iconStyle); - }); - }); - - ['standard', 'outlined', 'filled'].forEach((variant) => { - it(`variant overrides should work for "${variant}" variant`, function test() { - const theme = createTheme({ - components: { - MuiSelect: { - variants: [ - { - props: { - variant, - }, - style: { - fontWeight: '200', - }, - }, - ], - }, - }, - }); - - const { getByTestId } = render( - <ThemeProvider theme={theme}> - <Select variant={variant} value="first" data-testid="input"> - <MenuItem value="first" /> - <MenuItem value="second" /> - </Select> - </ThemeProvider>, - ); - - expect(getByTestId('input')).to.toHaveComputedStyle({ - fontWeight: '200', - }); - }); - }); - - describe('prop: input', () => { - it('merges `ref` of `Select` and `input`', () => { - const Input = React.forwardRef(function Input(props, ref) { - const { inputProps, inputComponent: Component, ...other } = props; - - React.useImperativeHandle(ref, () => { - return { refToInput: true }; - }); - - return <Component {...inputProps} {...other} ref={ref} />; - }); - const inputRef = React.createRef(); - const selectRef = React.createRef(); - render( - <Select input={<Input data-testid="input" ref={inputRef} value="" />} ref={selectRef} />, - ); - - expect(inputRef).to.deep.equal({ current: { refToInput: true } }); - expect(selectRef).to.deep.equal({ current: { refToInput: true } }); - }); - - it('should merge the class names', () => { - const { getByTestId } = render( - <Select - className="foo" - input={<OutlinedInput data-testid="root" className="bar" />} - value="" - />, - ); - expect(getByTestId('root')).to.have.class('foo'); - expect(getByTestId('root')).to.have.class('bar'); - }); - }); - - it('should not focus select when clicking an arbitrary element with id="undefined"', () => { - const { getByRole, getByTestId } = render( - <React.Fragment> - <div id="undefined" data-testid="test-element" /> - <Select value="" /> - </React.Fragment>, - ); - - fireEvent.click(getByTestId('test-element')); - - expect(getByRole('combobox')).not.toHaveFocus(); - }); -}); diff --git a/packages/mui-material-next/src/Select/SelectInput.d.ts b/packages/mui-material-next/src/Select/SelectInput.d.ts deleted file mode 100644 index 66e95a9c94a53c..00000000000000 --- a/packages/mui-material-next/src/Select/SelectInput.d.ts +++ /dev/null @@ -1,47 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -// TODO v6: replace with material-next MenuProps when available https://github.com/mui/material-ui/pull/38934 -import { MenuProps } from '@mui/material/Menu'; -import { Theme } from '..'; - -/** - * The change can be caused by different kind of events. - * The type of event depends on what caused the change. - * For example, when the browser auto-fills the `Select` you'll receive a `React.ChangeEvent`. - */ -export type SelectChangeEvent<Value = string> = - | (Event & { target: { value: Value; name: string } }) - | React.ChangeEvent<HTMLInputElement>; - -export interface SelectInputProps<Value = unknown> { - autoFocus?: boolean; - autoWidth: boolean; - defaultOpen?: boolean; - disabled?: boolean; - error?: boolean; - IconComponent?: React.ElementType; - inputRef?: ( - ref: HTMLSelectElement | { node: HTMLInputElement; value: SelectInputProps<Value>['value'] }, - ) => void; - MenuProps?: Partial<MenuProps>; - multiple: boolean; - name?: string; - native: boolean; - onBlur?: React.FocusEventHandler<any>; - onChange?: (event: SelectChangeEvent<Value>, child: React.ReactNode) => void; - onClose?: (event: React.SyntheticEvent) => void; - onFocus?: React.FocusEventHandler<any>; - onOpen?: (event: React.SyntheticEvent) => void; - open?: boolean; - readOnly?: boolean; - renderValue?: (value: SelectInputProps<Value>['value']) => React.ReactNode; - SelectDisplayProps?: React.HTMLAttributes<HTMLDivElement>; - sx?: SxProps<Theme>; - tabIndex?: number; - value?: Value; - variant?: 'standard' | 'outlined' | 'filled'; -} - -declare const SelectInput: React.JSXElementConstructor<SelectInputProps>; - -export default SelectInput; diff --git a/packages/mui-material-next/src/Select/SelectInput.js b/packages/mui-material-next/src/Select/SelectInput.js deleted file mode 100644 index a90bd619bbe932..00000000000000 --- a/packages/mui-material-next/src/Select/SelectInput.js +++ /dev/null @@ -1,727 +0,0 @@ -'use client'; -import * as React from 'react'; -import { isFragment } from 'react-is'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import MuiError from '@mui/internal-babel-macros/MuiError.macro'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { - refType, - unstable_useId as useId, - unstable_capitalize as capitalize, - unstable_ownerDocument as ownerDocument, - unstable_useForkRef as useForkRef, - unstable_useControlled as useControlled, -} from '@mui/utils'; -import { shouldForwardProp } from '@mui/system'; -import { - nativeSelectSelectStyles, - nativeSelectIconStyles, -} from '@mui/material/NativeSelect/NativeSelectInput'; -// TODO v6: replace with material-next Menu component when available https://github.com/mui/material-ui/pull/38934 -import Menu from '@mui/material/Menu/Menu'; -import { isFilled } from '../InputBase/utils'; -import styled from '../styles/styled'; -import selectClasses, { getSelectUtilityClasses } from './selectClasses'; - -const SelectSelect = styled('div', { - name: 'MuiSelect', - slot: 'Select', - overridesResolver: (props, styles) => { - const { ownerState } = props; - return [ - // Win specificity over the input base - { [`&.${selectClasses.select}`]: styles.select }, - { [`&.${selectClasses.select}`]: styles[ownerState.variant] }, - { [`&.${selectClasses.error}`]: styles.error }, - { [`&.${selectClasses.multiple}`]: styles.multiple }, - ]; - }, -})(nativeSelectSelectStyles, { - // Win specificity over the input base - [`&.${selectClasses.select}`]: { - height: 'auto', // Resets for multiple select with chips - minHeight: '1.4375em', // Required for select\text-field height consistency - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - overflow: 'hidden', - }, -}); - -const SelectIcon = styled('svg', { - name: 'MuiSelect', - slot: 'Icon', - overridesResolver: (props, styles) => { - const { ownerState } = props; - return [ - styles.icon, - ownerState.variant && styles[`icon${capitalize(ownerState.variant)}`], - ownerState.open && styles.iconOpen, - ]; - }, -})(nativeSelectIconStyles); - -const SelectNativeInput = styled('input', { - shouldForwardProp: (prop) => shouldForwardProp(prop) && prop !== 'classes', - name: 'MuiSelect', - slot: 'NativeInput', - overridesResolver: (props, styles) => styles.nativeInput, -})({ - bottom: 0, - left: 0, - position: 'absolute', - opacity: 0, - pointerEvents: 'none', - width: '100%', - boxSizing: 'border-box', -}); - -function areEqualValues(a, b) { - if (typeof b === 'object' && b !== null) { - return a === b; - } - - // The value could be a number, the DOM will stringify it anyway. - return String(a) === String(b); -} - -function isEmpty(display) { - return display == null || (typeof display === 'string' && !display.trim()); -} - -const useUtilityClasses = (ownerState) => { - const { classes, variant, disabled, multiple, open, error } = ownerState; - - const slots = { - select: ['select', variant, disabled && 'disabled', multiple && 'multiple', error && 'error'], - icon: ['icon', `icon${capitalize(variant)}`, open && 'iconOpen', disabled && 'disabled'], - nativeInput: ['nativeInput'], - }; - - return composeClasses(slots, getSelectUtilityClasses, classes); -}; - -/** - * @ignore - internal component. - */ -const SelectInput = React.forwardRef(function SelectInput(props, ref) { - const { - 'aria-describedby': ariaDescribedby, - 'aria-label': ariaLabel, - autoFocus, - autoWidth, - children, - className, - defaultOpen, - defaultValue, - disabled, - displayEmpty, - error = false, - IconComponent, - inputRef: inputRefProp, - labelId, - MenuProps = {}, - multiple, - name, - onBlur, - onChange, - onClose, - onFocus, - onOpen, - open: openProp, - readOnly, - renderValue, - SelectDisplayProps = {}, - tabIndex: tabIndexProp, - // catching `type` from Input which makes no sense for SelectInput - type, - value: valueProp, - variant = 'standard', - ...other - } = props; - - const [value, setValueState] = useControlled({ - controlled: valueProp, - default: defaultValue, - name: 'Select', - }); - const [openState, setOpenState] = useControlled({ - controlled: openProp, - default: defaultOpen, - name: 'Select', - }); - - const inputRef = React.useRef(null); - const displayRef = React.useRef(null); - const [displayNode, setDisplayNode] = React.useState(null); - const { current: isOpenControlled } = React.useRef(openProp != null); - const [menuMinWidthState, setMenuMinWidthState] = React.useState(); - const handleRef = useForkRef(ref, inputRefProp); - - const handleDisplayRef = React.useCallback((node) => { - displayRef.current = node; - - if (node) { - setDisplayNode(node); - } - }, []); - - const anchorElement = displayNode?.parentNode; - - React.useImperativeHandle( - handleRef, - () => ({ - focus: () => { - displayRef.current.focus(); - }, - node: inputRef.current, - value, - }), - [value], - ); - - // Resize menu on `defaultOpen` automatic toggle. - React.useEffect(() => { - if (defaultOpen && openState && displayNode && !isOpenControlled) { - setMenuMinWidthState(autoWidth ? null : anchorElement.clientWidth); - displayRef.current.focus(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [displayNode, autoWidth]); - // `isOpenControlled` is ignored because the component should never switch between controlled and uncontrolled modes. - // `defaultOpen` and `openState` are ignored to avoid unnecessary callbacks. - React.useEffect(() => { - if (autoFocus) { - displayRef.current.focus(); - } - }, [autoFocus]); - - React.useEffect(() => { - if (!labelId) { - return undefined; - } - const label = ownerDocument(displayRef.current).getElementById(labelId); - if (label) { - const handler = () => { - if (getSelection().isCollapsed) { - displayRef.current.focus(); - } - }; - label.addEventListener('click', handler); - return () => { - label.removeEventListener('click', handler); - }; - } - return undefined; - }, [labelId]); - - const update = (open, event) => { - if (open) { - if (onOpen) { - onOpen(event); - } - } else if (onClose) { - onClose(event); - } - - if (!isOpenControlled) { - setMenuMinWidthState(autoWidth ? null : anchorElement.clientWidth); - setOpenState(open); - } - }; - - const handleMouseDown = (event) => { - // Ignore everything but left-click - if (event.button !== 0) { - return; - } - // Hijack the default focus behavior. - event.preventDefault(); - displayRef.current.focus(); - - update(true, event); - }; - - const handleClose = (event) => { - update(false, event); - }; - - const childrenArray = React.Children.toArray(children); - - // Support autofill. - const handleChange = (event) => { - const child = childrenArray.find((childItem) => childItem.props.value === event.target.value); - - if (child === undefined) { - return; - } - - setValueState(child.props.value); - - if (onChange) { - onChange(event, child); - } - }; - - const handleItemClick = (child) => (event) => { - let newValue; - - // We use the tabindex attribute to signal the available options. - if (!event.currentTarget.hasAttribute('tabindex')) { - return; - } - - if (multiple) { - newValue = Array.isArray(value) ? value.slice() : []; - const itemIndex = value.indexOf(child.props.value); - if (itemIndex === -1) { - newValue.push(child.props.value); - } else { - newValue.splice(itemIndex, 1); - } - } else { - newValue = child.props.value; - } - - if (child.props.onClick) { - child.props.onClick(event); - } - - if (value !== newValue) { - setValueState(newValue); - - if (onChange) { - // Redefine target to allow name and value to be read. - // This allows seamless integration with the most popular form libraries. - // https://github.com/mui/material-ui/issues/13485#issuecomment-676048492 - // Clone the event to not override `target` of the original event. - const nativeEvent = event.nativeEvent || event; - const clonedEvent = new nativeEvent.constructor(nativeEvent.type, nativeEvent); - - Object.defineProperty(clonedEvent, 'target', { - writable: true, - value: { value: newValue, name }, - }); - onChange(clonedEvent, child); - } - } - - if (!multiple) { - update(false, event); - } - }; - - const handleKeyDown = (event) => { - if (!readOnly) { - const validKeys = [ - ' ', - 'ArrowUp', - 'ArrowDown', - // The native select doesn't respond to enter on macOS, but it's recommended by - // https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-select-only/ - 'Enter', - ]; - - if (validKeys.indexOf(event.key) !== -1) { - event.preventDefault(); - update(true, event); - } - } - }; - - const open = displayNode !== null && openState; - - const handleBlur = (event) => { - // if open event.stopImmediatePropagation - if (!open && onBlur) { - // Preact support, target is read only property on a native event. - Object.defineProperty(event, 'target', { writable: true, value: { value, name } }); - onBlur(event); - } - }; - - delete other['aria-invalid']; - - let display; - let displaySingle; - const displayMultiple = []; - let computeDisplay = false; - let foundMatch = false; - - // No need to display any value if the field is empty. - if (isFilled({ value }) || displayEmpty) { - if (renderValue) { - display = renderValue(value); - } else { - computeDisplay = true; - } - } - - const items = childrenArray.map((child) => { - if (!React.isValidElement(child)) { - return null; - } - - if (process.env.NODE_ENV !== 'production') { - if (isFragment(child)) { - console.error( - [ - "MUI: The Select component doesn't accept a Fragment as a child.", - 'Consider providing an array instead.', - ].join('\n'), - ); - } - } - - let selected; - - if (multiple) { - if (!Array.isArray(value)) { - throw new MuiError( - 'MUI: The `value` prop must be an array ' + - 'when using the `Select` component with `multiple`.', - ); - } - - selected = value.some((v) => areEqualValues(v, child.props.value)); - if (selected && computeDisplay) { - displayMultiple.push(child.props.children); - } - } else { - selected = areEqualValues(value, child.props.value); - if (selected && computeDisplay) { - displaySingle = child.props.children; - } - } - - if (selected) { - foundMatch = true; - } - - return React.cloneElement(child, { - 'aria-selected': selected ? 'true' : 'false', - onClick: handleItemClick(child), - onKeyUp: (event) => { - if (event.key === ' ') { - // otherwise our MenuItems dispatches a click event - // it's not behavior of the native <option> and causes - // the select to close immediately since we open on space keydown - event.preventDefault(); - } - - if (child.props.onKeyUp) { - child.props.onKeyUp(event); - } - }, - role: 'option', - selected, - value: undefined, // The value is most likely not a valid HTML attribute. - 'data-value': child.props.value, // Instead, we provide it as a data attribute. - }); - }); - - if (process.env.NODE_ENV !== 'production') { - // eslint-disable-next-line react-hooks/rules-of-hooks - React.useEffect(() => { - if (!foundMatch && !multiple && value !== '') { - const values = childrenArray.map((child) => child.props.value); - console.warn( - [ - `MUI: You have provided an out-of-range value \`${value}\` for the select ${ - name ? `(name="${name}") ` : '' - }component.`, - "Consider providing a value that matches one of the available options or ''.", - `The available values are ${ - values - .filter((x) => x != null) - .map((x) => `\`${x}\``) - .join(', ') || '""' - }.`, - ].join('\n'), - ); - } - }, [foundMatch, childrenArray, multiple, name, value]); - } - - if (computeDisplay) { - if (multiple) { - if (displayMultiple.length === 0) { - display = null; - } else { - display = displayMultiple.reduce((output, child, index) => { - output.push(child); - if (index < displayMultiple.length - 1) { - output.push(', '); - } - return output; - }, []); - } - } else { - display = displaySingle; - } - } - - // Avoid performing a layout computation in the render method. - let menuMinWidth = menuMinWidthState; - - if (!autoWidth && isOpenControlled && displayNode) { - menuMinWidth = anchorElement.clientWidth; - } - - let tabIndex; - if (typeof tabIndexProp !== 'undefined') { - tabIndex = tabIndexProp; - } else { - tabIndex = disabled ? null : 0; - } - - const buttonId = SelectDisplayProps.id || (name ? `mui-component-select-${name}` : undefined); - - const ownerState = { - ...props, - variant, - value, - open, - error, - }; - - const classes = useUtilityClasses(ownerState); - - const paperProps = { - ...MenuProps.PaperProps, - ...MenuProps.slotProps?.paper, - }; - - const listboxId = useId(); - - return ( - <React.Fragment> - <SelectSelect - ref={handleDisplayRef} - tabIndex={tabIndex} - role="combobox" - aria-controls={listboxId} - aria-disabled={disabled ? 'true' : undefined} - aria-expanded={open ? 'true' : 'false'} - aria-haspopup="listbox" - aria-label={ariaLabel} - aria-labelledby={[labelId, buttonId].filter(Boolean).join(' ') || undefined} - aria-describedby={ariaDescribedby} - onKeyDown={handleKeyDown} - onMouseDown={disabled || readOnly ? null : handleMouseDown} - onBlur={handleBlur} - onFocus={onFocus} - {...SelectDisplayProps} - ownerState={ownerState} - className={clsx(SelectDisplayProps.className, classes.select, className)} - // The id is required for proper a11y - id={buttonId} - > - {/* So the vertical align positioning algorithm kicks in. */} - {isEmpty(display) ? ( - // notranslate needed while Google Translate will not fix zero-width space issue - <span className="notranslate">​</span> - ) : ( - display - )} - </SelectSelect> - <SelectNativeInput - aria-invalid={error} - value={Array.isArray(value) ? value.join(',') : value} - name={name} - ref={inputRef} - aria-hidden - onChange={handleChange} - tabIndex={-1} - disabled={disabled} - className={classes.nativeInput} - autoFocus={autoFocus} - ownerState={ownerState} - {...other} - /> - <SelectIcon as={IconComponent} className={classes.icon} ownerState={ownerState} /> - <Menu - id={`menu-${name || ''}`} - anchorEl={anchorElement} - open={open} - onClose={handleClose} - anchorOrigin={{ - vertical: 'bottom', - horizontal: 'center', - }} - transformOrigin={{ - vertical: 'top', - horizontal: 'center', - }} - {...MenuProps} - MenuListProps={{ - 'aria-labelledby': labelId, - role: 'listbox', - 'aria-multiselectable': multiple ? 'true' : undefined, - disableListWrap: true, - id: listboxId, - ...MenuProps.MenuListProps, - }} - slotProps={{ - ...MenuProps.slotProps, - paper: { - ...paperProps, - style: { - minWidth: menuMinWidth, - ...(paperProps != null ? paperProps.style : null), - }, - }, - }} - > - {items} - </Menu> - </React.Fragment> - ); -}); - -SelectInput.propTypes = { - /** - * @ignore - */ - 'aria-describedby': PropTypes.string, - /** - * @ignore - */ - 'aria-label': PropTypes.string, - /** - * @ignore - */ - autoFocus: PropTypes.bool, - /** - * If `true`, the width of the popover will automatically be set according to the items inside the - * menu, otherwise it will be at least the width of the select input. - */ - autoWidth: PropTypes.bool, - /** - * The option elements to populate the select with. - * Can be some `<MenuItem>` elements. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * The CSS class name of the select element. - */ - className: PropTypes.string, - /** - * If `true`, the component is toggled on mount. Use when the component open state is not controlled. - * You can only use it when the `native` prop is `false` (default). - */ - defaultOpen: PropTypes.bool, - /** - * The default value. Use when the component is not controlled. - */ - defaultValue: PropTypes.any, - /** - * If `true`, the select is disabled. - */ - disabled: PropTypes.bool, - /** - * If `true`, the selected item is displayed even if its value is empty. - */ - displayEmpty: PropTypes.bool, - /** - * If `true`, the `select input` will indicate an error. - */ - error: PropTypes.bool, - /** - * The icon that displays the arrow. - */ - IconComponent: PropTypes.elementType.isRequired, - /** - * Imperative handle implementing `{ value: T, node: HTMLElement, focus(): void }` - * Equivalent to `ref` - */ - inputRef: refType, - /** - * The ID of an element that acts as an additional label. The Select will - * be labelled by the additional label and the selected value. - */ - labelId: PropTypes.string, - /** - * Props applied to the [`Menu`](/material-ui/api/menu/) element. - */ - MenuProps: PropTypes.object, - /** - * If `true`, `value` must be an array and the menu will support multiple selections. - */ - multiple: PropTypes.bool, - /** - * Name attribute of the `select` or hidden `input` element. - */ - name: PropTypes.string, - /** - * @ignore - */ - onBlur: PropTypes.func, - /** - * Callback fired when a menu item is selected. - * - * @param {object} event The event source of the callback. - * You can pull out the new value by accessing `event.target.value` (any). - * @param {object} [child] The react element that was selected. - */ - onChange: PropTypes.func, - /** - * Callback fired when the component requests to be closed. - * Use in controlled mode (see open). - * - * @param {object} event The event source of the callback. - */ - onClose: PropTypes.func, - /** - * @ignore - */ - onFocus: PropTypes.func, - /** - * Callback fired when the component requests to be opened. - * Use in controlled mode (see open). - * - * @param {object} event The event source of the callback. - */ - onOpen: PropTypes.func, - /** - * If `true`, the component is shown. - */ - open: PropTypes.bool, - /** - * @ignore - */ - readOnly: PropTypes.bool, - /** - * Render the selected value. - * - * @param {any} value The `value` provided to the component. - * @returns {ReactNode} - */ - renderValue: PropTypes.func, - /** - * Props applied to the clickable div element. - */ - SelectDisplayProps: PropTypes.object, - /** - * @ignore - */ - tabIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - /** - * @ignore - */ - type: PropTypes.any, - /** - * The input value. - */ - value: PropTypes.any, - /** - * The variant to use. - */ - variant: PropTypes.oneOf(['standard', 'outlined', 'filled']), -}; - -export default SelectInput; diff --git a/packages/mui-material-next/src/Select/index.d.ts b/packages/mui-material-next/src/Select/index.d.ts deleted file mode 100644 index cda0a7d7864f95..00000000000000 --- a/packages/mui-material-next/src/Select/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './Select'; -export * from './Select'; - -export { default as selectClasses } from './selectClasses'; -export * from './selectClasses'; diff --git a/packages/mui-material-next/src/Select/index.js b/packages/mui-material-next/src/Select/index.js deleted file mode 100644 index 9c160bf8a57d7f..00000000000000 --- a/packages/mui-material-next/src/Select/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './Select'; - -export { default as selectClasses } from './selectClasses'; -export * from './selectClasses'; diff --git a/packages/mui-material-next/src/Select/selectClasses.ts b/packages/mui-material-next/src/Select/selectClasses.ts deleted file mode 100644 index 4799ddcab6c649..00000000000000 --- a/packages/mui-material-next/src/Select/selectClasses.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface SelectClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the select component `select` class. */ - select: string; - /** Styles applied to the select component if `multiple={true}`. */ - multiple: string; - /** Styles applied to the select component if `variant="filled"`. */ - filled: string; - /** Styles applied to the select component if `variant="outlined"`. */ - outlined: string; - /** Styles applied to the select component if `variant="standard"`. */ - standard: string; - /** State class applied to the select component `disabled` class. */ - disabled: string; - /** Styles applied to the icon component. */ - icon: string; - /** Styles applied to the icon component if the popup is open. */ - iconOpen: string; - /** Styles applied to the icon component if `variant="filled"`. */ - iconFilled: string; - /** Styles applied to the icon component if `variant="outlined"`. */ - iconOutlined: string; - /** Styles applied to the icon component if `variant="standard"`. */ - iconStandard: string; - /** Styles applied to the underlying native input component. */ - nativeInput: string; - /** State class applied to the root element if `error={true}`. */ - error: string; -} - -export type SelectClassKey = keyof SelectClasses; - -export function getSelectUtilityClasses(slot: string): string { - return generateUtilityClass('MuiSelect', slot); -} - -const selectClasses: SelectClasses = generateUtilityClasses('MuiSelect', [ - 'root', - 'select', - 'multiple', - 'filled', - 'outlined', - 'standard', - 'disabled', - 'focused', - 'icon', - 'iconOpen', - 'iconFilled', - 'iconOutlined', - 'iconStandard', - 'nativeInput', - 'error', -]); - -export default selectClasses; diff --git a/packages/mui-material-next/src/Slider/Slider.spec.tsx b/packages/mui-material-next/src/Slider/Slider.spec.tsx deleted file mode 100644 index eaeb4e7bcbc73c..00000000000000 --- a/packages/mui-material-next/src/Slider/Slider.spec.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import * as React from 'react'; -import Slider from '@mui/material-next/Slider'; -import { SliderOwnerState } from './Slider.types'; - -function testOnChange() { - function handleSliderChange(event: Event, value: unknown) {} - function handleSliderChangeCommitted(event: React.SyntheticEvent | Event, value: unknown) {} - <Slider onChange={handleSliderChange} onChangeCommitted={handleSliderChangeCommitted} />; - - function handleElementChange(event: React.ChangeEvent) {} - <Slider onChange={handleElementChange} onChangeCommitted={handleElementChange} />; -} - -<Slider track="inverted" />; - -// slotProps as object -<Slider - slotProps={{ - root: { onMouseDown: () => 'onMouseDown event triggered' }, - input: { disabled: true }, - mark: { onClick: () => 'clicked' }, - markLabel: { className: 'markLabel' }, - rail: { className: 'rail' }, - thumb: { className: 'thumb' }, - valueLabel: { valueLabelDisplay: 'auto' }, - }} -/>; - -// slotProps as function -<Slider - slotProps={{ - root: ({ color }: SliderOwnerState) => ({ - className: color === 'primary' ? 'root_primary' : 'root_secondary', - }), - input: ({ size }: SliderOwnerState) => ({ disabled: size === 'medium' }), - mark: ({ marked }: SliderOwnerState) => ({ - className: marked ? 'marked' : '', - }), - markLabel: ({ max }: SliderOwnerState) => ({ - className: max === 99 ? 'red' : 'normal', - }), - rail: ({ dragging }: SliderOwnerState) => ({ - className: dragging ? 'rail' : '', - }), - thumb: ({ orientation }: SliderOwnerState) => ({ - className: orientation === 'vertical' ? 'thumb_vertical' : '', - }), - }} -/>; diff --git a/packages/mui-material-next/src/Slider/Slider.test.js b/packages/mui-material-next/src/Slider/Slider.test.js deleted file mode 100644 index 05e0cd28116e01..00000000000000 --- a/packages/mui-material-next/src/Slider/Slider.test.js +++ /dev/null @@ -1,1503 +0,0 @@ -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { spy, stub } from 'sinon'; -import { expect } from 'chai'; -import { act, createRenderer, fireEvent, screen } from '@mui-internal/test-utils'; -import { Slider as BaseSlider } from '@mui/base/Slider'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import Slider, { sliderClasses as classes } from '@mui/material-next/Slider'; -import describeConformance from '../../test/describeConformance'; - -function createTouches(touches) { - return { - changedTouches: touches.map( - (touch) => - new Touch({ - target: document.body, - ...touch, - }), - ), - }; -} - -describe('<Slider />', () => { - before(function beforeHook() { - // only run in supported browsers - if (typeof Touch === 'undefined') { - this.skip(); - } - }); - - let originalMatchmedia; - - beforeEach(() => { - originalMatchmedia = window.matchMedia; - window.matchMedia = () => ({ - addListener: () => {}, - removeListener: () => {}, - }); - }); - - afterEach(() => { - window.matchMedia = originalMatchmedia; - }); - - const { render } = createRenderer(); - - describeConformance( - <Slider value={0} marks={[{ value: 0, label: '0' }]} valueLabelDisplay="on" />, - () => ({ - classes, - inheritComponent: 'span', - render, - refInstanceof: window.HTMLSpanElement, - muiName: 'MuiSlider', - testDeepOverrides: { slotName: 'thumb', slotClassName: classes.thumb }, - testVariantProps: { color: 'primary', orientation: 'vertical', size: 'small' }, - testStateOverrides: { prop: 'color', value: 'secondary', styleKey: 'colorSecondary' }, - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - slots: { - root: { - expectedClassName: classes.root, - }, - thumb: { - expectedClassName: classes.thumb, - }, - track: { - expectedClassName: classes.track, - }, - rail: { - expectedClassName: classes.rail, - }, - input: { - expectedClassName: classes.input, - }, - mark: { - expectedClassName: classes.mark, - }, - markLabel: { - expectedClassName: classes.markLabel, - }, - }, - skip: [ - 'componentsProp', - 'slotPropsCallback', // not supported yet - ], - }), - ); - - it('should call handlers', () => { - const handleChange = spy(); - const handleChangeCommitted = spy(); - - const { container, getByRole } = render( - <Slider onChange={handleChange} onChangeCommitted={handleChangeCommitted} value={0} />, - ); - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - left: 0, - })); - const slider = getByRole('slider'); - - fireEvent.mouseDown(container.firstChild, { - buttons: 1, - clientX: 10, - }); - fireEvent.mouseUp(container.firstChild, { - buttons: 1, - clientX: 10, - }); - - expect(handleChange.callCount).to.equal(1); - expect(handleChange.args[0][1]).to.equal(10); - expect(handleChangeCommitted.callCount).to.equal(1); - expect(handleChangeCommitted.args[0][1]).to.equal(10); - - act(() => { - slider.focus(); - }); - fireEvent.change(slider, { target: { value: 23 } }); - expect(handleChange.callCount).to.equal(2); - expect(handleChangeCommitted.callCount).to.equal(2); - }); - - it('should only listen to changes from the same touchpoint', () => { - const handleChange = spy(); - const handleChangeCommitted = spy(); - const { container } = render( - <Slider onChange={handleChange} onChangeCommitted={handleChangeCommitted} value={0} />, - ); - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 10, - bottom: 10, - left: 0, - })); - - fireEvent.touchStart(container.firstChild, createTouches([{ identifier: 1, clientX: 0 }])); - expect(handleChange.callCount).to.equal(0); - expect(handleChangeCommitted.callCount).to.equal(0); - - fireEvent.touchStart(document.body, createTouches([{ identifier: 2, clientX: 40 }])); - expect(handleChange.callCount).to.equal(0); - expect(handleChangeCommitted.callCount).to.equal(0); - - fireEvent.touchMove(document.body, createTouches([{ identifier: 1, clientX: 1 }])); - expect(handleChange.callCount).to.equal(1); - expect(handleChangeCommitted.callCount).to.equal(0); - - fireEvent.touchMove(document.body, createTouches([{ identifier: 2, clientX: 41 }])); - expect(handleChange.callCount).to.equal(1); - expect(handleChangeCommitted.callCount).to.equal(0); - - fireEvent.touchEnd(document.body, createTouches([{ identifier: 1, clientX: 2 }])); - expect(handleChange.callCount).to.equal(1); - expect(handleChangeCommitted.callCount).to.equal(1); - }); - - it('should hedge against a dropped mouseup event', () => { - const handleChange = spy(); - const { container } = render(<Slider onChange={handleChange} value={0} />); - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - left: 0, - })); - - fireEvent.mouseDown(container.firstChild, { - buttons: 1, - clientX: 1, - }); - expect(handleChange.callCount).to.equal(1); - expect(handleChange.args[0][1]).to.equal(1); - - fireEvent.mouseMove(document.body, { - buttons: 1, - clientX: 10, - }); - expect(handleChange.callCount).to.equal(2); - expect(handleChange.args[1][1]).to.equal(10); - - fireEvent.mouseMove(document.body, { - buttons: 0, - clientX: 11, - }); - // The mouse's button was released, stop the dragging session. - expect(handleChange.callCount).to.equal(2); - }); - - it('should only fire onChange when the value changes', () => { - const handleChange = spy(); - const { container } = render(<Slider defaultValue={20} onChange={handleChange} />); - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - left: 0, - })); - - fireEvent.mouseDown(container.firstChild, { - buttons: 1, - clientX: 21, - }); - - fireEvent.mouseMove(document.body, { - buttons: 1, - clientX: 22, - }); - // Sometimes another event with the same position is fired by the browser. - fireEvent.mouseMove(document.body, { - buttons: 1, - clientX: 22, - }); - - expect(handleChange.callCount).to.equal(2); - expect(handleChange.args[0][1]).to.deep.equal(21); - expect(handleChange.args[1][1]).to.deep.equal(22); - }); - - describe('prop: classes', () => { - it('adds custom classes to the component', () => { - const selectedClasses = ['root', 'rail', 'track', 'mark']; - const customClasses = selectedClasses.reduce((acc, curr) => { - acc[curr] = `custom-${curr}`; - return acc; - }, {}); - - const { container } = render( - <Slider - marks={[{ value: 0 }, { value: 20 }, { value: 30 }]} - defaultValue={0} - classes={customClasses} - />, - ); - - expect(container.firstChild).to.have.class(classes.root); - expect(container.firstChild).to.have.class('custom-root'); - selectedClasses.slice(1).forEach((className, index) => { - expect(container.firstChild.children[index]).to.have.class(`custom-${className}`); - }); - }); - }); - - describe('prop: orientation', () => { - it('should render with the vertical classes', () => { - const { container, getByRole } = render(<Slider orientation="vertical" value={0} />); - expect(container.firstChild).to.have.class(classes.vertical); - expect(getByRole('slider')).to.have.attribute('aria-orientation', 'vertical'); - }); - - it('should report the right position', () => { - const handleChange = spy(); - const { container } = render( - <Slider orientation="vertical" defaultValue={20} onChange={handleChange} />, - ); - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 10, - height: 100, - bottom: 100, - left: 0, - })); - - fireEvent.touchStart( - container.firstChild, - createTouches([{ identifier: 1, clientX: 0, clientY: 20 }]), - ); - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: 0, clientY: 22 }]), - ); - - expect(handleChange.callCount).to.equal(2); - expect(handleChange.args[0][1]).to.equal(80); - expect(handleChange.args[1][1]).to.equal(78); - }); - }); - - describe('range', () => { - it('should support keyboard', () => { - const { getAllByRole } = render(<Slider defaultValue={[20, 30]} />); - const [slider1, slider2] = getAllByRole('slider'); - - act(() => { - slider1.focus(); - }); - fireEvent.change(slider1, { target: { value: '21' } }); - - expect(slider1.getAttribute('aria-valuenow')).to.equal('21'); - expect(slider2.getAttribute('aria-valuenow')).to.equal('30'); - - act(() => { - slider2.focus(); - fireEvent.change(slider2, { target: { value: '31' } }); - }); - - expect(slider1.getAttribute('aria-valuenow')).to.equal('21'); - expect(slider2.getAttribute('aria-valuenow')).to.equal('31'); - - act(() => { - slider1.focus(); - }); - fireEvent.change(slider1, { target: { value: '31' } }); - - expect(slider1.getAttribute('aria-valuenow')).to.equal('31'); - expect(slider2.getAttribute('aria-valuenow')).to.equal('31'); - expect(document.activeElement).to.have.attribute('data-index', '0'); - - act(() => { - slider1.focus(); - }); - fireEvent.change(slider1, { target: { value: '32' } }); - - expect(slider1.getAttribute('aria-valuenow')).to.equal('31'); - expect(slider2.getAttribute('aria-valuenow')).to.equal('32'); - expect(document.activeElement).to.have.attribute('data-index', '1'); - }); - - it('should focus the slider when dragging', () => { - const { getByRole, getByTestId, container } = render( - <Slider - slotProps={{ thumb: { 'data-testid': 'thumb' } }} - defaultValue={30} - step={10} - marks - />, - ); - const slider = getByRole('slider'); - const thumb = getByTestId('thumb'); - - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - left: 0, - })); - - fireEvent.mouseDown(thumb, { - buttons: 1, - clientX: 1, - }); - - expect(slider).toHaveFocus(); - }); - - it('should support touch events', () => { - const handleChange = spy(); - const { container } = render(<Slider defaultValue={[20, 30]} onChange={handleChange} />); - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 10, - bottom: 10, - left: 0, - })); - - fireEvent.touchStart(container.firstChild, createTouches([{ identifier: 1, clientX: 20 }])); - - fireEvent.touchMove(document.body, createTouches([{ identifier: 1, clientX: 21 }])); - - fireEvent.touchEnd(document.body, createTouches([{ identifier: 1, clientX: 21 }])); - - fireEvent.touchStart(container.firstChild, createTouches([{ identifier: 1, clientX: 21 }])); - - fireEvent.touchMove(document.body, createTouches([{ identifier: 1, clientX: 22 }])); - - fireEvent.touchEnd(document.body, createTouches([{ identifier: 1, clientX: 22 }])); - - fireEvent.touchStart(container.firstChild, createTouches([{ identifier: 1, clientX: 22 }])); - - fireEvent.touchMove(document.body, createTouches([{ identifier: 1, clientX: 22.1 }])); - - fireEvent.touchEnd(document.body, createTouches([{ identifier: 1, clientX: 22.1 }])); - - expect(handleChange.callCount).to.equal(2); - expect(handleChange.args[0][1]).to.deep.equal([21, 30]); - expect(handleChange.args[1][1]).to.deep.equal([22, 30]); - }); - - it('should not react to right clicks', () => { - const handleChange = spy(); - const { getByRole } = render( - <Slider onChange={handleChange} defaultValue={30} step={10} marks />, - ); - const thumb = getByRole('slider'); - fireEvent.mouseDown(thumb, { button: 2 }); - expect(handleChange.callCount).to.equal(0); - }); - }); - - it('should not break when initial value is out of range', () => { - const { container } = render(<Slider value={[19, 41]} min={20} max={40} />); - - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 10, - bottom: 10, - left: 0, - })); - - fireEvent.touchStart( - container.firstChild, - createTouches([{ identifier: 1, clientX: 100, clientY: 0 }]), - ); - - fireEvent.touchMove(document.body, createTouches([{ identifier: 1, clientX: 20, clientY: 0 }])); - }); - - it('focuses the thumb on when touching', () => { - const { getByRole } = render(<Slider value={0} min={20} max={40} />); - const thumb = getByRole('slider'); - - fireEvent.touchStart(thumb, createTouches([{ identifier: 1, clientX: 0, clientY: 0 }])); - - expect(thumb).toHaveFocus(); - }); - - describe('prop: step', () => { - it('should handle a null step', () => { - const { getByRole, container } = render( - <Slider - step={null} - marks={[{ value: 0 }, { value: 20 }, { value: 30 }]} - defaultValue={0} - />, - ); - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 10, - bottom: 10, - left: 0, - })); - const slider = getByRole('slider'); - - fireEvent.touchStart( - container.firstChild, - createTouches([{ identifier: 1, clientX: 21, clientY: 0 }]), - ); - expect(slider).to.have.attribute('aria-valuenow', '20'); - - fireEvent.change(slider, { - target: { - value: 21, - }, - }); - expect(slider).to.have.attribute('aria-valuenow', '30'); - - fireEvent.change(slider, { - target: { - value: 29, - }, - }); - expect(slider).to.have.attribute('aria-valuenow', '20'); - }); - - it('change events with non integer numbers should work', () => { - const { getByRole } = render( - <Slider defaultValue={0.2} min={-100} max={100} step={0.00000001} />, - ); - const slider = getByRole('slider'); - act(() => { - slider.focus(); - }); - - fireEvent.change(slider, { target: { value: '51.1' } }); - expect(slider).to.have.attribute('aria-valuenow', '51.1'); - - fireEvent.change(slider, { target: { value: '0.00000005' } }); - expect(slider).to.have.attribute('aria-valuenow', '5e-8'); - - fireEvent.change(slider, { target: { value: '1e-7' } }); - expect(slider).to.have.attribute('aria-valuenow', '1e-7'); - }); - - it('should round value to step precision', () => { - const { getByRole, container } = render( - <Slider defaultValue={0.2} min={0} max={1} step={0.1} />, - ); - const slider = getByRole('slider'); - - act(() => { - slider.focus(); - }); - - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 10, - bottom: 10, - left: 0, - })); - - act(() => { - slider.focus(); - }); - - expect(slider).to.have.attribute('aria-valuenow', '0.2'); - - fireEvent.touchStart( - container.firstChild, - createTouches([{ identifier: 1, clientX: 20, clientY: 0 }]), - ); - - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: 80, clientY: 0 }]), - ); - expect(slider).to.have.attribute('aria-valuenow', '0.8'); - - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: 40, clientY: 0 }]), - ); - expect(slider).to.have.attribute('aria-valuenow', '0.4'); - }); - - it('should not fail to round value to step precision when step is very small', () => { - const { getByRole, container } = render( - <Slider defaultValue={0.00000002} min={0} max={0.0000001} step={0.00000001} />, - ); - const slider = getByRole('slider'); - - act(() => { - slider.focus(); - }); - - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 10, - bottom: 10, - left: 0, - })); - - act(() => { - slider.focus(); - }); - - expect(slider).to.have.attribute('aria-valuenow', '2e-8'); - - fireEvent.touchStart( - container.firstChild, - createTouches([{ identifier: 1, clientX: 20, clientY: 0 }]), - ); - - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: 80, clientY: 0 }]), - ); - expect(slider).to.have.attribute('aria-valuenow', '8e-8'); - }); - - it('should not fail to round value to step precision when step is very small and negative', () => { - const { getByRole, container } = render( - <Slider defaultValue={-0.00000002} min={-0.0000001} max={0} step={0.00000001} />, - ); - const slider = getByRole('slider'); - - act(() => { - slider.focus(); - }); - - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 10, - bottom: 10, - left: 0, - })); - - act(() => { - slider.focus(); - }); - - expect(slider).to.have.attribute('aria-valuenow', '-2e-8'); - - fireEvent.touchStart( - container.firstChild, - createTouches([{ identifier: 1, clientX: 80, clientY: 0 }]), - ); - - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: 20, clientY: 0 }]), - ); - expect(slider).to.have.attribute('aria-valuenow', '-8e-8'); - }); - }); - - describe('prop: disabled', () => { - it('should render the disabled classes', () => { - const { container, getByRole } = render(<Slider disabled value={0} />); - expect(container.firstChild).to.have.class(classes.disabled); - expect(getByRole('slider')).not.to.have.attribute('tabIndex'); - }); - - it('should not respond to drag events after becoming disabled', function test() { - // TODO: Don't skip once a fix for https://github.com/jsdom/jsdom/issues/3029 is released. - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const { getByRole, setProps, container } = render(<Slider defaultValue={0} />); - - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 10, - bottom: 10, - left: 0, - })); - - fireEvent.touchStart( - container.firstChild, - createTouches([{ identifier: 1, clientX: 21, clientY: 0 }]), - ); - - const thumb = getByRole('slider'); - - expect(thumb).to.have.attribute('aria-valuenow', '21'); - expect(thumb).toHaveFocus(); - - setProps({ disabled: true }); - expect(thumb).not.toHaveFocus(); - expect(thumb).not.to.have.class(classes.active); - - fireEvent.touchMove( - container.firstChild, - createTouches([{ identifier: 1, clientX: 30, clientY: 0 }]), - ); - - expect(thumb).to.have.attribute('aria-valuenow', '21'); - }); - - it('is not focused (visibly) after becoming disabled', function test() { - // TODO: Don't skip once a fix for https://github.com/jsdom/jsdom/issues/3029 is released. - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const { getByRole, setProps } = render(<Slider defaultValue={0} />); - - const thumb = getByRole('slider'); - act(() => { - thumb.focus(); - }); - setProps({ disabled: true }); - expect(thumb).not.toHaveFocus(); - expect(thumb).not.to.have.class(classes.focusVisible); - }); - - it('should be customizable in the theme', () => { - const theme = extendTheme({ - components: { - MuiSlider: { - styleOverrides: { - root: { - [`&.${classes.disabled}`]: { - mixBlendMode: 'darken', - }, - }, - }, - }, - }, - }); - - const { container } = render( - <CssVarsProvider theme={theme}> - <Slider disabled value={0} /> - </CssVarsProvider>, - ); - expect(container.firstChild).to.toHaveComputedStyle({ - mixBlendMode: 'darken', - }); - }); - }); - - describe('prop: track', () => { - it('should render the track classes for false', () => { - const { container } = render(<Slider track={false} value={50} />); - expect(container.firstChild).to.have.class(classes.trackFalse); - }); - - it('should render the track classes for inverted', () => { - const { container } = render(<Slider track="inverted" value={50} />); - expect(container.firstChild).to.have.class(classes.trackInverted); - }); - }); - - describe('aria-valuenow', () => { - it('should update the aria-valuenow', () => { - const { getByRole } = render(<Slider defaultValue={50} />); - const slider = getByRole('slider'); - act(() => { - slider.focus(); - }); - - fireEvent.change(slider, { target: { value: 51 } }); - expect(slider).to.have.attribute('aria-valuenow', '51'); - - fireEvent.change(slider, { target: { value: 52 } }); - expect(slider).to.have.attribute('aria-valuenow', '52'); - }); - }); - - describe('prop: min', () => { - it('should set the min and aria-valuemin on the input', () => { - const min = 150; - const { getByRole } = render(<Slider defaultValue={150} step={100} max={750} min={min} />); - const slider = getByRole('slider'); - - expect(slider).to.have.attribute('aria-valuemin', String(min)); - expect(slider).to.have.attribute('min', String(min)); - }); - - it('should use min as the step origin', () => { - const min = 150; - const { getByRole } = render(<Slider defaultValue={150} step={100} max={750} min={min} />); - const slider = getByRole('slider'); - act(() => { - slider.focus(); - }); - - expect(slider).to.have.attribute('aria-valuenow', String(min)); - }); - - it('should not go less than the min', () => { - const min = 150; - const { getByRole } = render(<Slider defaultValue={150} step={100} max={750} min={min} />); - const slider = getByRole('slider'); - act(() => { - slider.focus(); - }); - - fireEvent.change(slider, { target: { value: String(min - 100) } }); - expect(slider).to.have.attribute('aria-valuenow', String(min)); - }); - }); - - describe('prop: max', () => { - it('should set the max and aria-valuemax on the input', () => { - const max = 750; - const { getByRole } = render(<Slider defaultValue={150} step={100} max={max} min={150} />); - const slider = getByRole('slider'); - - expect(slider).to.have.attribute('aria-valuemax', String(max)); - expect(slider).to.have.attribute('max', String(max)); - }); - - it('should not go more than the max', () => { - const max = 750; - const { getByRole } = render(<Slider defaultValue={150} step={100} max={max} min={150} />); - const slider = getByRole('slider'); - act(() => { - slider.focus(); - }); - - fireEvent.change(slider, { target: { value: String(max + 100) } }); - expect(slider).to.have.attribute('aria-valuenow', String(max)); - }); - - it('should reach right edge value', () => { - const { getByRole, container } = render( - <Slider defaultValue={90} min={6} max={108} step={10} />, - ); - - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 10, - bottom: 10, - left: 0, - })); - - const thumb = getByRole('slider'); - act(() => { - thumb.focus(); - }); - - expect(thumb).to.have.attribute('aria-valuenow', '90'); - - fireEvent.touchStart( - container.firstChild, - createTouches([{ identifier: 1, clientX: 20, clientY: 0 }]), - ); - - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: 100, clientY: 0 }]), - ); - expect(thumb).to.have.attribute('aria-valuenow', '106'); - - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: 200, clientY: 0 }]), - ); - expect(thumb).to.have.attribute('aria-valuenow', '108'); - - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: 50, clientY: 0 }]), - ); - expect(thumb).to.have.attribute('aria-valuenow', '56'); - - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: -100, clientY: 0 }]), - ); - expect(thumb).to.have.attribute('aria-valuenow', '6'); - }); - }); - - describe('prop: valueLabelDisplay', () => { - it('should always display the value label according to on and off', () => { - const { setProps } = render( - <Slider - valueLabelDisplay="on" - value={50} - slotProps={{ thumb: { 'data-testid': 'thumb' } }} - />, - ); - expect(document.querySelector(`.${classes.valueLabelOpen}`)).not.to.equal(null); - - setProps({ - valueLabelDisplay: 'off', - }); - - expect(document.querySelector(`.${classes.valueLabelOpen}`)).to.equal(null); - }); - - it('should display the value label only on hover for auto', () => { - const { getByTestId } = render( - <Slider - valueLabelDisplay="auto" - value={50} - slotProps={{ thumb: { 'data-testid': 'thumb' } }} - />, - ); - const thumb = getByTestId('thumb'); - expect(document.querySelector(`.${classes.valueLabelOpen}`)).to.equal(null); - - fireEvent.mouseOver(thumb); - - expect(document.querySelector(`.${classes.valueLabelOpen}`)).not.to.equal(null); - }); - - it('should be respected when using custom value label', () => { - const ValueLabelComponent = React.forwardRef((props, ref) => { - const { value, open } = props; - return ( - <span data-testid="value-label" className={open ? 'open' : ''} ref={ref}> - {value} - </span> - ); - }); - ValueLabelComponent.propTypes = { value: PropTypes.number }; - - const { setProps } = render( - <Slider slots={{ valueLabel: ValueLabelComponent }} valueLabelDisplay="on" value={50} />, - ); - - expect(screen.queryByTestId('value-label')).to.have.class('open'); - - setProps({ - valueLabelDisplay: 'off', - }); - - expect(screen.queryByTestId('value-label')).to.equal(null); - }); - }); - - describe('markActive state', () => { - function getActives(container) { - return Array.from(container.querySelectorAll(`.${classes.mark}`)).map((node) => - node.classList.contains(classes.markActive), - ); - } - - it('sets the marks active that are `within` the value', () => { - const marks = [{ value: 5 }, { value: 10 }, { value: 15 }]; - - const { container: container1 } = render( - <Slider min={0} max={20} value={12} marks={marks} />, - ); - expect(getActives(container1)).to.deep.equal([true, true, false]); - - const { container: container2 } = render( - <Slider min={0} max={20} value={[8, 12]} marks={marks} />, - ); - expect(getActives(container2)).to.deep.equal([false, true, false]); - }); - - it('uses closed intervals for the within check', () => { - const { container: container1 } = render( - <Slider value={10} min={0} max={10} marks step={5} />, - ); - expect(getActives(container1)).to.deep.equal([true, true, true]); - - const { container: container2 } = render( - <Slider value={9.99999} min={0} max={10} marks step={5} />, - ); - expect(getActives(container2)).to.deep.equal([true, true, false]); - }); - - it('should support inverted track', () => { - const marks = [{ value: 5 }, { value: 10 }, { value: 15 }]; - - const { container: container1 } = render( - <Slider min={0} max={20} value={12} marks={marks} track="inverted" />, - ); - expect(getActives(container1)).to.deep.equal([false, false, true]); - - const { container: container2 } = render( - <Slider min={0} max={20} value={[8, 12]} marks={marks} track="inverted" />, - ); - expect(getActives(container2)).to.deep.equal([true, false, true]); - }); - }); - - it('should forward mouseDown', () => { - const handleMouseDown = spy(); - const { container } = render(<Slider disabled onMouseDown={handleMouseDown} value={0} />); - fireEvent.mouseDown(container.firstChild); - expect(handleMouseDown.callCount).to.equal(1); - }); - - describe('rtl', () => { - it('should add direction css', () => { - const { getByRole } = render( - <CssVarsProvider - theme={extendTheme({ - direction: 'rtl', - })} - > - <Slider defaultValue={30} /> - </CssVarsProvider>, - ); - const thumb = getByRole('slider'); - act(() => { - thumb.focus(); - }); - - expect(thumb.style.direction).to.equal('rtl'); - }); - - it('should handle RTL', () => { - const handleChange = spy(); - const { container, getByTestId } = render( - <CssVarsProvider - theme={extendTheme({ - direction: 'rtl', - })} - > - <Slider - value={30} - onChange={handleChange} - slotProps={{ thumb: { 'data-testid': 'thumb' } }} - /> - </CssVarsProvider>, - ); - const thumb = getByTestId('thumb'); - expect(thumb.style.right).to.equal('30%'); - - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 10, - bottom: 10, - left: 0, - })); - - fireEvent.touchStart( - container.firstChild, - createTouches([{ identifier: 1, clientX: 20, clientY: 0 }]), - ); - - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: 22, clientY: 0 }]), - ); - - expect(handleChange.callCount).to.equal(2); - expect(handleChange.args[0][1]).to.equal(80); - expect(handleChange.args[1][1]).to.equal(78); - }); - }); - - describe('warnings', () => { - beforeEach(() => { - PropTypes.resetWarningCache(); - }); - - it('should warn if aria-valuetext is provided', () => { - expect(() => { - PropTypes.checkPropTypes( - Slider.propTypes, - { classes: {}, value: [20, 50], 'aria-valuetext': 'hot' }, - 'prop', - 'MockedSlider', - ); - }).toErrorDev('MUI: You need to use the `getAriaValueText` prop instead of'); - }); - - it('should warn if aria-label is provided', () => { - expect(() => { - PropTypes.checkPropTypes( - Slider.propTypes, - { classes: {}, value: [20, 50], 'aria-label': 'hot' }, - 'prop', - 'MockedSlider', - ); - }).toErrorDev('MUI: You need to use the `getAriaLabel` prop instead of'); - }); - - it('should warn when switching from controlled to uncontrolled', () => { - const { setProps } = render(<Slider value={[20, 50]} />); - - expect(() => { - setProps({ value: undefined }); - }).toErrorDev( - 'MUI: A component is changing the controlled value state of Slider to be uncontrolled.', - ); - }); - - it('should warn when switching between uncontrolled to controlled', () => { - const { setProps } = render(<Slider />); - - expect(() => { - setProps({ value: [20, 50] }); - }).toErrorDev( - 'MUI: A component is changing the uncontrolled value state of Slider to be controlled.', - ); - }); - }); - - it('should support getAriaValueText', () => { - const getAriaValueText = (value) => `${value}°C`; - const { getAllByRole } = render( - <Slider value={[20, 50]} getAriaValueText={getAriaValueText} />, - ); - const sliders = getAllByRole('slider'); - - expect(sliders[0]).to.have.attribute('aria-valuetext', '20°C'); - expect(sliders[1]).to.have.attribute('aria-valuetext', '50°C'); - }); - - it('should support getAriaLabel', () => { - const getAriaLabel = (index) => `Label ${index}`; - const { getAllByRole } = render(<Slider value={[20, 50]} getAriaLabel={getAriaLabel} />); - const sliders = getAllByRole('slider'); - - expect(sliders[0]).to.have.attribute('aria-label', 'Label 0'); - expect(sliders[1]).to.have.attribute('aria-label', 'Label 1'); - }); - - it('should allow customization of the marks', () => { - const { container } = render( - <Slider - marks={[ - { value: 0, label: 0 }, - { value: 20, label: 20 }, - { value: 30, label: 30 }, - ]} - defaultValue={0} - />, - ); - expect(container.querySelectorAll(`.${classes.markLabel}`).length).to.equal(3); - expect(container.querySelectorAll(`.${classes.mark}`).length).to.equal(3); - expect(container.querySelectorAll(`.${classes.markLabel}[data-index="2"]`).length).to.equal(1); - expect(container.querySelectorAll(`.${classes.mark}[data-index="2"]`).length).to.equal(1); - }); - - it('should correctly display mark labels when ranges slider have the same start and end', () => { - const getMarks = (value) => value.map((val) => ({ value: val, label: val })); - - const { container, setProps } = render( - <Slider value={[100, 100]} marks={getMarks([100, 100])} />, - ); - expect(container.querySelectorAll(`.${classes.markLabel}`).length).to.equal(2); - - setProps({ value: [40, 60], marks: getMarks([40, 60]) }); - expect(container.querySelectorAll(`.${classes.markLabel}`).length).to.equal(2); - }); - - it('should pass "name" and "value" as part of the event.target for onChange', () => { - const handleChange = stub().callsFake((event) => event.target); - const { getByRole } = render( - <Slider onChange={handleChange} name="change-testing" value={3} />, - ); - const slider = getByRole('slider'); - - act(() => { - slider.focus(); - }); - fireEvent.change(slider, { - target: { - value: 4, - }, - }); - - expect(handleChange.callCount).to.equal(1); - const target = handleChange.firstCall.returnValue; - expect(target).to.deep.equal({ - name: 'change-testing', - value: 4, - }); - }); - - describe('prop: ValueLabelComponent', () => { - it('receives the formatted value', () => { - const ValueLabelComponent = React.forwardRef((props, ref) => { - const { value } = props; - return ( - <span data-testid="value-label" ref={ref}> - {value} - </span> - ); - }); - ValueLabelComponent.propTypes = { value: PropTypes.string }; - - const { getByTestId } = render( - <Slider - value={10} - slots={{ valueLabel: ValueLabelComponent }} - valueLabelDisplay="on" - valueLabelFormat={(n) => n.toString(2)} - />, - ); - - expect(getByTestId('value-label')).to.have.text('1010'); - }); - }); - - it('should not override the event.target on touch events', () => { - const handleChange = spy(); - const handleNativeEvent = spy(); - const handleEvent = spy(); - function Test() { - React.useEffect(() => { - document.addEventListener('touchstart', handleNativeEvent); - return () => { - document.removeEventListener('touchstart', handleNativeEvent); - }; - }); - - return ( - <div onTouchStart={handleEvent}> - <Slider data-testid="slider" value={0} onChange={handleChange} /> - </div> - ); - } - - render(<Test />); - const slider = screen.getByTestId('slider'); - - stub(slider, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 10, - bottom: 10, - left: 0, - })); - - fireEvent.touchStart(slider, createTouches([{ identifier: 1, clientX: 0 }])); - - expect(handleChange.callCount).to.equal(0); - expect(handleNativeEvent.callCount).to.equal(1); - expect(handleNativeEvent.firstCall.args[0]).to.have.property('target', slider); - expect(handleEvent.callCount).to.equal(1); - expect(handleEvent.firstCall.args[0]).to.have.property('target', slider); - }); - - it('should not override the event.target on mouse events', () => { - const handleChange = spy(); - const handleNativeEvent = spy(); - const handleEvent = spy(); - function Test() { - React.useEffect(() => { - document.addEventListener('mousedown', handleNativeEvent); - return () => { - document.removeEventListener('mousedown', handleNativeEvent); - }; - }); - - return ( - <div onMouseDown={handleEvent}> - <Slider data-testid="slider" value={0} onChange={handleChange} /> - </div> - ); - } - render(<Test />); - const slider = screen.getByTestId('slider'); - - stub(slider, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 10, - bottom: 10, - left: 0, - })); - - fireEvent.mouseDown(slider); - - expect(handleChange.callCount).to.equal(0); - expect(handleNativeEvent.callCount).to.equal(1); - expect(handleNativeEvent.firstCall.args[0]).to.have.property('target', slider); - expect(handleEvent.callCount).to.equal(1); - expect(handleEvent.firstCall.args[0]).to.have.property('target', slider); - }); - - describe('dragging state', () => { - it('should not apply class name for click modality', () => { - const { container } = render(<Slider defaultValue={90} />); - - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 10, - bottom: 10, - left: 0, - })); - - fireEvent.touchStart( - container.firstChild, - createTouches([{ identifier: 1, clientX: 20, clientY: 0 }]), - ); - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: 21, clientY: 0 }]), - ); - expect(container.firstChild).not.to.have.class(classes.dragging); - fireEvent.touchEnd(document.body, createTouches([{ identifier: 1 }])); - }); - - it('should apply class name for dragging modality', () => { - const { container } = render(<Slider defaultValue={90} />); - - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 10, - bottom: 10, - left: 0, - })); - - fireEvent.touchStart( - container.firstChild, - createTouches([{ identifier: 1, clientX: 20, clientY: 0 }]), - ); - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: 200, clientY: 0 }]), - ); - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: 200, clientY: 0 }]), - ); - - expect(container.firstChild).not.to.have.class(classes.dragging); - - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: 200, clientY: 0 }]), - ); - - expect(container.firstChild).to.have.class(classes.dragging); - fireEvent.touchEnd(document.body, createTouches([{ identifier: 1 }])); - expect(container.firstChild).not.to.have.class(classes.dragging); - }); - }); - - it('should remove the slider from the tab sequence', () => { - render(<BaseSlider tabIndex={-1} value={30} />); - expect(screen.getByRole('slider')).to.have.property('tabIndex', -1); - }); - - describe('prop: disableSwap', () => { - it('should bound the value when using the keyboard', () => { - const handleChange = spy(); - const { getAllByRole } = render( - <Slider defaultValue={[20, 30]} disableSwap onChange={handleChange} />, - ); - const [slider1, slider2] = getAllByRole('slider'); - - act(() => { - slider1.focus(); - }); - fireEvent.change(slider2, { target: { value: '19' } }); - expect(handleChange.args[0][1]).to.deep.equal([20, 20]); - expect(document.activeElement).to.have.attribute('data-index', '1'); - }); - - it('should bound the value when using the mouse', () => { - const handleChange = spy(); - const { container } = render( - <Slider defaultValue={[20, 30]} disableSwap onChange={handleChange} />, - ); - - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 10, - bottom: 10, - left: 0, - })); - - fireEvent.touchStart( - container.firstChild, - createTouches([{ identifier: 1, clientX: 35, clientY: 0 }]), - ); - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: 19, clientY: 0 }]), - ); - expect(handleChange.args[0][1]).to.deep.equal([20, 35]); - expect(handleChange.args[1][1]).to.deep.equal([20, 20]); - expect(document.activeElement).to.have.attribute('data-index', '1'); - }); - - it('should bound the value when moving the first behind the second', () => { - const handleChange = spy(); - const { container } = render( - <Slider defaultValue={[20, 30]} disableSwap onChange={handleChange} />, - ); - - stub(container.firstChild, 'getBoundingClientRect').callsFake(() => ({ - width: 100, - height: 10, - bottom: 10, - left: 0, - })); - - fireEvent.touchStart( - container.firstChild, - createTouches([{ identifier: 1, clientX: 15, clientY: 0 }]), - ); - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: 40, clientY: 0 }]), - ); - expect(handleChange.args[0][1]).to.deep.equal([15, 30]); - expect(handleChange.args[1][1]).to.deep.equal([30, 30]); - expect(document.activeElement).to.have.attribute('data-index', '0'); - }); - }); - - describe('prop: size', () => { - it('should render default slider', () => { - render(<Slider />); - - const root = document.querySelector(`.${classes.root}`); - const thumb = document.querySelector(`.${classes.thumb}`); - expect(root).not.to.have.class(classes.sizeSmall); - expect(thumb).not.to.have.class(classes.thumbSizeSmall); - }); - - it('should render small slider', () => { - render(<Slider size="small" />); - - const root = document.querySelector(`.${classes.root}`); - const thumb = document.querySelector(`.${classes.thumb}`); - expect(root).to.have.class(classes.sizeSmall); - expect(thumb).to.have.class(classes.thumbSizeSmall); - }); - }); - - describe('prop: slots', () => { - it('should render custom components if specified', () => { - // ARRANGE - const dataTestId = 'slider-input-testid'; - const name = 'custom-input'; - function CustomInput({ ownerState, ...props }) { - return <input {...props} data-testid={dataTestId} name={name} />; - } - - // ACT - const { getByTestId } = render(<Slider slots={{ input: CustomInput }} />); - - // ASSERT - expect(getByTestId(dataTestId).name).to.equal(name); - }); - }); - - describe('prop: slotProps', () => { - it('should forward the props to their respective components', () => { - // ARRANGE - const dataTestId = 'slider-input-testid'; - const id = 'slider-input-id'; - - // ACT - const { getByTestId } = render( - <Slider defaultValue={10} slotProps={{ input: { 'data-testid': dataTestId, id } }} />, - ); - - // ASSERT - expect(getByTestId(dataTestId).id).to.equal(id); - }); - }); - - it('marked slider should be customizable in the theme', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const theme = extendTheme({ - components: { - MuiSlider: { - styleOverrides: { - marked: { - marginTop: 40, - marginBottom: 0, - }, - }, - }, - }, - }); - - const { container } = render( - <CssVarsProvider theme={theme}> - <Slider - marks={[ - { label: '1', value: 1 }, - { label: '2', value: 2 }, - ]} - step={null} - /> - </CssVarsProvider>, - ); - - expect(container.querySelector(`.${classes.marked}`)).toHaveComputedStyle({ - marginTop: '40px', - marginBottom: '0px', - }); - }); - - it('active marks should be customizable in theme', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const theme = extendTheme({ - components: { - MuiSlider: { - styleOverrides: { - markActive: { - height: '10px', - width: '10px', - }, - }, - }, - }, - }); - - const { container } = render( - <CssVarsProvider theme={theme}> - <Slider value={2} min={1} max={3} step={1} marks /> - </CssVarsProvider>, - ); - - expect(container.querySelector(`.${classes.markActive}`)).toHaveComputedStyle({ - height: '10px', - width: '10px', - }); - }); - - describe('overlapping state', () => { - it('should apply thumb overlap classes', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const { getAllByRole } = render(<Slider sx={{ width: 100 }} value={[0, 0]} />); - - const [firstThumb, lastThumb] = document.querySelectorAll(`.${classes.thumb}`); - const firstThumbInput = getAllByRole('slider')[0]; - act(() => { - firstThumbInput.focus(); - }); - - expect(firstThumb).to.have.class(classes.thumbOverlap); - expect(lastThumb).not.to.have.class(classes.thumbOverlap); - }); - - it('should overlap last active thumb', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const { getAllByRole } = render(<Slider sx={{ width: 100 }} value={[0, 0]} />); - - const [firstThumb, lastThumb] = document.querySelectorAll(`.${classes.thumb}`); - - const lastThumbInput = getAllByRole('slider')[1]; - act(() => { - lastThumbInput.focus(); - }); - - expect(firstThumb).not.to.have.class(classes.thumbOverlap); - expect(lastThumb).to.have.class(classes.thumbOverlap); - }); - - it('should apply value label overlap classes', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const { getAllByRole } = render( - <Slider sx={{ width: 100 }} value={[0, 0]} valueLabelDisplay="on" />, - ); - - const [firstValueLabel, lastValueLabel] = document.querySelectorAll(`.${classes.valueLabel}`); - const firstThumbInput = getAllByRole('slider')[0]; - act(() => { - firstThumbInput.focus(); - }); - - expect(firstValueLabel).to.have.class(classes.valueLabelOverlap); - expect(lastValueLabel).not.to.have.class(classes.valueLabelOverlap); - }); - }); -}); diff --git a/packages/mui-material-next/src/Slider/Slider.tsx b/packages/mui-material-next/src/Slider/Slider.tsx deleted file mode 100644 index 89b82db9939897..00000000000000 --- a/packages/mui-material-next/src/Slider/Slider.tsx +++ /dev/null @@ -1,1012 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { chainPropTypes, unstable_capitalize as capitalize } from '@mui/utils'; -import { - isHostComponent, - useSlotProps, - unstable_composeClasses as composeClasses, -} from '@mui/base'; -import { useSlider, valueToPercent } from '@mui/base/useSlider'; -import { shouldForwardProp } from '@mui/system'; -import useThemeProps from '../styles/useThemeProps'; -import styled from '../styles/styled'; -import useTheme from '../styles/useTheme'; -import shouldSpreadAdditionalProps from '../utils/shouldSpreadAdditionalProps'; -import sliderClasses, { getSliderUtilityClass } from './sliderClasses'; -import { SliderOwnerState, SliderTypeMap, SliderProps } from './Slider.types'; -import { MD3ColorSchemeTokens, MD3State } from '../styles'; -import useSliderElementsOverlap from './useSliderElementsOverlap'; - -function Identity<Type>(x: Type): Type { - return x; -} - -export const SliderRoot = styled('span', { - name: 'MuiSlider', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - styles[`color${capitalize(ownerState.color || 'primary')}`], - ownerState.size !== 'medium' && styles[`size${capitalize(ownerState.size)}`], - ownerState.marked && styles.marked, - ownerState.orientation === 'vertical' && styles.vertical, - ownerState.track === 'inverted' && styles.trackInverted, - ownerState.track === false && styles.trackFalse, - ]; - }, -})<{ ownerState: SliderOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - return { - '--md-comp-slider-thumb-size': ownerState.size === 'small' ? '12px' : '20px', - borderRadius: tokens.sys.shape.corner.full, - boxSizing: 'content-box', - display: 'inline-block', - position: 'relative', - cursor: 'pointer', - touchAction: 'none', - color: tokens.sys.color[ownerState.color || 'primary'], - WebkitTapHighlightColor: 'transparent', - ...(ownerState.orientation === 'horizontal' && { - height: 4, - width: '100%', - // 40px touch target - padding: '18px 0', - ...(ownerState.size === 'small' && { - height: 2, - }), - ...(ownerState.marked && { - marginBottom: 20, - }), - }), - ...(ownerState.orientation === 'vertical' && { - height: '100%', - width: 4, - padding: '0 18px', - ...(ownerState.size === 'small' && { - width: 2, - }), - ...(ownerState.marked && { - marginRight: 44, - }), - }), - '@media print': { - colorAdjust: 'exact', - }, - [`&.${sliderClasses.disabled}`]: { - pointerEvents: 'none', - cursor: 'default', - color: tokens.sys.color.outline, - }, - [`&.${sliderClasses.dragging}`]: { - [`& .${sliderClasses.track}`]: { - transition: 'none', - }, - [`& .${sliderClasses.thumb}, .${sliderClasses.valueLabel}`]: { - transition: theme.transitions.create(['border'], { - duration: theme.transitions.duration.shortest, - }), - }, - }, - }; -}); - -export const SliderRail = styled('span', { - name: 'MuiSlider', - slot: 'Rail', - overridesResolver: (props, styles) => styles.rail, -})<{ ownerState: SliderOwnerState }>(({ theme: { vars: tokens }, ownerState }) => ({ - display: 'block', - position: 'absolute', - borderRadius: 'inherit', - backgroundColor: tokens.sys.color.surfaceContainerHighest, - boxShadow: tokens.sys.elevation[0], - ...(ownerState.orientation === 'horizontal' && { - width: '100%', - height: 'inherit', - top: '50%', - transform: 'translateY(-50%)', - }), - ...(ownerState.orientation === 'vertical' && { - height: '100%', - width: 'inherit', - left: '50%', - transform: 'translateX(-50%)', - }), - ...(ownerState.track === 'inverted' && { - backgroundColor: ownerState.disabled ? tokens.sys.color.outline : 'currentColor', - }), -})); - -export const SliderTrack = styled('span', { - name: 'MuiSlider', - slot: 'Track', - overridesResolver: (props, styles) => styles.track, -})<{ ownerState: SliderOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - return { - display: 'block', - position: 'absolute', - borderRadius: 'inherit', - backgroundColor: 'currentColor', - boxShadow: tokens.sys.elevation[0], - transition: theme.transitions.create(['left', 'width', 'bottom', 'height'], { - duration: theme.transitions.duration.shortest, - }), - ...(ownerState.orientation === 'horizontal' && { - height: 'inherit', - top: '50%', - transform: 'translateY(-50%)', - }), - ...(ownerState.orientation === 'vertical' && { - width: 'inherit', - left: '50%', - transform: 'translateX(-50%)', - }), - ...(ownerState.track === false && { - display: 'none', - }), - ...(ownerState.track === 'inverted' && { - backgroundColor: tokens.sys.color.surfaceContainerHighest, - }), - }; -}); - -export const SliderThumb = styled('span', { - name: 'MuiSlider', - slot: 'Thumb', - shouldForwardProp: (prop) => shouldForwardProp(prop) && prop !== 'isOverlapping', - overridesResolver: (props, styles) => { - const { ownerState } = props; - return [ - styles.thumb, - props.isOverlapping && styles.thumbOverlap, - styles[`thumbColor${capitalize(ownerState.color || 'primary')}`], - ownerState.size !== 'medium' && styles[`thumbSize${capitalize(ownerState.size)}`], - ]; - }, -})<{ ownerState: SliderOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - function getBoxShadow(state: keyof MD3State) { - return `0px 0px 0px 10px rgba(${tokens.sys.color.primaryChannel} / ${tokens.sys.state[state].stateLayerOpacity})`; - } - - return { - zIndex: 1, - position: 'absolute', - height: 'var(--md-comp-slider-thumb-size)', - width: 'var(--md-comp-slider-thumb-size)', - boxSizing: 'border-box', - borderRadius: tokens.sys.shape.corner.full, - outline: 0, - backgroundColor: 'currentColor', - border: '1px solid currentColor', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - transition: theme.transitions.create(['box-shadow', 'left', 'right', 'bottom', 'border'], { - duration: theme.transitions.duration.shortest, - }), - ...(ownerState.orientation === 'horizontal' && { - top: '50%', - transform: 'translate(-50%, -50%)', - }), - ...(ownerState.orientation === 'vertical' && { - left: '50%', - transform: 'translate(-50%, 50%)', - }), - '&::before': { - position: 'absolute', - content: '""', - borderRadius: 'inherit', - width: 'var(--md-comp-slider-thumb-size)', - height: 'var(--md-comp-slider-thumb-size)', - boxShadow: tokens.sys.elevation[1], - }, - '&::after': { - position: 'absolute', - content: '""', - borderRadius: '50%', - // 40px is the hit target - width: 40, - height: 40, - top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)', - }, - '&:hover': { - boxShadow: getBoxShadow('hover'), - '@media (hover: none)': { - boxShadow: 'none', - }, - }, - [`&.${sliderClasses.focusVisible}`]: { - boxShadow: getBoxShadow('focus'), - }, - [`&.${sliderClasses.active}`]: { - zIndex: 2, - boxShadow: getBoxShadow('pressed'), - }, - [`&.${sliderClasses.thumbOverlap}`]: { - border: `1px solid ${tokens.ref.palette.common.white}`, - }, - [`&.${sliderClasses.disabled}`]: { - boxShadow: 'none', - '&::before': { - boxShadow: tokens.sys.elevation[0], - }, - }, - }; -}); - -export const SliderValueLabel = styled('span', { - name: 'MuiSlider', - slot: 'ValueLabel', - shouldForwardProp: (prop) => - shouldForwardProp(prop) && - prop !== 'isOverlapping' && - prop !== 'valueLabelDisplay' && - prop !== 'valueLabelFormat', - overridesResolver: (props, styles) => [ - styles.valueLabel, - props.isOverlapping && styles.valueLabelOverlap, - ], -})<{ ownerState: SliderOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - const letterSpacing = `${ - theme.sys.typescale.label.medium.tracking / theme.sys.typescale.label.medium.size - }rem`; - - const labelStyle = { - color: ownerState.disabled - ? tokens.sys.color.surface - : tokens.sys.color[ - `on${capitalize(ownerState.color || 'primary')}` as keyof MD3ColorSchemeTokens - ], - // paddingLeft compensates letter spacing being added only on the right side - paddingLeft: letterSpacing, - }; - - return { - zIndex: 1, - whiteSpace: 'nowrap', - fontFamily: tokens.sys.typescale.label.medium.family, - lineHeight: `calc(${tokens.sys.typescale.label.large.lineHeight} / ${tokens.sys.typescale.label.medium.size})`, - fontWeight: tokens.sys.typescale.label.medium.weight, - fontSize: theme.typography.pxToRem(theme.sys.typescale.label.medium.size), - letterSpacing, - transition: theme.transitions.create(['transform', 'border'], { - duration: theme.transitions.duration.shortest, - }), - position: 'absolute', - backgroundColor: 'currentColor', - boxShadow: tokens.sys.elevation[0], - borderRadius: '50% 50% 50% 0', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - boxSizing: 'border-box', - border: '1px solid currentColor', - width: 28, - height: 28, - [`&.${sliderClasses.valueLabelOverlap}`]: { - border: `1px solid ${tokens.ref.palette.common.white}`, - }, - ...(ownerState.orientation === 'horizontal' && { - top: ownerState.size === 'small' ? -32 : -36, - transform: 'translateY(50%) rotate(-45deg) scale(0)', - [`& .${sliderClasses.valueLabelLabel}`]: { - transform: 'rotate(45deg)', - ...labelStyle, - }, - [`&.${sliderClasses.valueLabelOpen}`]: { - transform: 'translateY(0) rotate(-45deg) scale(1)', - }, - }), - ...(ownerState.orientation === 'vertical' && { - left: ownerState.size === 'small' ? -32 : -36, - transform: 'translateX(50%) rotate(225deg) scale(0)', - [`& .${sliderClasses.valueLabelLabel}`]: { - transform: 'rotate(-225deg)', - ...labelStyle, - }, - [`&.${sliderClasses.valueLabelOpen}`]: { - transform: 'translateX(0) rotate(225deg) scale(1)', - }, - }), - ...(ownerState.size === 'small' && { - fontSize: theme.typography.pxToRem(theme.sys.typescale.label.small.size), - width: 24, - height: 24, - }), - }; -}); - -export const SliderMark = styled('span', { - name: 'MuiSlider', - slot: 'Mark', - shouldForwardProp: (prop) => shouldForwardProp(prop) && prop !== 'markActive', - overridesResolver: (props, styles) => { - const { markActive } = props; - - return [styles.mark, markActive && styles.markActive]; - }, -})<{ ownerState: SliderOwnerState; markActive: boolean }>( - ({ theme: { vars: tokens }, ownerState, markActive }) => ({ - position: 'absolute', - width: 2, - height: 2, - borderRadius: tokens.sys.shape.corner.full, - backgroundColor: tokens.sys.color.onSurfaceVariant, - opacity: 0.38, - ...(ownerState.orientation === 'horizontal' && { - top: '50%', - transform: 'translate(-1px, -50%)', - }), - ...(ownerState.orientation === 'vertical' && { - left: '50%', - transform: 'translate(-50%, 1px)', - }), - ...(markActive && { - backgroundColor: - tokens.sys.color[ - `on${capitalize(ownerState.color || 'primary')}` as keyof MD3ColorSchemeTokens - ], - }), - }), -); - -export const SliderMarkLabel = styled('span', { - name: 'MuiSlider', - slot: 'MarkLabel', - shouldForwardProp: (prop) => shouldForwardProp(prop) && prop !== 'markLabelActive', - overridesResolver: (props, styles) => styles.markLabel, -})<{ ownerState: SliderOwnerState; markLabelActive: boolean }>(({ - theme, - ownerState, - markLabelActive, -}) => { - const { vars: tokens } = theme; - - return { - fontFamily: tokens.sys.typescale.label.medium.family, - lineHeight: `calc(${tokens.sys.typescale.label.medium.lineHeight} / ${tokens.sys.typescale.label.medium.size})`, - fontWeight: tokens.sys.typescale.label.medium.weight, - fontSize: theme.typography.pxToRem(theme.sys.typescale.label.medium.size), - letterSpacing: tokens.sys.typescale.label.medium.tracking, - color: tokens.sys.color.onSurfaceVariant, - position: 'absolute', - whiteSpace: 'nowrap', - ...(ownerState.orientation === 'horizontal' && { - top: 36, - transform: 'translateX(-50%)', - '@media (pointer: coarse)': { - top: 44, - }, - }), - ...(ownerState.orientation === 'vertical' && { - left: 36, - transform: 'translateY(50%)', - '@media (pointer: coarse)': { - left: 44, - }, - }), - ...(markLabelActive && { - color: tokens.sys.color.onSurface, - }), - }; -}); - -const useUtilityClasses = (ownerState: SliderOwnerState) => { - const { disabled, dragging, marked, orientation, track, classes, color, size } = ownerState; - - const slots = { - root: [ - 'root', - disabled && 'disabled', - dragging && 'dragging', - marked && 'marked', - orientation === 'vertical' && 'vertical', - track === 'inverted' && 'trackInverted', - track === false && 'trackFalse', - color && `color${capitalize(color)}`, - size && `size${capitalize(size)}`, - ], - rail: ['rail'], - track: ['track'], - mark: ['mark'], - markActive: ['markActive'], - markLabel: ['markLabel'], - markLabelActive: ['markLabelActive'], - valueLabel: ['valueLabel'], - valueLabelOpen: ['valueLabelOpen'], - valueLabelLabel: ['valueLabelLabel'], - valueLabelOverlap: ['valueLabelOverlap'], - thumb: [ - 'thumb', - disabled && 'disabled', - size && `thumbSize${capitalize(size)}`, - color && `thumbColor${capitalize(color)}`, - ], - thumbOverlap: ['thumbOverlap'], - active: ['active'], - disabled: ['disabled'], - focusVisible: ['focusVisible'], - }; - - return composeClasses(slots, getSliderUtilityClass, classes); -}; - -const Slider = React.forwardRef(function Slider< - BaseComponentType extends React.ElementType = SliderTypeMap['defaultComponent'], ->(inProps: SliderProps<BaseComponentType>, ref: React.ForwardedRef<any>) { - const props = useThemeProps({ - props: inProps, - name: 'MuiSlider', - }); - - const theme = useTheme(); - const isRtl = theme.direction === 'rtl'; - - const { - 'aria-label': ariaLabel, - 'aria-valuetext': ariaValuetext, - 'aria-labelledby': ariaLabelledby, - component = 'span', - color = 'primary', - classes: classesProp, - className, - disableSwap = false, - disabled = false, - getAriaLabel, - getAriaValueText, - marks: marksProp = false, - max = 100, - min = 0, - name, - onChange, - onChangeCommitted, - orientation = 'horizontal', - size = 'medium', - step = 1, - scale = Identity, - slotProps = {}, - slots = {}, - tabIndex, - track = 'normal', - value: valueProp, - valueLabelDisplay = 'off', - valueLabelFormat = Identity, - ...other - } = props; - - const propsWithDefaultValues = { - ...props, - isRtl, - max, - min, - classes: classesProp, - disabled, - disableSwap, - orientation, - marks: marksProp, - color, - size, - step, - scale, - track, - valueLabelDisplay, - valueLabelFormat, - } as Partial<SliderOwnerState>; - - const { - axisProps, - getRootProps, - getHiddenInputProps, - getThumbProps, - open, - active, - axis, - focusedThumbIndex, - range, - dragging, - marks, - values, - trackOffset, - trackLeap, - } = useSlider({ ...propsWithDefaultValues, rootRef: ref }); - - const ownerState: SliderOwnerState = { - ...propsWithDefaultValues, - marked: marks.length > 0 && marks.some((mark) => mark.label), - dragging, - focusedThumbIndex, - }; - - const classes = useUtilityClasses(ownerState); - - const overlapApi = useSliderElementsOverlap(axis); - const lastActiveThumbIndexRef = React.useRef<number>(-1); - if (focusedThumbIndex !== -1) { - lastActiveThumbIndexRef.current = focusedThumbIndex; - } else if (active !== -1) { - lastActiveThumbIndexRef.current = active; - } - - const RootSlot = slots.root ?? SliderRoot; - const RailSlot = slots.rail ?? SliderRail; - const TrackSlot = slots.track ?? SliderTrack; - const ThumbSlot = slots.thumb ?? SliderThumb; - const ValueLabelSlot = slots.valueLabel ?? SliderValueLabel; - const MarkSlot = slots.mark ?? SliderMark; - const MarkLabelSlot = slots.markLabel ?? SliderMarkLabel; - const InputSlot = slots.input ?? 'input'; - - const rootProps = useSlotProps({ - elementType: RootSlot, - getSlotProps: getRootProps, - externalSlotProps: slotProps.root, - externalForwardedProps: other, - additionalProps: { - ...(shouldSpreadAdditionalProps(RootSlot) && { - as: component, - }), - }, - ownerState, - className: [classes.root, className], - }); - - const railProps = useSlotProps({ - elementType: RailSlot, - externalSlotProps: slotProps.rail, - ownerState, - className: classes.rail, - }); - - const trackProps = useSlotProps({ - elementType: TrackSlot, - externalSlotProps: slotProps.track, - additionalProps: { - style: { - ...axisProps[axis].offset(trackOffset), - ...axisProps[axis].leap(trackLeap), - }, - }, - ownerState, - className: classes.track, - }); - - const thumbProps = useSlotProps({ - elementType: ThumbSlot, - getSlotProps: () => - getThumbProps({ - onTransitionEnd: overlapApi.onThumbMoved, - onPointerMove: overlapApi.onThumbMoved, - }), - externalSlotProps: slotProps.thumb, - ownerState, - className: classes.thumb, - }); - - const valueLabelProps = useSlotProps({ - elementType: ValueLabelSlot, - externalSlotProps: slotProps.valueLabel, - ownerState, - className: classes.valueLabel, - }); - - const markProps = useSlotProps({ - elementType: MarkSlot, - externalSlotProps: slotProps.mark, - ownerState, - className: classes.mark, - }); - - const markLabelProps = useSlotProps({ - elementType: MarkLabelSlot, - externalSlotProps: slotProps.markLabel, - ownerState, - className: classes.markLabel, - }); - - const inputSliderProps = useSlotProps({ - elementType: InputSlot, - getSlotProps: getHiddenInputProps, - externalSlotProps: slotProps.input, - ownerState, - }); - - return ( - <RootSlot {...rootProps}> - <RailSlot {...railProps} /> - <TrackSlot {...trackProps} /> - {marks - .filter((mark) => mark.value >= min && mark.value <= max) - .map((mark, index) => { - const percent = valueToPercent(mark.value, min, max); - const style = axisProps[axis].offset(percent); - - let markActive; - if (track === false) { - markActive = values.indexOf(mark.value) !== -1; - } else { - markActive = - (track === 'normal' && - (range - ? mark.value >= values[0] && mark.value <= values[values.length - 1] - : mark.value <= values[0])) || - (track === 'inverted' && - (range - ? mark.value <= values[0] || mark.value >= values[values.length - 1] - : mark.value >= values[0])); - } - - return ( - <React.Fragment key={index}> - <MarkSlot - data-index={index} - {...markProps} - {...(!isHostComponent(MarkSlot) && { - markActive, - })} - style={{ ...style, ...markProps.style }} - className={clsx(markProps.className, { - [classes.markActive]: markActive, - })} - /> - {mark.label != null ? ( - <MarkLabelSlot - aria-hidden - data-index={index} - {...markLabelProps} - {...(!isHostComponent(MarkLabelSlot) && { - markLabelActive: markActive, - })} - style={{ ...style, ...markLabelProps.style }} - className={clsx(classes.markLabel, markLabelProps.className, { - [classes.markLabelActive]: markActive, - })} - > - {mark.label} - </MarkLabelSlot> - ) : null} - </React.Fragment> - ); - })} - {values.map((value, index) => { - const percent = valueToPercent(value, min, max); - const style = axisProps[axis].offset(percent); - - const isThumbOverlapping = overlapApi.getIsThumbOverlapping( - index, - lastActiveThumbIndexRef.current, - ); - - const isValueLabelOverlapping = - valueLabelDisplay === 'on' && - overlapApi.getIsValueLabelOverlapping(index, lastActiveThumbIndexRef.current); - - return ( - <ThumbSlot - key={index} - data-index={index} - {...thumbProps} - className={clsx(classes.thumb, thumbProps.className, { - [classes.active]: active === index, - [classes.focusVisible]: focusedThumbIndex === index, - [classes.thumbOverlap]: isThumbOverlapping, - })} - style={{ - ...style, - pointerEvents: disableSwap && active !== index ? 'none' : undefined, - zIndex: isValueLabelOverlapping || isThumbOverlapping ? 2 : undefined, - ...thumbProps.style, - }} - ref={(thumbRef: HTMLElement) => { - thumbProps.ref?.(thumbRef); - overlapApi.setThumbRef(index, thumbRef); - }} - {...(!isHostComponent(ThumbSlot) && { isOverlapping: isThumbOverlapping })} - > - <InputSlot - data-index={index} - aria-label={getAriaLabel ? getAriaLabel(index) : ariaLabel} - aria-valuenow={scale(value)} - aria-labelledby={ariaLabelledby} - aria-valuetext={ - getAriaValueText ? getAriaValueText(scale(value), index) : ariaValuetext - } - value={values[index]} - {...inputSliderProps} - /> - {valueLabelDisplay !== 'off' ? ( - <ValueLabelSlot - {...(!isHostComponent(ValueLabelSlot) && { - valueLabelFormat, - valueLabelDisplay, - value: - typeof valueLabelFormat === 'function' - ? valueLabelFormat(scale(value), index) - : valueLabelFormat, - disabled, - index, - open: open === index || active === index || valueLabelDisplay === 'on', - isOverlapping: isValueLabelOverlapping, - })} - {...valueLabelProps} - className={clsx(valueLabelProps.className, { - [classes.valueLabelOpen]: - open === index || active === index || valueLabelDisplay === 'on', - [classes.valueLabelOverlap]: isValueLabelOverlapping, - })} - ref={(valueLabelRef: HTMLElement) => { - valueLabelProps.ref?.(valueLabelRef); - overlapApi.setValueLabelRef(index, valueLabelRef); - }} - > - <span className={classes.valueLabelLabel}> - {typeof valueLabelFormat === 'function' - ? valueLabelFormat(scale(value), index) - : valueLabelFormat} - </span> - </ValueLabelSlot> - ) : null} - </ThumbSlot> - ); - })} - </RootSlot> - ); -}); - -Slider.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 label of the slider. - */ - 'aria-label': chainPropTypes(PropTypes.string, (props) => { - const range = Array.isArray(props.value || props.defaultValue); - - if (range && props['aria-label'] != null) { - return new Error( - 'MUI: You need to use the `getAriaLabel` prop instead of `aria-label` when using a range slider.', - ); - } - - return null; - }), - /** - * The id of the element containing a label for the slider. - */ - 'aria-labelledby': PropTypes.string, - /** - * A string value that provides a user-friendly name for the current value of the slider. - */ - 'aria-valuetext': chainPropTypes(PropTypes.string, (props) => { - const range = Array.isArray(props.value || props.defaultValue); - - if (range && props['aria-valuetext'] != null) { - return new Error( - 'MUI: You need to use the `getAriaValueText` prop instead of `aria-valuetext` when using a range slider.', - ); - } - - return null; - }), - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['primary', 'secondary']), - PropTypes.string, - ]), - /** - * The default value. Use when the component is not controlled. - */ - defaultValue: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.number]), - /** - * If `true`, the component is disabled. - * @default false - */ - disabled: PropTypes.bool, - /** - * If `true`, the active thumb doesn't swap when moving pointer over a thumb while dragging another thumb. - * @default false - */ - disableSwap: PropTypes.bool, - /** - * Accepts a function which returns a string value that provides a user-friendly name for the thumb labels of the slider. - * This is important for screen reader users. - * @param {number} index The thumb label's index to format. - * @returns {string} - */ - getAriaLabel: PropTypes.func, - /** - * Accepts a function which returns a string value that provides a user-friendly name for the current value of the slider. - * This is important for screen reader users. - * @param {number} value The thumb label's value to format. - * @param {number} index The thumb label's index to format. - * @returns {string} - */ - getAriaValueText: PropTypes.func, - /** - * Marks indicate predetermined values to which the user can move the slider. - * If `true` the marks are spaced according the value of the `step` prop. - * If an array, it should contain objects with `value` and an optional `label` keys. - * @default false - */ - marks: PropTypes.oneOfType([ - PropTypes.arrayOf( - PropTypes.shape({ - label: PropTypes.node, - value: PropTypes.number.isRequired, - }), - ), - PropTypes.bool, - ]), - /** - * The maximum allowed value of the slider. - * Should not be equal to min. - * @default 100 - */ - max: PropTypes.number, - /** - * The minimum allowed value of the slider. - * Should not be equal to max. - * @default 0 - */ - min: PropTypes.number, - /** - * Name attribute of the hidden `input` element. - */ - name: PropTypes.string, - /** - * Callback function that is fired when the slider's value changed. - * - * @param {Event} event The event source of the callback. - * You can pull out the new value by accessing `event.target.value` (any). - * **Warning**: This is a generic event, not a change event. - * @param {number | number[]} value The new value. - * @param {number} activeThumb Index of the currently moved thumb. - */ - onChange: PropTypes.func, - /** - * Callback function that is fired when the `mouseup` is triggered. - * - * @param {React.SyntheticEvent | Event} event The event source of the callback. **Warning**: This is a generic event, not a change event. - * @param {number | number[]} value The new value. - */ - onChangeCommitted: PropTypes.func, - /** - * The component orientation. - * @default 'horizontal' - */ - orientation: PropTypes.oneOf(['horizontal', 'vertical']), - /** - * A transformation function, to change the scale of the slider. - * @param {any} x - * @returns {any} - * @default function Identity(x) { - * return x; - * } - */ - scale: PropTypes.func, - /** - * The size of the slider. - * @default 'medium' - */ - size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['small', 'medium']), - PropTypes.string, - ]), - /** - * The props used for each slot inside the Slider. - * @default {} - */ - slotProps: PropTypes.shape({ - input: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - mark: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - markLabel: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - rail: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - thumb: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - track: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - valueLabel: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - }), - /** - * The components used for each slot inside the Slider. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots: PropTypes.shape({ - input: PropTypes.elementType, - mark: PropTypes.elementType, - markLabel: PropTypes.elementType, - rail: PropTypes.elementType, - root: PropTypes.elementType, - thumb: PropTypes.elementType, - track: PropTypes.elementType, - valueLabel: PropTypes.elementType, - }), - /** - * The granularity with which the slider can step through values. (A "discrete" slider.) - * The `min` prop serves as the origin for the valid values. - * We recommend (max - min) to be evenly divisible by the step. - * - * When step is `null`, the thumb can only be slid onto marks provided with the `marks` prop. - * @default 1 - */ - step: PropTypes.number, - /** - * 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, - ]), - /** - * Tab index attribute of the hidden `input` element. - */ - tabIndex: PropTypes.number, - /** - * The track presentation: - * - * - `normal` the track will render a bar representing the slider value. - * - `inverted` the track will render a bar representing the remaining slider value. - * - `false` the track will render without a bar. - * @default 'normal' - */ - track: PropTypes.oneOf(['inverted', 'normal', false]), - /** - * The value of the slider. - * For ranged sliders, provide an array with two values. - */ - value: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.number]), - /** - * Controls when the value label is displayed: - * - * - `auto` the value label will display when the thumb is hovered or focused. - * - `on` will display persistently. - * - `off` will never display. - * @default 'off' - */ - valueLabelDisplay: PropTypes.oneOf(['auto', 'off', 'on']), - /** - * The format function the value label's value. - * - * When a function is provided, it should have the following signature: - * - * - {number} value The value label's value to format - * - {number} index The value label's index to format - * @param {any} x - * @returns {any} - * @default function Identity(x) { - * return x; - * } - */ - valueLabelFormat: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), -} as any; - -export default Slider; diff --git a/packages/mui-material-next/src/Slider/Slider.types.ts b/packages/mui-material-next/src/Slider/Slider.types.ts deleted file mode 100644 index 26eec9279d3d91..00000000000000 --- a/packages/mui-material-next/src/Slider/Slider.types.ts +++ /dev/null @@ -1,277 +0,0 @@ -import * as React from 'react'; -import { SlotComponentProps } from '@mui/base'; -import { Mark } from '@mui/base/useSlider'; -import { SxProps } from '@mui/system'; -import { OverridableStringUnion, OverrideProps, OverridableComponent } from '@mui/types'; -import { Theme } from '../styles'; -import { SliderClasses } from './sliderClasses'; - -export interface SliderPropsColorOverrides {} - -export interface SliderPropsSizeOverrides {} - -export interface SliderSlotPropsOverrides {} - -export interface SliderOwnerState extends SliderProps { - dragging: boolean; - marked: boolean; - focusedThumbIndex: number; -} - -export interface SliderTypeMap< - DefaultComponent extends React.ElementType = 'span', - AdditionalProps = {}, -> { - props: AdditionalProps & { - /** - * The label of the slider. - */ - 'aria-label'?: string; - /** - * The id of the element containing a label for the slider. - */ - 'aria-labelledby'?: string; - /** - * A string value that provides a user-friendly name for the current value of the slider. - */ - 'aria-valuetext'?: string; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color?: OverridableStringUnion<'primary' | 'secondary', SliderPropsColorOverrides>; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<SliderClasses>; - /** - * @ignore - */ - className?: string; - /** - * The default value. Use when the component is not controlled. - */ - defaultValue?: number | number[]; - /** - * If `true`, the component is disabled. - * @default false - */ - disabled?: boolean; - /** - * If `true`, the active thumb doesn't swap when moving pointer over a thumb while dragging another thumb. - * @default false - */ - disableSwap?: boolean; - /** - * Accepts a function which returns a string value that provides a user-friendly name for the thumb labels of the slider. - * This is important for screen reader users. - * @param {number} index The thumb label's index to format. - * @returns {string} - */ - getAriaLabel?: (index: number) => string; - /** - * Accepts a function which returns a string value that provides a user-friendly name for the current value of the slider. - * This is important for screen reader users. - * @param {number} value The thumb label's value to format. - * @param {number} index The thumb label's index to format. - * @returns {string} - */ - getAriaValueText?: (value: number, index: number) => string; - /** - * Marks indicate predetermined values to which the user can move the slider. - * If `true` the marks are spaced according the value of the `step` prop. - * If an array, it should contain objects with `value` and an optional `label` keys. - * @default false - */ - marks?: boolean | Mark[]; - /** - * The maximum allowed value of the slider. - * Should not be equal to min. - * @default 100 - */ - max?: number; - /** - * The minimum allowed value of the slider. - * Should not be equal to max. - * @default 0 - */ - min?: number; - /** - * Name attribute of the hidden `input` element. - */ - name?: string; - /** - * Callback function that is fired when the slider's value changed. - * - * @param {Event} event The event source of the callback. - * You can pull out the new value by accessing `event.target.value` (any). - * **Warning**: This is a generic event, not a change event. - * @param {number | number[]} value The new value. - * @param {number} activeThumb Index of the currently moved thumb. - */ - onChange?: (event: Event, value: number | number[], activeThumb: number) => void; - /** - * Callback function that is fired when the `mouseup` is triggered. - * - * @param {React.SyntheticEvent | Event} event The event source of the callback. **Warning**: This is a generic event, not a change event. - * @param {number | number[]} value The new value. - */ - onChangeCommitted?: (event: React.SyntheticEvent | Event, value: number | number[]) => void; - /** - * The component orientation. - * @default 'horizontal' - */ - orientation?: 'horizontal' | 'vertical'; - /** - * A transformation function, to change the scale of the slider. - * @param {any} x - * @returns {any} - * @default function Identity(x) { - * return x; - * } - */ - scale?: (value: number) => number; - /** - * The size of the slider. - * @default 'medium' - */ - size?: OverridableStringUnion<'small' | 'medium', SliderPropsSizeOverrides>; - /** - * The props used for each slot inside the Slider. - * @default {} - */ - slotProps?: { - root?: SlotComponentProps<'span', SliderSlotPropsOverrides, SliderOwnerState>; - track?: SlotComponentProps<'span', SliderSlotPropsOverrides, SliderOwnerState>; - rail?: SlotComponentProps<'span', SliderSlotPropsOverrides, SliderOwnerState>; - thumb?: SlotComponentProps<'span', SliderSlotPropsOverrides, SliderOwnerState>; - mark?: SlotComponentProps<'span', SliderSlotPropsOverrides, SliderOwnerState>; - markLabel?: SlotComponentProps<'span', SliderSlotPropsOverrides, SliderOwnerState>; - valueLabel?: SlotComponentProps<'span', SliderSlotPropsOverrides, SliderOwnerState>; - input?: SlotComponentProps<'input', SliderSlotPropsOverrides, SliderOwnerState>; - }; - /** - * The components used for each slot inside the Slider. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots?: { - root?: React.ElementType; - track?: React.ElementType; - rail?: React.ElementType; - thumb?: React.ElementType; - mark?: React.ElementType; - markLabel?: React.ElementType; - valueLabel?: React.ElementType; - input?: React.ElementType; - }; - /** - * The granularity with which the slider can step through values. (A "discrete" slider.) - * The `min` prop serves as the origin for the valid values. - * We recommend (max - min) to be evenly divisible by the step. - * - * When step is `null`, the thumb can only be slid onto marks provided with the `marks` prop. - * @default 1 - */ - step?: number | null; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; - /** - * Tab index attribute of the hidden `input` element. - */ - tabIndex?: number; - /** - * The track presentation: - * - * - `normal` the track will render a bar representing the slider value. - * - `inverted` the track will render a bar representing the remaining slider value. - * - `false` the track will render without a bar. - * @default 'normal' - */ - track?: 'normal' | false | 'inverted'; - /** - * The value of the slider. - * For ranged sliders, provide an array with two values. - */ - value?: number | number[]; - /** - * Controls when the value label is displayed: - * - * - `auto` the value label will display when the thumb is hovered or focused. - * - `on` will display persistently. - * - `off` will never display. - * @default 'off' - */ - valueLabelDisplay?: 'on' | 'auto' | 'off'; - /** - * The format function the value label's value. - * - * When a function is provided, it should have the following signature: - * - * - {number} value The value label's value to format - * - {number} index The value label's index to format - * @param {any} x - * @returns {any} - * @default function Identity(x) { - * return x; - * } - */ - valueLabelFormat?: string | ((value: number, index: number) => React.ReactNode); - }; - defaultComponent: DefaultComponent; -} - -export type SliderValueLabelProps = NonNullable<SliderTypeMap['props']['slotProps']>['valueLabel'] & - Pick<SliderTypeMap['props'], 'valueLabelFormat' | 'valueLabelDisplay' | 'value' | 'disabled'> & { - /** - * The index of the value label. - * Useful for range sliders. - */ - index: number; - /** - * If `true`, the value label is visible. - */ - open: boolean; - /** - * If `true`, the value label is overlaping another value label. - */ - isOverlapping?: boolean; - }; - -type SliderRootProps = NonNullable<SliderTypeMap['props']['slotProps']>['root']; -type SliderMarkProps = NonNullable<SliderTypeMap['props']['slotProps']>['mark']; -type SliderMarkLabelProps = NonNullable<SliderTypeMap['props']['slotProps']>['markLabel']; -type SliderRailProps = NonNullable<SliderTypeMap['props']['slotProps']>['rail']; -type SliderTrackProps = NonNullable<SliderTypeMap['props']['slotProps']>['track']; -type SliderThumbProps = NonNullable<SliderTypeMap['props']['slotProps']>['thumb']; - -export declare const SliderRoot: React.FC<SliderRootProps>; -export declare const SliderMark: React.FC<SliderMarkProps>; -export declare const SliderMarkLabel: React.FC<SliderMarkLabelProps>; -export declare const SliderRail: React.FC<SliderRailProps>; -export declare const SliderTrack: React.FC<SliderTrackProps>; -export declare const SliderThumb: React.FC<SliderThumbProps>; -export declare const SliderValueLabel: React.FC<SliderValueLabelProps>; - -/** - * - * Demos: - * - * - [Slider](https://mui.com/material-ui/react-slider/) - * - * API: - * - * - [Slider API](https://mui.com/material-ui/api/slider/) - */ -declare const Slider: OverridableComponent<SliderTypeMap>; - -export type SliderProps< - RootComponent extends React.ElementType = SliderTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<SliderTypeMap<RootComponent, AdditionalProps>, RootComponent>; - -export default Slider; diff --git a/packages/mui-material-next/src/Slider/index.ts b/packages/mui-material-next/src/Slider/index.ts deleted file mode 100644 index 4e20c2e2b8a724..00000000000000 --- a/packages/mui-material-next/src/Slider/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -'use client'; -export { default } from './Slider'; -export * from './Slider'; - -export { default as sliderClasses } from './sliderClasses'; -export * from './sliderClasses'; diff --git a/packages/mui-material-next/src/Slider/sliderClasses.ts b/packages/mui-material-next/src/Slider/sliderClasses.ts deleted file mode 100644 index 166f63ad9bebcd..00000000000000 --- a/packages/mui-material-next/src/Slider/sliderClasses.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface SliderClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `color="primary"`. */ - colorPrimary: string; - /** Styles applied to the root element if `color="secondary"`. */ - colorSecondary: string; - /** Styles applied to the root element if `marks` is provided with at least one label. */ - marked: string; - /** Styles applied to the root element if `orientation="vertical"`. */ - vertical: string; - /** State class applied to the root and thumb element if `disabled={true}`. */ - disabled: string; - /** State class applied to the root if a thumb is being dragged. */ - dragging: string; - /** Styles applied to the rail element. */ - rail: string; - /** Styles applied to the track element. */ - track: string; - /** Styles applied to the root element if `track={false}`. */ - trackFalse: string; - /** Styles applied to the root element if `track="inverted"`. */ - trackInverted: string; - /** Styles applied to the thumb element. */ - thumb: string; - /** Styles applied to the thumb element if it overlaps another thumb */ - thumbOverlap: string; - /** State class applied to the thumb element if it's active. */ - active: string; - /** State class applied to the thumb element if keyboard focused. */ - focusVisible: string; - /** Styles applied to the mark element. */ - mark: string; - /** Styles applied to the mark element if active (depending on the value). */ - markActive: string; - /** Styles applied to the mark label element. */ - markLabel: string; - /** Styles applied to the mark label element if active (depending on the value). */ - markLabelActive: string; - /** Styles applied to the root element if `size="small"`. */ - sizeSmall: string; - /** Styles applied to the thumb element if `color="primary"`. */ - thumbColorPrimary: string; - /** Styles applied to the thumb element if `color="secondary"`. */ - thumbColorSecondary: string; - /** Styles applied to the thumb element if `size="small"`. */ - thumbSizeSmall: string; - /** Styles applied to the thumb label element. */ - valueLabel: string; - /** Styles applied to the thumb label element if it's open. */ - valueLabelOpen: string; - /** Styles applied to the thumb's label element if it overlaps another thumb's label */ - valueLabelOverlap: string; - /** Styles applied to the thumb label's label element. */ - valueLabelLabel: string; -} - -export type SliderClassKey = keyof SliderClasses; - -export function getSliderUtilityClass(slot: string): string { - return generateUtilityClass('MuiSlider', slot); -} - -const sliderClasses: SliderClasses = generateUtilityClasses('MuiSlider', [ - 'root', - 'active', - 'colorPrimary', - 'colorSecondary', - 'disabled', - 'dragging', - 'focusVisible', - 'mark', - 'markActive', - 'marked', - 'markLabel', - 'markLabelActive', - 'rail', - 'sizeSmall', - 'thumb', - 'thumbOverlap', - 'thumbColorPrimary', - 'thumbColorSecondary', - 'track', - 'trackInverted', - 'trackFalse', - 'thumbSizeSmall', - 'valueLabel', - 'valueLabelOpen', - 'valueLabelOverlap', - 'valueLabelLabel', - 'vertical', -]); - -export default sliderClasses; diff --git a/packages/mui-material-next/src/Slider/useSliderElementsOverlap.ts b/packages/mui-material-next/src/Slider/useSliderElementsOverlap.ts deleted file mode 100644 index 34e36ccffd646a..00000000000000 --- a/packages/mui-material-next/src/Slider/useSliderElementsOverlap.ts +++ /dev/null @@ -1,99 +0,0 @@ -'use client'; -import * as React from 'react'; -import { Axis, areArraysEqual } from '@mui/base'; -import { - unstable_useEnhancedEffect as useEnhancedEffect, - unstable_debounce as debounce, -} from '@mui/utils'; - -const overlapCompareFunctionByAxis = { - horizontal: (firstElementRect: DOMRect, secondElementRect: DOMRect, margin: number) => - firstElementRect.right + margin > secondElementRect.left, - 'horizontal-reverse': (firstElementRect: DOMRect, secondElementRect: DOMRect, margin: number) => - secondElementRect.right + margin > firstElementRect.left, - vertical: (firstElementRect: DOMRect, secondElementRect: DOMRect, margin: number) => - secondElementRect.bottom + margin > firstElementRect.top, -}; - -function getSliderElementsOverlap(elements: HTMLElement[], axis: Axis, margin: number) { - const overlapArray = elements.map(() => false); - - for (let elementIndex = 0; elementIndex < elements.length - 1; elementIndex += 1) { - const firstElementRect = elements[elementIndex].getBoundingClientRect(); - const secondElementRect = elements[elementIndex + 1].getBoundingClientRect(); - - if (overlapCompareFunctionByAxis[axis](firstElementRect, secondElementRect, margin)) { - overlapArray[elementIndex] = true; - overlapArray[elementIndex + 1] = true; - } - } - return overlapArray; -} - -function useElementsOverlap(axis: Axis, margin: number = 0) { - const elementsRefList = React.useRef<HTMLElement[]>([]); - const [elementsOverlapArray, setElementsOverlapArray] = React.useState<boolean[]>([]); - - const setRef = React.useCallback((elementIndex: number, ref: HTMLElement) => { - if (!!ref && elementsRefList.current[elementIndex] !== ref) { - elementsRefList.current[elementIndex] = ref; - } - }, []); - - const getIsOverlapping = React.useCallback( - (elementIndex: number, lastActiveIndex: number) => { - if (elementsRefList.current.length < 2) { - return false; - } - - return elementsOverlapArray[elementIndex] && lastActiveIndex === elementIndex; - }, - [elementsOverlapArray], - ); - - const onMove = React.useCallback(() => { - if (elementsRefList.current.length > 1) { - const updatedOverlap = getSliderElementsOverlap(elementsRefList.current, axis, margin); - if (!areArraysEqual(updatedOverlap, elementsOverlapArray)) { - setElementsOverlapArray(updatedOverlap); - } - } - }, [axis, elementsOverlapArray, margin]); - - return { setRef, getIsOverlapping, onMove }; -} - -export default function useSliderElementsOverlap(axis: Axis) { - const { - setRef: setThumbRef, - getIsOverlapping: getIsThumbOverlapping, - onMove: onThumbMove, - } = useElementsOverlap(axis); - - // ValueLabel -12px margin is required due to how its "inverted water drop" - // shape is built with CSS. Might want to allow this to be configurable in the future. - const { - setRef: setValueLabelRef, - getIsOverlapping: getIsValueLabelOverlapping, - onMove: onValueLabelMove, - } = useElementsOverlap(axis, -12); - - const onThumbMoved = React.useCallback(() => { - onThumbMove(); - onValueLabelMove(); - }, [onThumbMove, onValueLabelMove]); - - const debouncedOnThumbMoved = React.useMemo(() => debounce(onThumbMoved, 50), [onThumbMoved]); - - useEnhancedEffect(() => { - onThumbMoved(); - }, [onThumbMoved]); - - return { - setThumbRef, - setValueLabelRef, - getIsThumbOverlapping, - getIsValueLabelOverlapping, - onThumbMoved: debouncedOnThumbMoved, - }; -} diff --git a/packages/mui-material-next/src/Snackbar/Snackbar.d.ts b/packages/mui-material-next/src/Snackbar/Snackbar.d.ts deleted file mode 100644 index b274c35d804f0d..00000000000000 --- a/packages/mui-material-next/src/Snackbar/Snackbar.d.ts +++ /dev/null @@ -1,130 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { ClickAwayListenerProps } from '@mui/base/ClickAwayListener'; -import { InternalStandardProps as StandardProps } from '@mui/material'; -import { SnackbarContentProps } from '@mui/material/SnackbarContent'; -import { TransitionProps } from '@mui/material/transitions'; -import { Theme } from '../styles'; -import { SnackbarClasses } from './snackbarClasses'; - -export interface SnackbarOrigin { - vertical: 'top' | 'bottom'; - horizontal: 'left' | 'center' | 'right'; -} - -export type SnackbarCloseReason = 'timeout' | 'clickaway' | 'escapeKeyDown'; - -export interface SnackbarProps extends StandardProps<React.HTMLAttributes<HTMLDivElement>> { - /** - * The action to display. It renders after the message, at the end of the snackbar. - */ - action?: SnackbarContentProps['action']; - /** - * The anchor of the `Snackbar`. - * On smaller screens, the component grows to occupy all the available width, - * the horizontal alignment is ignored. - * @default { vertical: 'bottom', horizontal: 'left' } - */ - anchorOrigin?: SnackbarOrigin; - /** - * The number of milliseconds to wait before automatically calling the - * `onClose` function. `onClose` should then set the state of the `open` - * prop to hide the Snackbar. This behavior is disabled by default with - * the `null` value. - * @default null - */ - autoHideDuration?: number | null; - /** - * Replace the `SnackbarContent` component. - */ - children?: React.ReactElement<any, any>; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<SnackbarClasses>; - /** - * Props applied to the `ClickAwayListener` element. - */ - ClickAwayListenerProps?: Partial<ClickAwayListenerProps>; - /** - * Props applied to the [`SnackbarContent`](/material-ui/api/snackbar-content/) element. - */ - ContentProps?: Partial<SnackbarContentProps>; - /** - * If `true`, the `autoHideDuration` timer will expire even if the window is not focused. - * @default false - */ - disableWindowBlurListener?: boolean; - /** - * When displaying multiple consecutive snackbars using a single parent-rendered - * `<Snackbar/>`, add the `key` prop to ensure independent treatment of each message. - * For instance, use `<Snackbar key={message} />`. Otherwise, messages might update - * in place, and features like `autoHideDuration` could be affected. - */ - key?: any; - /** - * The message to display. - */ - message?: SnackbarContentProps['message']; - /** - * Callback fired when the component requests to be closed. - * Typically `onClose` is used to set state in the parent component, - * which is used to control the `Snackbar` `open` prop. - * The `reason` parameter can optionally be used to control the response to `onClose`, - * for example ignoring `clickaway`. - * - * @param {React.SyntheticEvent<any> | Event} event The event source of the callback. - * @param {string} reason Can be: `"timeout"` (`autoHideDuration` expired), `"clickaway"`, or `"escapeKeyDown"`. - */ - onClose?: (event: React.SyntheticEvent<any> | Event, reason: SnackbarCloseReason) => void; - /** - * If `true`, the component is shown. - */ - open?: boolean; - /** - * The number of milliseconds to wait before dismissing after user interaction. - * If `autoHideDuration` prop isn't specified, it does nothing. - * If `autoHideDuration` prop is specified but `resumeHideDuration` isn't, - * we default to `autoHideDuration / 2` ms. - */ - resumeHideDuration?: number; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; - /** - * The component used for the transition. - * [Follow this guide](/material-ui/transitions/#transitioncomponent-prop) to learn more about the requirements for this component. - * @default Grow - */ - TransitionComponent?: React.JSXElementConstructor< - TransitionProps & { children: React.ReactElement<any, any> } - >; - /** - * The duration for the transition, in milliseconds. - * You may specify a single timeout for all transitions, or individually with an object. - * @default { - * enter: theme.transitions.duration.enteringScreen, - * exit: theme.transitions.duration.leavingScreen, - * } - */ - transitionDuration?: TransitionProps['timeout']; - /** - * Props applied to the transition element. - * By default, the element is based on this [`Transition`](https://reactcommunity.org/react-transition-group/transition/) component. - * @default {} - */ - TransitionProps?: TransitionProps; -} - -/** - * - * Demos: - * - * - [Snackbar](https://mui.com/material-ui/react-snackbar/) - * - * API: - * - * - [Snackbar API](https://mui.com/material-ui/api/snackbar/) - */ -export default function Snackbar(props: SnackbarProps): JSX.Element; diff --git a/packages/mui-material-next/src/Snackbar/Snackbar.js b/packages/mui-material-next/src/Snackbar/Snackbar.js deleted file mode 100644 index 5915bb79aa7b64..00000000000000 --- a/packages/mui-material-next/src/Snackbar/Snackbar.js +++ /dev/null @@ -1,312 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { unstable_composeClasses as composeClasses, useSlotProps } from '@mui/base'; -import { ClickAwayListener } from '@mui/base/ClickAwayListener'; -import { useSnackbar } from '@mui/base/useSnackbar'; -import { unstable_capitalize as capitalize } from '@mui/utils'; -import Grow from '@mui/material/Grow'; -import SnackbarContent from '@mui/material/SnackbarContent'; -import styled from '../styles/styled'; -import useTheme from '../styles/useTheme'; -import useThemeProps from '../styles/useThemeProps'; -import { getSnackbarUtilityClass } from './snackbarClasses'; - -const useUtilityClasses = (ownerState) => { - const { classes, anchorOrigin } = ownerState; - - const slots = { - root: [ - 'root', - `anchorOrigin${capitalize(anchorOrigin.vertical)}${capitalize(anchorOrigin.horizontal)}`, - ], - }; - - return composeClasses(slots, getSnackbarUtilityClass, classes); -}; - -const SnackbarRoot = styled('div', { - name: 'MuiSnackbar', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - styles[ - `anchorOrigin${capitalize(ownerState.anchorOrigin.vertical)}${capitalize( - ownerState.anchorOrigin.horizontal, - )}` - ], - ]; - }, -})(({ theme, ownerState }) => { - const center = { - left: '50%', - right: 'auto', - transform: 'translateX(-50%)', - }; - - return { - zIndex: (theme.vars || theme).zIndex.snackbar, - position: 'fixed', - display: 'flex', - left: 8, - right: 8, - justifyContent: 'center', - alignItems: 'center', - ...(ownerState.anchorOrigin.vertical === 'top' ? { top: 8 } : { bottom: 8 }), - ...(ownerState.anchorOrigin.horizontal === 'left' && { justifyContent: 'flex-start' }), - ...(ownerState.anchorOrigin.horizontal === 'right' && { justifyContent: 'flex-end' }), - [theme.breakpoints.up('sm')]: { - ...(ownerState.anchorOrigin.vertical === 'top' ? { top: 24 } : { bottom: 24 }), - ...(ownerState.anchorOrigin.horizontal === 'center' && center), - ...(ownerState.anchorOrigin.horizontal === 'left' && { - left: 24, - right: 'auto', - }), - ...(ownerState.anchorOrigin.horizontal === 'right' && { - right: 24, - left: 'auto', - }), - }, - }; -}); - -const Snackbar = React.forwardRef(function Snackbar(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiSnackbar' }); - const theme = useTheme(); - const defaultTransitionDuration = { - enter: theme.transitions.duration.enteringScreen, - exit: theme.transitions.duration.leavingScreen, - }; - - const { - action, - anchorOrigin: { vertical, horizontal } = { vertical: 'bottom', horizontal: 'left' }, - autoHideDuration = null, - children, - className, - ClickAwayListenerProps, - ContentProps, - disableWindowBlurListener = false, - message, - onBlur, - onClose, - onFocus, - onMouseEnter, - onMouseLeave, - open, - resumeHideDuration, - TransitionComponent = Grow, - transitionDuration = defaultTransitionDuration, - TransitionProps: { onEnter, onExited, ...TransitionProps } = {}, - ...other - } = props; - - const ownerState = { - ...props, - anchorOrigin: { vertical, horizontal }, - autoHideDuration, - disableWindowBlurListener, - TransitionComponent, - transitionDuration, - }; - - const classes = useUtilityClasses(ownerState); - - const { getRootProps, onClickAway } = useSnackbar({ ...ownerState }); - - const [exited, setExited] = React.useState(true); - - const rootProps = useSlotProps({ - elementType: SnackbarRoot, - getSlotProps: getRootProps, - externalForwardedProps: other, - ownerState, - additionalProps: { - ref, - }, - className: [classes.root, className], - }); - - const handleExited = (node) => { - setExited(true); - - if (onExited) { - onExited(node); - } - }; - - const handleEnter = (node, isAppearing) => { - setExited(false); - - if (onEnter) { - onEnter(node, isAppearing); - } - }; - - // So we only render active snackbars. - if (!open && exited) { - return null; - } - - return ( - <ClickAwayListener onClickAway={onClickAway} {...ClickAwayListenerProps}> - <SnackbarRoot {...rootProps}> - <TransitionComponent - appear - in={open} - timeout={transitionDuration} - direction={vertical === 'top' ? 'down' : 'up'} - onEnter={handleEnter} - onExited={handleExited} - {...TransitionProps} - > - {children || <SnackbarContent message={message} action={action} {...ContentProps} />} - </TransitionComponent> - </SnackbarRoot> - </ClickAwayListener> - ); -}); - -Snackbar.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The action to display. It renders after the message, at the end of the snackbar. - */ - action: PropTypes.node, - /** - * The anchor of the `Snackbar`. - * On smaller screens, the component grows to occupy all the available width, - * the horizontal alignment is ignored. - * @default { vertical: 'bottom', horizontal: 'left' } - */ - anchorOrigin: PropTypes.shape({ - horizontal: PropTypes.oneOf(['center', 'left', 'right']).isRequired, - vertical: PropTypes.oneOf(['bottom', 'top']).isRequired, - }), - /** - * The number of milliseconds to wait before automatically calling the - * `onClose` function. `onClose` should then set the state of the `open` - * prop to hide the Snackbar. This behavior is disabled by default with - * the `null` value. - * @default null - */ - autoHideDuration: PropTypes.number, - /** - * Replace the `SnackbarContent` component. - */ - children: PropTypes.element, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * Props applied to the `ClickAwayListener` element. - */ - ClickAwayListenerProps: PropTypes.object, - /** - * Props applied to the [`SnackbarContent`](/material-ui/api/snackbar-content/) element. - */ - ContentProps: PropTypes.object, - /** - * If `true`, the `autoHideDuration` timer will expire even if the window is not focused. - * @default false - */ - disableWindowBlurListener: PropTypes.bool, - /** - * When displaying multiple consecutive snackbars using a single parent-rendered - * `<Snackbar/>`, add the `key` prop to ensure independent treatment of each message. - * For instance, use `<Snackbar key={message} />`. Otherwise, messages might update - * in place, and features like `autoHideDuration` could be affected. - */ - key: () => null, - /** - * The message to display. - */ - message: PropTypes.node, - /** - * @ignore - */ - onBlur: PropTypes.func, - /** - * Callback fired when the component requests to be closed. - * Typically `onClose` is used to set state in the parent component, - * which is used to control the `Snackbar` `open` prop. - * The `reason` parameter can optionally be used to control the response to `onClose`, - * for example ignoring `clickaway`. - * - * @param {React.SyntheticEvent<any> | Event} event The event source of the callback. - * @param {string} reason Can be: `"timeout"` (`autoHideDuration` expired), `"clickaway"`, or `"escapeKeyDown"`. - */ - onClose: PropTypes.func, - /** - * @ignore - */ - onFocus: PropTypes.func, - /** - * @ignore - */ - onMouseEnter: PropTypes.func, - /** - * @ignore - */ - onMouseLeave: PropTypes.func, - /** - * If `true`, the component is shown. - */ - open: PropTypes.bool, - /** - * The number of milliseconds to wait before dismissing after user interaction. - * If `autoHideDuration` prop isn't specified, it does nothing. - * If `autoHideDuration` prop is specified but `resumeHideDuration` isn't, - * we default to `autoHideDuration / 2` ms. - */ - resumeHideDuration: PropTypes.number, - /** - * 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 component used for the transition. - * [Follow this guide](/material-ui/transitions/#transitioncomponent-prop) to learn more about the requirements for this component. - * @default Grow - */ - TransitionComponent: PropTypes.elementType, - /** - * The duration for the transition, in milliseconds. - * You may specify a single timeout for all transitions, or individually with an object. - * @default { - * enter: theme.transitions.duration.enteringScreen, - * exit: theme.transitions.duration.leavingScreen, - * } - */ - transitionDuration: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.shape({ - appear: PropTypes.number, - enter: PropTypes.number, - exit: PropTypes.number, - }), - ]), - /** - * Props applied to the transition element. - * By default, the element is based on this [`Transition`](https://reactcommunity.org/react-transition-group/transition/) component. - * @default {} - */ - TransitionProps: PropTypes.object, -}; - -export default Snackbar; diff --git a/packages/mui-material-next/src/Snackbar/Snackbar.test.js b/packages/mui-material-next/src/Snackbar/Snackbar.test.js deleted file mode 100644 index a8968db29b4bc4..00000000000000 --- a/packages/mui-material-next/src/Snackbar/Snackbar.test.js +++ /dev/null @@ -1,600 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { spy } from 'sinon'; -import { act, createRenderer, fireEvent } from '@mui-internal/test-utils'; -import Snackbar, { snackbarClasses as classes } from '@mui/material-next/Snackbar'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; -import describeConformance from '../../test/describeConformance'; - -describe('<Snackbar />', () => { - const { clock, render: clientRender } = createRenderer({ clock: 'fake' }); - /** - * @type {typeof plainRender extends (...args: infer T) => any ? T : never} args - * - * @remarks - * This is for all intents and purposes the same as our client render method. - * `plainRender` is already wrapped in act(). - * However, React has a bug that flushes effects in a portal synchronously. - * We have to defer the effect manually like `useEffect` would so we have to flush the effect manually instead of relying on `act()`. - * React bug: https://github.com/facebook/react/issues/20074 - */ - function render(...args) { - const result = clientRender(...args); - clock.tick(0); - return result; - } - - describeConformance(<Snackbar open message="message" />, () => ({ - classes, - inheritComponent: 'div', - render, - refInstanceof: window.HTMLDivElement, - muiName: 'MuiSnackbar', - skip: [ - 'componentProp', - 'componentsProp', - 'themeVariants', - // react-transition-group issue - 'reactTestRenderer', - ], - })); - - describe('prop: onClose', () => { - it('should be call when clicking away', () => { - const handleClose = spy(); - render(<Snackbar open onClose={handleClose} message="message" />); - - const event = new window.Event('click', { bubbles: true, cancelable: true }); - document.body.dispatchEvent(event); - - expect(handleClose.callCount).to.equal(1); - expect(handleClose.args[0]).to.deep.equal([event, 'clickaway']); - }); - - it('should be called when pressing Escape', () => { - const handleClose = spy(); - render(<Snackbar open onClose={handleClose} message="message" />); - - expect(fireEvent.keyDown(document.body, { key: 'Escape' })).to.equal(true); - expect(handleClose.callCount).to.equal(1); - expect(handleClose.args[0][1]).to.deep.equal('escapeKeyDown'); - }); - - it('can limit which Snackbars are closed when pressing Escape', () => { - const handleCloseA = spy((event) => event.preventDefault()); - const handleCloseB = spy(); - render( - <React.Fragment> - <Snackbar open onClose={handleCloseA} message="messageA" /> - <Snackbar open onClose={handleCloseB} message="messageB" /> - </React.Fragment>, - ); - - fireEvent.keyDown(document.body, { key: 'Escape' }); - - expect(handleCloseA.callCount).to.equal(1); - expect(handleCloseB.callCount).to.equal(0); - }); - }); - - describe('Consecutive messages', () => { - it('should support synchronous onExited callback', () => { - const messageCount = 2; - - const onClose = spy(); - const onExited = spy(); - const duration = 250; - - let setSnackbarOpen; - function Test() { - const [open, setOpen] = React.useState(false); - setSnackbarOpen = setOpen; - - function handleClose() { - setOpen(false); - onClose(); - } - - function handleExited() { - onExited(); - if (onExited.callCount < messageCount) { - setOpen(true); - } - } - - return ( - <Snackbar - open={open} - onClose={handleClose} - TransitionProps={{ onExited: handleExited }} - message="message" - autoHideDuration={duration} - transitionDuration={duration / 2} - /> - ); - } - render( - <Test - onClose={onClose} - onExited={onExited} - message="message" - autoHideDuration={duration} - transitionDuration={duration / 2} - />, - ); - - expect(onClose.callCount).to.equal(0); - expect(onExited.callCount).to.equal(0); - - act(() => { - setSnackbarOpen(true); - }); - clock.tick(duration); - - expect(onClose.callCount).to.equal(1); - expect(onExited.callCount).to.equal(0); - - clock.tick(duration / 2); - - expect(onClose.callCount).to.equal(1); - expect(onExited.callCount).to.equal(1); - - clock.tick(duration); - - expect(onClose.callCount).to.equal(messageCount); - expect(onExited.callCount).to.equal(1); - - clock.tick(duration / 2); - - expect(onClose.callCount).to.equal(messageCount); - expect(onExited.callCount).to.equal(messageCount); - }); - }); - - describe('prop: autoHideDuration', () => { - it('should call onClose when the timer is done', () => { - const handleClose = spy(); - const autoHideDuration = 2e3; - const { setProps } = render( - <Snackbar - open={false} - onClose={handleClose} - message="message" - autoHideDuration={autoHideDuration} - />, - ); - - setProps({ open: true }); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(autoHideDuration); - - expect(handleClose.callCount).to.equal(1); - expect(handleClose.args[0]).to.deep.equal([null, 'timeout']); - }); - - it('calls onClose at timeout even if the prop changes', () => { - const handleClose1 = spy(); - const handleClose2 = spy(); - const autoHideDuration = 2e3; - const { setProps } = render( - <Snackbar - open={false} - onClose={handleClose1} - message="message" - autoHideDuration={autoHideDuration} - />, - ); - - setProps({ open: true }); - clock.tick(autoHideDuration / 2); - setProps({ open: true, onClose: handleClose2 }); - clock.tick(autoHideDuration / 2); - - expect(handleClose1.callCount).to.equal(0); - expect(handleClose2.callCount).to.equal(1); - }); - - it('should not call onClose when the autoHideDuration is reset', () => { - const handleClose = spy(); - const autoHideDuration = 2e3; - const { setProps } = render( - <Snackbar - open={false} - onClose={handleClose} - message="message" - autoHideDuration={autoHideDuration} - />, - ); - - setProps({ open: true }); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(autoHideDuration / 2); - setProps({ autoHideDuration: undefined }); - clock.tick(autoHideDuration / 2); - - expect(handleClose.callCount).to.equal(0); - }); - - it('should not call onClose if autoHideDuration is undefined', () => { - const handleClose = spy(); - const autoHideDuration = 2e3; - render( - <Snackbar open onClose={handleClose} message="message" autoHideDuration={undefined} />, - ); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(autoHideDuration); - - expect(handleClose.callCount).to.equal(0); - }); - - it('should not call onClose if autoHideDuration is null', () => { - const handleClose = spy(); - const autoHideDuration = 2e3; - - render(<Snackbar open onClose={handleClose} message="message" autoHideDuration={null} />); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(autoHideDuration); - - expect(handleClose.callCount).to.equal(0); - }); - - it('should not call onClose when closed', () => { - const handleClose = spy(); - const autoHideDuration = 2e3; - - const { setProps } = render( - <Snackbar - open - onClose={handleClose} - message="message" - autoHideDuration={autoHideDuration} - />, - ); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(autoHideDuration / 2); - setProps({ open: false }); - clock.tick(autoHideDuration / 2); - - expect(handleClose.callCount).to.equal(0); - }); - }); - - [ - { - type: 'mouse', - enter: (container) => fireEvent.mouseEnter(container.querySelector('button')), - leave: (container) => fireEvent.mouseLeave(container.querySelector('button')), - }, - { - type: 'keyboard', - enter: (container) => act(() => container.querySelector('button').focus()), - leave: (container) => act(() => container.querySelector('button').blur()), - }, - ].forEach((userInteraction) => { - describe(`interacting with ${userInteraction.type}`, () => { - it('should be able to interrupt the timer', () => { - const handleMouseEnter = spy(); - const handleMouseLeave = spy(); - const handleBlur = spy(); - const handleFocus = spy(); - const handleClose = spy(); - const autoHideDuration = 2e3; - - const { container } = render( - <Snackbar - action={<button>undo</button>} - open - onBlur={handleBlur} - onFocus={handleFocus} - onMouseEnter={handleMouseEnter} - onMouseLeave={handleMouseLeave} - onClose={handleClose} - message="message" - autoHideDuration={autoHideDuration} - />, - ); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(autoHideDuration / 2); - userInteraction.enter(container.querySelector('div')); - - if (userInteraction.type === 'keyboard') { - expect(handleFocus.callCount).to.equal(1); - } else { - expect(handleMouseEnter.callCount).to.equal(1); - } - - clock.tick(autoHideDuration / 2); - userInteraction.leave(container.querySelector('div')); - - if (userInteraction.type === 'keyboard') { - expect(handleBlur.callCount).to.equal(1); - } else { - expect(handleMouseLeave.callCount).to.equal(1); - } - expect(handleClose.callCount).to.equal(0); - - clock.tick(2e3); - - expect(handleClose.callCount).to.equal(1); - expect(handleClose.args[0]).to.deep.equal([null, 'timeout']); - }); - - it('should not call onClose with not timeout after user interaction', () => { - const handleClose = spy(); - const autoHideDuration = 2e3; - const resumeHideDuration = 3e3; - - const { container } = render( - <Snackbar - action={<button>undo</button>} - open - onClose={handleClose} - message="message" - autoHideDuration={autoHideDuration} - resumeHideDuration={resumeHideDuration} - />, - ); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(autoHideDuration / 2); - userInteraction.enter(container.querySelector('div')); - clock.tick(autoHideDuration / 2); - userInteraction.leave(container.querySelector('div')); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(2e3); - - expect(handleClose.callCount).to.equal(0); - }); - - it('should call onClose when timer done after user interaction', () => { - const handleClose = spy(); - const autoHideDuration = 2e3; - const resumeHideDuration = 3e3; - - const { container } = render( - <Snackbar - action={<button>undo</button>} - open - onClose={handleClose} - message="message" - autoHideDuration={autoHideDuration} - resumeHideDuration={resumeHideDuration} - />, - ); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(autoHideDuration / 2); - userInteraction.enter(container.querySelector('div')); - clock.tick(autoHideDuration / 2); - userInteraction.leave(container.querySelector('div')); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(resumeHideDuration); - - expect(handleClose.callCount).to.equal(1); - expect(handleClose.args[0]).to.deep.equal([null, 'timeout']); - }); - - it('should call onClose immediately after user interaction when 0', () => { - const handleClose = spy(); - const autoHideDuration = 6e3; - const resumeHideDuration = 0; - const { setProps, container } = render( - <Snackbar - action={<button>undo</button>} - open - onClose={handleClose} - message="message" - autoHideDuration={autoHideDuration} - resumeHideDuration={resumeHideDuration} - />, - ); - - setProps({ open: true }); - - expect(handleClose.callCount).to.equal(0); - - userInteraction.enter(container.querySelector('div')); - clock.tick(100); - userInteraction.leave(container.querySelector('div')); - clock.tick(resumeHideDuration); - - expect(handleClose.callCount).to.equal(1); - expect(handleClose.args[0]).to.deep.equal([null, 'timeout']); - }); - }); - }); - - describe('prop: disableWindowBlurListener', () => { - it('should pause auto hide when not disabled and window lost focus', () => { - const handleClose = spy(); - const autoHideDuration = 2e3; - render( - <Snackbar - open - onClose={handleClose} - message="message" - autoHideDuration={autoHideDuration} - disableWindowBlurListener={false} - />, - ); - - act(() => { - const bEvent = new window.Event('blur', { - bubbles: false, - cancelable: false, - }); - window.dispatchEvent(bEvent); - }); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(autoHideDuration); - - expect(handleClose.callCount).to.equal(0); - - act(() => { - const fEvent = new window.Event('focus', { - bubbles: false, - cancelable: false, - }); - window.dispatchEvent(fEvent); - }); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(autoHideDuration); - - expect(handleClose.callCount).to.equal(1); - expect(handleClose.args[0]).to.deep.equal([null, 'timeout']); - }); - - it('should not pause auto hide when disabled and window lost focus', () => { - const handleClose = spy(); - const autoHideDuration = 2e3; - render( - <Snackbar - open - onClose={handleClose} - message="message" - autoHideDuration={autoHideDuration} - disableWindowBlurListener - />, - ); - - act(() => { - const event = new window.Event('blur', { bubbles: false, cancelable: false }); - window.dispatchEvent(event); - }); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(autoHideDuration); - - expect(handleClose.callCount).to.equal(1); - expect(handleClose.args[0]).to.deep.equal([null, 'timeout']); - }); - }); - - describe('prop: open', () => { - it('should not render anything when closed', () => { - const { container } = render(<Snackbar open={false} message="Hello, World!" />); - expect(container).to.have.text(''); - }); - - it('should be able show it after mounted', () => { - const { container, setProps } = render(<Snackbar open={false} message="Hello, World!" />); - expect(container).to.have.text(''); - setProps({ open: true }); - expect(container).to.have.text('Hello, World!'); - }); - }); - - describe('prop: children', () => { - it('should render the children', () => { - const nodeRef = React.createRef(); - const children = <div ref={nodeRef} />; - const { container } = render(<Snackbar open>{children}</Snackbar>); - expect(container).to.contain(nodeRef.current); - }); - }); - - describe('prop: TransitionComponent', () => { - it('should use a Grow by default', () => { - const childRef = React.createRef(); - render( - <Snackbar open message="message"> - <div ref={childRef} /> - </Snackbar>, - ); - expect(childRef.current.style.transform).to.contain('scale'); - }); - - it('accepts a different component that handles the transition', () => { - const transitionRef = React.createRef(); - function Transition() { - return <div className="cloned-element-class" ref={transitionRef} />; - } - const { container } = render(<Snackbar open TransitionComponent={Transition} />); - expect(container).to.contain(transitionRef.current); - }); - }); - - describe('prop: transitionDuration', () => { - it('should render the default theme values by default', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const theme = createTheme(); - const enteringScreenDurationInSeconds = theme.transitions.duration.enteringScreen / 1000; - const { getByTestId } = render( - <Snackbar open message="Hello, World!"> - <div data-testid="child">Foo</div> - </Snackbar>, - ); - - const child = getByTestId('child'); - expect(child).toHaveComputedStyle({ - transitionDuration: `${enteringScreenDurationInSeconds}s, 0.15s`, - }); - }); - - it('should render the custom theme values', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const theme = createTheme({ - transitions: { - duration: { - enteringScreen: 1, - }, - }, - }); - - const { getByTestId } = render( - <ThemeProvider theme={theme}> - <Snackbar open message="Hello, World!"> - <div data-testid="child">Foo</div> - </Snackbar> - </ThemeProvider>, - ); - - const child = getByTestId('child'); - expect(child).toHaveComputedStyle({ transitionDuration: '0.001s, 0.001s' }); - }); - - it('should render the values provided via prop', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const { getByTestId } = render( - <Snackbar open message="Hello, World!" transitionDuration={1}> - <div data-testid="child">Foo</div> - </Snackbar>, - ); - - const child = getByTestId('child'); - expect(child).toHaveComputedStyle({ transitionDuration: '0.001s, 0.001s' }); - }); - }); -}); diff --git a/packages/mui-material-next/src/Snackbar/index.d.ts b/packages/mui-material-next/src/Snackbar/index.d.ts deleted file mode 100644 index e55c472b4faa67..00000000000000 --- a/packages/mui-material-next/src/Snackbar/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './Snackbar'; -export * from './Snackbar'; - -export { default as snackbarClasses } from './snackbarClasses'; -export * from './snackbarClasses'; diff --git a/packages/mui-material-next/src/Snackbar/index.js b/packages/mui-material-next/src/Snackbar/index.js deleted file mode 100644 index c21dbfa25ec91d..00000000000000 --- a/packages/mui-material-next/src/Snackbar/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './Snackbar'; - -export { default as snackbarClasses } from './snackbarClasses'; -export * from './snackbarClasses'; diff --git a/packages/mui-material-next/src/Snackbar/snackbarClasses.ts b/packages/mui-material-next/src/Snackbar/snackbarClasses.ts deleted file mode 100644 index 201882ecc5539c..00000000000000 --- a/packages/mui-material-next/src/Snackbar/snackbarClasses.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface SnackbarClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `anchorOrigin={{ 'top', 'center' }}`. */ - anchorOriginTopCenter: string; - /** Styles applied to the root element if `anchorOrigin={{ 'bottom', 'center' }}`. */ - anchorOriginBottomCenter: string; - /** Styles applied to the root element if `anchorOrigin={{ 'top', 'right' }}`. */ - anchorOriginTopRight: string; - /** Styles applied to the root element if `anchorOrigin={{ 'bottom', 'right' }}`. */ - anchorOriginBottomRight: string; - /** Styles applied to the root element if `anchorOrigin={{ 'top', 'left' }}`. */ - anchorOriginTopLeft: string; - /** Styles applied to the root element if `anchorOrigin={{ 'bottom', 'left' }}`. */ - anchorOriginBottomLeft: string; -} - -export type SnackbarClassKey = keyof SnackbarClasses; - -export function getSnackbarUtilityClass(slot: string): string { - return generateUtilityClass('MuiSnackbar', slot); -} - -const snackbarClasses: SnackbarClasses = generateUtilityClasses('MuiSnackbar', [ - 'root', - 'anchorOriginTopCenter', - 'anchorOriginBottomCenter', - 'anchorOriginTopRight', - 'anchorOriginBottomRight', - 'anchorOriginTopLeft', - 'anchorOriginBottomLeft', -]); - -export default snackbarClasses; diff --git a/packages/mui-material-next/src/SnackbarContent/SnackbarContent.d.ts b/packages/mui-material-next/src/SnackbarContent/SnackbarContent.d.ts deleted file mode 100644 index e75358e1e5b3af..00000000000000 --- a/packages/mui-material-next/src/SnackbarContent/SnackbarContent.d.ts +++ /dev/null @@ -1,41 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { InternalStandardProps as StandardProps, PaperProps, Theme } from '@mui/material'; -import { SnackbarContentClasses } from './snackbarContentClasses'; - -export interface SnackbarContentProps extends StandardProps<PaperProps, 'children'> { - /** - * The action to display. It renders after the message, at the end of the snackbar. - */ - action?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<SnackbarContentClasses>; - /** - * The message to display. - */ - message?: React.ReactNode; - /** - * The ARIA role attribute of the element. - * @default 'alert' - */ - role?: PaperProps['role']; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -/** - * - * Demos: - * - * - [Snackbar](https://mui.com/material-ui/react-snackbar/) - * - * API: - * - * - [SnackbarContent API](https://mui.com/material-ui/api/snackbar-content/) - * - inherits [Paper API](https://mui.com/material-ui/api/paper/) - */ -export default function SnackbarContent(props: SnackbarContentProps): JSX.Element; diff --git a/packages/mui-material-next/src/SnackbarContent/SnackbarContent.js b/packages/mui-material-next/src/SnackbarContent/SnackbarContent.js deleted file mode 100644 index 53cdccbac54284..00000000000000 --- a/packages/mui-material-next/src/SnackbarContent/SnackbarContent.js +++ /dev/null @@ -1,135 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { emphasize } from '@mui/system'; -import styled from '@mui/material/styles/styled'; -import useThemeProps from '@mui/material/styles/useThemeProps'; -import Paper from '@mui/material/Paper'; -import { getSnackbarContentUtilityClass } from './snackbarContentClasses'; - -const useUtilityClasses = (ownerState) => { - const { classes } = ownerState; - - const slots = { - root: ['root'], - action: ['action'], - message: ['message'], - }; - - return composeClasses(slots, getSnackbarContentUtilityClass, classes); -}; - -const SnackbarContentRoot = styled(Paper, { - name: 'MuiSnackbarContent', - slot: 'Root', - overridesResolver: (props, styles) => styles.root, -})(({ theme }) => { - const emphasis = theme.palette.mode === 'light' ? 0.8 : 0.98; - const backgroundColor = emphasize(theme.palette.background.default, emphasis); - - return { - ...theme.typography.body2, - color: theme.vars - ? theme.vars.palette.SnackbarContent.color - : theme.palette.getContrastText(backgroundColor), - backgroundColor: theme.vars ? theme.vars.palette.SnackbarContent.bg : backgroundColor, - display: 'flex', - alignItems: 'center', - flexWrap: 'wrap', - padding: '6px 16px', - borderRadius: (theme.vars || theme).shape.borderRadius, - flexGrow: 1, - [theme.breakpoints.up('sm')]: { - flexGrow: 'initial', - minWidth: 288, - }, - }; -}); - -const SnackbarContentMessage = styled('div', { - name: 'MuiSnackbarContent', - slot: 'Message', - overridesResolver: (props, styles) => styles.message, -})({ - padding: '8px 0', -}); - -const SnackbarContentAction = styled('div', { - name: 'MuiSnackbarContent', - slot: 'Action', - overridesResolver: (props, styles) => styles.action, -})({ - display: 'flex', - alignItems: 'center', - marginLeft: 'auto', - paddingLeft: 16, - marginRight: -8, -}); - -const SnackbarContent = React.forwardRef(function SnackbarContent(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiSnackbarContent' }); - const { action, className, message, role = 'alert', ...other } = props; - const ownerState = props; - const classes = useUtilityClasses(ownerState); - - return ( - <SnackbarContentRoot - role={role} - square - elevation={6} - className={clsx(classes.root, className)} - ownerState={ownerState} - ref={ref} - {...other} - > - <SnackbarContentMessage className={classes.message} ownerState={ownerState}> - {message} - </SnackbarContentMessage> - {action ? ( - <SnackbarContentAction className={classes.action} ownerState={ownerState}> - {action} - </SnackbarContentAction> - ) : null} - </SnackbarContentRoot> - ); -}); - -SnackbarContent.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The action to display. It renders after the message, at the end of the snackbar. - */ - action: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The message to display. - */ - message: PropTypes.node, - /** - * The ARIA role attribute of the element. - * @default 'alert' - */ - role: PropTypes /* @typescript-to-proptypes-ignore */.string, - /** - * 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, - ]), -}; - -export default SnackbarContent; diff --git a/packages/mui-material-next/src/SnackbarContent/SnackbarContent.test.js b/packages/mui-material-next/src/SnackbarContent/SnackbarContent.test.js deleted file mode 100644 index 5a43c787ffa00e..00000000000000 --- a/packages/mui-material-next/src/SnackbarContent/SnackbarContent.test.js +++ /dev/null @@ -1,64 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -import Paper from '@mui/material/Paper'; -import SnackbarContent, { - snackbarContentClasses as classes, -} from '@mui/material-next/SnackbarContent'; -import describeConformance from '../../test/describeConformance'; - -describe('<SnackbarContent />', () => { - const { render } = createRenderer(); - - describeConformance(<SnackbarContent message="conform?" />, () => ({ - classes, - inheritComponent: Paper, - render, - muiName: 'MuiSnackbarContent', - refInstanceof: window.HTMLDivElement, - skip: ['componentProp', 'componentsProp', 'themeVariants'], - })); - - describe('prop: action', () => { - it('should render the action', () => { - const action = <span>action</span>; - const { container } = render( - <SnackbarContent message="message" data-testid="action" action={action} />, - ); - expect(container.querySelector(`.${classes.action}`)).to.have.class(classes.action); - expect(container.querySelector(`.${classes.action}`)).to.contain('span'); - }); - - it('should render an array of elements', () => { - const action0 = <span key={0}>action0</span>; - const action1 = <span key={1}>action1</span>; - const { getByText } = render( - <SnackbarContent message="message" action={[action0, action1]} />, - ); - expect(getByText('action0')).not.to.equal(null); - expect(getByText('action1')).not.to.equal(null); - }); - }); - - describe('prop: message', () => { - it('should render the message', () => { - const message = 'message prop text'; - const { getByRole } = render(<SnackbarContent message={<span>{message}</span>} />); - expect(getByRole('alert')).to.have.text(message); - }); - }); - - describe('prop: role', () => { - it('renders the default role', () => { - const { getByRole } = render(<SnackbarContent message="alert message" />); - expect(getByRole('alert')).to.have.text('alert message'); - }); - - it('can override the role', () => { - const { queryByRole } = render( - <SnackbarContent message="alertdialog message" role="alertdialog" />, - ); - expect(queryByRole('alertdialog')).to.have.text('alertdialog message'); - }); - }); -}); diff --git a/packages/mui-material-next/src/SnackbarContent/index.d.ts b/packages/mui-material-next/src/SnackbarContent/index.d.ts deleted file mode 100644 index 658d42f88a58ea..00000000000000 --- a/packages/mui-material-next/src/SnackbarContent/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './SnackbarContent'; -export * from './SnackbarContent'; - -export { default as snackbarContentClasses } from './snackbarContentClasses'; -export * from './snackbarContentClasses'; diff --git a/packages/mui-material-next/src/SnackbarContent/index.js b/packages/mui-material-next/src/SnackbarContent/index.js deleted file mode 100644 index bdea04e7db6591..00000000000000 --- a/packages/mui-material-next/src/SnackbarContent/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './SnackbarContent'; - -export { default as snackbarContentClasses } from './snackbarContentClasses'; -export * from './snackbarContentClasses'; diff --git a/packages/mui-material-next/src/SnackbarContent/snackbarContentClasses.ts b/packages/mui-material-next/src/SnackbarContent/snackbarContentClasses.ts deleted file mode 100644 index 2ca54fe7230814..00000000000000 --- a/packages/mui-material-next/src/SnackbarContent/snackbarContentClasses.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface SnackbarContentClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the message wrapper element. */ - message: string; - /** Styles applied to the action wrapper element if `action` is provided. */ - action: string; -} - -export type SnackbarContentClassKey = keyof SnackbarContentClasses; - -export function getSnackbarContentUtilityClass(slot: string): string { - return generateUtilityClass('MuiSnackbarContent', slot); -} - -const snackbarContentClasses: SnackbarContentClasses = generateUtilityClasses( - 'MuiSnackbarContent', - ['root', 'message', 'action'], -); - -export default snackbarContentClasses; diff --git a/packages/mui-material-next/src/Switch/Switch.test.tsx b/packages/mui-material-next/src/Switch/Switch.test.tsx deleted file mode 100644 index 85cfa8c9cf8e9e..00000000000000 --- a/packages/mui-material-next/src/Switch/Switch.test.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { act, createRenderer, fireEvent } from '@mui-internal/test-utils'; -import Switch, { switchClasses as classes } from '@mui/material-next/Switch'; -import FormControl from '@mui/material/FormControl'; -import describeConformance from '../../test/describeConformance'; - -describe('<Switch />', () => { - const { render } = createRenderer(); - - describeConformance(<Switch />, () => ({ - classes, - render, - muiName: 'MuiSwitch', - testDeepOverrides: [ - { slotName: 'track', slotClassName: classes.track }, - { slotName: 'input', slotClassName: classes.input }, - ], - refInstanceof: window.HTMLSpanElement, - skip: ['componentProp', 'componentsProp', 'propsSpread', 'themeDefaultProps', 'themeVariants'], - })); - - describe('styleSheet', () => { - it('should have the classes required for SwitchBase', () => { - expect(classes).to.include.all.keys(['root', 'checked', 'disabled']); - }); - }); - - specify('should render an .thumb element inside the .switchBase element', () => { - const { container } = render( - <Switch classes={{ thumb: 'thumb', switchBase: 'switch-base' }} />, - ); - - expect(container.querySelector('.switch-base .thumb')).not.to.equal(null); - }); - - it('should render the track as the 2nd child', () => { - const { - container: { firstChild: root }, - } = render(<Switch />); - - expect(root?.childNodes[1]).to.have.property('tagName', 'SPAN'); - expect(root?.childNodes[1]).to.have.class(classes.track); - }); - - it('renders a `role="checkbox"` with the Unchecked state by default', () => { - const { getByRole } = render(<Switch />); - - expect(getByRole('checkbox')).to.have.property('checked', false); - }); - - it('renders a checkbox with the Checked state when checked', () => { - const { getByRole } = render(<Switch defaultChecked />); - - expect(getByRole('checkbox')).to.have.property('checked', true); - }); - - specify('the switch can be disabled', () => { - const { getByRole } = render(<Switch disabled />); - - expect(getByRole('checkbox')).to.have.property('disabled', true); - }); - - specify('the switch can be readonly', () => { - const { getByRole } = render(<Switch readOnly />); - - expect(getByRole('checkbox')).to.have.property('readOnly', true); - }); - - specify('renders a custom icon when provided', () => { - const { getByTestId } = render(<Switch icon={<span data-testid="icon" />} />); - - expect(getByTestId('icon')).toBeVisible(); - }); - - specify('renders a custom checked icon when provided', () => { - const { getByTestId } = render( - <Switch defaultChecked checkedIcon={<span data-testid="icon" />} />, - ); - - expect(getByTestId('icon')).toBeVisible(); - }); - - specify('the Checked state changes after change events', () => { - const { getByRole } = render(<Switch defaultChecked />); - - // how a user would trigger it - act(() => { - getByRole('checkbox').click(); - fireEvent.change(getByRole('checkbox'), { target: { checked: '' } }); - }); - - expect(getByRole('checkbox')).to.have.property('checked', false); - }); - - it('should not show warnings when custom `type` is provided', () => { - expect(() => render(<Switch type="submit" />)).not.toErrorDev(); - }); - - describe('with FormControl', () => { - describe('enabled', () => { - it('should not have the disabled class', () => { - const { getByRole } = render( - <FormControl> - <Switch /> - </FormControl>, - ); - - expect(getByRole('checkbox')).not.to.have.attribute('disabled'); - }); - - it('should be overridden by props', () => { - const { getByRole } = render( - <FormControl> - <Switch disabled /> - </FormControl>, - ); - - expect(getByRole('checkbox')).to.have.attribute('disabled'); - }); - }); - - describe('disabled', () => { - it('should have the disabled class', () => { - const { getByRole } = render( - <FormControl disabled> - <Switch /> - </FormControl>, - ); - - expect(getByRole('checkbox')).to.have.attribute('disabled'); - }); - - it('should be overridden by props', () => { - const { getByRole } = render( - <FormControl disabled> - <Switch disabled={false} /> - </FormControl>, - ); - - expect(getByRole('checkbox')).not.to.have.attribute('disabled'); - }); - }); - }); -}); diff --git a/packages/mui-material-next/src/Switch/Switch.tsx b/packages/mui-material-next/src/Switch/Switch.tsx deleted file mode 100644 index d14652ce1fda39..00000000000000 --- a/packages/mui-material-next/src/Switch/Switch.tsx +++ /dev/null @@ -1,362 +0,0 @@ -'use client'; -// @inheritedComponent IconButton -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { refType, unstable_capitalize as capitalize } from '@mui/utils'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { alpha, darken, lighten } from '@mui/system'; -import SwitchBase from '@mui/material/internal/SwitchBase'; -import { OverridableComponent } from '@mui/types'; -import useThemeProps from '../styles/useThemeProps'; -import styled from '../styles/styled'; -import switchClasses, { getSwitchUtilityClass } from './switchClasses'; -import { SwitchOwnerState, SwitchProps, SwitchTypeMap } from './Switch.types'; - -const useUtilityClasses = (ownerState: SwitchOwnerState) => { - const { classes, edge, size, color, checked, disabled } = ownerState; - - const slots = { - root: ['root', edge && `edge${capitalize(edge)}`, `size${capitalize(size)}`], - switchBase: [ - 'switchBase', - `color${capitalize(color)}`, - checked && 'checked', - disabled && 'disabled', - ], - thumb: ['thumb'], - track: ['track'], - input: ['input'], - }; - - const composedClasses = composeClasses(slots, getSwitchUtilityClass, classes); - - return { - ...classes, // forward the disabled and checked classes to the SwitchBase - ...composedClasses, - }; -}; - -const SwitchRoot = styled('span', { - name: 'MuiSwitch', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - ownerState.edge && styles[`edge${capitalize(ownerState.edge)}`], - styles[`size${capitalize(ownerState.size)}`], - ]; - }, -})<{ ownerState: SwitchOwnerState }>(({ ownerState }) => ({ - display: 'inline-flex', - width: 34 + 12 * 2, - height: 14 + 12 * 2, - overflow: 'hidden', - padding: 12, - boxSizing: 'border-box', - position: 'relative', - flexShrink: 0, - zIndex: 0, // Reset the stacking context. - verticalAlign: 'middle', // For correct alignment with the text. - '@media print': { - colorAdjust: 'exact', - }, - ...(ownerState.edge === 'start' && { - marginLeft: -8, - }), - ...(ownerState.edge === 'end' && { - marginRight: -8, - }), - ...(ownerState.size === 'small' && { - width: 40, - height: 24, - padding: 7, - [`& .${switchClasses.thumb}`]: { - width: 16, - height: 16, - }, - [`& .${switchClasses.switchBase}`]: { - padding: 4, - [`&.${switchClasses.checked}`]: { - transform: 'translateX(16px)', - }, - }, - }), -})); - -const SwitchSwitchBase = styled(SwitchBase, { - name: 'MuiSwitch', - slot: 'SwitchBase', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.switchBase, - { [`& .${switchClasses.input}`]: styles.input }, - ownerState.color !== 'default' && styles[`color${capitalize(ownerState.color)}`], - ]; - }, -})<{ ownerState: SwitchOwnerState }>( - ({ theme }) => ({ - position: 'absolute', - top: 0, - left: 0, - zIndex: 1, // Render above the focus ripple. - color: theme.vars - ? theme.vars.palette.Switch.defaultColor - : `${theme.palette.mode === 'light' ? theme.palette.common.white : theme.palette.grey[300]}`, - transition: theme.transitions.create(['left', 'transform'], { - duration: theme.transitions.duration.shortest, - }), - [`&.${switchClasses.checked}`]: { - transform: 'translateX(20px)', - }, - [`&.${switchClasses.disabled}`]: { - color: theme.vars - ? theme.vars.palette.Switch.defaultDisabledColor - : `${theme.palette.mode === 'light' ? theme.palette.grey[100] : theme.palette.grey[600]}`, - }, - [`&.${switchClasses.checked} + .${switchClasses.track}`]: { - opacity: 0.5, - }, - [`&.${switchClasses.disabled} + .${switchClasses.track}`]: { - opacity: theme.vars - ? theme.vars.opacity.switchTrackDisabled - : `${theme.palette.mode === 'light' ? 0.12 : 0.2}`, - }, - [`& .${switchClasses.input}`]: { - left: '-100%', - width: '300%', - }, - }), - ({ theme, ownerState }) => ({ - '&:hover': { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.action.activeChannel} / ${theme.vars.palette.action.hoverOpacity})` - : alpha(theme.palette.action.active, theme.palette.action.hoverOpacity), - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - }, - ...(ownerState.color !== 'default' && { - [`&.${switchClasses.checked}`]: { - color: (theme.vars || theme).palette[ownerState.color].main, - '&:hover': { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette[ownerState.color].mainChannel} / ${ - theme.vars.palette.action.hoverOpacity - })` - : alpha(theme.palette[ownerState.color].main, theme.palette.action.hoverOpacity), - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - }, - [`&.${switchClasses.disabled}`]: { - color: theme.vars - ? theme.vars.palette.Switch[`${ownerState.color}DisabledColor`] - : `${ - theme.palette.mode === 'light' - ? lighten(theme.palette[ownerState.color].main, 0.62) - : darken(theme.palette[ownerState.color].main, 0.55) - }`, - }, - }, - [`&.${switchClasses.checked} + .${switchClasses.track}`]: { - backgroundColor: (theme.vars || theme).palette[ownerState.color].main, - }, - }), - }), -); - -const SwitchTrack = styled('span', { - name: 'MuiSwitch', - slot: 'Track', - overridesResolver: (props, styles) => styles.track, -})<{ ownerState: SwitchOwnerState }>(({ theme }) => ({ - height: '100%', - width: '100%', - borderRadius: 14 / 2, - zIndex: -1, - transition: theme.transitions.create(['opacity', 'background-color'], { - duration: theme.transitions.duration.shortest, - }), - backgroundColor: theme.vars - ? theme.vars.palette.common.onBackground - : `${theme.palette.mode === 'light' ? theme.palette.common.black : theme.palette.common.white}`, - opacity: theme.vars - ? theme.vars.opacity.switchTrack - : `${theme.palette.mode === 'light' ? 0.38 : 0.3}`, -})); - -const SwitchThumb = styled('span', { - name: 'MuiSwitch', - slot: 'Thumb', - overridesResolver: (props, styles) => styles.thumb, -})<{ ownerState: SwitchOwnerState }>(({ theme }) => ({ - boxShadow: (theme.vars || theme).shadows[1], - backgroundColor: 'currentColor', - width: 20, - height: 20, - borderRadius: '50%', -})); - -/** - * - * Demos: - * - * - [Switch](https://mui.com/material-ui/react-switch/) - * - [Transfer List](https://mui.com/material-ui/react-transfer-list/) - * - * API: - * - * - [Switch API](https://mui.com/material-ui/api/switch/) - * - inherits [IconButton API](https://mui.com/material-ui/api/icon-button/) - */ -const Switch = React.forwardRef(function Switch< - BaseComponentType extends React.ElementType = SwitchTypeMap['defaultComponent'], ->(inProps: SwitchProps<BaseComponentType>, ref: React.ForwardedRef<HTMLSpanElement>) { - const props = useThemeProps({ props: inProps, name: 'MuiSwitch' }); - const { className, color = 'primary', edge = false, size = 'medium', sx, ...other } = props; - - const ownerState = { - ...props, - color, - edge, - size, - }; - - const classes = useUtilityClasses(ownerState); - const icon = <SwitchThumb className={classes.thumb} ownerState={ownerState} />; - - return ( - <SwitchRoot className={clsx(classes.root, className)} sx={sx} ownerState={ownerState}> - <SwitchSwitchBase - type="checkbox" - icon={icon} - checkedIcon={icon} - ref={ref} - ownerState={ownerState} - {...other} - classes={{ - ...classes, - root: classes.switchBase, - }} - /> - <SwitchTrack className={classes.track} ownerState={ownerState} /> - </SwitchRoot> - ); -}) as OverridableComponent<SwitchTypeMap>; - -Switch.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * If `true`, the component is checked. - */ - checked: PropTypes.bool, - /** - * The icon to display when the component is checked. - */ - checkedIcon: PropTypes.node, - /** - * @ignore - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['default', 'primary', 'secondary', 'error', 'info', 'success', 'warning']), - PropTypes.string, - ]), - /** - * The default checked state. Use when the component is not controlled. - */ - defaultChecked: PropTypes.bool, - /** - * If `true`, the component is disabled. - */ - disabled: PropTypes.bool, - /** - * If `true`, the ripple effect is disabled. - * @default false - */ - disableRipple: PropTypes.bool, - /** - * If given, uses a negative margin to counteract the padding on one - * side (this is often helpful for aligning the left or right - * side of the icon with content above or below, without ruining the border - * size and shape). - * @default false - */ - edge: PropTypes.oneOf(['end', 'start', false]), - /** - * The icon to display when the component is unchecked. - */ - icon: PropTypes.node, - /** - * The id of the `input` element. - */ - id: PropTypes.string, - /** - * [Attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Attributes) applied to the `input` element. - */ - inputProps: PropTypes.object, - /** - * Pass a ref to the `input` element. - */ - inputRef: refType, - /** - * Callback fired when the state is changed. - * - * @param {React.ChangeEvent<HTMLInputElement>} event The event source of the callback. - * You can pull out the new value by accessing `event.target.value` (string). - * You can pull out the new checked state by accessing `event.target.checked` (boolean). - */ - onChange: PropTypes.func, - /** - * If `true`, the `input` element is required. - * @default false - */ - required: PropTypes.bool, - /** - * The size of the component. - * `small` is equivalent to the dense switch styling. - * @default 'medium' - */ - size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['medium', 'small']), - PropTypes.string, - ]), - /** - * 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 value of the component. The DOM API casts this to a string. - * The browser uses "on" as the default value. - */ - value: PropTypes.any, -} as any; - -export default Switch; diff --git a/packages/mui-material-next/src/Switch/Switch.types.ts b/packages/mui-material-next/src/Switch/Switch.types.ts deleted file mode 100644 index 4eff5796daf7c3..00000000000000 --- a/packages/mui-material-next/src/Switch/Switch.types.ts +++ /dev/null @@ -1,72 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverridableStringUnion, OverrideProps, PartiallyRequired } from '@mui/types'; -// eslint-disable-next-line no-restricted-imports -import { InternalStandardProps as StandardProps } from '@mui/material'; -import type { SwitchBaseProps } from '@mui/material/internal/SwitchBase'; -import { Theme } from '../styles'; -import { SwitchClasses } from './switchClasses'; - -export interface SwitchPropsSizeOverrides {} - -export interface SwitchPropsColorOverrides {} - -export interface SwitchOwnProps - extends StandardProps<SwitchBaseProps, 'checkedIcon' | 'color' | 'icon' | 'sx'> { - /** - * The icon to display when the component is checked. - */ - checkedIcon?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<SwitchClasses>; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color?: OverridableStringUnion< - 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' | 'default', - SwitchPropsColorOverrides - >; - /** - * If `true`, the component is disabled. - */ - disabled?: boolean; - /** - * The icon to display when the component is unchecked. - */ - icon?: React.ReactNode; - /** - * The size of the component. - * `small` is equivalent to the dense switch styling. - * @default 'medium' - */ - size?: OverridableStringUnion<'small' | 'medium', SwitchPropsSizeOverrides>; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; - /** - * The value of the component. The DOM API casts this to a string. - * The browser uses "on" as the default value. - */ - value?: unknown; -} - -export interface SwitchTypeMap< - AdditionalProps = {}, - RootComponentType extends React.ElementType = 'span', -> { - props: SwitchOwnProps & AdditionalProps; - defaultComponent: RootComponentType; -} - -export type SwitchProps< - RootComponentType extends React.ElementType = SwitchTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<SwitchTypeMap<AdditionalProps, RootComponentType>, RootComponentType>; - -export interface SwitchOwnerState extends PartiallyRequired<SwitchProps, 'color' | 'size'> {} diff --git a/packages/mui-material-next/src/Switch/index.ts b/packages/mui-material-next/src/Switch/index.ts deleted file mode 100644 index 6e53e01c5c3dba..00000000000000 --- a/packages/mui-material-next/src/Switch/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './Switch'; - -export { default as switchClasses } from './switchClasses'; -export * from './switchClasses'; diff --git a/packages/mui-material-next/src/Switch/switchClasses.ts b/packages/mui-material-next/src/Switch/switchClasses.ts deleted file mode 100644 index e7dd2e99a31ed9..00000000000000 --- a/packages/mui-material-next/src/Switch/switchClasses.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface SwitchClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `edge="start"`. */ - edgeStart: string; - /** Styles applied to the root element if `edge="end"`. */ - edgeEnd: string; - /** Styles applied to the internal `SwitchBase` component's `root` class. */ - switchBase: string; - /** Styles applied to the internal SwitchBase component's root element if `color="primary"`. */ - colorPrimary: string; - /** Styles applied to the internal SwitchBase component's root element if `color="secondary"`. */ - colorSecondary: string; - /** Styles applied to the root element if `size="small"`. */ - sizeSmall: string; - /** Styles applied to the root element if `size="medium"`. */ - sizeMedium: string; - /** State class applied to the internal `SwitchBase` component's `checked` class. */ - checked: string; - /** State class applied to the internal SwitchBase component's disabled class. */ - disabled: string; - /** Styles applied to the internal SwitchBase component's input element. */ - input: string; - /** Styles used to create the thumb passed to the internal `SwitchBase` component `icon` prop. */ - thumb: string; - /** Styles applied to the track element. */ - track: string; -} - -export type SwitchClassKey = keyof SwitchClasses; - -export function getSwitchUtilityClass(slot: string): string { - return generateUtilityClass('MuiSwitch', slot); -} - -const switchClasses: SwitchClasses = generateUtilityClasses('MuiSwitch', [ - 'root', - 'edgeStart', - 'edgeEnd', - 'switchBase', - 'colorPrimary', - 'colorSecondary', - 'sizeSmall', - 'sizeMedium', - 'checked', - 'disabled', - 'input', - 'thumb', - 'track', -]); - -export default switchClasses; diff --git a/packages/mui-material-next/src/Tab/Tab.d.ts b/packages/mui-material-next/src/Tab/Tab.d.ts deleted file mode 100644 index 50dd2de45ddf01..00000000000000 --- a/packages/mui-material-next/src/Tab/Tab.d.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { Theme } from '@mui/material/styles'; -import { ExtendButtonBase, ExtendButtonBaseTypeMap } from '@mui/material/ButtonBase'; -import { OverrideProps } from '@mui/material/OverridableComponent'; -import { TabClasses } from './tabClasses'; - -export interface TabOwnProps { - /** - * This prop isn't supported. - * Use the `component` prop if you need to change the children structure. - */ - children?: null; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<TabClasses>; - /** - * If `true`, the component is disabled. - * @default false - */ - disabled?: boolean; - /** - * If `true`, the keyboard focus ripple is disabled. - * @default false - */ - disableFocusRipple?: boolean; - /** - * The icon to display. - */ - icon?: string | React.ReactElement; - /** - * The position of the icon relative to the label. - * @default 'top' - */ - iconPosition?: 'top' | 'bottom' | 'start' | 'end'; - /** - * The label element. - */ - label?: React.ReactNode; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; - /** - * You can provide your own value. Otherwise, we fallback to the child position index. - */ - value?: any; - /** - * Tab labels appear in a single row. - * They can use a second line if needed. - * @default false - */ - wrapped?: boolean; -} - -export type TabTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'div', -> = ExtendButtonBaseTypeMap<{ - props: AdditionalProps & TabOwnProps; - defaultComponent: RootComponent; -}>; - -/** - * - * Demos: - * - * - [Tabs](https://mui.com/components/tabs/) - * - * API: - * - * - [Tab API](https://mui.com/api/tab/) - * - inherits [ButtonBase API](https://mui.com/api/button-base/) - */ -declare const Tab: ExtendButtonBase<TabTypeMap>; - -export type TabProps< - RootComponent extends React.ElementType = TabTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<TabTypeMap<AdditionalProps, RootComponent>, RootComponent>; - -export default Tab; diff --git a/packages/mui-material-next/src/Tab/Tab.js b/packages/mui-material-next/src/Tab/Tab.js deleted file mode 100644 index 766c386b76ec5e..00000000000000 --- a/packages/mui-material-next/src/Tab/Tab.js +++ /dev/null @@ -1,272 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base'; -import { useTab } from '@mui/base/useTab'; -// TODO: use useButton hook here -import ButtonBase from '@mui/material/ButtonBase'; -import { - unstable_capitalize as capitalize, - unstable_unsupportedProp as unsupportedProp, -} from '@mui/utils'; -import { styled, useThemeProps } from '@mui/material/styles'; -import tabClasses, { getTabUtilityClass } from './tabClasses'; -import TabsListContext from '../Tabs/TabsListContext'; - -const useUtilityClasses = (ownerState) => { - const { classes, textColor, fullWidth, wrapped, icon, label, selected, disabled } = ownerState; - - const slots = { - root: [ - 'root', - icon && label && 'labelIcon', - `textColor${capitalize(textColor)}`, - fullWidth && 'fullWidth', - wrapped && 'wrapped', - selected && 'selected', - disabled && 'disabled', - ], - iconWrapper: ['iconWrapper'], - }; - - return composeClasses(slots, getTabUtilityClass, classes); -}; - -const TabRoot = styled(ButtonBase, { - name: 'MuiTab', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - ownerState.label && ownerState.icon && styles.labelIcon, - styles[`textColor${capitalize(ownerState.textColor)}`], - ownerState.fullWidth && styles.fullWidth, - ownerState.wrapped && styles.wrapped, - ]; - }, -})(({ theme, ownerState }) => ({ - ...theme.typography.button, - maxWidth: 360, - minWidth: 90, - position: 'relative', - minHeight: 48, - flexShrink: 0, - padding: '12px 16px', - overflow: 'hidden', - whiteSpace: 'normal', - textAlign: 'center', - ...(ownerState.label && { - flexDirection: - ownerState.iconPosition === 'top' || ownerState.iconPosition === 'bottom' ? 'column' : 'row', - }), - lineHeight: 1.25, - ...(ownerState.icon && - ownerState.label && { - minHeight: 72, - paddingTop: 9, - paddingBottom: 9, - [`& > .${tabClasses.iconWrapper}`]: { - ...(ownerState.iconPosition === 'top' && { - marginBottom: 6, - }), - ...(ownerState.iconPosition === 'bottom' && { - marginTop: 6, - }), - ...(ownerState.iconPosition === 'start' && { - marginRight: theme.spacing(1), - }), - ...(ownerState.iconPosition === 'end' && { - marginLeft: theme.spacing(1), - }), - }, - }), - ...(ownerState.textColor === 'inherit' && { - color: 'inherit', - opacity: 0.6, // same opacity as theme.palette.text.secondary - [`&.${tabClasses.selected}`]: { - opacity: 1, - }, - [`&.${tabClasses.disabled}`]: { - opacity: theme.palette.action.disabledOpacity, - }, - }), - ...(ownerState.textColor === 'primary' && { - color: theme.palette.text.secondary, - [`&.${tabClasses.selected}`]: { - color: theme.palette.primary.main, - }, - [`&.${tabClasses.disabled}`]: { - color: theme.palette.text.disabled, - }, - }), - ...(ownerState.textColor === 'secondary' && { - color: theme.palette.text.secondary, - [`&.${tabClasses.selected}`]: { - color: theme.palette.secondary.main, - }, - [`&.${tabClasses.disabled}`]: { - color: theme.palette.text.disabled, - }, - }), - ...(ownerState.fullWidth && { - flexShrink: 1, - flexGrow: 1, - flexBasis: 0, - maxWidth: 'none', - }), - ...(ownerState.wrapped && { - fontSize: theme.typography.pxToRem(12), - }), -})); - -const Tab = React.forwardRef(function Tab(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiTab' }); - const { - className, - disableFocusRipple = false, - // eslint-disable-next-line react/prop-types - fullWidth: fullWidthProp, - icon: iconProp, - iconPosition = 'top', - // eslint-disable-next-line react/prop-types - indicator: indicatorProp, - label, - // eslint-disable-next-line react/prop-types - textColor: textColorProp = 'inherit', - value, - wrapped = false, - ...other - } = props; - - const { disabled, selected, getRootProps } = useTab({ ...props, rootRef: ref }); - - let indicator = indicatorProp; - let textColor = textColorProp; - let fullWidth = fullWidthProp; - - const tabsListContext = React.useContext(TabsListContext); - if (tabsListContext != null) { - indicator ??= tabsListContext.indicator; - textColor ??= tabsListContext.textColor; - fullWidth ??= tabsListContext.fullWidth; - } - - const ownerState = { - ...props, - disabled, - disableFocusRipple, - selected, - icon: !!iconProp, - iconPosition, - label: !!label, - fullWidth, - textColor, - wrapped, - }; - - const classes = useUtilityClasses(ownerState); - const icon = - iconProp && label && React.isValidElement(iconProp) - ? React.cloneElement(iconProp, { - className: clsx(classes.iconWrapper, iconProp.props.className), - }) - : iconProp; - - return ( - <TabRoot - focusRipple={!disableFocusRipple} - className={clsx(classes.root, className)} - ownerState={ownerState} - {...other} - {...getRootProps()} - > - {iconPosition === 'top' || iconPosition === 'start' ? ( - <React.Fragment> - {icon} - {label} - </React.Fragment> - ) : ( - <React.Fragment> - {label} - {icon} - </React.Fragment> - )} - - {selected && indicator} - </TabRoot> - ); -}); - -Tab.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * This prop isn't supported. - * Use the `component` prop if you need to change the children structure. - */ - children: unsupportedProp, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * If `true`, the component is disabled. - * @default false - */ - disabled: PropTypes.bool, - /** - * If `true`, the keyboard focus ripple is disabled. - * @default false - */ - disableFocusRipple: PropTypes.bool, - /** - * If `true`, the ripple effect is disabled. - * - * ⚠️ Without a ripple there is no styling for :focus-visible by default. Be sure - * to highlight the element by applying separate styles with the `.Mui-focusVisible` class. - * @default false - */ - disableRipple: PropTypes.bool, - /** - * The icon to display. - */ - icon: PropTypes.oneOfType([PropTypes.element, PropTypes.string]), - /** - * The position of the icon relative to the label. - * @default 'top' - */ - iconPosition: PropTypes.oneOf(['bottom', 'end', 'start', 'top']), - /** - * The label element. - */ - label: PropTypes.node, - /** - * 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, - ]), - /** - * You can provide your own value. Otherwise, we fallback to the child position index. - */ - value: PropTypes.any, - /** - * Tab labels appear in a single row. - * They can use a second line if needed. - * @default false - */ - wrapped: PropTypes.bool, -}; - -export default Tab; diff --git a/packages/mui-material-next/src/Tab/Tab.test.js b/packages/mui-material-next/src/Tab/Tab.test.js deleted file mode 100644 index 24f1ac4aba1620..00000000000000 --- a/packages/mui-material-next/src/Tab/Tab.test.js +++ /dev/null @@ -1,170 +0,0 @@ -import ButtonBase from '@mui/material/ButtonBase'; -import Tab, { tabClasses as classes } from '@mui/material/Tab'; -import { expect } from 'chai'; -import * as React from 'react'; -import { spy } from 'sinon'; -import { act, createRenderer, fireEvent } from '@mui-internal/test-utils'; -import describeConformance from '../../test/describeConformance'; - -describe('<Tab />', () => { - const { render } = createRenderer(); - - describeConformance(<Tab textColor="inherit" />, () => ({ - classes, - inheritComponent: ButtonBase, - render, - muiName: 'MuiTab', - testVariantProps: { variant: 'foo' }, - refInstanceof: window.HTMLButtonElement, - skip: ['componentProp', 'componentsProp'], - })); - - it('should have a ripple by default', () => { - const { container } = render(<Tab TouchRippleProps={{ className: 'touch-ripple' }} />); - - expect(container.querySelector('.touch-ripple')).not.to.equal(null); - }); - - it('can disable the ripple', () => { - const { container } = render( - <Tab disableRipple TouchRippleProps={{ className: 'touch-ripple' }} />, - ); - - expect(container.querySelector('.touch-ripple')).to.equal(null); - }); - - it('should have a focusRipple by default', () => { - const { container, getByRole } = render( - <Tab TouchRippleProps={{ classes: { ripplePulsate: 'focus-ripple' } }} />, - ); - // simulate pointer device - fireEvent.pointerDown(document.body); - - act(() => { - fireEvent.keyDown(document.body, { key: 'Tab' }); - // jsdom doesn't actually support tab focus, we need to do it manually - getByRole('tab').focus(); - }); - - expect(container.querySelector('.focus-ripple')).not.to.equal(null); - }); - - it('can disable the focusRipple', () => { - const { container, getByRole } = render( - <Tab disableFocusRipple TouchRippleProps={{ classes: { ripplePulsate: 'focus-ripple' } }} />, - ); - // simulate pointer device - fireEvent.pointerDown(document.body); - - act(() => { - fireEvent.keyDown(document.body, { key: 'Tab' }); - // jsdom doesn't actually support tab focus, we need to do it manually - getByRole('tab').focus(); - }); - - expect(container.querySelector('.focus-ripple')).to.equal(null); - }); - - describe('prop: selected', () => { - it('should render with the selected and root classes', () => { - const { getByRole } = render(<Tab selected textColor="secondary" />); - - const tab = getByRole('tab'); - expect(tab).to.have.class(classes.root); - expect(tab).to.have.class(classes.selected); - expect(tab).to.have.class(classes.textColorSecondary); - expect(tab).to.have.attribute('aria-selected', 'true'); - }); - }); - - describe('prop: disabled', () => { - it('should render with the disabled and root classes', () => { - const { getByRole } = render(<Tab disabled textColor="secondary" />); - - const tab = getByRole('tab'); - expect(tab).to.have.class(classes.root); - expect(tab).to.have.class(classes.disabled); - expect(tab).to.have.class(classes.textColorSecondary); - }); - }); - - describe('prop: onClick', () => { - it('should be called when a click is triggered', () => { - const handleClick = spy(); - const { getByRole } = render(<Tab onClick={handleClick} />); - - getByRole('tab').click(); - - expect(handleClick.callCount).to.equal(1); - }); - }); - - describe('prop: label', () => { - it('should render label', () => { - const { getByRole } = render(<Tab label="foo" />); - - expect(getByRole('tab')).to.have.text('foo'); - }); - }); - - describe('prop: wrapped', () => { - it('should add the wrapped class', () => { - const { getByRole } = render(<Tab wrapped />); - - expect(getByRole('tab')).to.have.class(classes.wrapped); - }); - }); - - describe('prop: icon', () => { - it('should render icon element', () => { - const { getByTestId } = render(<Tab icon={<div data-testid="icon" />} />); - - expect(getByTestId('icon')).not.to.equal(null); - }); - - it('should add a classname when passed together with label', () => { - const { getByRole } = render(<Tab icon={<div className="test-icon" />} label="foo" />); - const wrapper = getByRole('tab').children[0]; - expect(wrapper).to.have.class(classes.iconWrapper); - expect(wrapper).to.have.class('test-icon'); - }); - - it('should have bottom margin when passed together with label', () => { - const { getByRole } = render(<Tab icon={<div />} label="foo" />); - const wrapper = getByRole('tab').children[0]; - expect(wrapper).toHaveComputedStyle({ marginBottom: '6px' }); - }); - }); - - describe('prop: textColor', () => { - it('should support the inherit value', () => { - const { getByRole } = render(<Tab selected textColor="inherit" />); - - const tab = getByRole('tab'); - expect(tab).to.have.class(classes.selected); - expect(tab).to.have.class(classes.textColorInherit); - expect(tab).to.have.class(classes.root); - }); - }); - - describe('prop: fullWidth', () => { - it('should have the fullWidth class', () => { - const { getByRole } = render(<Tab fullWidth />); - - expect(getByRole('tab')).to.have.class(classes.fullWidth); - }); - }); - - describe('prop: style', () => { - it('should be able to override everything', () => { - const { getByRole } = render( - <Tab fullWidth style={{ width: '80%', color: 'red', alignText: 'center' }} />, - ); - - const { style } = getByRole('tab'); - expect(style).to.have.property('width', '80%'); - expect(style).to.have.property('color', 'red'); - expect(style).to.have.property('alignText', 'center'); - }); - }); -}); diff --git a/packages/mui-material-next/src/Tab/index.d.ts b/packages/mui-material-next/src/Tab/index.d.ts deleted file mode 100644 index dae620b149a0af..00000000000000 --- a/packages/mui-material-next/src/Tab/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './Tab'; -export * from './Tab'; - -export { default as tabClasses } from './tabClasses'; -export * from './tabClasses'; diff --git a/packages/mui-material-next/src/Tab/index.js b/packages/mui-material-next/src/Tab/index.js deleted file mode 100644 index 29213284d8cbd8..00000000000000 --- a/packages/mui-material-next/src/Tab/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './Tab'; - -export { default as tabClasses } from './tabClasses'; -export * from './tabClasses'; diff --git a/packages/mui-material-next/src/Tab/tabClasses.ts b/packages/mui-material-next/src/Tab/tabClasses.ts deleted file mode 100644 index ef995aeb7b3c23..00000000000000 --- a/packages/mui-material-next/src/Tab/tabClasses.ts +++ /dev/null @@ -1,46 +0,0 @@ -import generateUtilityClass from '@mui/utils/generateUtilityClass'; -import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; - -export interface TabClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if both `icon` and `label` are provided. */ - labelIcon: string; - /** Styles applied to the root element if the parent [`Tabs`](/material-ui/api/tabs/) has `textColor="inherit"`. */ - textColorInherit: string; - /** Styles applied to the root element if the parent [`Tabs`](/material-ui/api/tabs/) has `textColor="primary"`. */ - textColorPrimary: string; - /** Styles applied to the root element if the parent [`Tabs`](/material-ui/api/tabs/) has `textColor="secondary"`. */ - textColorSecondary: string; - /** State class applied to the root element if `selected={true}` (controlled by the Tabs component). */ - selected: string; - /** State class applied to the root element if `disabled={true}` (controlled by the Tabs component). */ - disabled: string; - /** Styles applied to the root element if `fullWidth={true}` (controlled by the Tabs component). */ - fullWidth: string; - /** Styles applied to the root element if `wrapped={true}`. */ - wrapped: string; - /** Styles applied to the wrapper element of `icon` if `icon` is provided. */ - iconWrapper: string; -} - -export type TabClassKey = keyof TabClasses; - -export function getTabUtilityClass(slot: string): string { - return generateUtilityClass('MuiTab', slot); -} - -const tabClasses: TabClasses = generateUtilityClasses('MuiTab', [ - 'root', - 'labelIcon', - 'textColorInherit', - 'textColorPrimary', - 'textColorSecondary', - 'selected', - 'disabled', - 'fullWidth', - 'wrapped', - 'iconWrapper', -]); - -export default tabClasses; diff --git a/packages/mui-material-next/src/TabScrollButton/TabScrollButton.d.ts b/packages/mui-material-next/src/TabScrollButton/TabScrollButton.d.ts deleted file mode 100644 index 45675384cc2b59..00000000000000 --- a/packages/mui-material-next/src/TabScrollButton/TabScrollButton.d.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { InternalStandardProps as StandardProps, Theme } from '@mui/material'; -import { TabScrollButtonClasses } from './tabScrollButtonClasses'; - -export interface TabScrollButtonProps extends StandardProps<React.HTMLAttributes<HTMLDivElement>> { - /** - * The content of the component. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<TabScrollButtonClasses>; - /** - * The direction the button should indicate. - */ - direction: 'left' | 'right'; - /** - * If `true`, the component is disabled. - */ - disabled?: boolean; - /** - * The component orientation (layout flow direction). - */ - orientation: 'horizontal' | 'vertical'; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -/** - * - * Demos: - * - * - [Tabs](https://mui.com/components/tabs/) - * - * API: - * - * - [TabScrollButton API](https://mui.com/api/tab-scroll-button/) - */ -export default function TabScrollButton(props: TabScrollButtonProps): JSX.Element; diff --git a/packages/mui-material-next/src/TabScrollButton/TabScrollButton.js b/packages/mui-material-next/src/TabScrollButton/TabScrollButton.js deleted file mode 100644 index e154b58258adb9..00000000000000 --- a/packages/mui-material-next/src/TabScrollButton/TabScrollButton.js +++ /dev/null @@ -1,117 +0,0 @@ -'use client'; -/* eslint-disable jsx-a11y/aria-role */ -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base'; -import KeyboardArrowLeft from '@mui/material/internal/svg-icons/KeyboardArrowLeft'; -import KeyboardArrowRight from '@mui/material/internal/svg-icons/KeyboardArrowRight'; -// TODO: use useButton hook here -import ButtonBase from '@mui/material/ButtonBase'; -import { styled, useTheme, useThemeProps } from '@mui/material/styles'; -import tabScrollButtonClasses, { getTabScrollButtonUtilityClass } from './tabScrollButtonClasses'; - -const useUtilityClasses = (ownerState) => { - const { classes, orientation, disabled } = ownerState; - - const slots = { - root: ['root', orientation, disabled && 'disabled'], - }; - - return composeClasses(slots, getTabScrollButtonUtilityClass, classes); -}; - -const TabScrollButtonRoot = styled(ButtonBase, { - name: 'MuiTabScrollButton', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [styles.root, ownerState.orientation && styles[ownerState.orientation]]; - }, -})(({ ownerState }) => ({ - width: 40, - flexShrink: 0, - opacity: 0.8, - [`&.${tabScrollButtonClasses.disabled}`]: { - opacity: 0, - }, - ...(ownerState.orientation === 'vertical' && { - width: '100%', - height: 40, - '& svg': { - transform: `rotate(${ownerState.isRtl ? -90 : 90}deg)`, - }, - }), -})); - -const TabScrollButton = React.forwardRef(function TabScrollButton(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiTabScrollButton' }); - const { className, direction, orientation, disabled, ...other } = props; - - const theme = useTheme(); - const isRtl = theme.direction === 'rtl'; - - const ownerState = { isRtl, ...props }; - - const classes = useUtilityClasses(ownerState); - - return ( - <TabScrollButtonRoot - component="div" - className={clsx(classes.root, className)} - ref={ref} - role={null} - ownerState={ownerState} - tabIndex={null} - {...other} - > - {direction === 'left' ? ( - <KeyboardArrowLeft fontSize="small" /> - ) : ( - <KeyboardArrowRight fontSize="small" /> - )} - </TabScrollButtonRoot> - ); -}); - -TabScrollButton.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The content of the component. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The direction the button should indicate. - */ - direction: PropTypes.oneOf(['left', 'right']).isRequired, - /** - * If `true`, the component is disabled. - */ - disabled: PropTypes.bool, - /** - * The component orientation (layout flow direction). - */ - orientation: PropTypes.oneOf(['horizontal', 'vertical']).isRequired, - /** - * 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, - ]), -}; - -export default TabScrollButton; diff --git a/packages/mui-material-next/src/TabScrollButton/TabScrollButton.test.js b/packages/mui-material-next/src/TabScrollButton/TabScrollButton.test.js deleted file mode 100644 index 20032aa756f97f..00000000000000 --- a/packages/mui-material-next/src/TabScrollButton/TabScrollButton.test.js +++ /dev/null @@ -1,55 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -import TabScrollButton, { - tabScrollButtonClasses as classes, -} from '@mui/material-next/TabScrollButton'; -import describeConformance from '../../test/describeConformance'; - -describe('<TabScrollButton />', () => { - const defaultProps = { - direction: 'left', - orientation: 'horizontal', - }; - const { render } = createRenderer(); - - describeConformance(<TabScrollButton {...defaultProps} />, () => ({ - classes, - inheritComponent: 'div', - render, - muiName: 'MuiTabScrollButton', - testVariantProps: { orientation: 'vertical' }, - refInstanceof: window.HTMLDivElement, - skip: ['componentProp', 'componentsProp'], - })); - - it('should render as a button with the root class', () => { - const { container } = render(<TabScrollButton {...defaultProps} />); - const button = container.firstChild; - expect(button).to.have.class(classes.root); - }); - - describe('prop: disabled', () => { - it('should render with a opacity of 0', () => { - const { container } = render(<TabScrollButton {...defaultProps} disabled />); - const button = container.firstChild; - expect(button).to.have.class(classes.disabled); - }); - }); - - describe('prop: direction', () => { - it('should render with the left icon', () => { - const { getAllByTestId } = render( - <TabScrollButton {...defaultProps} {...defaultProps} direction="left" disabled />, - ); - expect(getAllByTestId('KeyboardArrowLeftIcon').length).to.equal(1); - }); - - it('should render with the right icon', () => { - const { getAllByTestId } = render( - <TabScrollButton {...defaultProps} {...defaultProps} direction="right" disabled />, - ); - expect(getAllByTestId('KeyboardArrowRightIcon').length).to.equal(1); - }); - }); -}); diff --git a/packages/mui-material-next/src/TabScrollButton/index.d.ts b/packages/mui-material-next/src/TabScrollButton/index.d.ts deleted file mode 100644 index 6a0eacea7df14d..00000000000000 --- a/packages/mui-material-next/src/TabScrollButton/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './TabScrollButton'; -export * from './TabScrollButton'; - -export { default as tabScrollButtonClasses } from './tabScrollButtonClasses'; -export * from './tabScrollButtonClasses'; diff --git a/packages/mui-material-next/src/TabScrollButton/index.js b/packages/mui-material-next/src/TabScrollButton/index.js deleted file mode 100644 index 15aa330441752b..00000000000000 --- a/packages/mui-material-next/src/TabScrollButton/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './TabScrollButton'; - -export { default as tabScrollButtonClasses } from './tabScrollButtonClasses'; -export * from './tabScrollButtonClasses'; diff --git a/packages/mui-material-next/src/TabScrollButton/tabScrollButtonClasses.ts b/packages/mui-material-next/src/TabScrollButton/tabScrollButtonClasses.ts deleted file mode 100644 index 57e770c16184dc..00000000000000 --- a/packages/mui-material-next/src/TabScrollButton/tabScrollButtonClasses.ts +++ /dev/null @@ -1,24 +0,0 @@ -import generateUtilityClass from '@mui/utils/generateUtilityClass'; -import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; - -export interface TabScrollButtonClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `orientation="vertical"`. */ - vertical: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; -} - -export type TabScrollButtonClassKey = keyof TabScrollButtonClasses; - -export function getTabScrollButtonUtilityClass(slot: string): string { - return generateUtilityClass('MuiTabScrollButton', slot); -} - -const tabScrollButtonClasses: TabScrollButtonClasses = generateUtilityClasses( - 'MuiTabScrollButton', - ['root', 'vertical', 'horizontal', 'disabled'], -); - -export default tabScrollButtonClasses; diff --git a/packages/mui-material-next/src/TablePagination/TablePagination.d.ts b/packages/mui-material-next/src/TablePagination/TablePagination.d.ts deleted file mode 100644 index 8acb44381a0ebc..00000000000000 --- a/packages/mui-material-next/src/TablePagination/TablePagination.d.ts +++ /dev/null @@ -1,183 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { Theme } from '@mui/material/styles'; -import { OverridableComponent, OverrideProps } from '@mui/material/OverridableComponent'; -import { TableCellProps } from '@mui/material/TableCell'; -import { IconButtonProps } from '@mui/material/IconButton'; -import { SelectProps } from '@mui/material/Select'; -import { TablePaginationClasses } from './tablePaginationClasses'; - -export interface LabelDisplayedRowsArgs { - from: number; - to: number; - count: number; - page: number; -} - -export interface TablePaginationActionsProps extends React.HTMLAttributes<HTMLDivElement> { - backIconButtonProps?: Partial<IconButtonProps>; - /** - * Override or extend the styles applied to the component. - */ - classes?: {}; - count: number; - /** - * Accepts a function which returns a string value that provides a user-friendly name for the current page. - * This is important for screen reader users. - * - * For localization purposes, you can use the provided [translations](/material-ui/guides/localization/). - * @param {string} type The link or button type to format ('first' | 'last' | 'next' | 'previous'). - * @returns {string} - */ - getItemAriaLabel: (type: 'first' | 'last' | 'next' | 'previous') => string; - nextIconButtonProps?: Partial<IconButtonProps>; - onPageChange: (event: React.MouseEvent<HTMLButtonElement> | null, page: number) => void; - page: number; - rowsPerPage: number; - showFirstButton: boolean; - showLastButton: boolean; -} - -/** - * This type is kept for compatibility. Use `TablePaginationOwnProps` instead. - */ -export type TablePaginationBaseProps = Omit<TableCellProps, 'classes' | 'component' | 'children'>; - -export interface TablePaginationOwnProps extends TablePaginationBaseProps { - /** - * The component used for displaying the actions. - * Either a string to use a HTML element or a component. - * @default TablePaginationActions - */ - ActionsComponent?: React.ElementType<TablePaginationActionsProps>; - /** - * Props applied to the back arrow [`IconButton`](/material-ui/api/icon-button/) component. - */ - backIconButtonProps?: Partial<IconButtonProps>; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<TablePaginationClasses>; - /** - * The total number of rows. - * - * To enable server side pagination for an unknown number of items, provide -1. - */ - count: number; - /** - * Accepts a function which returns a string value that provides a user-friendly name for the current page. - * This is important for screen reader users. - * - * For localization purposes, you can use the provided [translations](/material-ui/guides/localization/). - * @param {string} type The link or button type to format ('first' | 'last' | 'next' | 'previous'). - * @returns {string} - * @default function defaultGetAriaLabel(type) { - * return `Go to ${type} page`; - * } - */ - getItemAriaLabel?: (type: 'first' | 'last' | 'next' | 'previous') => string; - /** - * Customize the displayed rows label. Invoked with a `{ from, to, count, page }` - * object. - * - * For localization purposes, you can use the provided [translations](/material-ui/guides/localization/). - * @default function defaultLabelDisplayedRows({ from, to, count }) { - * return `${from}–${to} of ${count !== -1 ? count : `more than ${to}`}`; - * } - */ - labelDisplayedRows?: (paginationInfo: LabelDisplayedRowsArgs) => React.ReactNode; - /** - * Customize the rows per page label. - * - * For localization purposes, you can use the provided [translations](/material-ui/guides/localization/). - * @default 'Rows per page:' - */ - labelRowsPerPage?: React.ReactNode; - /** - * Props applied to the next arrow [`IconButton`](/material-ui/api/icon-button/) element. - */ - nextIconButtonProps?: Partial<IconButtonProps>; - /** - * Callback fired when the page is changed. - * - * @param {React.MouseEvent<HTMLButtonElement> | null} event The event source of the callback. - * @param {number} page The page selected. - */ - onPageChange: (event: React.MouseEvent<HTMLButtonElement> | null, page: number) => void; - /** - * Callback fired when the number of rows per page is changed. - * - * @param {React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>} event The event source of the callback. - */ - onRowsPerPageChange?: React.ChangeEventHandler<HTMLTextAreaElement | HTMLInputElement>; - /** - * The zero-based index of the current page. - */ - page: number; - /** - * The number of rows per page. - * - * Set -1 to display all the rows. - */ - rowsPerPage: number; - /** - * Customizes the options of the rows per page select field. If less than two options are - * available, no select field will be displayed. - * Use -1 for the value with a custom label to show all the rows. - * @default [10, 25, 50, 100] - */ - rowsPerPageOptions?: ReadonlyArray< - | number - | { - value: number; - label: string; - } - >; - /** - * Props applied to the rows per page [`Select`](/material-ui/api/select/) element. - * @default {} - */ - SelectProps?: Partial<SelectProps>; - /** - * If `true`, show the first-page button. - * @default false - */ - showFirstButton?: boolean; - /** - * If `true`, show the last-page button. - * @default false - */ - showLastButton?: boolean; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -export interface TablePaginationTypeMap<AdditionalProps, RootComponent extends React.ElementType> { - props: AdditionalProps & TablePaginationOwnProps; - defaultComponent: RootComponent; -} - -/** - * A `TableCell` based component for placing inside `TableFooter` for pagination. - * - * Demos: - * - * - [Tables](https://mui.com/components/tables/) - * - * API: - * - * - [TablePagination API](https://mui.com/api/table-pagination/) - * - inherits [TableCell API](https://mui.com/api/table-cell/) - */ -declare const TablePagination: OverridableComponent< - TablePaginationTypeMap<{}, React.JSXElementConstructor<TablePaginationBaseProps>> ->; - -export type TablePaginationProps< - RootComponent extends React.ElementType = React.JSXElementConstructor<TablePaginationBaseProps>, - AdditionalProps = {}, -> = OverrideProps<TablePaginationTypeMap<AdditionalProps, RootComponent>, RootComponent>; - -export default TablePagination; diff --git a/packages/mui-material-next/src/TablePagination/TablePagination.js b/packages/mui-material-next/src/TablePagination/TablePagination.js deleted file mode 100644 index 8b2411adefdac5..00000000000000 --- a/packages/mui-material-next/src/TablePagination/TablePagination.js +++ /dev/null @@ -1,399 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_useId as useId, chainPropTypes, integerPropType } from '@mui/utils'; -import { unstable_composeClasses as composeClasses, appendOwnerState } from '@mui/base'; -import { TablePagination as BaseTablePagination } from '@mui/base/TablePagination'; -import { styled, useThemeProps } from '@mui/material/styles'; -import InputBase from '@mui/material/InputBase'; -import MenuItem from '@mui/material/MenuItem'; -import Select from '@mui/material/Select'; -import TableCell from '@mui/material/TableCell'; -import Toolbar from '@mui/material/Toolbar'; -import IconButton from '@mui/material/IconButton'; -import LastPageIcon from '@mui/material/internal/svg-icons/LastPage'; -import FirstPageIcon from '@mui/material/internal/svg-icons/FirstPage'; -import KeyboardArrowLeft from '@mui/material/internal/svg-icons/KeyboardArrowLeft'; -import KeyboardArrowRight from '@mui/material/internal/svg-icons/KeyboardArrowRight'; -import tablePaginationClasses, { getTablePaginationUtilityClass } from './tablePaginationClasses'; - -// This component is needed as the IconButton does not merge the ownerState -// coming from props. This results in the prop overriding the internal ownerState -const CustomIconButton = React.forwardRef((props, ref) => { - // eslint-disable-next-line react/prop-types - const { ownerState, ...other } = props; - return <IconButton ref={ref} {...other} />; -}); - -const TablePaginationRoot = styled('td', { - name: 'MuiTablePagination', - slot: 'Root', - overridesResolver: (props, styles) => styles.root, -})(({ theme }) => ({ - overflow: 'auto', - color: theme.palette.text.primary, - fontSize: theme.typography.pxToRem(14), - // Increase the specificity to override TableCell. - '&:last-child': { - padding: 0, - }, -})); - -const TablePaginationToolbar = styled(Toolbar, { - name: 'MuiTablePagination', - slot: 'Toolbar', - overridesResolver: (props, styles) => ({ - [`& .${tablePaginationClasses.actions}`]: styles.actions, - ...styles.toolbar, - }), -})(({ theme }) => ({ - minHeight: 52, - paddingRight: 2, - [`${theme.breakpoints.up('xs')} and (orientation: landscape)`]: { - minHeight: 52, - }, - [theme.breakpoints.up('sm')]: { - minHeight: 52, - paddingRight: 2, - }, - [`& .${tablePaginationClasses.actions}`]: { - flexShrink: 0, - marginLeft: 20, - }, -})); - -const TablePaginationSpacer = styled('div', { - name: 'MuiTablePagination', - slot: 'Spacer', - overridesResolver: (props, styles) => styles.spacer, -})({ - flex: '1 1 100%', -}); - -const TablePaginationSelectLabel = styled('p', { - name: 'MuiTablePagination', - slot: 'SelectLabel', - overridesResolver: (props, styles) => styles.selectLabel, -})(({ theme }) => ({ - ...theme.typography.body2, - flexShrink: 0, -})); - -const TablePaginationSelect = styled(Select, { - name: 'MuiTablePagination', - slot: 'Select', - overridesResolver: (props, styles) => ({ - [`& .${tablePaginationClasses.selectIcon}`]: styles.selectIcon, - [`& .${tablePaginationClasses.select}`]: styles.select, - ...styles.input, - ...styles.selectRoot, - }), -})({ - color: 'inherit', - fontSize: 'inherit', - flexShrink: 0, - marginRight: 32, - marginLeft: 8, - [`& .${tablePaginationClasses.select}`]: { - paddingLeft: 8, - paddingRight: 24, - textAlign: 'right', - textAlignLast: 'right', // Align <select> on Chrome. - }, -}); - -const TablePaginationMenuItem = styled(MenuItem, { - name: 'MuiTablePagination', - slot: 'MenuItem', - overridesResolver: (props, styles) => styles.menuItem, -})({}); - -const TablePaginationDisplayedRows = styled('p', { - name: 'MuiTablePagination', - slot: 'DisplayedRows', - overridesResolver: (props, styles) => styles.displayedRows, -})(({ theme }) => ({ - ...theme.typography.body2, - flexShrink: 0, -})); - -const useUtilityClasses = (ownerState) => { - const { classes } = ownerState; - const slots = { - root: ['root'], - toolbar: ['toolbar'], - spacer: ['spacer'], - selectLabel: ['selectLabel'], - select: ['select'], - input: ['input'], - selectIcon: ['selectIcon'], - menuItem: ['menuItem'], - displayedRows: ['displayedRows'], - actions: ['actions'], - }; - - return composeClasses(slots, getTablePaginationUtilityClass, classes); -}; - -/** - * A `TableCell` based component for placing inside `TableFooter` for pagination. - */ -const TablePagination = React.forwardRef(function TablePagination(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiTablePagination' }); - const { - ActionsComponent, - backIconButtonProps, - className, - component = TableCell, - nextIconButtonProps, - SelectProps = {}, - showFirstButton = false, - showLastButton = false, - ...other - } = props; - - const ownerState = props; - const classes = useUtilityClasses(ownerState); - - const MenuItemComponent = SelectProps.native ? 'option' : TablePaginationMenuItem; - - const selectId = useId(SelectProps.id); - const labelId = useId(SelectProps['aria-labelledby']); - - const rootProps = appendOwnerState(TablePaginationRoot, {}, ownerState); - const actionsProps = appendOwnerState(ActionsComponent, {}, ownerState); - const selectProps = appendOwnerState(TablePaginationSelect, SelectProps, ownerState); - const menuItemProps = appendOwnerState(MenuItemComponent, {}, ownerState); - const displayedRowsProps = appendOwnerState(TablePaginationDisplayedRows, {}, ownerState); - const selectLabelProps = appendOwnerState(TablePaginationSelectLabel, {}, ownerState); - const spacerProps = appendOwnerState(TablePaginationSpacer, {}, ownerState); - const toolbarProps = appendOwnerState(TablePaginationToolbar, {}, ownerState); - - return ( - <BaseTablePagination - slots={{ - root: TablePaginationRoot, - actions: ActionsComponent, - toolbar: TablePaginationToolbar, - spacer: TablePaginationSpacer, - selectLabel: TablePaginationSelectLabel, - select: TablePaginationSelect, - menuItem: MenuItemComponent, - displayedRows: TablePaginationDisplayedRows, - }} - slotProps={{ - root: { - as: component, - ...rootProps, - }, - actions: { - slots: { - firstButton: CustomIconButton, - lastButton: CustomIconButton, - nextButton: CustomIconButton, - backButton: CustomIconButton, - lastPageIcon: LastPageIcon, - firstPageIcon: FirstPageIcon, - nextPageIcon: KeyboardArrowRight, - backPageIcon: KeyboardArrowLeft, - }, - slotProps: { - backButton: backIconButtonProps, - nextButton: nextIconButtonProps, - }, - showFirstButton, - showLastButton, - className: classes.actions, - ownerState, - ...actionsProps, - }, - select: { - variant: 'standard', - input: <InputBase />, - ...selectProps, - ...(SelectProps.native ? {} : { labelId }), - 'aria-labelledby': labelId, - className: clsx(classes.select, SelectProps.className), - classes: { - ...SelectProps.classes, - root: clsx(classes.selectRoot, (SelectProps.classes || {}).root), - select: clsx(classes.select, (SelectProps.classes || {}).select), - }, - }, - menuItem: { - className: classes.menuItem, - ...menuItemProps, - }, - displayedRows: { - className: classes.displayedRows, - ...displayedRowsProps, - }, - selectLabel: { - className: classes.selectLabel, - ...selectLabelProps, - }, - spacer: { - className: classes.spacer, - ...spacerProps, - }, - toolbar: { - className: classes.toolbar, - ...toolbarProps, - }, - }} - selectId={selectId} - labelId={labelId} - ref={ref} - {...other} - className={clsx(classes.root, className)} - /> - ); -}); - -TablePagination.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The component used for displaying the actions. - * Either a string to use a HTML element or a component. - * @default TablePaginationActions - */ - ActionsComponent: PropTypes.elementType, - /** - * Props applied to the back arrow [`IconButton`](/material-ui/api/icon-button/) component. - */ - backIconButtonProps: PropTypes.object, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * The total number of rows. - * - * To enable server side pagination for an unknown number of items, provide -1. - */ - count: integerPropType.isRequired, - /** - * Accepts a function which returns a string value that provides a user-friendly name for the current page. - * This is important for screen reader users. - * - * For localization purposes, you can use the provided [translations](/material-ui/guides/localization/). - * @param {string} type The link or button type to format ('first' | 'last' | 'next' | 'previous'). - * @returns {string} - * @default function defaultGetAriaLabel(type) { - * return `Go to ${type} page`; - * } - */ - getItemAriaLabel: PropTypes.func, - /** - * Customize the displayed rows label. Invoked with a `{ from, to, count, page }` - * object. - * - * For localization purposes, you can use the provided [translations](/material-ui/guides/localization/). - * @default function defaultLabelDisplayedRows({ from, to, count }) { - * return `${from}–${to} of ${count !== -1 ? count : `more than ${to}`}`; - * } - */ - labelDisplayedRows: PropTypes.func, - /** - * Customize the rows per page label. - * - * For localization purposes, you can use the provided [translations](/material-ui/guides/localization/). - * @default 'Rows per page:' - */ - labelRowsPerPage: PropTypes.node, - /** - * Props applied to the next arrow [`IconButton`](/material-ui/api/icon-button/) element. - */ - nextIconButtonProps: PropTypes.object, - /** - * Callback fired when the page is changed. - * - * @param {React.MouseEvent<HTMLButtonElement> | null} event The event source of the callback. - * @param {number} page The page selected. - */ - onPageChange: PropTypes.func.isRequired, - /** - * Callback fired when the number of rows per page is changed. - * - * @param {React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>} event The event source of the callback. - */ - onRowsPerPageChange: PropTypes.func, - /** - * The zero-based index of the current page. - */ - page: chainPropTypes(integerPropType.isRequired, (props) => { - const { count, page, rowsPerPage } = props; - - if (count === -1) { - return null; - } - - const newLastPage = Math.max(0, Math.ceil(count / rowsPerPage) - 1); - if (page < 0 || page > newLastPage) { - return new Error( - 'MUI: The page prop of a TablePagination is out of range ' + - `(0 to ${newLastPage}, but page is ${page}).`, - ); - } - return null; - }), - /** - * The number of rows per page. - * - * Set -1 to display all the rows. - */ - rowsPerPage: integerPropType.isRequired, - /** - * Customizes the options of the rows per page select field. If less than two options are - * available, no select field will be displayed. - * Use -1 for the value with a custom label to show all the rows. - * @default [10, 25, 50, 100] - */ - rowsPerPageOptions: PropTypes.arrayOf( - PropTypes.oneOfType([ - PropTypes.number, - PropTypes.shape({ - label: PropTypes.string.isRequired, - value: PropTypes.number.isRequired, - }), - ]).isRequired, - ), - /** - * Props applied to the rows per page [`Select`](/material-ui/api/select/) element. - * @default {} - */ - SelectProps: PropTypes.object, - /** - * If `true`, show the first-page button. - * @default false - */ - showFirstButton: PropTypes.bool, - /** - * If `true`, show the last-page button. - * @default false - */ - showLastButton: PropTypes.bool, - /** - * 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, - ]), -}; - -export default TablePagination; diff --git a/packages/mui-material-next/src/TablePagination/TablePagination.spec.tsx b/packages/mui-material-next/src/TablePagination/TablePagination.spec.tsx deleted file mode 100644 index bc6eb7718f797f..00000000000000 --- a/packages/mui-material-next/src/TablePagination/TablePagination.spec.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import * as React from 'react'; -import TablePagination from '@mui/material/TablePagination'; - -function classesTest() { - const defaultProps = { - count: 1, - onPageChange: () => {}, - page: 1, - rowsPerPage: 1, - }; - - <TablePagination classes={{ actions: 'actions' }} {...defaultProps} />; - // @ts-expect-error desired - <TablePagination classes={{ alignCenter: 'center' }} {...defaultProps} />; -} diff --git a/packages/mui-material-next/src/TablePagination/TablePagination.test.js b/packages/mui-material-next/src/TablePagination/TablePagination.test.js deleted file mode 100644 index d5faa7568d7bb4..00000000000000 --- a/packages/mui-material-next/src/TablePagination/TablePagination.test.js +++ /dev/null @@ -1,452 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { spy } from 'sinon'; -import PropTypes from 'prop-types'; -import { fireEvent, createRenderer } from '@mui-internal/test-utils'; -import TableFooter from '@mui/material/TableFooter'; -import TableCell from '@mui/material/TableCell'; -import TableRow from '@mui/material/TableRow'; -import TablePagination, { - tablePaginationClasses as classes, -} from '@mui/material-next/TablePagination'; -import describeConformance from '../../test/describeConformance'; - -describe('<TablePagination />', () => { - const noop = () => {}; - const { render } = createRenderer(); - - describeConformance( - <TablePagination count={1} onPageChange={noop} page={0} rowsPerPage={10} />, - () => ({ - classes, - inheritComponent: TableCell, - render: (node) => { - const { container, ...other } = render( - <table> - <tbody> - <tr>{node}</tr> - </tbody> - </table>, - ); - return { container: container.firstChild.firstChild.firstChild, ...other }; - }, - wrapMount: (mount) => (node) => { - const wrapper = mount( - <table> - <tbody> - <tr>{node}</tr> - </tbody> - </table>, - ); - return wrapper.find('tr').childAt(0); - }, - muiName: 'MuiTablePagination', - refInstanceof: window.HTMLTableCellElement, - testComponentPropWith: 'td', - testComponentsRootPropWith: 'td', - testDeepOverrides: { slotName: 'toolbar', slotClassName: classes.toolbar }, - skip: ['themeVariants', 'componentsProp'], - }), - ); - - describe('prop: labelDisplayedRows', () => { - it('should use the labelDisplayedRows callback', () => { - let labelDisplayedRowsCalled = false; - function labelDisplayedRows({ from, to, count, page }) { - labelDisplayedRowsCalled = true; - expect(from).to.equal(11); - expect(to).to.equal(20); - expect(count).to.equal(42); - expect(page).to.equal(1); - return `Page ${page}`; - } - - const { container } = render( - <table> - <TableFooter> - <TableRow> - <TablePagination - count={42} - page={1} - onPageChange={noop} - onRowsPerPageChange={noop} - rowsPerPage={10} - labelDisplayedRows={labelDisplayedRows} - /> - </TableRow> - </TableFooter> - </table>, - ); - expect(labelDisplayedRowsCalled).to.equal(true); - expect(container.innerHTML.includes('Page 1')).to.equal(true); - }); - }); - - describe('prop: labelRowsPerPage', () => { - it('labels the select for the current page', () => { - const { getByRole } = render( - <table> - <TableFooter> - <TableRow> - <TablePagination - count={1} - page={0} - onPageChange={noop} - onRowsPerPageChange={noop} - rowsPerPage={10} - labelRowsPerPage="lines per page:" - /> - </TableRow> - </TableFooter> - </table>, - ); - - const combobox = getByRole('combobox'); - expect(combobox).toHaveAccessibleName('lines per page:'); - }); - - it('accepts React nodes', () => { - const { getByRole } = render( - <table> - <TableFooter> - <TableRow> - <TablePagination - count={1} - page={0} - onPageChange={noop} - onRowsPerPageChange={noop} - rowsPerPage={10} - labelRowsPerPage={ - <React.Fragment> - <em>lines</em> per page: - </React.Fragment> - } - /> - </TableRow> - </TableFooter> - </table>, - ); - - const combobox = getByRole('combobox'); - expect(combobox).toHaveAccessibleName('lines per page:'); - }); - }); - - describe('prop: page', () => { - it('should disable the back button on the first page', () => { - const { getByRole } = render( - <table> - <TableFooter> - <TableRow> - <TablePagination - count={11} - page={0} - onPageChange={noop} - onRowsPerPageChange={noop} - rowsPerPage={10} - /> - </TableRow> - </TableFooter> - </table>, - ); - - const backButton = getByRole('button', { name: 'Go to previous page' }); - const nextButton = getByRole('button', { name: 'Go to next page' }); - expect(backButton).to.have.property('disabled', true); - expect(nextButton).to.have.property('disabled', false); - }); - - it('should disable the next button on the last page', () => { - const { getByRole } = render( - <table> - <TableFooter> - <TableRow> - <TablePagination - count={11} - page={1} - onPageChange={noop} - onRowsPerPageChange={noop} - rowsPerPage={10} - /> - </TableRow> - </TableFooter> - </table>, - ); - - const backButton = getByRole('button', { name: 'Go to previous page' }); - const nextButton = getByRole('button', { name: 'Go to next page' }); - expect(backButton).to.have.property('disabled', false); - expect(nextButton).to.have.property('disabled', true); - }); - }); - - describe('prop: onPageChange', () => { - it('should handle next button clicks properly', () => { - let page = 1; - const { getByRole } = render( - <table> - <TableFooter> - <TableRow> - <TablePagination - count={30} - page={page} - onPageChange={(event, nextPage) => { - page = nextPage; - }} - onRowsPerPageChange={noop} - rowsPerPage={10} - /> - </TableRow> - </TableFooter> - </table>, - ); - - const nextButton = getByRole('button', { name: 'Go to next page' }); - fireEvent.click(nextButton); - expect(page).to.equal(2); - }); - - it('should handle back button clicks properly', () => { - let page = 1; - const { getByRole } = render( - <table> - <TableFooter> - <TableRow> - <TablePagination - count={30} - page={page} - onPageChange={(event, nextPage) => { - page = nextPage; - }} - onRowsPerPageChange={noop} - rowsPerPage={10} - /> - </TableRow> - </TableFooter> - </table>, - ); - - const backButton = getByRole('button', { name: 'Go to previous page' }); - fireEvent.click(backButton); - expect(page).to.equal(0); - }); - }); - - describe('label', () => { - it('should display 0 as start number if the table is empty ', () => { - const { container } = render( - <table> - <TableFooter> - <TableRow> - <TablePagination - count={0} - page={0} - rowsPerPage={10} - onPageChange={noop} - onRowsPerPageChange={noop} - /> - </TableRow> - </TableFooter> - </table>, - ); - expect(container.querySelectorAll('p')[1]).to.have.text('0–0 of 0'); - }); - - it('should hide the rows per page selector if there are less than two options', () => { - const { container, queryByRole } = render( - <table> - <TableFooter> - <TableRow> - <TablePagination - page={0} - rowsPerPage={5} - rowsPerPageOptions={[5]} - onPageChange={noop} - onRowsPerPageChange={noop} - count={10} - /> - </TableRow> - </TableFooter> - </table>, - ); - - expect(container).not.to.include.text('Rows per page'); - expect(queryByRole('listbox')).to.equal(null); - }); - }); - - describe('prop: count=-1', () => { - it('should display the "of more than" text and keep the nextButton enabled', () => { - function Test() { - const [page, setPage] = React.useState(0); - return ( - <table> - <TableFooter> - <TableRow> - <TablePagination - page={page} - rowsPerPage={10} - count={-1} - onPageChange={(_, newPage) => { - setPage(newPage); - }} - /> - </TableRow> - </TableFooter> - </table> - ); - } - - const { container, getByRole } = render(<Test />); - - expect(container).to.have.text('Rows per page:101–10 of more than 10'); - fireEvent.click(getByRole('button', { name: 'Go to next page' })); - expect(container).to.have.text('Rows per page:1011–20 of more than 20'); - }); - }); - - describe('prop: showFirstButton', () => { - it('should change the page', () => { - const handleChangePage = spy(); - const { getByRole } = render( - <table> - <TableFooter> - <TableRow> - <TablePagination - showFirstButton - page={1} - rowsPerPage={10} - count={98} - onPageChange={handleChangePage} - /> - </TableRow> - </TableFooter> - </table>, - ); - - fireEvent.click(getByRole('button', { name: 'Go to first page' })); - expect(handleChangePage.args[0][1]).to.equal(0); - }); - }); - - describe('prop: showLastButton', () => { - it('should change the page', () => { - const handleChangePage = spy(); - const { getByRole } = render( - <table> - <TableFooter> - <TableRow> - <TablePagination - showLastButton - page={0} - rowsPerPage={10} - count={98} - onPageChange={handleChangePage} - /> - </TableRow> - </TableFooter> - </table>, - ); - - fireEvent.click(getByRole('button', { name: 'Go to last page' })); - expect(handleChangePage.args[0][1]).to.equal(9); - }); - }); - - describe('warnings', () => { - beforeEach(() => { - PropTypes.resetWarningCache(); - }); - - it('should raise a warning if the page prop is out of range', () => { - expect(() => { - PropTypes.checkPropTypes( - TablePagination.propTypes, - { - classes: {}, - page: 2, - count: 20, - rowsPerPage: 10, - onPageChange: noop, - onRowsPerPageChange: noop, - }, - 'prop', - 'MockedTablePagination', - ); - }).toErrorDev( - 'MUI: The page prop of a TablePagination is out of range (0 to 1, but page is 2).', - ); - }); - }); - - describe('prop: SelectProps', () => { - it('does allow manual label ids', () => { - const { getByRole } = render( - <table> - <TableFooter> - <TableRow> - <TablePagination - count={1} - page={0} - onPageChange={noop} - onRowsPerPageChange={noop} - rowsPerPage={10} - SelectProps={{ id: 'foo', labelId: 'bar' }} - /> - </TableRow> - </TableFooter> - </table>, - ); - - const combobox = getByRole('combobox'); - expect(combobox).toHaveAccessibleName('Rows per page:'); - }); - }); - - describe('prop: rowsPerPage', () => { - it('should display max number of rows text when prop is -1', () => { - const { container } = render( - <table> - <TableFooter> - <TableRow> - <TablePagination - rowsPerPageOptions={[5, 10, 25, { label: 'All', value: -1 }]} - count={25} - page={0} - rowsPerPage={-1} - onPageChange={noop} - /> - </TableRow> - </TableFooter> - </table>, - ); - - expect(container).to.include.text('All'); - expect(container).to.include.text('1–25 of 25'); - }); - }); - - describe('duplicated keys', () => { - it('should not raise a warning due to duplicated keys', () => { - render( - <table> - <TableFooter> - <TableRow> - <TablePagination - rowsPerPageOptions={[5, 10, { label: 'All', value: 10 }]} - count={10} - rowsPerPage={10} - page={0} - onPageChange={noop} - SelectProps={{ - inputProps: { 'aria-label': 'rows per page' }, - native: true, - }} - /> - </TableRow> - </TableFooter> - </table>, - ); - }); - }); -}); diff --git a/packages/mui-material-next/src/TablePagination/index.d.ts b/packages/mui-material-next/src/TablePagination/index.d.ts deleted file mode 100644 index 2df08ab13db13f..00000000000000 --- a/packages/mui-material-next/src/TablePagination/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './TablePagination'; -export * from './TablePagination'; - -export { default as tablePaginationClasses } from './tablePaginationClasses'; -export * from './tablePaginationClasses'; diff --git a/packages/mui-material-next/src/TablePagination/index.js b/packages/mui-material-next/src/TablePagination/index.js deleted file mode 100644 index 12c25d2f980416..00000000000000 --- a/packages/mui-material-next/src/TablePagination/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './TablePagination'; - -export { default as tablePaginationClasses } from './tablePaginationClasses'; -export * from './tablePaginationClasses'; diff --git a/packages/mui-material-next/src/TablePagination/tablePaginationClasses.ts b/packages/mui-material-next/src/TablePagination/tablePaginationClasses.ts deleted file mode 100644 index bd6aaedd1574d5..00000000000000 --- a/packages/mui-material-next/src/TablePagination/tablePaginationClasses.ts +++ /dev/null @@ -1,52 +0,0 @@ -import generateUtilityClass from '@mui/utils/generateUtilityClass'; -import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; - -export interface TablePaginationClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the Toolbar component. */ - toolbar: string; - /** Styles applied to the spacer element. */ - spacer: string; - /** Styles applied to the select label Typography element. */ - selectLabel: string; - /** Styles applied to the Select component `root` element. */ - selectRoot: string; - /** Styles applied to the Select component `select` class. */ - select: string; - /** Styles applied to the Select component `icon` class. */ - selectIcon: string; - /** Styles applied to the Select component `root` element. */ - input: string; - /** Styles applied to the MenuItem component. */ - menuItem: string; - /** Styles applied to the displayed rows Typography element. */ - displayedRows: string; - /** Styles applied to the internal `TablePaginationActions` component. */ - actions: string; -} - -export type TablePaginationClassKey = keyof TablePaginationClasses; - -export function getTablePaginationUtilityClass(slot: string): string { - return generateUtilityClass('MuiTablePagination', slot); -} - -const tablePaginationClasses: TablePaginationClasses = generateUtilityClasses( - 'MuiTablePagination', - [ - 'root', - 'toolbar', - 'spacer', - 'selectLabel', - 'selectRoot', - 'select', - 'selectIcon', - 'input', - 'menuItem', - 'displayedRows', - 'actions', - ], -); - -export default tablePaginationClasses; diff --git a/packages/mui-material-next/src/Tabs/ScrollbarSize.js b/packages/mui-material-next/src/Tabs/ScrollbarSize.js deleted file mode 100644 index 1c75f89f6f6acd..00000000000000 --- a/packages/mui-material-next/src/Tabs/ScrollbarSize.js +++ /dev/null @@ -1,56 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { unstable_debounce as debounce, unstable_ownerWindow as ownerWindow } from '@mui/utils'; - -const styles = { - width: 99, - height: 99, - position: 'absolute', - top: -9999, - overflow: 'scroll', -}; - -/** - * @ignore - internal component. - * The component originates from https://github.com/STORIS/react-scrollbar-size. - * It has been moved into the core in order to minimize the bundle size. - */ -export default function ScrollbarSize(props) { - const { onChange, ...other } = props; - const scrollbarHeight = React.useRef(); - const nodeRef = React.useRef(null); - - const setMeasurements = () => { - scrollbarHeight.current = nodeRef.current.offsetHeight - nodeRef.current.clientHeight; - }; - - React.useEffect(() => { - const handleResize = debounce(() => { - const prevHeight = scrollbarHeight.current; - setMeasurements(); - - if (prevHeight !== scrollbarHeight.current) { - onChange(scrollbarHeight.current); - } - }); - - const containerWindow = ownerWindow(nodeRef.current); - containerWindow.addEventListener('resize', handleResize); - return () => { - handleResize.clear(); - containerWindow.removeEventListener('resize', handleResize); - }; - }, [onChange]); - - React.useEffect(() => { - setMeasurements(); - onChange(scrollbarHeight.current); - }, [onChange]); - - return <div style={styles} ref={nodeRef} {...other} />; -} - -ScrollbarSize.propTypes = { - onChange: PropTypes.func.isRequired, -}; diff --git a/packages/mui-material-next/src/Tabs/ScrollbarSize.test.js b/packages/mui-material-next/src/Tabs/ScrollbarSize.test.js deleted file mode 100644 index eb6e6899303525..00000000000000 --- a/packages/mui-material-next/src/Tabs/ScrollbarSize.test.js +++ /dev/null @@ -1,50 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { spy, stub } from 'sinon'; -import { createRenderer } from '@mui-internal/test-utils'; -import ScrollbarSize from './ScrollbarSize'; - -describe('<ScrollbarSize />', () => { - const { clock, render } = createRenderer({ clock: 'fake' }); - - describe('mount', () => { - it('should call on initial load', () => { - const onChange = spy(); - render(<ScrollbarSize onChange={onChange} />); - - expect(onChange.called).to.equal(true); - }); - }); - - describe('prop: onChange', () => { - it('should call on first resize event', () => { - const onChange = spy(); - const { container } = render(<ScrollbarSize onChange={onChange} />); - stub(container.firstChild, 'offsetHeight').get(() => 20); - stub(container.firstChild, 'clientHeight').get(() => 0); - - onChange.resetHistory(); - - window.dispatchEvent(new window.Event('resize', {})); - clock.tick(166); - expect(onChange.callCount).to.equal(1); - expect(onChange.args[0][0]).to.equal(20); - }); - - it('should not call if height has not changed from previous resize', () => { - const onChange = spy(); - const { container } = render(<ScrollbarSize onChange={onChange} />); - stub(container.firstChild, 'offsetHeight').get(() => 20); - stub(container.firstChild, 'clientHeight').get(() => 0); - - onChange.resetHistory(); - - window.dispatchEvent(new window.Event('resize', {})); - clock.tick(166); - window.dispatchEvent(new window.Event('resize', {})); - clock.tick(166); - expect(onChange.callCount).to.equal(1); - expect(onChange.args[0][0]).to.equal(20); - }); - }); -}); diff --git a/packages/mui-material-next/src/Tabs/Tabs.d.ts b/packages/mui-material-next/src/Tabs/Tabs.d.ts deleted file mode 100644 index 66150dfa1be10e..00000000000000 --- a/packages/mui-material-next/src/Tabs/Tabs.d.ts +++ /dev/null @@ -1,159 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { Theme } from '@mui/material/styles'; -import ButtonBase from '@mui/material/ButtonBase'; -import { OverridableComponent, OverrideProps } from '@mui/material/OverridableComponent'; -import { TabScrollButtonProps } from '../TabScrollButton'; -import { TabsClasses } from './tabsClasses'; - -export interface TabsOwnProps { - /** - * Callback fired when the component mounts. - * This is useful when you want to trigger an action programmatically. - * It supports two actions: `updateIndicator()` and `updateScrollButtons()` - * - * @param {object} actions This object contains all possible actions - * that can be triggered programmatically. - */ - action?: React.Ref<TabsActions>; - /** - * If `true`, the scroll buttons aren't forced hidden on mobile. - * By default the scroll buttons are hidden on mobile and takes precedence over `scrollButtons`. - * @default false - */ - allowScrollButtonsMobile?: boolean; - /** - * The label for the Tabs as a string. - */ - 'aria-label'?: string; - /** - * An id or list of ids separated by a space that label the Tabs. - */ - 'aria-labelledby'?: string; - /** - * If `true`, the tabs are centered. - * This prop is intended for large views. - * @default false - */ - centered?: boolean; - /** - * The content of the component. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial<TabsClasses>; - /** - * Determines the color of the indicator. - * @default 'primary' - */ - indicatorColor?: 'secondary' | 'primary'; - /** - * Callback fired when the value changes. - * - * @param {React.SyntheticEvent} event The event source of the callback. **Warning**: This is a generic event not a change event. - * @param {any} value We default to the index of the child (number) - */ - onChange?: (event: React.SyntheticEvent, value: any) => void; - /** - * The component orientation (layout flow direction). - * @default 'horizontal' - */ - orientation?: 'horizontal' | 'vertical'; - /** - * The component used to render the scroll buttons. - * @default TabScrollButton - */ - ScrollButtonComponent?: React.ElementType; - /** - * Determine behavior of scroll buttons when tabs are set to scroll: - * - * - `auto` will only present them when not all the items are visible. - * - `true` will always present them. - * - `false` will never present them. - * - * By default the scroll buttons are hidden on mobile. - * This behavior can be disabled with `allowScrollButtonsMobile`. - * @default 'auto' - */ - scrollButtons?: 'auto' | true | false; - /** - * If `true` the selected tab changes on focus. Otherwise it only - * changes on activation. - */ - selectionFollowsFocus?: boolean; - /** - * Props applied to the tab indicator element. - * @default {} - */ - TabIndicatorProps?: React.HTMLAttributes<HTMLDivElement>; - /** - * Props applied to the [`TabScrollButton`](/material-ui/api/tab-scroll-button/) element. - * @default {} - */ - TabScrollButtonProps?: Partial<TabScrollButtonProps>; - /** - * Determines the color of the `Tab`. - * @default 'primary' - */ - textColor?: 'secondary' | 'primary' | 'inherit'; - /** - * The value of the currently selected `Tab`. - * If you don't want any selected `Tab`, you can set this prop to `false`. - */ - value?: any; - /** - * Determines additional display behavior of the tabs: - * - * - `scrollable` will invoke scrolling properties and allow for horizontally - * scrolling (or swiping) of the tab bar. - * -`fullWidth` will make the tabs grow to use all the available space, - * which should be used for small views, like on mobile. - * - `standard` will render the default state. - * @default 'standard' - */ - variant?: 'standard' | 'scrollable' | 'fullWidth'; - /** - * If `true`, the scrollbar is visible. It can be useful when displaying - * a long vertical list of tabs. - * @default false - */ - visibleScrollbar?: boolean; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps<Theme>; -} - -export interface TabsTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = typeof ButtonBase, -> { - props: AdditionalProps & TabsOwnProps; - defaultComponent: RootComponent; -} - -/** - * - * Demos: - * - * - [Tabs](https://mui.com/components/tabs/) - * - * API: - * - * - [Tabs API](https://mui.com/api/tabs/) - */ -declare const Tabs: OverridableComponent<TabsTypeMap>; - -export interface TabsActions { - updateIndicator(): void; - updateScrollButtons(): void; -} - -export type TabsProps< - RootComponent extends React.ElementType = TabsTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps<TabsTypeMap<AdditionalProps, RootComponent>, RootComponent>; - -export default Tabs; diff --git a/packages/mui-material-next/src/Tabs/Tabs.js b/packages/mui-material-next/src/Tabs/Tabs.js deleted file mode 100644 index 49c721321cd245..00000000000000 --- a/packages/mui-material-next/src/Tabs/Tabs.js +++ /dev/null @@ -1,762 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { - refType, - unstable_debounce as debounce, - unstable_getNormalizedScrollLeft as getNormalizedScrollLeft, - unstable_detectScrollType as detectScrollType, - unstable_useEventCallback as useEventCallback, - unstable_ownerWindow as ownerWindow, -} from '@mui/utils'; -import { unstable_composeClasses as composeClasses } from '@mui/base'; -import { useTabs, TabsProvider } from '@mui/base/useTabs'; -import { styled, useThemeProps, useTheme } from '@mui/material/styles'; -import animate from '@mui/material/internal/animate'; -import TabScrollButton from '../TabScrollButton'; -import ScrollbarSize from './ScrollbarSize'; -import tabsClasses, { getTabsUtilityClass } from './tabsClasses'; -import TabsList from './TabsList'; - -const useUtilityClasses = (ownerState) => { - const { - vertical, - fixed, - hideScrollbar, - scrollableX, - scrollableY, - centered, - scrollButtonsHideMobile, - classes, - } = ownerState; - - const slots = { - root: ['root', vertical && 'vertical'], - scroller: [ - 'scroller', - fixed && 'fixed', - hideScrollbar && 'hideScrollbar', - scrollableX && 'scrollableX', - scrollableY && 'scrollableY', - ], - flexContainer: ['flexContainer', vertical && 'flexContainerVertical', centered && 'centered'], - indicator: ['indicator'], - scrollButtons: ['scrollButtons', scrollButtonsHideMobile && 'scrollButtonsHideMobile'], - scrollableX: [scrollableX && 'scrollableX'], - hideScrollbar: [hideScrollbar && 'hideScrollbar'], - }; - - return composeClasses(slots, getTabsUtilityClass, classes); -}; - -const TabsRoot = styled('div', { - name: 'MuiTabs', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - { [`& .${tabsClasses.scrollButtons}`]: styles.scrollButtons }, - { - [`& .${tabsClasses.scrollButtons}`]: - ownerState.scrollButtonsHideMobile && styles.scrollButtonsHideMobile, - }, - styles.root, - ownerState.vertical && styles.vertical, - ]; - }, -})(({ ownerState, theme }) => ({ - overflow: 'hidden', - minHeight: 48, - // Add iOS momentum scrolling for iOS < 13.0 - WebkitOverflowScrolling: 'touch', - display: 'flex', - ...(ownerState.vertical && { - flexDirection: 'column', - }), - ...(ownerState.scrollButtonsHideMobile && { - [`& .${tabsClasses.scrollButtons}`]: { - [theme.breakpoints.down('sm')]: { - display: 'none', - }, - }, - }), -})); - -const TabsScroller = styled('div', { - name: 'MuiTabs', - slot: 'Scroller', - overridesResolver: (props, styles) => { - const { ownerState } = props; - return [ - styles.scroller, - ownerState.fixed && styles.fixed, - ownerState.hideScrollbar && styles.hideScrollbar, - ownerState.scrollableX && styles.scrollableX, - ownerState.scrollableY && styles.scrollableY, - ]; - }, -})(({ ownerState }) => ({ - position: 'relative', - display: 'inline-block', - flex: '1 1 auto', - whiteSpace: 'nowrap', - ...(ownerState.fixed && { - overflowX: 'hidden', - width: '100%', - }), - ...(ownerState.hideScrollbar && { - // Hide dimensionless scrollbar on macOS - scrollbarWidth: 'none', // Firefox - '&::-webkit-scrollbar': { - display: 'none', // Safari + Chrome - }, - }), - ...(ownerState.scrollableX && { - overflowX: 'auto', - overflowY: 'hidden', - }), - ...(ownerState.scrollableY && { - overflowY: 'auto', - overflowX: 'hidden', - }), -})); - -const TabsIndicator = styled('span', { - name: 'MuiTabs', - slot: 'Indicator', - overridesResolver: (props, styles) => styles.indicator, -})(({ ownerState, theme }) => ({ - position: 'absolute', - height: 2, - bottom: 0, - width: '100%', - transition: theme.transitions.create(), - ...(ownerState.indicatorColor === 'primary' && { - backgroundColor: theme.palette.primary.main, - }), - ...(ownerState.indicatorColor === 'secondary' && { - backgroundColor: theme.palette.secondary.main, - }), - ...(ownerState.vertical && { - height: '100%', - width: 2, - right: 0, - }), -})); - -const TabsScrollbarSize = styled(ScrollbarSize)({ - overflowX: 'auto', - overflowY: 'hidden', - // Hide dimensionless scrollbar on macOS - scrollbarWidth: 'none', // Firefox - '&::-webkit-scrollbar': { - display: 'none', // Safari + Chrome - }, -}); - -const defaultIndicatorStyle = {}; - -let warnedOnceTabPresent = false; - -const Tabs = React.forwardRef(function Tabs(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiTabs' }); - const theme = useTheme(); - const isRtl = theme.direction === 'rtl'; - const { - 'aria-label': ariaLabel, - 'aria-labelledby': ariaLabelledBy, - action, - centered = false, - children, - className, - component = 'div', - allowScrollButtonsMobile = false, - indicatorColor = 'primary', - onChange, - orientation = 'horizontal', - ScrollButtonComponent = TabScrollButton, - scrollButtons = 'auto', - selectionFollowsFocus, - TabIndicatorProps = {}, - TabScrollButtonProps = {}, - textColor = 'primary', - value, - variant = 'standard', - visibleScrollbar = false, - ...other - } = props; - const scrollable = variant === 'scrollable'; - const vertical = orientation === 'vertical'; - - const scrollStart = vertical ? 'scrollTop' : 'scrollLeft'; - const start = vertical ? 'top' : 'left'; - const end = vertical ? 'bottom' : 'right'; - const clientSize = vertical ? 'clientHeight' : 'clientWidth'; - const size = vertical ? 'height' : 'width'; - - const ownerState = { - ...props, - component, - allowScrollButtonsMobile, - indicatorColor, - orientation, - vertical, - scrollButtons, - textColor, - variant, - visibleScrollbar, - fixed: !scrollable, - hideScrollbar: scrollable && !visibleScrollbar, - scrollableX: scrollable && !vertical, - scrollableY: scrollable && vertical, - centered: centered && !scrollable, - scrollButtonsHideMobile: !allowScrollButtonsMobile, - }; - - const { contextValue: tabsContextValue } = useTabs({ - ...ownerState, - direction: theme.direction ?? 'ltr', - }); - - const classes = useUtilityClasses(ownerState); - - if (process.env.NODE_ENV !== 'production') { - if (centered && scrollable) { - console.error( - 'MUI: You can not use the `centered={true}` and `variant="scrollable"` properties ' + - 'at the same time on a `Tabs` component.', - ); - } - } - - const [mounted, setMounted] = React.useState(false); - const [indicatorStyle, setIndicatorStyle] = React.useState(defaultIndicatorStyle); - const [displayScroll, setDisplayScroll] = React.useState({ - start: false, - end: false, - }); - - const [scrollerStyle, setScrollerStyle] = React.useState({ - overflow: 'hidden', - scrollbarWidth: 0, - }); - - const tabsRef = React.useRef(null); - const tabListRef = React.useRef(null); - - const valueToIndex = new Map(); - let childIndex = 0; - React.Children.forEach(children, (child) => { - if (!React.isValidElement(child)) { - return null; - } - const childValue = child.props.value === undefined ? childIndex : child.props.value; - valueToIndex.set(childValue, childIndex); - childIndex += 1; - return null; - }); - - const getTabsMeta = () => { - const tabsNode = tabsRef.current; - let tabsMeta; - if (tabsNode) { - const rect = tabsNode.getBoundingClientRect(); - // create a new object with ClientRect class props + scrollLeft - tabsMeta = { - clientWidth: tabsNode.clientWidth, - scrollLeft: tabsNode.scrollLeft, - scrollTop: tabsNode.scrollTop, - scrollLeftNormalized: getNormalizedScrollLeft(tabsNode, theme.direction), - scrollWidth: tabsNode.scrollWidth, - top: rect.top, - bottom: rect.bottom, - left: rect.left, - right: rect.right, - }; - } - - let tabMeta; - if (tabsNode && value !== null) { - const currentChildren = tabListRef.current.children; - - if (currentChildren.length > 0) { - const tab = currentChildren[valueToIndex.get(value)]; - if (process.env.NODE_ENV !== 'production') { - if (!tab) { - console.error( - [ - `MUI: The \`value\` provided to the Tabs component is invalid.`, - `None of the Tabs' children match with "${value}".`, - valueToIndex.keys - ? `You can provide one of the following values: ${Array.from( - valueToIndex.keys(), - ).join(', ')}.` - : null, - ].join('\n'), - ); - } - } - tabMeta = tab ? tab.getBoundingClientRect() : null; - - if (process.env.NODE_ENV !== 'production') { - if ( - process.env.NODE_ENV !== 'test' && - !warnedOnceTabPresent && - tabMeta && - tabMeta.width === 0 && - tabMeta.height === 0 - ) { - tabsMeta = null; - console.error( - [ - 'MUI: The `value` provided to the Tabs component is invalid.', - `The Tab with this \`value\` ("${value}") is not part of the document layout.`, - "Make sure the tab item is present in the document or that it's not `display: none`.", - ].join('\n'), - ); - - warnedOnceTabPresent = true; - } - } - } - } - return { tabsMeta, tabMeta }; - }; - - const updateIndicatorState = useEventCallback(() => { - const { tabsMeta, tabMeta } = getTabsMeta(); - let startValue = 0; - let startIndicator; - - if (vertical) { - startIndicator = 'top'; - if (tabMeta && tabsMeta) { - startValue = tabMeta.top - tabsMeta.top + tabsMeta.scrollTop; - } - } else { - startIndicator = isRtl ? 'right' : 'left'; - if (tabMeta && tabsMeta) { - const correction = isRtl - ? tabsMeta.scrollLeftNormalized + tabsMeta.clientWidth - tabsMeta.scrollWidth - : tabsMeta.scrollLeft; - startValue = - (isRtl ? -1 : 1) * (tabMeta[startIndicator] - tabsMeta[startIndicator] + correction); - } - } - - const newIndicatorStyle = { - [startIndicator]: startValue, - // May be wrong until the font is loaded. - [size]: tabMeta ? tabMeta[size] : 0, - }; - - // IE11 support, replace with Number.isNaN - // eslint-disable-next-line no-restricted-globals - if (isNaN(indicatorStyle[startIndicator]) || isNaN(indicatorStyle[size])) { - setIndicatorStyle(newIndicatorStyle); - } else { - const dStart = Math.abs(indicatorStyle[startIndicator] - newIndicatorStyle[startIndicator]); - const dSize = Math.abs(indicatorStyle[size] - newIndicatorStyle[size]); - - if (dStart >= 1 || dSize >= 1) { - setIndicatorStyle(newIndicatorStyle); - } - } - }); - - const scroll = (scrollValue, { animation = true } = {}) => { - if (animation) { - animate(scrollStart, tabsRef.current, scrollValue, { - duration: theme.transitions.duration.standard, - }); - } else { - tabsRef.current[scrollStart] = scrollValue; - } - }; - - const moveTabsScroll = (delta) => { - let scrollValue = tabsRef.current[scrollStart]; - - if (vertical) { - scrollValue += delta; - } else { - scrollValue += delta * (isRtl ? -1 : 1); - // Fix for Edge - scrollValue *= isRtl && detectScrollType() === 'reverse' ? -1 : 1; - } - - scroll(scrollValue); - }; - - const getScrollSize = () => { - const containerSize = tabsRef.current[clientSize]; - let totalSize = 0; - const currentChildren = Array.from(tabListRef.current.children); - - for (let i = 0; i < currentChildren.length; i += 1) { - const tab = currentChildren[i]; - if (totalSize + tab[clientSize] > containerSize) { - break; - } - totalSize += tab[clientSize]; - } - return totalSize; - }; - - const handleStartScrollClick = () => { - moveTabsScroll(-1 * getScrollSize()); - }; - - const handleEndScrollClick = () => { - moveTabsScroll(getScrollSize()); - }; - - // TODO Remove <ScrollbarSize /> as browser support for hiding the scrollbar - // with CSS improves. - const handleScrollbarSizeChange = React.useCallback((scrollbarWidth) => { - setScrollerStyle({ - overflow: null, - scrollbarWidth, - }); - }, []); - - const getConditionalElements = () => { - const conditionalElements = {}; - - conditionalElements.scrollbarSizeListener = scrollable ? ( - <TabsScrollbarSize - onChange={handleScrollbarSizeChange} - className={clsx(classes.scrollableX, classes.hideScrollbar)} - /> - ) : null; - - const scrollButtonsActive = displayScroll.start || displayScroll.end; - const showScrollButtons = - scrollable && ((scrollButtons === 'auto' && scrollButtonsActive) || scrollButtons === true); - - conditionalElements.scrollButtonStart = showScrollButtons ? ( - <ScrollButtonComponent - orientation={orientation} - direction={isRtl ? 'right' : 'left'} - onClick={handleStartScrollClick} - disabled={!displayScroll.start} - {...TabScrollButtonProps} - className={clsx(classes.scrollButtons, TabScrollButtonProps.className)} - /> - ) : null; - - conditionalElements.scrollButtonEnd = showScrollButtons ? ( - <ScrollButtonComponent - orientation={orientation} - direction={isRtl ? 'left' : 'right'} - onClick={handleEndScrollClick} - disabled={!displayScroll.end} - {...TabScrollButtonProps} - className={clsx(classes.scrollButtons, TabScrollButtonProps.className)} - /> - ) : null; - - return conditionalElements; - }; - - const scrollSelectedIntoView = useEventCallback((animation) => { - const { tabsMeta, tabMeta } = getTabsMeta(); - - if (!tabMeta || !tabsMeta) { - return; - } - - if (tabMeta[start] < tabsMeta[start]) { - // left side of button is out of view - const nextScrollStart = tabsMeta[scrollStart] + (tabMeta[start] - tabsMeta[start]); - scroll(nextScrollStart, { animation }); - } else if (tabMeta[end] > tabsMeta[end]) { - // right side of button is out of view - const nextScrollStart = tabsMeta[scrollStart] + (tabMeta[end] - tabsMeta[end]); - scroll(nextScrollStart, { animation }); - } - }); - - const updateScrollButtonState = useEventCallback(() => { - if (scrollable && scrollButtons !== false) { - const { scrollTop, scrollHeight, clientHeight, scrollWidth, clientWidth } = tabsRef.current; - let showStartScroll; - let showEndScroll; - - if (vertical) { - showStartScroll = scrollTop > 1; - showEndScroll = scrollTop < scrollHeight - clientHeight - 1; - } else { - const scrollLeft = getNormalizedScrollLeft(tabsRef.current, theme.direction); - // use 1 for the potential rounding error with browser zooms. - showStartScroll = isRtl ? scrollLeft < scrollWidth - clientWidth - 1 : scrollLeft > 1; - showEndScroll = !isRtl ? scrollLeft < scrollWidth - clientWidth - 1 : scrollLeft > 1; - } - - if (showStartScroll !== displayScroll.start || showEndScroll !== displayScroll.end) { - setDisplayScroll({ start: showStartScroll, end: showEndScroll }); - } - } - }); - - React.useEffect(() => { - const handleResize = debounce(() => { - updateIndicatorState(); - updateScrollButtonState(); - }); - const win = ownerWindow(tabsRef.current); - win.addEventListener('resize', handleResize); - - let resizeObserver; - - if (typeof ResizeObserver !== 'undefined') { - resizeObserver = new ResizeObserver(handleResize); - Array.from(tabListRef.current.children).forEach((child) => { - resizeObserver.observe(child); - }); - } - - return () => { - handleResize.clear(); - win.removeEventListener('resize', handleResize); - if (resizeObserver) { - resizeObserver.disconnect(); - } - }; - }, [updateIndicatorState, updateScrollButtonState]); - - const handleTabsScroll = React.useMemo( - () => - debounce(() => { - updateScrollButtonState(); - }), - [updateScrollButtonState], - ); - - React.useEffect(() => { - return () => { - handleTabsScroll.clear(); - }; - }, [handleTabsScroll]); - - React.useEffect(() => { - setMounted(true); - }, []); - - React.useEffect(() => { - updateIndicatorState(); - updateScrollButtonState(); - }); - - React.useEffect(() => { - // Don't animate on the first render. - scrollSelectedIntoView(defaultIndicatorStyle !== indicatorStyle); - }, [scrollSelectedIntoView, indicatorStyle]); - - React.useImperativeHandle( - action, - () => ({ - updateIndicator: updateIndicatorState, - updateScrollButtons: updateScrollButtonState, - }), - [updateIndicatorState, updateScrollButtonState], - ); - - const indicator = ( - <TabsIndicator - {...TabIndicatorProps} - className={clsx(classes.indicator, TabIndicatorProps.className)} - ownerState={ownerState} - style={{ - ...indicatorStyle, - ...TabIndicatorProps.style, - }} - /> - ); - - const conditionalElements = getConditionalElements(); - - return ( - <TabsProvider value={tabsContextValue}> - <TabsRoot - className={clsx(classes.root, className)} - ownerState={ownerState} - ref={ref} - as={component} - {...other} - > - {conditionalElements.scrollButtonStart} - {conditionalElements.scrollbarSizeListener} - <TabsScroller - className={classes.scroller} - ownerState={ownerState} - style={{ - overflow: scrollerStyle.overflow, - [vertical ? `margin${isRtl ? 'Left' : 'Right'}` : 'marginBottom']: visibleScrollbar - ? undefined - : -scrollerStyle.scrollbarWidth, - }} - ref={tabsRef} - onScroll={handleTabsScroll} - > - {/* The tablist isn't interactive but the tabs are */} - <TabsList - aria-label={ariaLabel} - aria-labelledby={ariaLabelledBy} - variant={variant} - indicator={!mounted && indicator} - textColor={textColor} - className={classes.flexContainer} - ownerState={ownerState} - ref={tabListRef} - > - {children} - </TabsList> - {mounted && indicator} - </TabsScroller> - {conditionalElements.scrollButtonEnd} - </TabsRoot> - </TabsProvider> - ); -}); - -Tabs.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * Callback fired when the component mounts. - * This is useful when you want to trigger an action programmatically. - * It supports two actions: `updateIndicator()` and `updateScrollButtons()` - * - * @param {object} actions This object contains all possible actions - * that can be triggered programmatically. - */ - action: refType, - /** - * If `true`, the scroll buttons aren't forced hidden on mobile. - * By default the scroll buttons are hidden on mobile and takes precedence over `scrollButtons`. - * @default false - */ - allowScrollButtonsMobile: PropTypes.bool, - /** - * The label for the Tabs as a string. - */ - 'aria-label': PropTypes.string, - /** - * An id or list of ids separated by a space that label the Tabs. - */ - 'aria-labelledby': PropTypes.string, - /** - * If `true`, the tabs are centered. - * This prop is intended for large views. - * @default false - */ - centered: PropTypes.bool, - /** - * The content of the component. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * Determines the color of the indicator. - * @default 'primary' - */ - indicatorColor: PropTypes.oneOf(['primary', 'secondary']), - /** - * Callback fired when the value changes. - * - * @param {React.SyntheticEvent} event The event source of the callback. **Warning**: This is a generic event not a change event. - * @param {any} value We default to the index of the child (number) - */ - onChange: PropTypes.func, - /** - * The component orientation (layout flow direction). - * @default 'horizontal' - */ - orientation: PropTypes.oneOf(['horizontal', 'vertical']), - /** - * The component used to render the scroll buttons. - * @default TabScrollButton - */ - ScrollButtonComponent: PropTypes.elementType, - /** - * Determine behavior of scroll buttons when tabs are set to scroll: - * - * - `auto` will only present them when not all the items are visible. - * - `true` will always present them. - * - `false` will never present them. - * - * By default the scroll buttons are hidden on mobile. - * This behavior can be disabled with `allowScrollButtonsMobile`. - * @default 'auto' - */ - scrollButtons: PropTypes /* @typescript-to-proptypes-ignore */.oneOf(['auto', false, true]), - /** - * If `true` the selected tab changes on focus. Otherwise it only - * changes on activation. - */ - selectionFollowsFocus: PropTypes.bool, - /** - * 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, - ]), - /** - * Props applied to the tab indicator element. - * @default {} - */ - TabIndicatorProps: PropTypes.object, - /** - * Props applied to the [`TabScrollButton`](/material-ui/api/tab-scroll-button/) element. - * @default {} - */ - TabScrollButtonProps: PropTypes.object, - /** - * Determines the color of the `Tab`. - * @default 'primary' - */ - textColor: PropTypes.oneOf(['inherit', 'primary', 'secondary']), - /** - * The value of the currently selected `Tab`. - * If you don't want any selected `Tab`, you can set this prop to `false`. - */ - value: PropTypes.any, - /** - * Determines additional display behavior of the tabs: - * - * - `scrollable` will invoke scrolling properties and allow for horizontally - * scrolling (or swiping) of the tab bar. - * -`fullWidth` will make the tabs grow to use all the available space, - * which should be used for small views, like on mobile. - * - `standard` will render the default state. - * @default 'standard' - */ - variant: PropTypes.oneOf(['fullWidth', 'scrollable', 'standard']), - /** - * If `true`, the scrollbar is visible. It can be useful when displaying - * a long vertical list of tabs. - * @default false - */ - visibleScrollbar: PropTypes.bool, -}; - -export default Tabs; diff --git a/packages/mui-material-next/src/Tabs/Tabs.spec.tsx b/packages/mui-material-next/src/Tabs/Tabs.spec.tsx deleted file mode 100644 index 4eb1ec9514991a..00000000000000 --- a/packages/mui-material-next/src/Tabs/Tabs.spec.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import * as React from 'react'; -import Tabs from '@mui/material-next/Tabs'; - -function testOnChange() { - function handleTabsChange(event: React.SyntheticEvent, tabsValue: unknown) {} - <Tabs onChange={handleTabsChange} />; - - function handleElementChange(event: React.ChangeEvent) {} - <Tabs - // @ts-expect-error internally it's either FocusEvent or ClickEvent - onChange={handleElementChange} - />; -} diff --git a/packages/mui-material-next/src/Tabs/Tabs.test.js b/packages/mui-material-next/src/Tabs/Tabs.test.js deleted file mode 100644 index bb2a317cd9da8d..00000000000000 --- a/packages/mui-material-next/src/Tabs/Tabs.test.js +++ /dev/null @@ -1,1314 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { spy } from 'sinon'; -import { - act, - createRenderer, - fireEvent, - screen, - strictModeDoubleLoggingSuppressed, -} from '@mui-internal/test-utils'; -import { createTheme, ThemeProvider } from '@mui/material/styles'; -import { unstable_capitalize as capitalize } from '@mui/utils'; -import Tab from '@mui/material-next/Tab'; -import Tabs, { tabsClasses as classes } from '@mui/material-next/Tabs'; -import describeConformance from '../../test/describeConformance'; - -function findScrollButton(container, direction) { - return container.querySelector(`svg[data-testid="KeyboardArrow${capitalize(direction)}Icon"]`); -} - -function hasLeftScrollButton(container) { - const scrollButton = findScrollButton(container, 'left'); - - if (!scrollButton) { - return false; - } - - return !scrollButton.parentElement.classList.contains('Mui-disabled'); -} - -function hasRightScrollButton(container) { - const scrollButton = findScrollButton(container, 'right'); - - if (!scrollButton) { - return false; - } - - return !scrollButton.parentElement.classList.contains('Mui-disabled'); -} - -describe('<Tabs />', () => { - // tests mocking getBoundingClientRect prevent mocha to exit - const isJSDOM = navigator.userAgent === 'node.js'; - - const { clock, render, renderToString } = createRenderer(); - - before(function beforeHook() { - const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); - - // The test fails on Safari with just: - // - // container.scrollLeft = 200; - // expect(container.scrollLeft).to.equal(200); 💥 - if (isSafari) { - this.skip(); - } - }); - - describeConformance(<Tabs value={0} />, () => ({ - classes, - inheritComponent: 'div', - render, - muiName: 'MuiTabs', - refInstanceof: window.HTMLDivElement, - testComponentPropWith: 'header', - testStateOverrides: { prop: 'orientation', value: 'vertical', styleKey: 'vertical' }, - skip: ['componentsProp', 'themeVariants'], - })); - - it('can be named via `aria-label`', () => { - render(<Tabs aria-label="string label" />); - - expect(screen.getByRole('tablist')).toHaveAccessibleName('string label'); - }); - - it('can be named via `aria-labelledby`', () => { - render( - <React.Fragment> - <h3 id="label-id">complex name</h3> - <Tabs aria-labelledby="label-id" /> - </React.Fragment>, - ); - - expect(screen.getByRole('tablist')).toHaveAccessibleName('complex name'); - }); - - describe('warnings', () => { - it('should warn if the input is invalid', () => { - expect(() => { - render(<Tabs value={0} centered variant="scrollable" />); - }).toErrorDev([ - 'MUI: You can not use the `centered={true}` and `variant="scrollable"`', - !strictModeDoubleLoggingSuppressed && - 'MUI: You can not use the `centered={true}` and `variant="scrollable"`', - 'MUI: You can not use the `centered={true}` and `variant="scrollable"`', - !strictModeDoubleLoggingSuppressed && - 'MUI: You can not use the `centered={true}` and `variant="scrollable"`', - ]); - }); - }); - - describe('prop: action', () => { - it('should be able to access updateIndicator function', () => { - let tabsActions = {}; - render( - <Tabs - value={0} - action={(actions) => { - tabsActions = actions; - }} - > - <Tab /> - <Tab /> - </Tabs>, - ); - - expect(typeof tabsActions.updateIndicator).to.equal('function'); - tabsActions.updateIndicator(); - }); - }); - - describe('prop: centered', () => { - it('should render with the centered class', () => { - const { container } = render( - <Tabs value={0} centered> - <Tab /> - <Tab /> - </Tabs>, - ); - const selector = `.${classes.flexContainer}.${classes.centered}`; - expect(container.querySelector(selector).nodeName).to.equal('DIV'); - }); - }); - - describe('prop: children', () => { - it('should accept a null child', () => { - const { getAllByRole } = render( - <Tabs value={0}> - {null} - <Tab /> - </Tabs>, - ); - expect(getAllByRole('tab')).to.have.lengthOf(1); - }); - - it('should support empty children', () => { - render(<Tabs value={1} />); - }); - - it('puts the selected child in tab order', () => { - const { getAllByRole, setProps } = render( - <Tabs value={1}> - <Tab /> - <Tab /> - </Tabs>, - ); - - expect(getAllByRole('tab').map((tab) => tab.tabIndex)).to.have.ordered.members([-1, 0]); - - setProps({ value: 0 }); - - expect(getAllByRole('tab').map((tab) => tab.tabIndex)).to.have.ordered.members([0, -1]); - }); - }); - - describe('prop: value', () => { - const tabs = ( - <Tabs value={1}> - <Tab /> - <Tab /> - </Tabs> - ); - - it('should pass selected prop to children', () => { - const { getAllByRole } = render(tabs); - const tabElements = getAllByRole('tab'); - expect(tabElements[0]).to.have.attribute('aria-selected', 'false'); - expect(tabElements[1]).to.have.attribute('aria-selected', 'true'); - }); - - it('should accept any value as selected tab value', () => { - const tab0 = {}; - const tab1 = {}; - expect(tab0).not.to.equal(tab1); - - const { getAllByRole } = render( - <Tabs value={tab0}> - <Tab value={tab0} /> - <Tab value={tab1} /> - </Tabs>, - ); - const tabElements = getAllByRole('tab'); - expect(tabElements[0]).to.have.attribute('aria-selected', 'true'); - expect(tabElements[1]).to.have.attribute('aria-selected', 'false'); - }); - - describe('indicator', () => { - it('should accept a null value', () => { - const { container } = render( - <Tabs value={null}> - <Tab /> - <Tab /> - </Tabs>, - ); - expect(container.querySelector(`.${classes.indicator}`).style.width).to.equal('0px'); - }); - - it('should render the indicator', () => { - const { container, getAllByRole } = render( - <Tabs value={1}> - <Tab /> - <Tab /> - </Tabs>, - ); - const tabElements = getAllByRole('tab'); - expect(tabElements[0].querySelector(`.${classes.indicator}`)).to.equal(null); - expect(tabElements[1].querySelector(`.${classes.indicator}`)).to.equal(null); - expect(container.querySelector(`.${classes.indicator}`)).not.to.equal(null); - }); - - it('should update the indicator at each render', function test() { - if (isJSDOM) { - this.skip(); - } - - const { forceUpdate, container, getByRole } = render( - <Tabs value={1}> - <Tab /> - <Tab /> - </Tabs>, - ); - const tablistContainer = getByRole('tablist').parentElement; - const tab = getByRole('tablist').children[1]; - - Object.defineProperty(tablistContainer, 'clientWidth', { value: 100 }); - Object.defineProperty(tablistContainer, 'scrollWidth', { value: 100 }); - tablistContainer.getBoundingClientRect = () => ({ - left: 0, - right: 100, - }); - tab.getBoundingClientRect = () => ({ - left: 50, - width: 50, - right: 100, - }); - forceUpdate(); - let style; - style = container.querySelector(`.${classes.indicator}`).style; - expect(style.left).to.equal('50px'); - expect(style.width).to.equal('50px'); - tab.getBoundingClientRect = () => ({ - left: 60, - width: 50, - right: 110, - }); - forceUpdate(); - style = container.querySelector(`.${classes.indicator}`).style; - expect(style.left).to.equal('60px'); - expect(style.width).to.equal('50px'); - }); - - it('should have "right" for RTL', () => { - const { forceUpdate, container, getByRole } = render( - <div dir="rtl"> - <Tabs value={1}> - <Tab /> - <Tab /> - </Tabs> - </div>, - { - wrapper: ({ children }) => ( - <ThemeProvider theme={createTheme({ direction: 'rtl' })}>{children}</ThemeProvider> - ), - }, - ); - - const tablistContainer = getByRole('tablist').parentElement; - const tab = getByRole('tablist').children[1]; - - Object.defineProperty(tablistContainer, 'clientWidth', { value: 100 }); - Object.defineProperty(tablistContainer, 'scrollWidth', { value: 100 }); - tablistContainer.getBoundingClientRect = () => ({ - left: 0, - right: 100, - }); - tab.getBoundingClientRect = () => ({ - left: 50, - width: 50, - right: 100, - }); - forceUpdate(); - expect(container.querySelector(`.${classes.indicator}`)).toHaveInlineStyle({ - right: '0px', - width: '50px', - }); - tab.getBoundingClientRect = () => ({ - left: 40, - width: 50, - right: 90, - }); - forceUpdate(); - expect(container.querySelector(`.${classes.indicator}`)).toHaveInlineStyle({ - right: '10px', - width: '50px', - }); - }); - }); - - describe('warnings', () => { - it('warns when the value is not present in any tab', () => { - expect(() => { - render( - <Tabs value={2}> - <Tab value={1} /> - <Tab value={3} /> - </Tabs>, - ); - }).toErrorDev([ - 'You can provide one of the following values: 1, 3', - // React 18 Strict Effects run mount effects twice - React.version.startsWith('18') && 'You can provide one of the following values: 1, 3', - 'You can provide one of the following values: 1, 3', - // React 18 Strict Effects run mount effects twice - React.version.startsWith('18') && 'You can provide one of the following values: 1, 3', - 'You can provide one of the following values: 1, 3', - 'You can provide one of the following values: 1, 3', - ]); - }); - - describe('hidden tab', () => { - let nodeEnv; - - before(function test() { - if (!/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - return; - } - - nodeEnv = process.env.NODE_ENV; - // We can't use a regular assignment, because it causes a syntax error in Karma - Object.defineProperty(process.env, 'NODE_ENV', { - value: 'development', - configurable: true, - writable: true, - enumerable: true, - }); - }); - - after(() => { - Object.defineProperty(process.env, 'NODE_ENV', { - value: nodeEnv, - configurable: true, - writable: true, - enumerable: true, - }); - }); - - it('should warn if a Tab has display: none', () => { - expect(() => { - render( - <Tabs value="hidden-tab"> - <Tab value="hidden-tab" style={{ display: 'none' }} /> - </Tabs>, - ); - }).toErrorDev([ - [ - 'MUI: The `value` provided to the Tabs component is invalid.', - 'The Tab with this `value` ("hidden-tab") is not part of the document layout.', - "Make sure the tab item is present in the document or that it's not `display: none`.", - ].join('\n'), - ]); - }); - }); - }); - }); - - describe('prop: onChange', () => { - it('should call onChange when clicking', () => { - const handleChange = spy(); - const { getAllByRole } = render( - <Tabs value={0} onChange={handleChange}> - <Tab /> - <Tab /> - </Tabs>, - ); - - fireEvent.click(getAllByRole('tab')[1]); - expect(handleChange.callCount).to.equal(1); - expect(handleChange.args[0][1]).to.equal(1); - }); - - it('should not call onChange when already selected', () => { - const handleChange = spy(); - const { getAllByRole } = render( - <Tabs value={0} onChange={handleChange}> - <Tab /> - <Tab /> - </Tabs>, - ); - - fireEvent.click(getAllByRole('tab')[0]); - expect(handleChange.callCount).to.equal(0); - }); - - it('when `selectionFollowsFocus` should call if an unselected tab gets focused', () => { - const handleChange = spy(); - const { getAllByRole } = render( - <Tabs value={1} onChange={handleChange} selectionFollowsFocus> - <Tab /> - <Tab /> - </Tabs>, - ); - const [, lastTab] = getAllByRole('tab'); - - act(() => { - lastTab.focus(); - }); - - fireEvent.keyDown(lastTab, { key: 'ArrowLeft' }); - - expect(handleChange.callCount).to.equal(1); - expect(handleChange.firstCall.args[1]).to.equal(0); - }); - - it('when `selectionFollowsFocus` should not call if an selected tab gets focused', () => { - const handleChange = spy(); - const { getAllByRole } = render( - <Tabs value={0} onChange={handleChange} selectionFollowsFocus> - <Tab /> - <Tab /> - </Tabs>, - ); - const [firstTab] = getAllByRole('tab'); - - act(() => { - firstTab.focus(); - }); - - expect(handleChange.callCount).to.equal(0); - }); - }); - - describe('prop: variant="scrollable"', () => { - clock.withFakeTimers(); - - const tabs = ( - <Tabs value={0} style={{ width: 200 }} variant="scrollable"> - <Tab style={{ width: 120, minWidth: 'auto' }} /> - <Tab style={{ width: 120, minWidth: 'auto' }} /> - <Tab style={{ width: 120, minWidth: 'auto' }} /> - </Tabs> - ); - - it('should render with the scrollable class', () => { - const { container } = render(tabs); - const selector = `.${classes.scroller}.${classes.scrollableX}`; - expect(container.querySelector(selector).tagName).to.equal('DIV'); - expect(container.querySelectorAll(selector)).to.have.lengthOf(1); - }); - - it('should response to scroll events', function test() { - if (isJSDOM) { - this.skip(); - } - const { container, forceUpdate, getByRole } = render(tabs); - const tablistContainer = getByRole('tablist').parentElement; - - Object.defineProperty(tablistContainer, 'clientWidth', { value: 200 - 40 * 2 }); - tablistContainer.scrollLeft = 10; - Object.defineProperty(tablistContainer, 'scrollWidth', { value: 216 }); - Object.defineProperty(tablistContainer, 'getBoundingClientRect', { - value: () => ({ - left: 0, - right: 50, - }), - }); - forceUpdate(); - act(() => { - clock.tick(1000); - }); - expect(hasLeftScrollButton(container)).to.equal(true); - expect(hasRightScrollButton(container)).to.equal(true); - tablistContainer.scrollLeft = 0; - fireEvent.scroll(container.querySelector(`.${classes.scroller}.${classes.scrollableX}`)); - act(() => { - clock.tick(166); - }); - - expect(hasLeftScrollButton(container)).to.equal(false); - expect(hasRightScrollButton(container)).to.equal(true); - }); - - it('should get a scrollbar size listener', () => { - const { setProps, getByRole } = render( - <Tabs value={0}> - <Tab /> - <Tab /> - </Tabs>, - ); - const tablistContainer = getByRole('tablist').parentElement; - expect(tablistContainer.style.overflow).to.equal('hidden'); - setProps({ - variant: 'scrollable', - }); - expect(tablistContainer.style.overflow).to.equal(''); - }); - }); - - describe('prop: !variant="scrollable"', () => { - it('should not render with the scrollable class', () => { - const { container } = render( - <Tabs value={0}> - <Tab /> - <Tab /> - </Tabs>, - ); - const baseSelector = `.${classes.scroller}`; - const selector = `.${classes.scroller}.${classes.scrollableX}`; - expect(container.querySelector(baseSelector)).not.to.equal(null); - expect(container.querySelector(selector)).to.equal(null); - }); - }); - - describe('prop: scrollButtons', () => { - clock.withFakeTimers(); - - it('should render scroll buttons', () => { - const { container } = render( - <Tabs value={0} variant="scrollable" scrollButtons> - <Tab /> - <Tab /> - </Tabs>, - ); - expect(container.querySelectorAll(`.${classes.scrollButtons}`)).to.have.lengthOf(2); - }); - - it('should append className from TabScrollButtonProps', () => { - const { container } = render( - <Tabs - value={0} - variant="scrollable" - scrollButtons - TabScrollButtonProps={{ className: 'foo' }} - > - <Tab /> - <Tab /> - </Tabs>, - ); - expect(container.querySelectorAll(`.${classes.scrollButtons}`)).to.have.lengthOf(2); - expect(container.querySelectorAll('.foo')).to.have.lengthOf(2); - }); - - it('should not hide scroll buttons when allowScrollButtonsMobile is true', () => { - const { container } = render( - <Tabs value={0} variant="scrollable" scrollButtons allowScrollButtonsMobile> - <Tab /> - <Tab /> - </Tabs>, - ); - - expect(container.querySelectorAll(`.${classes.scrollButtonsHideMobile}`)).to.have.lengthOf(0); - }); - - it('should handle window resize event', function test() { - if (isJSDOM) { - this.skip(); - } - - const { container, forceUpdate, getByRole } = render( - <Tabs value={0} variant="scrollable" scrollButtons style={{ width: 200 }}> - <Tab /> - <Tab /> - <Tab /> - </Tabs>, - ); - - const tablistContainer = getByRole('tablist').parentElement; - - Object.defineProperty(tablistContainer, 'clientWidth', { value: 200 - 40 * 2 }); - tablistContainer.scrollLeft = 10; - Object.defineProperty(tablistContainer, 'scrollWidth', { value: 216 }); - Object.defineProperty(tablistContainer, 'getBoundingClientRect', { - value: () => ({ - left: 0, - right: 100, - }), - }); - forceUpdate(); - act(() => { - clock.tick(1000); - }); - expect(hasLeftScrollButton(container)).to.equal(true); - expect(hasRightScrollButton(container)).to.equal(true); - tablistContainer.scrollLeft = 0; - - act(() => { - window.dispatchEvent(new window.Event('resize', {})); - clock.tick(166); - }); - - expect(hasLeftScrollButton(container)).to.equal(false); - expect(hasRightScrollButton(container)).to.equal(true); - }); - - describe('scroll button visibility states', () => { - it('should set neither left nor right scroll button state', () => { - const { container, forceUpdate, getByRole } = render( - <Tabs value={0} variant="scrollable" scrollButtons style={{ width: 200 }}> - <Tab style={{ width: 50, minWidth: 'auto' }} /> - <Tab style={{ width: 50, minWidth: 'auto' }} /> - </Tabs>, - ); - const tablistContainer = getByRole('tablist').parentElement; - - Object.defineProperty(tablistContainer, 'clientWidth', { value: 200 - 40 * 2 }); - Object.defineProperty(tablistContainer, 'scrollWidth', { value: 200 - 40 * 2 }); - - forceUpdate(); - expect(hasLeftScrollButton(container)).to.equal(false); - expect(hasRightScrollButton(container)).to.equal(false); - }); - - it('should set only left scroll button state', () => { - const { container, forceUpdate, getByRole } = render( - <Tabs value={0} variant="scrollable" scrollButtons style={{ width: 200 }}> - <Tab style={{ width: 120, minWidth: 'auto' }} /> - <Tab style={{ width: 120, minWidth: 'auto' }} /> - <Tab style={{ width: 120, minWidth: 'auto' }} /> - </Tabs>, - ); - const tablistContainer = getByRole('tablist').parentElement; - - Object.defineProperty(tablistContainer, 'clientWidth', { value: 200 - 40 * 2 }); - Object.defineProperty(tablistContainer, 'scrollWidth', { value: 216 }); - tablistContainer.scrollLeft = 96; - - forceUpdate(); - expect(hasLeftScrollButton(container)).to.equal(true); - expect(hasRightScrollButton(container)).to.equal(false); - }); - - it('should set only right scroll button state', () => { - const { container, forceUpdate, getByRole } = render( - <Tabs value={0} variant="scrollable" scrollButtons style={{ width: 200 }}> - <Tab /> - <Tab /> - <Tab /> - </Tabs>, - ); - const tablistContainer = getByRole('tablist').parentElement; - - Object.defineProperty(tablistContainer, 'clientWidth', { value: 200 - 40 * 2 }); - Object.defineProperty(tablistContainer, 'scrollWidth', { value: 216 }); - tablistContainer.scrollLeft = 0; - - forceUpdate(); - expect(hasLeftScrollButton(container)).to.equal(false); - expect(hasRightScrollButton(container)).to.equal(true); - }); - - it('should set both left and right scroll button state', () => { - const { container, forceUpdate, getByRole } = render( - <Tabs value={0} variant="scrollable" scrollButtons style={{ width: 200 }}> - <Tab style={{ width: 120, minWidth: 'auto' }} /> - <Tab style={{ width: 120, minWidth: 'auto' }} /> - </Tabs>, - ); - const tablistContainer = getByRole('tablist').parentElement; - - Object.defineProperty(tablistContainer, 'clientWidth', { value: 200 - 40 * 2 }); - Object.defineProperty(tablistContainer, 'scrollWidth', { value: 216 }); - tablistContainer.scrollLeft = 5; - - forceUpdate(); - expect(hasLeftScrollButton(container)).to.equal(true); - expect(hasRightScrollButton(container)).to.equal(true); - }); - }); - }); - - describe('scroll button behavior', () => { - clock.withFakeTimers(); - - it('should scroll visible items', () => { - const { container, forceUpdate, getByRole, getAllByRole } = render( - <Tabs value={0} variant="scrollable" scrollButtons style={{ width: 200 }}> - <Tab style={{ width: 100, minWidth: 'auto' }} /> - <Tab style={{ width: 50, minWidth: 'auto' }} /> - <Tab style={{ width: 100, minWidth: 'auto' }} /> - </Tabs>, - ); - const tablistContainer = getByRole('tablist').parentElement; - const tabs = getAllByRole('tab'); - Object.defineProperty(tablistContainer, 'clientWidth', { value: 200 - 40 * 2 }); - Object.defineProperty(tabs[0], 'clientWidth', { value: 100 }); - Object.defineProperty(tabs[1], 'clientWidth', { value: 50 }); - Object.defineProperty(tabs[2], 'clientWidth', { value: 100 }); - Object.defineProperty(tablistContainer, 'scrollWidth', { value: 100 + 50 + 100 }); - tablistContainer.scrollLeft = 20; - forceUpdate(); - act(() => { - clock.tick(1000); - }); - expect(hasLeftScrollButton(container)).to.equal(true); - expect(hasRightScrollButton(container)).to.equal(true); - - fireEvent.click(findScrollButton(container, 'left')); - act(() => { - clock.tick(1000); - }); - expect(tablistContainer.scrollLeft).not.to.be.above(0); - - tablistContainer.scrollLeft = 0; - fireEvent.click(findScrollButton(container, 'right')); - act(() => { - clock.tick(1000); - }); - expect(tablistContainer.scrollLeft).equal(100); - }); - }); - - describe('scroll into view behavior', () => { - clock.withFakeTimers(); - - it('should scroll left tab into view', function test() { - if (isJSDOM) { - this.skip(); - } - - const { forceUpdate, getByRole } = render( - <Tabs value={0} variant="scrollable" style={{ width: 200 }}> - <Tab style={{ width: 120, minWidth: 'auto' }} /> - <Tab style={{ width: 120, minWidth: 'auto' }} /> - <Tab style={{ width: 120, minWidth: 'auto' }} /> - </Tabs>, - ); - const tablist = getByRole('tablist'); - const tablistContainer = tablist.parentElement; - const tab = tablist.children[0]; - - Object.defineProperty(tablistContainer, 'clientWidth', { value: 200 - 40 * 2 }); - Object.defineProperty(tablistContainer, 'scrollWidth', { value: 216 }); - tablistContainer.scrollLeft = 20; - tablistContainer.getBoundingClientRect = () => ({ - left: 0, - right: 100, - }); - tab.getBoundingClientRect = () => ({ - left: -20, - width: 50, - right: 30, - }); - forceUpdate(); - act(() => { - clock.tick(1000); - }); - expect(tablistContainer.scrollLeft).to.equal(0); - }); - }); - - describe('prop: TabIndicatorProps', () => { - it('should merge the style', () => { - const { container } = render( - <Tabs value={0} TabIndicatorProps={{ style: { backgroundColor: 'green' } }}> - <Tab /> - </Tabs>, - ); - const style = container.querySelector(`.${classes.indicator}`).style; - expect(style.backgroundColor).to.equal('green'); - }); - }); - - describe('prop: orientation', () => { - it('should support orientation="vertical"', function test() { - if (isJSDOM) { - this.skip(); - } - - const { forceUpdate, container, getByRole } = render( - <Tabs value={1} variant="scrollable" scrollButtons orientation="vertical"> - <Tab /> - <Tab /> - </Tabs>, - ); - const tablist = getByRole('tablist'); - const tablistContainer = tablist.parentElement; - const tab = tablist.children[1]; - - Object.defineProperty(tablistContainer, 'clientHeight', { value: 100 }); - Object.defineProperty(tablistContainer, 'scrollHeight', { value: 100 }); - tablistContainer.getBoundingClientRect = () => ({ - top: 0, - bottom: 100, - }); - tab.getBoundingClientRect = () => ({ - top: 50, - height: 50, - bottom: 100, - }); - forceUpdate(); - let style; - style = container.querySelector(`.${classes.indicator}`).style; - expect(style.top).to.equal('50px'); - expect(style.height).to.equal('50px'); - tab.getBoundingClientRect = () => ({ - top: 60, - height: 50, - bottom: 110, - }); - forceUpdate(); - style = container.querySelector(`.${classes.indicator}`).style; - expect(style.top).to.equal('60px'); - expect(style.height).to.equal('50px'); - }); - - it('does not add aria-orientation by default', () => { - render(<Tabs value={0} />); - - expect(screen.getByRole('tablist')).not.to.have.attribute('aria-orientation'); - }); - - it('adds the proper aria-orientation when vertical', () => { - render(<Tabs value={0} orientation="vertical" />); - - expect(screen.getByRole('tablist')).to.have.attribute('aria-orientation', 'vertical'); - }); - }); - - describe('server-side render', () => { - it('should let the selected <Tab /> render the indicator server-side', () => { - const { container } = renderToString( - <Tabs value={1}> - <Tab value={0} /> - <Tab value={1} /> - </Tabs>, - ); - - const indicator = container.firstChild.querySelectorAll(`button > .${classes.indicator}`); - expect(indicator).to.have.lengthOf(1); - }); - }); - - describe('keyboard navigation when focus is on a tab', () => { - [ - ['horizontal', 'ltr', 'ArrowLeft', 'ArrowRight'], - ['horizontal', 'rtl', 'ArrowRight', 'ArrowLeft'], - ['vertical', undefined, 'ArrowUp', 'ArrowDown'], - ].forEach((entry) => { - const [orientation, direction, previousItemKey, nextItemKey] = entry; - - let wrapper; - before(() => { - const theme = createTheme({ direction }); - wrapper = ({ children }) => <ThemeProvider theme={theme}>{children}</ThemeProvider>; - }); - - describe(`when focus is on a tab element in a ${orientation} ${direction} tablist`, () => { - describe(previousItemKey, () => { - it('moves focus to the last tab without activating it if focus is on the first tab', () => { - const handleChange = spy(); - const handleKeyDown = spy(); - const { getAllByRole } = render( - <Tabs - onChange={handleChange} - onKeyDown={handleKeyDown} - orientation={orientation} - value={0} - > - <Tab /> - <Tab /> - <Tab /> - </Tabs>, - { wrapper }, - ); - const [firstTab, , lastTab] = getAllByRole('tab'); - act(() => { - firstTab.focus(); - }); - - fireEvent.keyDown(firstTab, { key: previousItemKey }); - - expect(lastTab).toHaveFocus(); - expect(handleChange.callCount).to.equal(0); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('when `selectionFollowsFocus` moves focus to the last tab while activating it if focus is on the first tab', () => { - const handleChange = spy(); - const handleKeyDown = spy(); - const { getAllByRole } = render( - <Tabs - onChange={handleChange} - onKeyDown={handleKeyDown} - orientation={orientation} - selectionFollowsFocus - value={0} - > - <Tab /> - <Tab /> - <Tab /> - </Tabs>, - { wrapper }, - ); - const [firstTab, , lastTab] = getAllByRole('tab'); - act(() => { - firstTab.focus(); - }); - - fireEvent.keyDown(firstTab, { key: previousItemKey }); - - expect(lastTab).toHaveFocus(); - expect(handleChange.callCount).to.equal(1); - expect(handleChange.firstCall.args[1]).to.equal(2); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('moves focus to the previous tab without activating it', () => { - const handleChange = spy(); - const handleKeyDown = spy(); - const { getAllByRole } = render( - <Tabs - onChange={handleChange} - onKeyDown={handleKeyDown} - orientation={orientation} - value={1} - > - <Tab /> - <Tab /> - <Tab /> - </Tabs>, - { wrapper }, - ); - const [firstTab, secondTab] = getAllByRole('tab'); - act(() => { - secondTab.focus(); - }); - - fireEvent.keyDown(secondTab, { key: previousItemKey }); - - expect(firstTab).toHaveFocus(); - expect(handleChange.callCount).to.equal(0); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('when `selectionFollowsFocus` moves focus to the previous tab while activating it', () => { - const handleChange = spy(); - const handleKeyDown = spy(); - const { getAllByRole } = render( - <Tabs - onChange={handleChange} - onKeyDown={handleKeyDown} - orientation={orientation} - selectionFollowsFocus - value={1} - > - <Tab /> - <Tab /> - <Tab /> - </Tabs>, - { wrapper }, - ); - const [firstTab, secondTab] = getAllByRole('tab'); - act(() => { - secondTab.focus(); - }); - - fireEvent.keyDown(secondTab, { key: previousItemKey }); - - expect(firstTab).toHaveFocus(); - expect(handleChange.callCount).to.equal(1); - expect(handleChange.firstCall.args[1]).to.equal(0); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('skips over disabled tabs', () => { - const handleKeyDown = spy(); - const { getAllByRole } = render( - <Tabs - onKeyDown={handleKeyDown} - orientation={orientation} - selectionFollowsFocus - value={1} - > - <Tab /> - <Tab disabled /> - <Tab /> - </Tabs>, - { wrapper }, - ); - const [firstTab, , lastTab] = getAllByRole('tab'); - act(() => { - lastTab.focus(); - }); - - fireEvent.keyDown(lastTab, { key: previousItemKey }); - - expect(firstTab).toHaveFocus(); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - }); - - describe(nextItemKey, () => { - it('moves focus to the first tab without activating it if focus is on the last tab', () => { - const handleChange = spy(); - const handleKeyDown = spy(); - const { getAllByRole } = render( - <Tabs - onChange={handleChange} - onKeyDown={handleKeyDown} - orientation={orientation} - value={2} - > - <Tab /> - <Tab /> - <Tab /> - </Tabs>, - { wrapper }, - ); - const [firstTab, , lastTab] = getAllByRole('tab'); - act(() => { - lastTab.focus(); - }); - - fireEvent.keyDown(lastTab, { key: nextItemKey }); - - expect(firstTab).toHaveFocus(); - expect(handleChange.callCount).to.equal(0); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('when `selectionFollowsFocus` moves focus to the first tab while activating it if focus is on the last tab', () => { - const handleChange = spy(); - const handleKeyDown = spy(); - const { getAllByRole } = render( - <Tabs - onChange={handleChange} - onKeyDown={handleKeyDown} - orientation={orientation} - selectionFollowsFocus - value={2} - > - <Tab /> - <Tab /> - <Tab /> - </Tabs>, - { wrapper }, - ); - const [firstTab, , lastTab] = getAllByRole('tab'); - act(() => { - lastTab.focus(); - }); - - fireEvent.keyDown(lastTab, { key: nextItemKey }); - - expect(firstTab).toHaveFocus(); - expect(handleChange.callCount).to.equal(1); - expect(handleChange.firstCall.args[1]).to.equal(0); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('moves focus to the next tab without activating it it', () => { - const handleChange = spy(); - const handleKeyDown = spy(); - const { getAllByRole } = render( - <Tabs - onChange={handleChange} - onKeyDown={handleKeyDown} - orientation={orientation} - value={1} - > - <Tab /> - <Tab /> - <Tab /> - </Tabs>, - { wrapper }, - ); - const [, secondTab, lastTab] = getAllByRole('tab'); - act(() => { - secondTab.focus(); - }); - - fireEvent.keyDown(secondTab, { key: nextItemKey }); - - expect(lastTab).toHaveFocus(); - expect(handleChange.callCount).to.equal(0); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('when `selectionFollowsFocus` moves focus to the next tab while activating it it', () => { - const handleChange = spy(); - const handleKeyDown = spy(); - const { getAllByRole } = render( - <Tabs - onChange={handleChange} - onKeyDown={handleKeyDown} - orientation={orientation} - selectionFollowsFocus - value={1} - > - <Tab /> - <Tab /> - <Tab /> - </Tabs>, - { wrapper }, - ); - const [, secondTab, lastTab] = getAllByRole('tab'); - act(() => { - secondTab.focus(); - }); - - fireEvent.keyDown(secondTab, { key: nextItemKey }); - - expect(lastTab).toHaveFocus(); - expect(handleChange.callCount).to.equal(1); - expect(handleChange.firstCall.args[1]).to.equal(2); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('skips over disabled tabs', () => { - const handleKeyDown = spy(); - const { getAllByRole } = render( - <Tabs - onKeyDown={handleKeyDown} - orientation={orientation} - selectionFollowsFocus - value={1} - > - <Tab /> - <Tab disabled /> - <Tab /> - </Tabs>, - { wrapper }, - ); - const [firstTab, , lastTab] = getAllByRole('tab'); - act(() => { - firstTab.focus(); - }); - - fireEvent.keyDown(firstTab, { key: nextItemKey }); - - expect(lastTab).toHaveFocus(); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - }); - }); - }); - - describe('when focus is on a tab regardless of orientation', () => { - describe('Home', () => { - it('moves focus to the first tab without activating it', () => { - const handleChange = spy(); - const handleKeyDown = spy(); - const { getAllByRole } = render( - <Tabs onChange={handleChange} onKeyDown={handleKeyDown} value={1}> - <Tab /> - <Tab /> - <Tab /> - </Tabs>, - ); - const [firstTab, , lastTab] = getAllByRole('tab'); - act(() => { - lastTab.focus(); - }); - - fireEvent.keyDown(lastTab, { key: 'Home' }); - - expect(firstTab).toHaveFocus(); - expect(handleChange.callCount).to.equal(0); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('when `selectionFollowsFocus` moves focus to the first tab without activating it', () => { - const handleChange = spy(); - const handleKeyDown = spy(); - const { getAllByRole } = render( - <Tabs onChange={handleChange} onKeyDown={handleKeyDown} selectionFollowsFocus value={2}> - <Tab /> - <Tab /> - <Tab /> - </Tabs>, - ); - const [firstTab, , lastTab] = getAllByRole('tab'); - act(() => { - lastTab.focus(); - }); - - fireEvent.keyDown(lastTab, { key: 'Home' }); - - expect(firstTab).toHaveFocus(); - expect(handleChange.callCount).to.equal(1); - expect(handleChange.firstCall.args[1]).to.equal(0); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('moves focus to first non-disabled tab', () => { - const handleKeyDown = spy(); - const { getAllByRole } = render( - <Tabs onKeyDown={handleKeyDown} selectionFollowsFocus value={2}> - <Tab disabled /> - <Tab /> - <Tab /> - </Tabs>, - ); - const [, secondTab, lastTab] = getAllByRole('tab'); - act(() => { - lastTab.focus(); - }); - - fireEvent.keyDown(lastTab, { key: 'Home' }); - - expect(secondTab).toHaveFocus(); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - }); - - describe('End', () => { - it('moves focus to the last tab without activating it', () => { - const handleChange = spy(); - const handleKeyDown = spy(); - const { getAllByRole } = render( - <Tabs onChange={handleChange} onKeyDown={handleKeyDown} value={1}> - <Tab /> - <Tab /> - <Tab /> - </Tabs>, - ); - const [firstTab, , lastTab] = getAllByRole('tab'); - act(() => { - firstTab.focus(); - }); - - fireEvent.keyDown(firstTab, { key: 'End' }); - - expect(lastTab).toHaveFocus(); - expect(handleChange.callCount).to.equal(0); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('when `selectionFollowsFocus` moves focus to the last tab without activating it', () => { - const handleChange = spy(); - const handleKeyDown = spy(); - const { getAllByRole } = render( - <Tabs onChange={handleChange} onKeyDown={handleKeyDown} selectionFollowsFocus value={0}> - <Tab /> - <Tab /> - <Tab /> - </Tabs>, - ); - const [firstTab, , lastTab] = getAllByRole('tab'); - act(() => { - firstTab.focus(); - }); - - fireEvent.keyDown(firstTab, { key: 'End' }); - - expect(lastTab).toHaveFocus(); - expect(handleChange.callCount).to.equal(1); - expect(handleChange.firstCall.args[1]).to.equal(2); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('moves focus to first non-disabled tab', () => { - const handleKeyDown = spy(); - const { getAllByRole } = render( - <Tabs onKeyDown={handleKeyDown} selectionFollowsFocus value={2}> - <Tab /> - <Tab /> - <Tab disabled /> - </Tabs>, - ); - const [firstTab, secondTab] = getAllByRole('tab'); - act(() => { - firstTab.focus(); - }); - - fireEvent.keyDown(firstTab, { key: 'End' }); - - expect(secondTab).toHaveFocus(); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - }); - }); - - it('should allow to focus first tab when there are no active tabs', () => { - const { getAllByRole } = render( - <Tabs value={null}> - <Tab /> - <Tab /> - </Tabs>, - ); - - expect(getAllByRole('tab').map((tab) => tab.getAttribute('tabIndex'))).to.deep.equal([ - '0', - '-1', - ]); - }); - }); -}); diff --git a/packages/mui-material-next/src/Tabs/TabsList.js b/packages/mui-material-next/src/Tabs/TabsList.js deleted file mode 100644 index 66a46e8c2149ab..00000000000000 --- a/packages/mui-material-next/src/Tabs/TabsList.js +++ /dev/null @@ -1,64 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { styled } from '@mui/material/styles'; -import { useTabsList, TabsListProvider } from '@mui/base/useTabsList'; -import TabsListContext from './TabsListContext'; - -const FlexContainer = styled('div', { - name: 'MuiTabs', - slot: 'FlexContainer', - overridesResolver: (props, styles) => { - const { ownerState } = props; - return [ - styles.flexContainer, - ownerState.vertical && styles.flexContainerVertical, - ownerState.centered && styles.centered, - ]; - }, -})(({ ownerState }) => ({ - display: 'flex', - ...(ownerState.vertical && { - flexDirection: 'column', - }), - ...(ownerState.centered && { - justifyContent: 'center', - }), -})); - -/** - * @ignore - internal component. - */ -const TabsList = React.forwardRef((props, ref) => { - const { variant, indicator, textColor, children, ...other } = props; - - const { getRootProps, contextValue } = useTabsList({ ...props, rootRef: ref }); - - const tabsListContextValue = React.useMemo( - () => ({ - indicator, - textColor, - fullWidth: variant === 'fullWidth', - }), - [indicator, textColor, variant], - ); - - return ( - <TabsListProvider value={contextValue}> - <TabsListContext.Provider value={tabsListContextValue}> - <FlexContainer {...other} {...getRootProps()}> - {children} - </FlexContainer> - </TabsListContext.Provider> - </TabsListProvider> - ); -}); - -TabsList.propTypes = { - children: PropTypes.node, - indicator: PropTypes.node, - textColor: PropTypes.oneOf(['primary', 'secondary']), - variant: PropTypes.oneOf(['fullWidth', 'scrollable', 'standard']), -}; - -export default TabsList; diff --git a/packages/mui-material-next/src/Tabs/TabsListContext.js b/packages/mui-material-next/src/Tabs/TabsListContext.js deleted file mode 100644 index 43208b9b7f0da4..00000000000000 --- a/packages/mui-material-next/src/Tabs/TabsListContext.js +++ /dev/null @@ -1,10 +0,0 @@ -'use client'; -import * as React from 'react'; - -const TabsListContext = React.createContext(null); - -if (process.env.NODE_ENV !== 'production') { - TabsListContext.displayName = 'TabsListContext'; -} - -export default TabsListContext; diff --git a/packages/mui-material-next/src/Tabs/index.d.ts b/packages/mui-material-next/src/Tabs/index.d.ts deleted file mode 100644 index b2e190bf9420fe..00000000000000 --- a/packages/mui-material-next/src/Tabs/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './Tabs'; -export * from './Tabs'; - -export { default as tabsClasses } from './tabsClasses'; -export * from './tabsClasses'; diff --git a/packages/mui-material-next/src/Tabs/index.js b/packages/mui-material-next/src/Tabs/index.js deleted file mode 100644 index e026a0ff9036ed..00000000000000 --- a/packages/mui-material-next/src/Tabs/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './Tabs'; - -export { default as tabsClasses } from './tabsClasses'; -export * from './tabsClasses'; diff --git a/packages/mui-material-next/src/Tabs/tabsClasses.ts b/packages/mui-material-next/src/Tabs/tabsClasses.ts deleted file mode 100644 index c97a936cd7db0e..00000000000000 --- a/packages/mui-material-next/src/Tabs/tabsClasses.ts +++ /dev/null @@ -1,55 +0,0 @@ -import generateUtilityClass from '@mui/utils/generateUtilityClass'; -import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; - -export interface TabsClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `orientation="vertical"`. */ - vertical: string; - /** Styles applied to the flex container element. */ - flexContainer: string; - /** Styles applied to the flex container element if `orientation="vertical"`. */ - flexContainerVertical: string; - /** Styles applied to the flex container element if `centered={true}` & `!variant="scrollable"`. */ - centered: string; - /** Styles applied to the tablist element. */ - scroller: string; - /** Styles applied to the tablist element if `!variant="scrollable"`. */ - fixed: string; - /** Styles applied to the tablist element if `variant="scrollable"` and `orientation="horizontal"`. */ - scrollableX: string; - /** Styles applied to the tablist element if `variant="scrollable"` and `orientation="vertical"`. */ - scrollableY: string; - /** Styles applied to the tablist element if `variant="scrollable"` and `visibleScrollbar={false}`. */ - hideScrollbar: string; - /** Styles applied to the ScrollButtonComponent component. */ - scrollButtons: string; - /** Styles applied to the ScrollButtonComponent component if `allowScrollButtonsMobile={true}`. */ - scrollButtonsHideMobile: string; - /** Styles applied to the TabIndicator component. */ - indicator: string; -} - -export type TabsClassKey = keyof TabsClasses; - -export function getTabsUtilityClass(slot: string): string { - return generateUtilityClass('MuiTabs', slot); -} - -const tabsClasses: TabsClasses = generateUtilityClasses('MuiTabs', [ - 'root', - 'vertical', - 'flexContainer', - 'flexContainerVertical', - 'centered', - 'scroller', - 'fixed', - 'scrollableX', - 'scrollableY', - 'hideScrollbar', - 'scrollButtons', - 'scrollButtonsHideMobile', - 'indicator', -]); - -export default tabsClasses; diff --git a/packages/mui-material-next/src/index.test.js b/packages/mui-material-next/src/index.test.js deleted file mode 100644 index 65c8491e0f4266..00000000000000 --- a/packages/mui-material-next/src/index.test.js +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint import/namespace: ['error', { allowComputed: true }] */ -/** - * Important: This test also serves as a point to - * import the entire lib for coverage reporting - */ -import { expect } from 'chai'; -import * as materialNext from './index'; - -describe('@mui/material-next', () => { - it('should have exports', () => { - expect(typeof materialNext).to.equal('object'); - }); - - it('should not have undefined exports', () => { - Object.keys(materialNext).forEach((exportKey) => - expect(Boolean(materialNext[exportKey])).to.equal(true), - ); - }); -}); diff --git a/packages/mui-material-next/src/index.ts b/packages/mui-material-next/src/index.ts deleted file mode 100644 index c8726df41789c2..00000000000000 --- a/packages/mui-material-next/src/index.ts +++ /dev/null @@ -1,93 +0,0 @@ -'use client'; - -export { default as Badge } from './Badge'; -export * from './Badge'; - -export { default as Button } from './Button'; -export * from './Button'; - -export { default as ButtonBase } from './ButtonBase'; -export * from './ButtonBase'; - -export { default as Chip } from './Chip'; -export * from './Chip'; - -export { default as FilledInput } from './FilledInput'; -export * from './FilledInput'; - -export { default as FormControl } from './FormControl'; -export * from './FormControl'; - -export { default as FormHelperText } from './FormHelperText'; -export * from './FormHelperText'; - -export { default as FormLabel } from './FormLabel'; -export * from './FormLabel'; - -export { default as InputAdornment } from './InputAdornment'; -export * from './InputAdornment'; - -export { default as InputBase } from './InputBase'; -export * from './InputBase'; - -export { default as InputLabel } from './InputLabel'; -export * from './InputLabel'; - -export { default as LinearProgress } from './LinearProgress'; -export * from './LinearProgress'; - -export { default as List } from './List'; -export * from './List'; - -export { default as ListItem } from './ListItem'; -export * from './ListItem'; - -export { default as ListItemAvatar } from './ListItemAvatar'; -export * from './ListItemAvatar'; - -export { default as ListItemIcon } from './ListItemIcon'; -export * from './ListItemIcon'; - -export { default as ListItemSecondaryAction } from './ListItemSecondaryAction'; -export * from './ListItemSecondaryAction'; - -export { default as ListItemText } from './ListItemText'; -export * from './ListItemText'; - -export { default as ListSubheader } from './ListSubheader'; -export * from './ListSubheader'; - -export { default as Menu } from './Menu'; -export * from './Menu'; - -export { default as MenuItem } from './MenuItem'; -export * from './MenuItem'; - -export { default as Option } from './Option'; -export * from './Option'; - -export { default as OutlinedInput } from './OutlinedInput'; -export * from './OutlinedInput'; - -export { default as Select } from './Select'; -export * from './Select'; - -export { default as Slider } from './Slider'; -export * from './Slider'; - -export { default as SnackbarContent } from './SnackbarContent'; -export * from './SnackbarContent'; - -export { default as Divider } from './Divider'; -export * from './Divider'; - -export { default as Tabs } from './Tabs'; -export * from './Tabs'; - -export { default as Tab } from './Tab'; -export * from './Tab'; - -export { default as TabScrollButton } from './TabScrollButton'; -export * from './TabScrollButton'; - -export * from './styles'; diff --git a/packages/mui-material-next/src/internal/svg-icons/ArrowDropDown.tsx b/packages/mui-material-next/src/internal/svg-icons/ArrowDropDown.tsx deleted file mode 100644 index a6726a90b39ce6..00000000000000 --- a/packages/mui-material-next/src/internal/svg-icons/ArrowDropDown.tsx +++ /dev/null @@ -1,8 +0,0 @@ -'use client'; -import * as React from 'react'; -import createSvgIcon from '../../utils/createSvgIcon'; - -/** - * @ignore - internal component. - */ -export default createSvgIcon(<path d="M7 10l5 5 5-5z" />, 'ArrowDropDown'); diff --git a/packages/mui-material-next/src/internal/svg-icons/CheckBox.tsx b/packages/mui-material-next/src/internal/svg-icons/CheckBox.tsx deleted file mode 100644 index 98ec3b027dbc35..00000000000000 --- a/packages/mui-material-next/src/internal/svg-icons/CheckBox.tsx +++ /dev/null @@ -1,11 +0,0 @@ -'use client'; -import * as React from 'react'; -import createSvgIcon from '../../utils/createSvgIcon'; - -/** - * @ignore - internal component. - */ -export default createSvgIcon( - <path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" />, - 'CheckBox', -); diff --git a/packages/mui-material-next/src/internal/svg-icons/Clear.tsx b/packages/mui-material-next/src/internal/svg-icons/Clear.tsx deleted file mode 100644 index 5a8831dd4aee69..00000000000000 --- a/packages/mui-material-next/src/internal/svg-icons/Clear.tsx +++ /dev/null @@ -1,11 +0,0 @@ -'use client'; -import * as React from 'react'; -import createSvgIcon from '../../utils/createSvgIcon'; - -/** - * @ignore - internal component. - */ -export default createSvgIcon( - <path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />, - 'Clear', -); diff --git a/packages/mui-material-next/src/styles/CssVarsProvider.tsx b/packages/mui-material-next/src/styles/CssVarsProvider.tsx deleted file mode 100644 index d06425405fef8b..00000000000000 --- a/packages/mui-material-next/src/styles/CssVarsProvider.tsx +++ /dev/null @@ -1,47 +0,0 @@ -'use client'; -// do not remove the following import (https://github.com/microsoft/TypeScript/issues/29808#issuecomment-1320713018) -/* eslint-disable @typescript-eslint/no-unused-vars */ -// @ts-ignore -import * as React from 'react'; -import { - unstable_createCssVarsProvider as createCssVarsProvider, - unstable_styleFunctionSx as styleFunctionSx, - SxProps, -} from '@mui/system'; -import { - THEME_ID, - SupportedColorScheme, - private_createTypography as createTypography, - private_excludeVariablesFromRoot as excludeVariablesFromRoot, -} from '@mui/material/styles'; -import { Theme } from './Theme.types'; -import defaultTheme from './defaultTheme'; - -const { CssVarsProvider, useColorScheme, getInitColorSchemeScript } = createCssVarsProvider< - SupportedColorScheme, - typeof THEME_ID ->({ - themeId: THEME_ID, - theme: defaultTheme, - attribute: 'data-mui-color-scheme', - modeStorageKey: 'mui-mode', - colorSchemeStorageKey: 'mui-color-scheme', - defaultColorScheme: { - light: 'light', - dark: 'dark', - }, - resolveTheme: (theme) => { - const newTheme = { - ...theme, - typography: createTypography(theme.palette, theme.typography), - }; - - newTheme.unstable_sx = function sx(props: SxProps<Theme>) { - return styleFunctionSx({ sx: props, theme: this }); - }; - return newTheme; - }, - excludeVariablesFromRoot, -}); - -export { useColorScheme, getInitColorSchemeScript, CssVarsProvider }; diff --git a/packages/mui-material-next/src/styles/Theme.types.ts b/packages/mui-material-next/src/styles/Theme.types.ts deleted file mode 100644 index f79e033251b433..00000000000000 --- a/packages/mui-material-next/src/styles/Theme.types.ts +++ /dev/null @@ -1,302 +0,0 @@ -import { SxProps as SystemSxProps } from '@mui/system'; -import { - CssVarsTheme as MD2Theme, - SupportedColorScheme, - ColorSystemOptions as MD2ColorSystemOptions, - CssVarsThemeOptions as MD2CssVarsThemeOptions, -} from '@mui/material/styles'; - -export interface MD3Tones { - 0: string; - 10: string; - 20: string; - 30: string; - 40: string; - 50: string; - 60: string; - 70: string; - 80: string; - 90: string; - 95: string; - 99: string; - 100: string; -} - -export interface MD3NeutralTones extends MD3Tones { - 17: string; - 22: string; - 92: string; - 96: string; -} - -export interface MD3Palettes { - primary: MD3Tones; - secondary: MD3Tones; - tertiary: MD3Tones; - neutral: MD3NeutralTones; - neutralVariant: MD3Tones; - error: MD3Tones; - info: MD3Tones; - warning: MD3Tones; - success: MD3Tones; - common: { - black: string; - white: string; - }; -} - -export interface MD3ColorSchemeTokens { - primary: string; - onPrimary: string; - primaryContainer: string; - onPrimaryContainer: string; - - secondary: string; - onSecondary: string; - secondaryContainer: string; - onSecondaryContainer: string; - - tertiary: string; - onTertiary: string; - tertiaryContainer: string; - onTertiaryContainer: string; - - error: string; - onError: string; - errorContainer: string; - onErrorContainer: string; - - info: string; - onInfo: string; - infoContainer: string; - onInfoContainer: string; - - warning: string; - onWarning: string; - warningContainer: string; - onWarningContainer: string; - - success: string; - onSuccess: string; - successContainer: string; - onSuccessContainer: string; - - background: string; - onBackground: string; - - surface: string; - onSurface: string; - surfaceVariant: string; - onSurfaceVariant: string; - surfaceContainerLow: string; - surfaceContainerHigh: string; - surfaceContainerHighest: string; - - inverseSurface: string; - inverseOnSurface: string; - inversePrimary: string; - surfaceTint?: string; - - outline: string; - outlineVariant: string; - shadow: string; - - // channels - primaryChannel: string; - secondaryChannel: string; - tertiaryChannel: string; - onSurfaceChannel: string; - secondaryContainerChannel: string; -} - -export interface MD3Typeface { - plain: string; - brand: string; - weight: { - bold: string; - medium: string; - regular: string; - }; -} - -export interface MD3State { - hover: { - stateLayerOpacity: number; - }; - focus: { - stateLayerOpacity: number; - }; - pressed: { - stateLayerOpacity: number; - }; - dragged: { - stateLayerOpacity: number; - }; -} - -export interface TypescaleValue { - small: { - family: string; - weight: string; - lineHeight: number; - size: number; - tracking: number; - }; - medium: { - family: string; - weight: string; - lineHeight: number; - size: number; - tracking: number; - }; - large: { - family: string; - weight: string; - lineHeight: number; - size: number; - tracking: number; - }; -} - -export interface MD3Typescale { - label: TypescaleValue; - body: TypescaleValue; - headline: TypescaleValue; - display: TypescaleValue; -} - -export interface MD3Shape { - corner: { - none: string; - extraSmall: string; - extraSmallTop: string; - small: string; - medium: string; - large: string; - largeEnd: string; - largeTop: string; - extraLarge: string; - extraLargeTop: string; - full: string; - }; -} - -export interface MD3ShapeOptions { - corner?: Partial<MD3Shape['corner']>; -} - -export interface MD3Easing { - linear: string; - standard: string; - standardAccelerate: string; - standardDecelerate: string; - emphasized: string; - emphasizedDecelerate: string; - emphasizedAccelerate: string; - legacy: string; - legacyDecelerate: string; - legacyAccelerate: string; -} -export interface MD3Duration { - short1: string; - short2: string; - short3: string; - short4: string; - medium1: string; - medium2: string; - medium3: string; - medium4: string; - long1: string; - long2: string; - long3: string; - long4: string; - extraLong1: string; - extraLong2: string; - extraLong3: string; - extraLong4: string; -} - -export interface MotionOptions { - easing?: Partial<MD3Easing>; - duration?: Partial<MD3Duration>; - create?: ( - props: string | string[], - options?: Partial<{ duration: number | string; easing: string; delay: number | string }>, - ) => string; - getAutoHeightDuration?: (height: number) => number; -} - -export interface Motion { - easing: MD3Easing; - duration: MD3Duration; - create: ( - props: string | string[], - options?: Partial<{ duration: number | string; easing: string; delay: number | string }>, - ) => string; - getAutoHeightDuration: (height: number) => number; -} - -export interface MD3CssVarsThemeOptions extends Omit<MD2CssVarsThemeOptions, 'colorSchemes'> { - ref?: { - typeface?: Partial<MD3Typeface>; - palette?: Partial<MD3Palettes>; - }; - sys?: { - typescale?: Partial<MD3Typescale>; - state?: Partial<MD3State>; - elevation?: string[]; - motion?: MotionOptions; - shape?: MD3ShapeOptions; - }; -} - -export interface ColorSystemOptions extends MD2ColorSystemOptions { - sys?: { - color?: Partial<MD3ColorSchemeTokens>; - elevation?: string[]; - }; -} - -export interface CssVarsThemeOptions extends Omit<MD3CssVarsThemeOptions, 'colorSchemes'> { - /** - * Color schemes configuration - */ - colorSchemes?: Partial<Record<SupportedColorScheme, ColorSystemOptions>>; -} - -export interface Theme extends Omit<MD2Theme, 'vars'> { - useMaterialYou?: boolean; - ref: { - palette: MD3Palettes; - typeface: MD3Typeface; - }; - sys: { - color: MD3ColorSchemeTokens; - typescale: MD3Typescale; - state: MD3State; - elevation: string[]; - motion: Motion; - shape: MD3Shape; - }; - palette: MD2Theme['palette']; - vars: MD2Theme['vars'] & { - ref: { - palette: MD3Palettes; - typeface: any; - }; - sys: { - color: MD3ColorSchemeTokens; - typescale: MD3Typescale; - state: MD3State; - elevation: string[]; - motion: Omit<Motion, 'create' | 'getAutoHeightDuration'>; - shape: MD3Shape; - }; - }; - generateCssVars: (colorScheme?: SupportedColorScheme) => { - css: Record<string, string | number>; - vars: Theme['vars']; - }; -} - -export type SxProps = SystemSxProps<Theme>; diff --git a/packages/mui-material-next/src/styles/createDarkColorScheme.ts b/packages/mui-material-next/src/styles/createDarkColorScheme.ts deleted file mode 100644 index cf9b023a890a9e..00000000000000 --- a/packages/mui-material-next/src/styles/createDarkColorScheme.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { MD3Palettes } from './Theme.types'; - -// convert all these values to CSS vars -const createDarkColorScheme = ( - getCssVar: (cssVar: string, defaultVal: string) => string, - palette: MD3Palettes, -) => ({ - surfaceTint: getCssVar('ref-palette-primary-40', palette.primary[40]), - onErrorContainer: getCssVar('ref-palette-error-90', palette.error[90]), - onError: getCssVar('ref-palette-error-20', palette.error[20]), - errorContainer: getCssVar('ref-palette-error-30', palette.error[30]), - onTertiaryContainer: getCssVar('ref-palette-tertiary-90', palette.tertiary[90]), - onTertiary: getCssVar('ref-palette-tertiary-20', palette.tertiary[20]), - tertiaryContainer: getCssVar('ref-palette-tertiary-30', palette.tertiary[30]), - tertiary: getCssVar('ref-palette-tertiary-80', palette.tertiary[80]), - shadow: getCssVar('ref-palette-common-black', palette.common.black), - error: getCssVar('ref-palette-error-80', palette.error[80]), - outline: getCssVar('ref-palette-neutralVariant-60', palette.neutralVariant[60]), - outlineVariant: getCssVar('ref-palette-neutralVariant-30', palette.neutralVariant[30]), - onBackground: getCssVar('ref-palette-neutral-90', palette.neutral[90]), - background: getCssVar('ref-palette-neutral-10', palette.neutral[10]), - inverseOnSurface: getCssVar('ref-palette-neutral-20', palette.neutral[20]), - inverseSurface: getCssVar('ref-palette-neutral-90', palette.neutral[90]), - onSurfaceVariant: getCssVar('ref-palette-neutralVariant-80', palette.neutralVariant[80]), - onSurface: getCssVar('ref-palette-neutral-90', palette.neutral[90]), - surfaceVariant: getCssVar('ref-palette-neutralVariant-30', palette.neutralVariant[30]), - surface: getCssVar('ref-palette-neutral-10', palette.neutral[10]), - surfaceContainerLow: getCssVar('ref-palette-neutral-10', palette.neutral[10]), - surfaceContainerHigh: getCssVar('ref-palette-neutral-17', palette.neutral[17]), - surfaceContainerHighest: getCssVar('ref-palette-neutral-22', palette.neutral[22]), - onSecondaryContainer: getCssVar('ref-palette-secondary-90', palette.secondary[90]), - onSecondary: getCssVar('ref-palette-secondary-20', palette.secondary[20]), - secondaryContainer: getCssVar('ref-palette-secondary-30', palette.secondary[30]), - secondary: getCssVar('ref-palette-secondary-80', palette.secondary[80]), - inversePrimary: getCssVar('ref-palette-primary-40', palette.primary[40]), - onPrimaryContainer: getCssVar('ref-palette-primary-90', palette.primary[90]), - onPrimary: getCssVar('ref-palette-primary-20', palette.primary[20]), - primaryContainer: getCssVar('ref-palette-primary-30', palette.primary[30]), - primary: getCssVar('ref-palette-primary-80', palette.primary[80]), - info: getCssVar('ref-palette-info-80', palette.info[80]), - onInfo: getCssVar('ref-palette-info-20', palette.info[20]), - infoContainer: getCssVar('ref-palette-info-30', palette.info[30]), - onInfoContainer: getCssVar('ref-palette-info-90', palette.info[90]), - warning: getCssVar('ref-palette-warning-80', palette.warning[80]), - onWarning: getCssVar('ref-palette-warning-20', palette.warning[20]), - warningContainer: getCssVar('ref-palette-warning-30', palette.warning[30]), - onWarningContainer: getCssVar('ref-palette-warning-90', palette.warning[90]), - success: getCssVar('ref-palette-success-80', palette.success[80]), - onSuccess: getCssVar('ref-palette-success-20', palette.success[20]), - successContainer: getCssVar('ref-palette-success-30', palette.success[30]), - onSuccessContainer: getCssVar('ref-palette-success-90', palette.success[90]), -}); - -export default createDarkColorScheme; diff --git a/packages/mui-material-next/src/styles/createLightColorScheme.ts b/packages/mui-material-next/src/styles/createLightColorScheme.ts deleted file mode 100644 index 0cac5c8dd0aca1..00000000000000 --- a/packages/mui-material-next/src/styles/createLightColorScheme.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { MD3Palettes } from './Theme.types'; - -const createLightColorScheme = ( - getCssVar: (cssVar: string, defaultVal: string) => string, - palette: MD3Palettes, -) => ({ - surfaceTint: getCssVar('ref-palette-primary-40', palette.primary[40]), - onErrorContainer: getCssVar('ref-palette-error-10', palette.error[10]), - onError: getCssVar('ref-palette-error-100', palette.error[100]), - errorContainer: getCssVar('ref-palette-error-90', palette.error[90]), - onTertiaryContainer: getCssVar('ref-palette-tertiary-10', palette.tertiary[10]), - onTertiary: getCssVar('ref-palette-tertiary-100', palette.tertiary[100]), - tertiaryContainer: getCssVar('ref-palette-tertiary-90', palette.tertiary[90]), - tertiary: getCssVar('ref-palette-tertiary-40', palette.tertiary[40]), - shadow: getCssVar('ref-palette-common-black', palette.common.black), - error: getCssVar('ref-palette-error-40', palette.error[40]), - outline: getCssVar('ref-palette-neutralVariant-50', palette.neutralVariant[50]), - outlineVariant: getCssVar('ref-palette-neutralVariant-80', palette.neutralVariant[80]), - onBackground: getCssVar('ref-palette-neutral-10', palette.neutral[10]), - background: getCssVar('ref-palette-neutral-99', palette.neutral[99]), - inverseOnSurface: getCssVar('ref-palette-neutral-95', palette.neutral[95]), - inverseSurface: getCssVar('ref-palette-neutral-20', palette.neutral[20]), - onSurfaceVariant: getCssVar('ref-palette-neutralVariant-30', palette.neutralVariant[30]), - onSurface: getCssVar('ref-palette-neutral-10', palette.neutral[10]), - surfaceVariant: getCssVar('ref-palette-neutralVariant-90', palette.neutralVariant[90]), - surface: getCssVar('ref-palette-neutral-99', palette.neutral[99]), - surfaceContainerLow: getCssVar('ref-palette-neutral-96', palette.neutral[96]), - surfaceContainerHigh: getCssVar('ref-palette-neutral-92', palette.neutral[92]), - surfaceContainerHighest: getCssVar('ref-palette-neutral-90', palette.neutral[90]), - onSecondaryContainer: getCssVar('ref-palette-secondary-10', palette.secondary[10]), - onSecondary: getCssVar('ref-palette-secondary-100', palette.secondary[100]), - secondaryContainer: getCssVar('ref-palette-secondary-90', palette.secondary[90]), - secondary: getCssVar('ref-palette-secondary-40', palette.secondary[40]), - inversePrimary: getCssVar('ref-palette-primary-80', palette.primary[80]), - onPrimaryContainer: getCssVar('ref-palette-primary-10', palette.primary[10]), - onPrimary: getCssVar('ref-palette-primary-100', palette.primary[100]), - primaryContainer: getCssVar('ref-palette-primary-90', palette.primary[90]), - primary: getCssVar('ref-palette-primary-40', palette.primary[40]), - info: getCssVar('ref-palette-info-40', palette.info[40]), - onInfo: getCssVar('ref-palette-info-100', palette.info[100]), - infoContainer: getCssVar('ref-palette-info-90', palette.info[90]), - onInfoContainer: getCssVar('ref-palette-info-10', palette.info[10]), - warning: getCssVar('ref-palette-warning-40', palette.warning[40]), - onWarning: getCssVar('ref-palette-warning-100', palette.warning[100]), - warningContainer: getCssVar('ref-palette-warning-90', palette.warning[90]), - onWarningContainer: getCssVar('ref-palette-warning-10', palette.warning[10]), - success: getCssVar('ref-palette-success-40', palette.success[40]), - onSuccess: getCssVar('ref-palette-success-100', palette.success[100]), - successContainer: getCssVar('ref-palette-success-90', palette.success[90]), - onSuccessContainer: getCssVar('ref-palette-success-10', palette.success[10]), -}); - -export default createLightColorScheme; diff --git a/packages/mui-material-next/src/styles/defaultTheme.ts b/packages/mui-material-next/src/styles/defaultTheme.ts deleted file mode 100644 index ade81cb790a0b2..00000000000000 --- a/packages/mui-material-next/src/styles/defaultTheme.ts +++ /dev/null @@ -1,5 +0,0 @@ -import extendTheme from './extendTheme'; - -const defaultTheme = extendTheme(); - -export default defaultTheme; diff --git a/packages/mui-material-next/src/styles/elevation.ts b/packages/mui-material-next/src/styles/elevation.ts deleted file mode 100644 index f5d7eda2a25eaf..00000000000000 --- a/packages/mui-material-next/src/styles/elevation.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const elevationLight = [ - 'none', - '0px 1px 2px rgba(0, 0, 0, 0.3), 0px 1px 3px rgba(0, 0, 0, 0.15)', - '0px 1px 2px rgba(0, 0, 0, 0.3), 0px 2px 6px 2px rgba(0, 0, 0, 0.15)', - '0px 4px 8px 3px rgba(0, 0, 0, 0.15), 0px 1px 3px rgba(0, 0, 0, 0.3)', - '0px 6px 10px 4px rgba(0, 0, 0, 0.15), 0px 2px 3px rgba(0, 0, 0, 0.3)', - '0px 8px 12px 6px rgba(0, 0, 0, 0.15), 0px 4px 4px rgba(0, 0, 0, 0.3)', -]; - -export const elevationDark = [ - 'none', - '0px 1px 3px 1px rgba(0, 0, 0, 0.15), 0px 1px 2px rgba(0, 0, 0, 0.3)', - '0px 2px 6px 2px rgba(0, 0, 0, 0.15), 0px 1px 2px rgba(0, 0, 0, 0.3)', - '0px 4px 8px 3px rgba(0, 0, 0, 0.15), 0px 1px 3px rgba(0, 0, 0, 0.3)', - '0px 6px 10px 4px rgba(0, 0, 0, 0.15), 0px 2px 3px rgba(0, 0, 0, 0.3)', - '0px 8px 12px 6px rgba(0, 0, 0, 0.15), 0px 4px 4px rgba(0, 0, 0, 0.3)', -]; diff --git a/packages/mui-material-next/src/styles/extendTheme.test.ts b/packages/mui-material-next/src/styles/extendTheme.test.ts deleted file mode 100644 index b72a952fe7fc2b..00000000000000 --- a/packages/mui-material-next/src/styles/extendTheme.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { expect } from 'chai'; -import { extendTheme, Theme } from '@mui/material-next/styles'; - -describe('extendTheme', () => { - it('should have default cssVarPrefix', () => { - expect(extendTheme().cssVarPrefix).to.equal('md'); - }); - - it('`getCssVar` return default prefix', () => { - expect(extendTheme().getCssVar('palette-primary-main')).to.equal( - 'var(--md-palette-primary-main)', - ); - }); - - it('should have the vars object', () => { - const theme = extendTheme(); - const keys = [ - // M2 specific tokens - 'palette', - 'shadows', - 'zIndex', - 'opacity', - 'overlays', - 'shape', - // M3 specific tokens - 'ref', - 'sys', - ]; - - Object.keys(keys).forEach((key) => { - expect(theme[key as keyof Theme]).to.deep.equal(theme.vars[key as keyof Theme['vars']]); - }); - }); -}); diff --git a/packages/mui-material-next/src/styles/extendTheme.ts b/packages/mui-material-next/src/styles/extendTheme.ts deleted file mode 100644 index 5729bf7c83a17d..00000000000000 --- a/packages/mui-material-next/src/styles/extendTheme.ts +++ /dev/null @@ -1,476 +0,0 @@ -import { deepmerge } from '@mui/utils'; -import { - colorChannel, - alpha, - darken, - lighten, - emphasize, - unstable_createGetCssVar as systemCreateGetCssVar, - unstable_styleFunctionSx as styleFunctionSx, - unstable_prepareCssVars as prepareCssVars, - SxProps, -} from '@mui/system'; -import { - createTheme as createThemeWithoutVars, - getOverlayAlpha, - SupportedColorScheme, - ColorSystem as MD2ColorSystem, - Overlays, -} from '@mui/material/styles'; -import defaultSxConfig from './sxConfig'; -import { Theme, MD3Palettes, MD3ColorSchemeTokens, CssVarsThemeOptions } from './Theme.types'; -import md3CommonPalette from './palette'; -import createMd3LightColorScheme from './createLightColorScheme'; -import createMd3DarkColorScheme from './createDarkColorScheme'; -import md3Typescale from './typescale'; -import md3Typeface from './typeface'; -import md3State from './state'; -import { elevationLight, elevationDark } from './elevation'; -import createMotions from './motion'; -import md3shape from './shape'; -import defaultShouldSkipGeneratingVar from './shouldSkipGeneratingVar'; - -const defaultLightOverlays: Overlays = [...Array(25)].map(() => undefined) as Overlays; -const defaultDarkOverlays: Overlays = [...Array(25)].map((_, index) => { - if (index === 0) { - return undefined; - } - const overlay = getOverlayAlpha(index); - return `linear-gradient(rgba(255 255 255 / ${overlay}), rgba(255 255 255 / ${overlay}))`; -}) as Overlays; - -function assignNode(obj: any, keys: string[]) { - keys.forEach((k) => { - if (!obj[k]) { - obj[k] = {}; - } - }); -} - -function setColor(obj: any, key: string, defaultValue: any) { - obj[key] = obj[key] || defaultValue; -} - -export const createGetCssVar = (cssVarPrefix = 'md') => systemCreateGetCssVar(cssVarPrefix); - -export default function extendTheme(options: CssVarsThemeOptions = {}, ...args: any[]) { - const { - colorSchemes: colorSchemesInput = {}, - cssVarPrefix = 'md', - shouldSkipGeneratingVar = defaultShouldSkipGeneratingVar, - ...input - } = options; - const getCssVar = createGetCssVar(cssVarPrefix); - - const md3LightColors = createMd3LightColorScheme(getCssVar, md3CommonPalette); - const md3DarkColors = createMd3DarkColorScheme(getCssVar, md3CommonPalette); - const shape = { - ...input.sys?.shape, - ...md3shape, - corner: { ...input.sys?.shape?.corner, ...md3shape.corner }, - }; - - const motion = createMotions(input.sys?.motion); - const typescale = { ...md3Typescale, ...input.sys?.typescale }; - const typeface = { ...md3Typeface, ...input.ref?.typeface }; - const state = { ...md3State, ...input.sys?.state }; - - const { - palette: lightPalette, - // @ts-ignore - sys is md3 specific token - sys: lightSys, - // @ts-ignore - ref is md3 specific token - ref: lightRef, - ...muiTheme - } = createThemeWithoutVars({ - ...input, - // Material Design 3 specific tokens - // @ts-ignore - it's fine, everything that is not supported will be spread - useMaterialYou: true, - ref: { - ...input.ref, - typeface, - palette: deepmerge(md3CommonPalette, input.ref?.palette), - }, - sys: { - ...input.sys, - typescale, - state, - motion, - color: { ...md3LightColors, ...colorSchemesInput.light?.sys?.color }, - elevation: colorSchemesInput.light?.sys?.elevation ?? elevationLight, - shape, - }, - palette: { - ...(colorSchemesInput.light && colorSchemesInput.light?.palette), - }, - }); - - const { - palette: darkPalette, - // @ts-ignore sys is md3 specific tokens - sys: darkSys, - // @ts-ignore ref is md3 specific tokens - ref: darkRef, - } = createThemeWithoutVars({ - palette: { - mode: 'dark', - ...colorSchemesInput.dark?.palette, - }, - // @ts-ignore - it's fine, everything that is not supported will be spread - ref: { - ...input.ref, - typeface, - palette: deepmerge(md3CommonPalette, input.ref?.palette), - }, - sys: { - ...input.sys, - typescale, - state, - motion, - color: { ...md3DarkColors, ...colorSchemesInput.dark?.sys?.color }, - elevation: colorSchemesInput.dark?.sys?.elevation ?? elevationDark, - shape, - }, - }); - - const { color: lightSysColor, elevation: lightSysElevation } = lightSys; - const { palette: lightRefPalette } = lightRef; - - const { color: darkSysColor, elevation: darkSysElevation } = darkSys; - const { palette: darkRefPalette } = darkRef; - - let theme: Theme = { - ...muiTheme, - cssVarPrefix, - getCssVar, - sys: lightSys, - ref: lightRef, - colorSchemes: { - ...colorSchemesInput, - light: { - ...colorSchemesInput.light, - // @ts-ignore they are added below - palette: lightPalette, - opacity: { - inputPlaceholder: 0.42, - inputUnderline: 0.42, - switchTrackDisabled: 0.12, - switchTrack: 0.38, - ...colorSchemesInput.light?.opacity, - }, - overlays: colorSchemesInput.light?.overlays || defaultLightOverlays, - sys: { color: lightSysColor, elevation: lightSysElevation }, - ref: { palette: lightRefPalette }, - }, - dark: { - ...colorSchemesInput.dark, - // @ts-ignore they are added below - palette: darkPalette, - opacity: { - inputPlaceholder: 0.5, - inputUnderline: 0.7, - switchTrackDisabled: 0.2, - switchTrack: 0.3, - ...colorSchemesInput.dark?.opacity, - }, - overlays: colorSchemesInput.dark?.overlays || defaultDarkOverlays, - sys: { color: darkSysColor, elevation: darkSysElevation }, - ref: { palette: darkRefPalette }, - }, - }, - }; - - Object.keys(theme.colorSchemes).forEach((key) => { - const palette = theme.colorSchemes[key as SupportedColorScheme] - .palette as MD2ColorSystem['palette'] & { - md3: MD3Palettes & { colors: MD3ColorSchemeTokens }; - }; - - // @ts-ignore sys is md3 specific token - const colorSchemeSys = theme.colorSchemes[key as SupportedColorScheme].sys; - // @ts-ignore ref is md3 specific token - const colorSchemeRef = theme.colorSchemes[key as SupportedColorScheme].ref; - - // attach black & white channels to common node - if (key === 'light') { - setColor(palette.common, 'background', '#fff'); - setColor(palette.common, 'onBackground', '#000'); - } else { - setColor(palette.common, 'background', '#000'); - setColor(palette.common, 'onBackground', '#fff'); - } - - // assign component variables - assignNode(palette, [ - 'Alert', - 'AppBar', - 'Avatar', - 'Button', - 'Chip', - 'FilledInput', - 'LinearProgress', - 'Skeleton', - 'Slider', - 'SnackbarContent', - 'SpeedDialAction', - 'StepConnector', - 'StepContent', - 'Switch', - 'TableCell', - 'Tooltip', - ]); - if (key === 'light') { - setColor(palette.Alert, 'errorColor', darken(palette.error.light, 0.6)); - setColor(palette.Alert, 'infoColor', darken(palette.info.light, 0.6)); - setColor(palette.Alert, 'successColor', darken(palette.success.light, 0.6)); - setColor(palette.Alert, 'warningColor', darken(palette.warning.light, 0.6)); - setColor(palette.Alert, 'errorFilledBg', getCssVar('palette-error-main')); - setColor(palette.Alert, 'infoFilledBg', getCssVar('palette-info-main')); - setColor(palette.Alert, 'successFilledBg', getCssVar('palette-success-main')); - setColor(palette.Alert, 'warningFilledBg', getCssVar('palette-warning-main')); - setColor(palette.Alert, 'errorFilledColor', lightPalette.getContrastText(palette.error.main)); - setColor(palette.Alert, 'infoFilledColor', lightPalette.getContrastText(palette.info.main)); - setColor( - palette.Alert, - 'successFilledColor', - lightPalette.getContrastText(palette.success.main), - ); - setColor( - palette.Alert, - 'warningFilledColor', - lightPalette.getContrastText(palette.warning.main), - ); - setColor(palette.Alert, 'errorStandardBg', lighten(palette.error.light, 0.9)); - setColor(palette.Alert, 'infoStandardBg', lighten(palette.info.light, 0.9)); - setColor(palette.Alert, 'successStandardBg', lighten(palette.success.light, 0.9)); - setColor(palette.Alert, 'warningStandardBg', lighten(palette.warning.light, 0.9)); - setColor(palette.Alert, 'errorIconColor', getCssVar('palette-error-light')); - setColor(palette.Alert, 'infoIconColor', getCssVar('palette-info-light')); - setColor(palette.Alert, 'successIconColor', getCssVar('palette-success-light')); - setColor(palette.Alert, 'warningIconColor', getCssVar('palette-warning-light')); - setColor(palette.AppBar, 'defaultBg', getCssVar('palette-grey-100')); - setColor(palette.Avatar, 'defaultBg', getCssVar('palette-grey-400')); - setColor(palette.Button, 'inheritContainedBg', getCssVar('palette-grey-300')); - setColor(palette.Button, 'inheritContainedHoverBg', getCssVar('palette-grey-A100')); - setColor(palette.Chip, 'defaultBorder', getCssVar('palette-grey-400')); - setColor(palette.Chip, 'defaultAvatarColor', getCssVar('palette-grey-700')); - setColor(palette.Chip, 'defaultIconColor', getCssVar('palette-grey-700')); - setColor(palette.FilledInput, 'bg', 'rgba(0, 0, 0, 0.06)'); - setColor(palette.FilledInput, 'hoverBg', 'rgba(0, 0, 0, 0.09)'); - setColor(palette.FilledInput, 'disabledBg', 'rgba(0, 0, 0, 0.12)'); - setColor(palette.LinearProgress, 'primaryBg', lighten(palette.primary.main, 0.62)); - setColor(palette.LinearProgress, 'secondaryBg', lighten(palette.secondary.main, 0.62)); - setColor(palette.LinearProgress, 'errorBg', lighten(palette.error.main, 0.62)); - setColor(palette.LinearProgress, 'infoBg', lighten(palette.info.main, 0.62)); - setColor(palette.LinearProgress, 'successBg', lighten(palette.success.main, 0.62)); - setColor(palette.LinearProgress, 'warningBg', lighten(palette.warning.main, 0.62)); - setColor(palette.Skeleton, 'bg', `rgba(${getCssVar('palette-text-primaryChannel')} / 0.11)`); - setColor(palette.Slider, 'primaryTrack', lighten(palette.primary.main, 0.62)); - setColor(palette.Slider, 'secondaryTrack', lighten(palette.secondary.main, 0.62)); - setColor(palette.Slider, 'errorTrack', lighten(palette.error.main, 0.62)); - setColor(palette.Slider, 'infoTrack', lighten(palette.info.main, 0.62)); - setColor(palette.Slider, 'successTrack', lighten(palette.success.main, 0.62)); - setColor(palette.Slider, 'warningTrack', lighten(palette.warning.main, 0.62)); - const snackbarContentBackground = emphasize(palette.background.default, 0.8); - setColor(palette.SnackbarContent, 'bg', snackbarContentBackground); - setColor( - palette.SnackbarContent, - 'color', - lightPalette.getContrastText(snackbarContentBackground), - ); - setColor(palette.SpeedDialAction, 'fabHoverBg', emphasize(palette.background.paper, 0.15)); - setColor(palette.StepConnector, 'border', getCssVar('palette-grey-400')); - setColor(palette.StepContent, 'border', getCssVar('palette-grey-400')); - setColor(palette.Switch, 'defaultColor', getCssVar('palette-common-white')); - setColor(palette.Switch, 'defaultDisabledColor', getCssVar('palette-grey-100')); - setColor(palette.Switch, 'primaryDisabledColor', lighten(palette.primary.main, 0.62)); - setColor(palette.Switch, 'secondaryDisabledColor', lighten(palette.secondary.main, 0.62)); - setColor(palette.Switch, 'errorDisabledColor', lighten(palette.error.main, 0.62)); - setColor(palette.Switch, 'infoDisabledColor', lighten(palette.info.main, 0.62)); - setColor(palette.Switch, 'successDisabledColor', lighten(palette.success.main, 0.62)); - setColor(palette.Switch, 'warningDisabledColor', lighten(palette.warning.main, 0.62)); - setColor(palette.TableCell, 'border', lighten(alpha(palette.divider, 1), 0.88)); - setColor(palette.Tooltip, 'bg', alpha(palette.grey[700], 0.92)); - } else { - setColor(palette.Alert, 'errorColor', lighten(palette.error.light, 0.6)); - setColor(palette.Alert, 'infoColor', lighten(palette.info.light, 0.6)); - setColor(palette.Alert, 'successColor', lighten(palette.success.light, 0.6)); - setColor(palette.Alert, 'warningColor', lighten(palette.warning.light, 0.6)); - setColor(palette.Alert, 'errorFilledBg', getCssVar('palette-error-dark')); - setColor(palette.Alert, 'infoFilledBg', getCssVar('palette-info-dark')); - setColor(palette.Alert, 'successFilledBg', getCssVar('palette-success-dark')); - setColor(palette.Alert, 'warningFilledBg', getCssVar('palette-warning-dark')); - setColor(palette.Alert, 'errorFilledColor', darkPalette.getContrastText(palette.error.dark)); - setColor(palette.Alert, 'infoFilledColor', darkPalette.getContrastText(palette.info.dark)); - setColor( - palette.Alert, - 'successFilledColor', - darkPalette.getContrastText(palette.success.dark), - ); - setColor( - palette.Alert, - 'warningFilledColor', - darkPalette.getContrastText(palette.warning.dark), - ); - setColor(palette.Alert, 'errorStandardBg', darken(palette.error.light, 0.9)); - setColor(palette.Alert, 'infoStandardBg', darken(palette.info.light, 0.9)); - setColor(palette.Alert, 'successStandardBg', darken(palette.success.light, 0.9)); - setColor(palette.Alert, 'warningStandardBg', darken(palette.warning.light, 0.9)); - setColor(palette.Alert, 'errorIconColor', getCssVar('palette-error-main')); - setColor(palette.Alert, 'infoIconColor', getCssVar('palette-info-main')); - setColor(palette.Alert, 'successIconColor', getCssVar('palette-success-main')); - setColor(palette.Alert, 'warningIconColor', getCssVar('palette-warning-main')); - setColor(palette.AppBar, 'defaultBg', getCssVar('palette-grey-900')); - setColor(palette.AppBar, 'darkBg', getCssVar('palette-background-paper')); // specific for dark mode - setColor(palette.AppBar, 'darkColor', getCssVar('palette-text-primary')); // specific for dark mode - setColor(palette.Avatar, 'defaultBg', getCssVar('palette-grey-600')); - setColor(palette.Button, 'inheritContainedBg', getCssVar('palette-grey-800')); - setColor(palette.Button, 'inheritContainedHoverBg', getCssVar('palette-grey-700')); - setColor(palette.Chip, 'defaultBorder', getCssVar('palette-grey-700')); - setColor(palette.Chip, 'defaultAvatarColor', getCssVar('palette-grey-300')); - setColor(palette.Chip, 'defaultIconColor', getCssVar('palette-grey-300')); - setColor(palette.FilledInput, 'bg', 'rgba(255, 255, 255, 0.09)'); - setColor(palette.FilledInput, 'hoverBg', 'rgba(255, 255, 255, 0.13)'); - setColor(palette.FilledInput, 'disabledBg', 'rgba(255, 255, 255, 0.12)'); - setColor(palette.LinearProgress, 'primaryBg', darken(palette.primary.main, 0.5)); - setColor(palette.LinearProgress, 'secondaryBg', darken(palette.secondary.main, 0.5)); - setColor(palette.LinearProgress, 'errorBg', darken(palette.error.main, 0.5)); - setColor(palette.LinearProgress, 'infoBg', darken(palette.info.main, 0.5)); - setColor(palette.LinearProgress, 'successBg', darken(palette.success.main, 0.5)); - setColor(palette.LinearProgress, 'warningBg', darken(palette.warning.main, 0.5)); - setColor(palette.Skeleton, 'bg', `rgba(${getCssVar('palette-text-primaryChannel')} / 0.13)`); - setColor(palette.Slider, 'primaryTrack', darken(palette.primary.main, 0.5)); - setColor(palette.Slider, 'secondaryTrack', darken(palette.secondary.main, 0.5)); - setColor(palette.Slider, 'errorTrack', darken(palette.error.main, 0.5)); - setColor(palette.Slider, 'infoTrack', darken(palette.info.main, 0.5)); - setColor(palette.Slider, 'successTrack', darken(palette.success.main, 0.5)); - setColor(palette.Slider, 'warningTrack', darken(palette.warning.main, 0.5)); - const snackbarContentBackground = emphasize(palette.background.default, 0.98); - setColor(palette.SnackbarContent, 'bg', snackbarContentBackground); - setColor( - palette.SnackbarContent, - 'color', - darkPalette.getContrastText(snackbarContentBackground), - ); - setColor(palette.SpeedDialAction, 'fabHoverBg', emphasize(palette.background.paper, 0.15)); - setColor(palette.StepConnector, 'border', getCssVar('palette-grey-600')); - setColor(palette.StepContent, 'border', getCssVar('palette-grey-600')); - setColor(palette.Switch, 'defaultColor', getCssVar('palette-grey-300')); - setColor(palette.Switch, 'defaultDisabledColor', getCssVar('palette-grey-600')); - setColor(palette.Switch, 'primaryDisabledColor', darken(palette.primary.main, 0.55)); - setColor(palette.Switch, 'secondaryDisabledColor', darken(palette.secondary.main, 0.55)); - setColor(palette.Switch, 'errorDisabledColor', darken(palette.error.main, 0.55)); - setColor(palette.Switch, 'infoDisabledColor', darken(palette.info.main, 0.55)); - setColor(palette.Switch, 'successDisabledColor', darken(palette.success.main, 0.55)); - setColor(palette.Switch, 'warningDisabledColor', darken(palette.warning.main, 0.55)); - setColor(palette.TableCell, 'border', darken(alpha(palette.divider, 1), 0.68)); - setColor(palette.Tooltip, 'bg', alpha(palette.grey[700], 0.92)); - } - - palette.common.backgroundChannel = colorChannel(palette.common.background); - palette.common.onBackgroundChannel = colorChannel(palette.common.onBackground); - - palette.dividerChannel = colorChannel(palette.divider); - - Object.keys(palette).forEach((c) => { - const color = c as keyof MD2ColorSystem['palette']; - const colors: any = palette[color]; - - // Color palettes: primary, secondary, error, info, success, and warning - if (colors.main) { - // @ts-ignore - palette[color].mainChannel = colorChannel(colors.main); - } - if (colors.light) { - // @ts-ignore - palette[color].lightChannel = colorChannel(colors.light); - } - if (colors.dark) { - // @ts-ignore - palette[color].darkChannel = colorChannel(colors.dark); - } - if (colors.contrastText) { - // @ts-ignore - palette[color].contrastTextChannel = colorChannel(colors.contrastText); - } - - // Text colors: text.primary, text.secondary - if (colors.primary && typeof colors.primary === 'string') { - // @ts-ignore - palette[color].primaryChannel = colorChannel(colors.primary); - } - if (colors.secondary && typeof colors.primary === 'string') { - // @ts-ignore - palette[color].secondaryChannel = colorChannel(colors.secondary); - } - - // Action colors: action.active, action.selected - if (colors.active) { - // @ts-ignore - palette[color].activeChannel = colorChannel(colors.active); - } - if (colors.selected) { - // @ts-ignore - palette[color].selectedChannel = colorChannel(colors.selected); - } - }); - - // Material Design 3 specific channels - if (key === 'light') { - colorSchemeSys.color.primaryChannel = colorChannel(colorSchemeRef.palette.primary['40']); - colorSchemeSys.color.onPrimaryChannel = colorChannel(colorSchemeRef.palette.primary['100']); - colorSchemeSys.color.secondaryChannel = colorChannel(colorSchemeRef.palette.secondary['40']); - colorSchemeSys.color.onSecondaryChannel = colorChannel( - colorSchemeRef.palette.secondary['100'], - ); - colorSchemeSys.color.tertiaryChannel = colorChannel(colorSchemeRef.palette.tertiary['40']); - colorSchemeSys.color.onTertiaryChannel = colorChannel(colorSchemeRef.palette.tertiary['100']); - colorSchemeSys.color.secondaryContainerChannel = colorChannel( - colorSchemeRef.palette.secondary['90'], - ); - colorSchemeSys.color.onSurfaceChannel = colorChannel(colorSchemeRef.palette.neutral['10']); - } else { - colorSchemeSys.color.primaryChannel = colorChannel(colorSchemeRef.palette.primary['80']); - colorSchemeSys.color.onPrimaryChannel = colorChannel(colorSchemeRef.palette.primary['20']); - colorSchemeSys.color.secondaryChannel = colorChannel(colorSchemeRef.palette.secondary['80']); - colorSchemeSys.color.onSecondaryChannel = colorChannel( - colorSchemeRef.palette.secondary['20'], - ); - colorSchemeSys.color.tertiaryChannel = colorChannel(colorSchemeRef.palette.tertiary['80']); - colorSchemeSys.color.onTertiaryChannel = colorChannel(colorSchemeRef.palette.tertiary['20']); - colorSchemeSys.color.secondaryContainerChannel = colorChannel( - colorSchemeRef.palette.secondary['30'], - ); - colorSchemeSys.color.onSurfaceChannel = colorChannel(colorSchemeRef.palette.neutral['90']); - } - }); - - theme = args.reduce((acc, argument) => deepmerge(acc, argument), theme); - - const parserConfig = { - prefix: cssVarPrefix, - shouldSkipGeneratingVar, - }; - - const { vars: themeVars, generateCssVars } = prepareCssVars<Theme, Theme['vars']>( - theme, - parserConfig, - ); - theme.vars = themeVars; - theme.generateCssVars = generateCssVars; - theme.shouldSkipGeneratingVar = shouldSkipGeneratingVar; - - theme.unstable_sxConfig = { - ...defaultSxConfig, - ...input?.unstable_sxConfig, - }; - theme.unstable_sx = function sx(props: SxProps<Theme>) { - return styleFunctionSx({ - sx: props, - theme: this, - }); - }; - - return theme; -} diff --git a/packages/mui-material-next/src/styles/identifier.ts b/packages/mui-material-next/src/styles/identifier.ts deleted file mode 100644 index 93c7bbf7859970..00000000000000 --- a/packages/mui-material-next/src/styles/identifier.ts +++ /dev/null @@ -1 +0,0 @@ -export default '$$material'; diff --git a/packages/mui-material-next/src/styles/index.ts b/packages/mui-material-next/src/styles/index.ts deleted file mode 100644 index 3f988d64ebef0a..00000000000000 --- a/packages/mui-material-next/src/styles/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -'use client'; -export { rootShouldForwardProp } from '@mui/material/styles/styled'; -export * from './Theme.types'; -export * from './extendTheme'; -export { default as extendTheme } from './extendTheme'; -export { default as styled } from './styled'; -export { default as defaultTheme } from './defaultTheme'; -export { default as useTheme } from './useTheme'; -export { default as shouldSkipGeneratingVar } from './shouldSkipGeneratingVar'; -export * from './CssVarsProvider'; -export { default as useThemeProps } from './useThemeProps'; diff --git a/packages/mui-material-next/src/styles/motion.test.js b/packages/mui-material-next/src/styles/motion.test.js deleted file mode 100644 index d62934285bc8d2..00000000000000 --- a/packages/mui-material-next/src/styles/motion.test.js +++ /dev/null @@ -1,156 +0,0 @@ -import { expect } from 'chai'; -import { extendTheme } from '@mui/material-next/styles'; -import createMotion, { easing, duration } from './motion'; - -describe('motion', () => { - const motion = createMotion({}); - const create = motion.create; - const getAutoHeightDuration = motion.getAutoHeightDuration; - - it('should allow to customize the default duration', () => { - const theme = extendTheme({ - sys: { - motion: { - duration: { - medium1: '310ms', - }, - }, - }, - }); - expect(theme.sys.motion.create('color')).to.equal(`color 310ms ${easing.standard} 0ms`); - }); - - describe('create() function', () => { - describe('warnings', () => { - it('should warn when first argument is of bad type', () => { - expect(() => create(5554)).toErrorDev('MUI: Argument "props" must be a string or Array'); - expect(() => create({})).toErrorDev('MUI: Argument "props" must be a string or Array'); - }); - - it('should warn when bad "duration" option type', () => { - expect(() => create('font', { duration: null })).toErrorDev( - 'MUI: Argument "duration" must be a number or a string but found null', - ); - expect(() => create('font', { duration: {} })).toErrorDev( - 'MUI: Argument "duration" must be a number or a string but found [object Object]', - ); - }); - - it('should warn when bad "easing" option type', () => { - expect(() => create('transform', { easing: 123 })).toErrorDev( - 'MUI: Argument "easing" must be a string', - ); - expect(() => create('transform', { easing: {} })).toErrorDev( - 'MUI: Argument "easing" must be a string', - ); - }); - - it('should warn when bad "delay" option type', () => { - expect(() => create('size', { delay: null })).toErrorDev( - 'MUI: Argument "delay" must be a number or a string', - ); - expect(() => create('size', { delay: {} })).toErrorDev( - 'MUI: Argument "delay" must be a number or a string', - ); - }); - - it('should warn when passed unrecognized option', () => { - expect(() => create('size', { fffds: 'value' })).toErrorDev( - 'MUI: Unrecognized argument(s) [fffds]', - ); - }); - }); - - it('should create default transition without arguments', () => { - const transition = create(); - expect(transition).to.equal(`all ${duration.medium1} ${easing.standard} 0ms`); - }); - - it('should take string props as a first argument', () => { - const transition = create('color'); - expect(transition).to.equal(`color ${duration.medium1} ${easing.standard} 0ms`); - }); - - it('should also take array of props as first argument', () => { - const options = { delay: 20 }; - const multiple = create(['color', 'size'], options); - const single1 = create('color', options); - const single2 = create('size', options); - const expected = `${single1},${single2}`; - expect(multiple).to.equal(expected); - }); - - it('should optionally accept number "duration" option in second argument', () => { - const transition = create('font', { duration: 500 }); - expect(transition).to.equal(`font 500ms ${easing.standard} 0ms`); - }); - - it('should optionally accept string "duration" option in second argument', () => { - const transition = create('font', { duration: '500ms' }); - expect(transition).to.equal(`font 500ms ${easing.standard} 0ms`); - }); - - it('should round decimal digits of "duration" prop to whole numbers', () => { - const transition = create('font', { duration: 12.125 }); - expect(transition).to.equal(`font 12ms ${easing.standard} 0ms`); - }); - - it('should optionally accept string "easing" option in second argument', () => { - const transition = create('transform', { easing: easing.linear }); - expect(transition).to.equal(`transform ${duration.medium1} ${easing.linear} 0ms`); - }); - - it('should optionally accept number "delay" option in second argument', () => { - const transition = create('size', { delay: 150 }); - expect(transition).to.equal(`size ${duration.medium1} ${easing.standard} 150ms`); - }); - - it('should optionally accept string "delay" option in second argument', () => { - const transition = create('size', { delay: '150ms' }); - expect(transition).to.equal(`size ${duration.medium1} ${easing.standard} 150ms`); - }); - - it('should round decimal digits of "delay" prop to whole numbers', () => { - const transition = create('size', { delay: 1.547 }); - expect(transition).to.equal(`size ${duration.medium1} ${easing.standard} 2ms`); - }); - - it('should return zero when not passed arguments', () => { - const zeroHeightDuration = getAutoHeightDuration(); - expect(zeroHeightDuration).to.equal(0); - }); - - it('should return zero when passed undefined', () => { - const zeroHeightDuration = getAutoHeightDuration(undefined); - expect(zeroHeightDuration).to.equal(0); - }); - - it('should return zero when passed null', () => { - const zeroHeightDuration = getAutoHeightDuration(null); - expect(zeroHeightDuration).to.equal(0); - }); - - it('should return NaN when passed a negative number', () => { - const zeroHeightDurationNegativeOne = getAutoHeightDuration(-1); - // eslint-disable-next-line no-restricted-globals - expect(isNaN(zeroHeightDurationNegativeOne)).to.equal(true); - const zeroHeightDurationSmallNegative = getAutoHeightDuration(-0.000001); - // eslint-disable-next-line no-restricted-globals - expect(isNaN(zeroHeightDurationSmallNegative)).to.equal(true); - const zeroHeightDurationBigNegative = getAutoHeightDuration(-100000); - // eslint-disable-next-line no-restricted-globals - expect(isNaN(zeroHeightDurationBigNegative)).to.equal(true); - }); - - it('should return values for pre-calculated positive examples', () => { - let zeroHeightDuration = getAutoHeightDuration(14); - expect(zeroHeightDuration).to.equal(159); - zeroHeightDuration = getAutoHeightDuration(100); - expect(zeroHeightDuration).to.equal(239); - zeroHeightDuration = getAutoHeightDuration(0.0001); - expect(zeroHeightDuration).to.equal(46); - zeroHeightDuration = getAutoHeightDuration(100000); - expect(zeroHeightDuration).to.equal(6685); - }); - }); -}); diff --git a/packages/mui-material-next/src/styles/motion.ts b/packages/mui-material-next/src/styles/motion.ts deleted file mode 100644 index 8e8f0a09855f58..00000000000000 --- a/packages/mui-material-next/src/styles/motion.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { MD3Duration, MD3Easing, MotionOptions } from './Theme.types'; - -// Follows https://m3.material.io/styles/motion/easing-and-duration/tokens-specs -export const duration: MD3Duration = { - short1: '50ms', - short2: '100ms', - short3: '150ms', - short4: '200ms', - medium1: '250ms', - medium2: '300ms', - medium3: '350ms', - medium4: '400ms', - long1: '450ms', - long2: '500ms', - long3: '550ms', - long4: '600ms', - extraLong1: '700ms', - extraLong2: '800ms', - extraLong3: '900ms', - extraLong4: '1000ms', -}; - -export const easing: MD3Easing = { - linear: 'cubic-bezier(0, 0, 1, 1)', - standard: 'cubic-bezier(0.2, 0, 0, 1)', - standardAccelerate: 'cubic-bezier(0.3, 0, 1, 1)', - standardDecelerate: 'cubic-bezier(0, 0, 0, 1)', - emphasized: 'cubic-bezier(0.2, 0, 0, 1)', - emphasizedDecelerate: 'cubic-bezier(0.05, 0.7, 0.1, 1)', - emphasizedAccelerate: 'cubic-bezier(0.3, 0, 0.8, 0.15)', - legacy: 'cubic-bezier(0.4, 0, 0.2, 1)', - legacyDecelerate: 'cubic-bezier(0.0, 0, 0.2, 1)', - legacyAccelerate: 'cubic-bezier(0.4, 0, 1.0, 1)', -}; - -function formatMs(milliseconds: number) { - return `${Math.round(milliseconds)}ms`; -} - -function getAutoHeightDuration(height: number) { - if (!height) { - return 0; - } - - const constant = height / 36; - - // https://www.wolframalpha.com/input/?i=(4+%2B+15+*+(x+%2F+36+)+**+0.25+%2B+(x+%2F+36)+%2F+5)+*+10 - return Math.round((4 + 15 * constant ** 0.25 + constant / 5) * 10); -} - -export default function createMotions(inputMotion: MotionOptions = {}) { - const mergedEasing = { - ...easing, - ...inputMotion.easing, - }; - - const mergedDuration = { - ...duration, - ...inputMotion.duration, - }; - - const create = ( - props = ['all'], - options: { duration?: number | string; easing?: string | string; delay?: number | string } = {}, - ) => { - const { - duration: durationOption = mergedDuration.medium1, - easing: easingOption = mergedEasing.standard, - delay = 0, - ...other - } = options; - - if (process.env.NODE_ENV !== 'production') { - const isString = (value: any) => typeof value === 'string'; - // IE11 support, replace with Number.isNaN - // eslint-disable-next-line no-restricted-globals - const isNumber = (value: any) => !isNaN(parseFloat(value)); - if (!isString(props) && !Array.isArray(props)) { - console.error('MUI: Argument "props" must be a string or Array.'); - } - - if (!isNumber(durationOption) && !isString(durationOption)) { - console.error( - `MUI: Argument "duration" must be a number or a string but found ${durationOption}.`, - ); - } - - if (!isString(easingOption)) { - console.error('MUI: Argument "easing" must be a string.'); - } - - if (!isNumber(delay) && !isString(delay)) { - console.error('MUI: Argument "delay" must be a number or a string.'); - } - - if (Object.keys(other).length !== 0) { - console.error(`MUI: Unrecognized argument(s) [${Object.keys(other).join(',')}].`); - } - } - - return (Array.isArray(props) ? props : [props]) - .map( - (animatedProp) => - `${animatedProp} ${ - typeof durationOption === 'string' ? durationOption : formatMs(durationOption) - } ${easingOption} ${typeof delay === 'string' ? delay : formatMs(delay)}`, - ) - .join(','); - }; - - return { - getAutoHeightDuration, - create, - ...inputMotion, - easing: mergedEasing, - duration: mergedDuration, - }; -} diff --git a/packages/mui-material-next/src/styles/palette.ts b/packages/mui-material-next/src/styles/palette.ts deleted file mode 100644 index bc0cfcc9bc770a..00000000000000 --- a/packages/mui-material-next/src/styles/palette.ts +++ /dev/null @@ -1,147 +0,0 @@ -const mdRefPalette = { - error: { - 0: '#000000', - 10: '#410e0b', - 20: '#601410', - 30: '#8c1d18', - 40: '#b3261e', - 50: '#dc362e', - 60: '#e46962', - 70: '#ec928e', - 80: '#f2b8b5', - 90: '#f9dedc', - 95: '#fceeee', - 99: '#fffbf9', - 100: '#ffffff', - }, - tertiary: { - 0: '#000000', - 10: '#31111d', - 20: '#492532', - 30: '#633b48', - 40: '#7d5260', - 50: '#986977', - 60: '#b58392', - 70: '#d29dac', - 80: '#efb8c8', - 90: '#ffd8e4', - 95: '#ffecf1', - 99: '#fffbfa', - 100: '#ffffff', - }, - secondary: { - 0: '#000000', - 10: '#1d192b', - 20: '#332d41', - 30: '#4a4458', - 40: '#625b71', - 50: '#7a7289', - 60: '#958da5', - 70: '#b0a7c0', - 80: '#ccc2dc', - 90: '#e8def8', - 95: '#f6edff', - 99: '#fffbfe', - 100: '#ffffff', - }, - primary: { - 0: '#000000', - 10: '#21005d', - 20: '#381e72', - 30: '#4f378b', - 40: '#6750a4', - 50: '#7f67be', - 60: '#9a82db', - 70: '#b69df8', - 80: '#d0bcff', - 90: '#eaddff', - 95: '#f6edff', - 99: '#fffbfe', - 100: '#ffffff', - }, - info: { - 0: '#000000', - 10: '#05154C', - 20: '#112874', - 30: '#2D418B', - 40: '#465AA4', - 50: '#6073BF', - 60: '#7A8CDA', - 70: '#95A7F7', - 80: '#B9C4FB', - 90: '#DDE1FD', - 95: '#EFF0FE', - 99: '#FEFBFF', - 100: '#ffffff', - }, - warning: { - 0: '#000000', - 10: '#221B04', - 20: '#3A2F09', - 30: '#534612', - 40: '#6E5D1A', - 50: '#8C7524', - 60: '#A88E31', - 70: '#C5A948', - 80: '#E2C45F', - 90: '#FAE190', - 95: '#FCF1CE', - 99: '#FEFBFF', - 100: '#FFFFFF', - }, - success: { - 0: '#000000', - 10: '#092018', - 20: '#14372A', - 30: '#20503E', - 40: '#2E6B54', - 50: '#3B856A', - 60: '#52A887', - 70: '#69BD9B', - 80: '#85D9B6', - 90: '#A0F5D1', - 95: '#CAFDE5', - 99: '#F6FFF9', - 100: '#FFFFFF', - }, - neutralVariant: { - 0: '#000000', - 10: '#1d1a22', - 20: '#322f37', - 30: '#49454f', - 40: '#605d66', - 50: '#79747e', - 60: '#938f99', - 70: '#aea9b4', - 80: '#cac4d0', - 90: '#e7e0ec', - 95: '#f5eefa', - 99: '#fffbfe', - 100: '#ffffff', - }, - neutral: { - 0: '#000000', - 10: '#1c1b1f', - 20: '#313033', - 17: '#2b2930', - 22: '#36343b', - 30: '#484649', - 40: '#605d62', - 50: '#787579', - 60: '#939094', - 70: '#aeaaae', - 80: '#c9c5ca', - 90: '#e6e1e5', - 92: '#ece6f0', - 95: '#f4eff4', - 96: '#f7f2fa', - 99: '#fffbfe', - 100: '#ffffff', - }, - common: { - black: '#000000', - white: '#ffffff', - }, -}; - -export default mdRefPalette; diff --git a/packages/mui-material-next/src/styles/shape.ts b/packages/mui-material-next/src/styles/shape.ts deleted file mode 100644 index 8da414f66e4e2a..00000000000000 --- a/packages/mui-material-next/src/styles/shape.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MD3Shape } from './Theme.types'; - -const mdSysShape: MD3Shape = { - corner: { - none: '0px', - extraSmall: '4px', - extraSmallTop: '4px 4px 0 0', - small: '8px', - medium: '12px', - large: '16px', - largeEnd: '0 16px 16px 0', - largeTop: '16px 16px 0 0', - extraLarge: '28px', - extraLargeTop: '28px 28px 0 0', - full: '100px', - }, -}; - -export default mdSysShape; diff --git a/packages/mui-material-next/src/styles/shouldSkipGeneratingVar.ts b/packages/mui-material-next/src/styles/shouldSkipGeneratingVar.ts deleted file mode 100644 index 0146d320943c88..00000000000000 --- a/packages/mui-material-next/src/styles/shouldSkipGeneratingVar.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default function shouldSkipGeneratingVar(keys: string[]) { - return ( - !!keys[0].match(/(cssVarPrefix|typography|mixins|breakpoints|direction|transitions)/) || - !!keys[0].match(/sxConfig$/) || // ends with sxConfig - (keys[0] === 'palette' && !!keys[1]?.match(/(mode|contrastThreshold|tonalOffset)/)) - ); -} diff --git a/packages/mui-material-next/src/styles/state.ts b/packages/mui-material-next/src/styles/state.ts deleted file mode 100644 index 2db34a26b47660..00000000000000 --- a/packages/mui-material-next/src/styles/state.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MD3State } from './Theme.types'; - -const mdSysState: MD3State = { - hover: { - stateLayerOpacity: 0.08, - }, - focus: { - stateLayerOpacity: 0.12, - }, - pressed: { - stateLayerOpacity: 0.12, - }, - dragged: { - stateLayerOpacity: 0.16, - }, -}; - -export default mdSysState; diff --git a/packages/mui-material-next/src/styles/styled.test.js b/packages/mui-material-next/src/styles/styled.test.js deleted file mode 100644 index 490fdd9d1ebaed..00000000000000 --- a/packages/mui-material-next/src/styles/styled.test.js +++ /dev/null @@ -1,237 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer, screen } from '@mui-internal/test-utils'; -import { styled } from '@mui/material-next/styles'; - -describe('styled', () => { - const { render } = createRenderer(); - - it('should work', () => { - const Div = styled('div')` - width: 200px; - `; - - const { container } = render(<Div>Test</Div>); - - expect(container.firstChild).toHaveComputedStyle({ - width: '200px', - }); - }); - - it('should use defaultTheme if no theme is provided', () => { - const Div = styled('div')` - width: ${(props) => props.theme.spacing(1)}; - `; - - const { container } = render(<Div>Test</Div>); - - expect(container.firstChild).toHaveComputedStyle({ - width: '8px', - }); - }); - - describe('dynamic styles', () => { - it('can adapt styles to props', () => { - const Div = styled('div')` - font-size: ${(props) => props.scale * 8}px; - padding-left: ${(props) => props.scale * 2}px; - `; - render(<Div scale={4} data-testid="target" />); - - expect(screen.getByTestId('target')).toHaveComputedStyle({ - fontSize: '32px', - paddingLeft: '8px', - }); - }); - }); - - describe('sx prop', () => { - it('should apply color prop to theme.sys.color if available', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - const Div = styled('div')``; - - render(<Div sx={{ color: 'error' }} data-testid="target" />); - - expect(screen.getByTestId('target')).toHaveComputedStyle({ - color: 'rgb(179, 38, 30)', - }); - }); - - it('should apply color prop to theme.ref.palette if available', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - const Div = styled('div')``; - - render(<Div sx={{ color: 'error.80' }} data-testid="target" />); - - expect(screen.getByTestId('target')).toHaveComputedStyle({ - color: 'rgb(242, 184, 181)', - }); - }); - - it('should apply bgcolor prop to theme.sys.color if available', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - const Div = styled('div')``; - - render(<Div sx={{ bgcolor: 'error' }} data-testid="target" />); - - expect(screen.getByTestId('target')).toHaveComputedStyle({ - backgroundColor: 'rgb(179, 38, 30)', - }); - }); - - it('should apply bgcolor prop to theme.ref.palette if available', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - const Div = styled('div')``; - - render(<Div sx={{ bgcolor: 'error.80' }} data-testid="target" />); - - expect(screen.getByTestId('target')).toHaveComputedStyle({ - backgroundColor: 'rgb(242, 184, 181)', - }); - }); - - it('should apply backgroundColor prop to theme.sys.color if available', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - const Div = styled('div')``; - - render(<Div sx={{ backgroundColor: 'error' }} data-testid="target" />); - - expect(screen.getByTestId('target')).toHaveComputedStyle({ - backgroundColor: 'rgb(179, 38, 30)', - }); - }); - - it('should apply backgroundColor prop to theme.ref.palette if available', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - const Div = styled('div')``; - - render(<Div sx={{ backgroundColor: 'error.80' }} data-testid="target" />); - - expect(screen.getByTestId('target')).toHaveComputedStyle({ - backgroundColor: 'rgb(242, 184, 181)', - }); - }); - - it('should apply border*Color props to theme.sys.color if available', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - const Div = styled('div')``; - - render( - <Div - sx={{ - borderTopColor: 'error', - borderBottomColor: 'error', - borderLeftColor: 'error', - borderRightColor: 'error', - }} - data-testid="target" - />, - ); - - expect(screen.getByTestId('target')).toHaveComputedStyle({ - borderTopColor: 'rgb(179, 38, 30)', - borderBottomColor: 'rgb(179, 38, 30)', - borderLeftColor: 'rgb(179, 38, 30)', - borderRightColor: 'rgb(179, 38, 30)', - }); - }); - - it('should apply border*Color props to theme.ref.palette if available', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - const Div = styled('div')``; - - render( - <Div - sx={{ - borderTopColor: 'error.80', - borderBottomColor: 'error.80', - borderLeftColor: 'error.80', - borderRightColor: 'error.80', - }} - data-testid="target" - />, - ); - - expect(screen.getByTestId('target')).toHaveComputedStyle({ - borderTopColor: 'rgb(242, 184, 181)', - borderBottomColor: 'rgb(242, 184, 181)', - borderLeftColor: 'rgb(242, 184, 181)', - borderRightColor: 'rgb(242, 184, 181)', - }); - }); - - it('should apply borderColor props to theme.sys.color if available', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - const Div = styled('div')``; - - render(<Div sx={{ borderColor: 'error' }} data-testid="target" />); - - expect(screen.getByTestId('target')).toHaveComputedStyle({ - borderTopColor: 'rgb(179, 38, 30)', - borderBottomColor: 'rgb(179, 38, 30)', - borderLeftColor: 'rgb(179, 38, 30)', - borderRightColor: 'rgb(179, 38, 30)', - }); - }); - - it('should apply borderColor props to theme.ref.palette if available', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - const Div = styled('div')``; - - render(<Div sx={{ borderColor: 'error.80' }} data-testid="target" />); - - expect(screen.getByTestId('target')).toHaveComputedStyle({ - borderTopColor: 'rgb(242, 184, 181)', - borderBottomColor: 'rgb(242, 184, 181)', - borderLeftColor: 'rgb(242, 184, 181)', - borderRightColor: 'rgb(242, 184, 181)', - }); - }); - - it('should apply borderRadius from sys.shape.corner', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - const Div = styled('div')``; - - render(<Div sx={{ borderRadius: 'small' }} data-testid="target" />); - - expect(screen.getByTestId('target')).toHaveComputedStyle({ - borderTopLeftRadius: '8px', - }); - }); - - it('should multiple borderRadius with theme.shape.borderRadius if provided as number', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - const Div = styled('div')``; - - render(<Div sx={{ borderRadius: 4 }} data-testid="target" />); - - expect(screen.getByTestId('target')).toHaveComputedStyle({ - borderTopLeftRadius: '16px', - }); - }); - }); -}); diff --git a/packages/mui-material-next/src/styles/styled.ts b/packages/mui-material-next/src/styles/styled.ts deleted file mode 100644 index ad240dab03d4cb..00000000000000 --- a/packages/mui-material-next/src/styles/styled.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createStyled, shouldForwardProp } from '@mui/system'; -import { THEME_ID } from '@mui/material/styles'; -import { Theme } from './Theme.types'; -import defaultTheme from './defaultTheme'; - -export const rootShouldForwardProp = (prop: PropertyKey) => - shouldForwardProp(prop) && prop !== 'classes'; - -const styled = createStyled<Theme>({ defaultTheme, themeId: THEME_ID, rootShouldForwardProp }); - -export default styled; diff --git a/packages/mui-material-next/src/styles/sxConfig.ts b/packages/mui-material-next/src/styles/sxConfig.ts deleted file mode 100644 index 83f2932de1c743..00000000000000 --- a/packages/mui-material-next/src/styles/sxConfig.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { - getPath, - handleBreakpoints, - SxConfig, - unstable_defaultSxConfig, - createUnaryUnit, - getValue, -} from '@mui/system'; - -interface PaletteStyleOptions { - prop: string; - cssProperty?: string | boolean; -} - -function createPaletteStyle(options: PaletteStyleOptions = { prop: 'color' }) { - const { prop, cssProperty = options.prop } = options; - - const fn = (props: Record<string, any>) => { - if (props[prop] == null) { - return null; - } - - const propValue: any = props[prop]; - const theme = props.theme; - - const styleFromPropValue = (propValueFinal: any) => { - const value = - getPath(theme, `sys.color.${propValueFinal}`, true) || - getPath(theme, `ref.palette.${propValueFinal}`, true) || - getPath(theme, `palette.${propValueFinal}`, true) || - propValueFinal; - - return { - [cssProperty as string]: value, - }; - }; - - return handleBreakpoints(props, propValue, styleFromPropValue); - }; - return fn; -} - -// eslint-disable-next-line no-restricted-globals -const isNumber = (value: string | number) => typeof value === 'number' || !isNaN(parseFloat(value)); - -const createBorderRadiusStyle = (props: Record<string, any>) => { - if (props.borderRadius !== undefined && props.borderRadius !== null) { - const numberTransformer = createUnaryUnit(props.theme, 'shape.borderRadius', 4, 'borderRadius'); - const styleFromPropValue = (propValue: string | number) => ({ - borderRadius: isNumber(propValue) - ? getValue(numberTransformer, propValue) - : getPath(props.theme, `sys.shape.corner.${propValue}`, true), - }); - return handleBreakpoints(props, props.borderRadius, styleFromPropValue); - } - - return null; -}; - -const sxConfig: SxConfig = { - ...unstable_defaultSxConfig, - color: { - style: createPaletteStyle({ prop: 'color' }), - }, - bgcolor: { - style: createPaletteStyle({ prop: 'bgcolor', cssProperty: 'backgroundColor' }), - }, - backgroundColor: { - style: createPaletteStyle({ prop: 'backgroundColor', cssProperty: 'backgroundColor' }), - }, - borderColor: { - style: createPaletteStyle({ prop: 'borderColor' }), - }, - borderTopColor: { - style: createPaletteStyle({ prop: 'borderTopColor' }), - }, - borderBottomColor: { - style: createPaletteStyle({ prop: 'borderBottomColor' }), - }, - borderLeftColor: { - style: createPaletteStyle({ prop: 'borderLeftColor' }), - }, - borderRightColor: { - style: createPaletteStyle({ prop: 'borderRightColor' }), - }, - borderRadius: { - style: createBorderRadiusStyle, - }, -}; - -export default sxConfig; diff --git a/packages/mui-material-next/src/styles/typeface.ts b/packages/mui-material-next/src/styles/typeface.ts deleted file mode 100644 index 31ff63f106ccaa..00000000000000 --- a/packages/mui-material-next/src/styles/typeface.ts +++ /dev/null @@ -1,11 +0,0 @@ -const mdRefTypeface = { - plain: 'Roboto', - brand: 'Roboto', - weight: { - bold: '700', - medium: '500', - regular: '400', - }, -}; - -export default mdRefTypeface; diff --git a/packages/mui-material-next/src/styles/typescale.ts b/packages/mui-material-next/src/styles/typescale.ts deleted file mode 100644 index 3777d6b7cbe734..00000000000000 --- a/packages/mui-material-next/src/styles/typescale.ts +++ /dev/null @@ -1,123 +0,0 @@ -const mdSysTypescale = { - label: { - small: { - family: 'Roboto', - weight: '500', - size: 11, - tracking: 0.5, - lineHeight: 16, - }, - medium: { - family: 'Roboto', - weight: '500', - // TODO: label.medium has an alternative font weight https://m3.material.io/styles/typography/type-scale-tokens - // md.sys.typescale.label-medium.weight.prominent -> md.sys.typescale.label-medium.weight.prominent -> '700' - size: 12, - tracking: 0.5, - lineHeight: 16, - }, - large: { - family: 'Roboto', - weight: '500', - // TODO: label.large has an alternative font weight https://m3.material.io/styles/typography/type-scale-tokens - // md.sys.typescale.label-large.weight.prominent -> md.sys.typescale.label-large.weight.prominent -> '700' - size: 14, - tracking: 0.1, - lineHeight: 20, - }, - }, - body: { - small: { - family: 'Roboto', - weight: '400', - size: 12, - tracking: 0.4, - lineHeight: 16, - }, - medium: { - family: 'Roboto', - weight: '400', - size: 14, - tracking: 0.25, - lineHeight: 20, - }, - large: { - family: 'Roboto', - weight: '400', - size: 16, - tracking: 0.5, - lineHeight: 24, - }, - }, - title: { - small: { - family: 'Roboto', - weight: '500', - size: 14, - tracking: 0.1, - lineHeight: 20, - }, - medium: { - family: 'Roboto', - weight: '500', - size: 16, - tracking: 0.15, - lineHeight: 24, - }, - large: { - family: 'Roboto', - weight: '400', - size: 22, - tracking: 0, - lineHeight: 28, - }, - }, - headline: { - small: { - family: 'Roboto', - weight: '400', - size: 24, - tracking: 0, - lineHeight: 32, - }, - medium: { - family: 'Roboto', - weight: '400', - size: 28, - tracking: 0, - lineHeight: 36, - }, - large: { - family: 'Roboto', - weight: '400', - size: 32, - tracking: 0, - lineHeight: 40, - }, - }, - display: { - small: { - family: 'Roboto', - weight: '400', - size: 36, - tracking: 0, - lineHeight: 44, - }, - medium: { - family: 'Roboto', - weight: '400', - size: 45, - tracking: 0, - lineHeight: 52, - }, - large: { - family: 'Roboto', - weight: '400', - size: 57, - tracking: -0.25, - lineHeight: 64, - }, - }, -}; - -export default mdSysTypescale; diff --git a/packages/mui-material-next/src/styles/useTheme.ts b/packages/mui-material-next/src/styles/useTheme.ts deleted file mode 100644 index 7ed3bde0980c9c..00000000000000 --- a/packages/mui-material-next/src/styles/useTheme.ts +++ /dev/null @@ -1,18 +0,0 @@ -'use client'; -import * as React from 'react'; -import { useTheme as useSystemTheme } from '@mui/system'; -import defaultTheme from './defaultTheme'; -import THEME_ID from './identifier'; -import { Theme } from './Theme.types'; - -export default function useTheme(): Theme { - const theme = useSystemTheme(defaultTheme); - - if (process.env.NODE_ENV !== 'production') { - // eslint-disable-next-line react-hooks/rules-of-hooks - React.useDebugValue(theme); - } - - // @ts-ignore internal logic - return theme[THEME_ID] || theme; -} diff --git a/packages/mui-material-next/src/styles/useThemeProps.ts b/packages/mui-material-next/src/styles/useThemeProps.ts deleted file mode 100644 index a7a45d28b73a4f..00000000000000 --- a/packages/mui-material-next/src/styles/useThemeProps.ts +++ /dev/null @@ -1,19 +0,0 @@ -'use client'; -import { useThemeProps as systemUseThemeProps } from '@mui/system'; -import defaultTheme from './defaultTheme'; -import THEME_ID from './identifier'; - -export default function useThemeProps<Props extends {}>({ - props, - name, -}: { - props: Props & {}; - name: string; -}) { - return systemUseThemeProps({ - props, - name, - defaultTheme: { ...defaultTheme, components: {} }, - themeId: THEME_ID, - }); -} diff --git a/packages/mui-material-next/src/utils/createSvgIcon.tsx b/packages/mui-material-next/src/utils/createSvgIcon.tsx deleted file mode 100644 index 99d0b9272d1ece..00000000000000 --- a/packages/mui-material-next/src/utils/createSvgIcon.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import * as React from 'react'; -import SvgIcon from '@mui/material/SvgIcon'; - -/** - * Private module reserved for @mui packages. - */ -export default function createSvgIcon(path: React.ReactNode, displayName: string): typeof SvgIcon { - // @ts-ignore internal component - function Component(props, ref) { - return ( - <SvgIcon data-testid={`${displayName}Icon`} ref={ref} {...props}> - {path} - </SvgIcon> - ); - } - - if (process.env.NODE_ENV !== 'production') { - // Need to set `displayName` on the inner component for React.memo. - // React prior to 16.14 ignores `displayName` on the wrapper. - Component.displayName = `${displayName}Icon`; - } - - // @ts-ignore internal component - Component.muiName = SvgIcon.muiName; - - // @ts-ignore internal component - return React.memo(React.forwardRef(Component)); -} diff --git a/packages/mui-material-next/src/utils/shouldSpreadAdditionalProps.ts b/packages/mui-material-next/src/utils/shouldSpreadAdditionalProps.ts deleted file mode 100644 index 0ac3d621438ee5..00000000000000 --- a/packages/mui-material-next/src/utils/shouldSpreadAdditionalProps.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { isHostComponent } from '@mui/base'; - -const shouldSpreadAdditionalProps = (Slot: any) => { - return !Slot || !isHostComponent(Slot); -}; - -export default shouldSpreadAdditionalProps; diff --git a/packages/mui-material-next/test/describeConformance.ts b/packages/mui-material-next/test/describeConformance.ts deleted file mode 100644 index 633cf72e03ebbe..00000000000000 --- a/packages/mui-material-next/test/describeConformance.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { - describeConformance as baseDescribeConformance, - ConformanceOptions, -} from '@mui-internal/test-utils'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; - -export default function describeConformance( - minimalElement: React.ReactElement, - getOptions: () => ConformanceOptions, -) { - function getOptionsWithDefaults() { - return { - ThemeProvider, - createTheme, - ...getOptions(), - }; - } - - return baseDescribeConformance(minimalElement, getOptionsWithDefaults); -} diff --git a/packages/mui-material-next/tsconfig.build.json b/packages/mui-material-next/tsconfig.build.json deleted file mode 100644 index 99aed0580da8a7..00000000000000 --- a/packages/mui-material-next/tsconfig.build.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - // This config is for emitting declarations (.d.ts) only - // Actual .ts source files are transpiled via babel - "extends": "./tsconfig.json", - "compilerOptions": { - "composite": true, - "declaration": true, - "noEmit": false, - "emitDeclarationOnly": true, - "outDir": "build", - "rootDir": "./src" - }, - "include": ["./src/**/*.ts*"], - "exclude": ["src/**/*.spec.ts*", "src/**/*.test.ts*"], - "references": [ - { "path": "../mui-base/tsconfig.build.json" }, - { "path": "../mui-material/tsconfig.build.json" }, - { "path": "../mui-system/tsconfig.build.json" } - ] -} diff --git a/packages/mui-material-next/tsconfig.json b/packages/mui-material-next/tsconfig.json deleted file mode 100644 index 52d43eaaa9b975..00000000000000 --- a/packages/mui-material-next/tsconfig.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "include": ["src/**/*"] -} diff --git a/packages/mui-material/README.md b/packages/mui-material/README.md index 1d88d3f9be3911..13b6dee92b9573 100644 --- a/packages/mui-material/README.md +++ b/packages/mui-material/README.md @@ -33,7 +33,7 @@ Our documentation features [a collection of example projects using Material UI] Read the [contributing guide](/CONTRIBUTING.md) to learn about our development process, how to propose bug fixes and improvements, and how to build and test your changes. Contributing to Material UI is about more than just issues and pull requests! -There are many other ways to [support MUI](https://mui.com/material-ui/getting-started/faq/#mui-is-awesome-how-can-i-support-the-project) beyond contributing to the code base. +There are many other ways to [support Material UI](https://mui.com/material-ui/getting-started/faq/#mui-is-awesome-how-can-i-support-the-project) beyond contributing to the code base. ## Changelog @@ -41,7 +41,7 @@ The [changelog](https://github.com/mui/material-ui/releases) is regularly update ## Roadmap -Future plans and high-priority features and enhancements can be found in our [roadmap](https://mui.com/material-ui/discover-more/roadmap/). +Future plans and high-priority features and enhancements can be found in the [roadmap](https://mui.com/material-ui/discover-more/roadmap/). ## License diff --git a/packages/mui-material/package.json b/packages/mui-material/package.json index a99a20c5f2f4e6..bb840b2d534dec 100644 --- a/packages/mui-material/package.json +++ b/packages/mui-material/package.json @@ -1,6 +1,6 @@ { "name": "@mui/material", - "version": "5.15.12", + "version": "5.15.14", "private": false, "author": "MUI Team", "description": "Material UI is an open-source React component library that implements Google's Material Design. It's comprehensive and can be used in production out of the box.", diff --git a/packages/mui-material/src/Accordion/Accordion.js b/packages/mui-material/src/Accordion/Accordion.js index d7274d92b2d1a9..4fcaf33da96b29 100644 --- a/packages/mui-material/src/Accordion/Accordion.js +++ b/packages/mui-material/src/Accordion/Accordion.js @@ -5,8 +5,7 @@ import PropTypes from 'prop-types'; import clsx from 'clsx'; import chainPropTypes from '@mui/utils/chainPropTypes'; import composeClasses from '@mui/utils/composeClasses'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; +import { styled, createUseThemeProps } from '../zero-styled'; import Collapse from '../Collapse'; import Paper from '../Paper'; import AccordionContext from './AccordionContext'; @@ -14,6 +13,8 @@ import useControlled from '../utils/useControlled'; import useSlot from '../utils/useSlot'; import accordionClasses, { getAccordionUtilityClass } from './accordionClasses'; +const useThemeProps = createUseThemeProps('MuiAccordion'); + const useUtilityClasses = (ownerState) => { const { classes, square, expanded, disabled, disableGutters } = ownerState; @@ -91,28 +92,36 @@ const AccordionRoot = styled(Paper, { }, }; }, - ({ theme, ownerState }) => ({ - ...(!ownerState.square && { - borderRadius: 0, - '&:first-of-type': { - borderTopLeftRadius: (theme.vars || theme).shape.borderRadius, - borderTopRightRadius: (theme.vars || theme).shape.borderRadius, - }, - '&:last-of-type': { - borderBottomLeftRadius: (theme.vars || theme).shape.borderRadius, - borderBottomRightRadius: (theme.vars || theme).shape.borderRadius, - // Fix a rendering issue on Edge - '@supports (-ms-ime-align: auto)': { - borderBottomLeftRadius: 0, - borderBottomRightRadius: 0, + ({ theme }) => ({ + variants: [ + { + props: (props) => !props.square, + style: { + borderRadius: 0, + '&:first-of-type': { + borderTopLeftRadius: (theme.vars || theme).shape.borderRadius, + borderTopRightRadius: (theme.vars || theme).shape.borderRadius, + }, + '&:last-of-type': { + borderBottomLeftRadius: (theme.vars || theme).shape.borderRadius, + borderBottomRightRadius: (theme.vars || theme).shape.borderRadius, + // Fix a rendering issue on Edge + '@supports (-ms-ime-align: auto)': { + borderBottomLeftRadius: 0, + borderBottomRightRadius: 0, + }, + }, }, }, - }), - ...(!ownerState.disableGutters && { - [`&.${accordionClasses.expanded}`]: { - margin: '16px 0', + { + props: (props) => !props.disableGutters, + style: { + [`&.${accordionClasses.expanded}`]: { + margin: '16px 0', + }, + }, }, - }), + ], }), ); diff --git a/packages/mui-material/src/AccordionActions/AccordionActions.js b/packages/mui-material/src/AccordionActions/AccordionActions.js index c6bc67b81056e9..48414535b5d490 100644 --- a/packages/mui-material/src/AccordionActions/AccordionActions.js +++ b/packages/mui-material/src/AccordionActions/AccordionActions.js @@ -3,10 +3,11 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; import composeClasses from '@mui/utils/composeClasses'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; +import { styled, createUseThemeProps } from '../zero-styled'; import { getAccordionActionsUtilityClass } from './accordionActionsClasses'; +const useThemeProps = createUseThemeProps('MuiAccordionActions'); + const useUtilityClasses = (ownerState) => { const { classes, disableSpacing } = ownerState; @@ -25,17 +26,22 @@ const AccordionActionsRoot = styled('div', { return [styles.root, !ownerState.disableSpacing && styles.spacing]; }, -})(({ ownerState }) => ({ +})({ display: 'flex', alignItems: 'center', padding: 8, justifyContent: 'flex-end', - ...(!ownerState.disableSpacing && { - '& > :not(style) ~ :not(style)': { - marginLeft: 8, + variants: [ + { + props: (props) => !props.disableSpacing, + style: { + '& > :not(style) ~ :not(style)': { + marginLeft: 8, + }, + }, }, - }), -})); + ], +}); const AccordionActions = React.forwardRef(function AccordionActions(inProps, ref) { const props = useThemeProps({ props: inProps, name: 'MuiAccordionActions' }); diff --git a/packages/mui-material/src/AccordionDetails/AccordionDetails.js b/packages/mui-material/src/AccordionDetails/AccordionDetails.js index d39ee3bd0b5f72..d49d13b141d3aa 100644 --- a/packages/mui-material/src/AccordionDetails/AccordionDetails.js +++ b/packages/mui-material/src/AccordionDetails/AccordionDetails.js @@ -3,10 +3,11 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; import composeClasses from '@mui/utils/composeClasses'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; +import { styled, createUseThemeProps } from '../zero-styled'; import { getAccordionDetailsUtilityClass } from './accordionDetailsClasses'; +const useThemeProps = createUseThemeProps('MuiAccordionDetails'); + const useUtilityClasses = (ownerState) => { const { classes } = ownerState; diff --git a/packages/mui-material/src/AccordionSummary/AccordionSummary.js b/packages/mui-material/src/AccordionSummary/AccordionSummary.js index 922f9615d5d6e4..6c9ed037c3bd7a 100644 --- a/packages/mui-material/src/AccordionSummary/AccordionSummary.js +++ b/packages/mui-material/src/AccordionSummary/AccordionSummary.js @@ -3,14 +3,15 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; import composeClasses from '@mui/utils/composeClasses'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; +import { styled, createUseThemeProps } from '../zero-styled'; import ButtonBase from '../ButtonBase'; import AccordionContext from '../Accordion/AccordionContext'; import accordionSummaryClasses, { getAccordionSummaryUtilityClass, } from './accordionSummaryClasses'; +const useThemeProps = createUseThemeProps('MuiAccordionSummary'); + const useUtilityClasses = (ownerState) => { const { classes, expanded, disabled, disableGutters } = ownerState; @@ -28,7 +29,7 @@ const AccordionSummaryRoot = styled(ButtonBase, { name: 'MuiAccordionSummary', slot: 'Root', overridesResolver: (props, styles) => styles.root, -})(({ theme, ownerState }) => { +})(({ theme }) => { const transition = { duration: theme.transitions.duration.shortest, }; @@ -47,11 +48,16 @@ const AccordionSummaryRoot = styled(ButtonBase, { [`&:hover:not(.${accordionSummaryClasses.disabled})`]: { cursor: 'pointer', }, - ...(!ownerState.disableGutters && { - [`&.${accordionSummaryClasses.expanded}`]: { - minHeight: 64, + variants: [ + { + props: (props) => !props.disableGutters, + style: { + [`&.${accordionSummaryClasses.expanded}`]: { + minHeight: 64, + }, + }, }, - }), + ], }; }); @@ -59,18 +65,23 @@ const AccordionSummaryContent = styled('div', { name: 'MuiAccordionSummary', slot: 'Content', overridesResolver: (props, styles) => styles.content, -})(({ theme, ownerState }) => ({ +})(({ theme }) => ({ display: 'flex', flexGrow: 1, margin: '12px 0', - ...(!ownerState.disableGutters && { - transition: theme.transitions.create(['margin'], { - duration: theme.transitions.duration.shortest, - }), - [`&.${accordionSummaryClasses.expanded}`]: { - margin: '20px 0', + variants: [ + { + props: (props) => !props.disableGutters, + style: { + transition: theme.transitions.create(['margin'], { + duration: theme.transitions.duration.shortest, + }), + [`&.${accordionSummaryClasses.expanded}`]: { + margin: '20px 0', + }, + }, }, - }), + ], })); const AccordionSummaryExpandIconWrapper = styled('div', { diff --git a/packages/mui-material/src/Autocomplete/Autocomplete.d.ts b/packages/mui-material/src/Autocomplete/Autocomplete.d.ts index 81a0c6d9a292da..bc93447fab6e13 100644 --- a/packages/mui-material/src/Autocomplete/Autocomplete.d.ts +++ b/packages/mui-material/src/Autocomplete/Autocomplete.d.ts @@ -171,7 +171,7 @@ export interface AutocompleteProps< }; /** * If `true`, the component is in a loading state. - * This shows the `loadingText` in place of suggestions (only if there are no suggestions to show, e.g. `options` are empty). + * This shows the `loadingText` in place of suggestions (only if there are no suggestions to show, for example `options` are empty). * @default false */ loading?: boolean; diff --git a/packages/mui-material/src/Autocomplete/Autocomplete.js b/packages/mui-material/src/Autocomplete/Autocomplete.js index 3672e8f334d4d6..5fcfc3d7abcad5 100644 --- a/packages/mui-material/src/Autocomplete/Autocomplete.js +++ b/packages/mui-material/src/Autocomplete/Autocomplete.js @@ -18,12 +18,13 @@ import outlinedInputClasses from '../OutlinedInput/outlinedInputClasses'; import filledInputClasses from '../FilledInput/filledInputClasses'; import ClearIcon from '../internal/svg-icons/Close'; import ArrowDropDownIcon from '../internal/svg-icons/ArrowDropDown'; -import useThemeProps from '../styles/useThemeProps'; -import styled from '../styles/styled'; +import { styled, createUseThemeProps } from '../zero-styled'; import autocompleteClasses, { getAutocompleteUtilityClass } from './autocompleteClasses'; import capitalize from '../utils/capitalize'; import useForkRef from '../utils/useForkRef'; +const useThemeProps = createUseThemeProps('MuiAutocomplete'); + const useUtilityClasses = (ownerState) => { const { classes, @@ -85,7 +86,7 @@ const AutocompleteRoot = styled('div', { hasClearIcon && styles.hasClearIcon, ]; }, -})(({ ownerState }) => ({ +})({ [`&.${autocompleteClasses.focused} .${autocompleteClasses.clearIndicator}`]: { visibility: 'visible', }, @@ -95,16 +96,9 @@ const AutocompleteRoot = styled('div', { visibility: 'visible', }, }, - ...(ownerState.fullWidth && { - width: '100%', - }), [`& .${autocompleteClasses.tag}`]: { margin: 3, maxWidth: 'calc(100% - 6px)', - ...(ownerState.size === 'small' && { - margin: 2, - maxWidth: 'calc(100% - 4px)', - }), }, [`& .${autocompleteClasses.inputRoot}`]: { flexWrap: 'wrap', @@ -198,11 +192,31 @@ const AutocompleteRoot = styled('div', { flexGrow: 1, textOverflow: 'ellipsis', opacity: 0, - ...(ownerState.inputFocused && { - opacity: 1, - }), }, -})); + variants: [ + { + props: { fullWidth: true }, + style: { width: '100%' }, + }, + { + props: { size: 'small' }, + style: { + [`& .${autocompleteClasses.tag}`]: { + margin: 2, + maxWidth: 'calc(100% - 4px)', + }, + }, + }, + { + props: { inputFocused: true }, + style: { + [`& .${autocompleteClasses.input}`]: { + opacity: 1, + }, + }, + }, + ], +}); const AutocompleteEndAdornment = styled('div', { name: 'MuiAutocomplete', @@ -233,13 +247,18 @@ const AutocompletePopupIndicator = styled(IconButton, { ...styles.popupIndicator, ...(ownerState.popupOpen && styles.popupIndicatorOpen), }), -})(({ ownerState }) => ({ +})({ padding: 2, marginRight: -2, - ...(ownerState.popupOpen && { - transform: 'rotate(180deg)', - }), -})); + variants: [ + { + props: { popupOpen: true }, + style: { + transform: 'rotate(180deg)', + }, + }, + ], +}); const AutocompletePopper = styled(Popper, { name: 'MuiAutocomplete', @@ -253,11 +272,16 @@ const AutocompletePopper = styled(Popper, { ownerState.disablePortal && styles.popperDisablePortal, ]; }, -})(({ theme, ownerState }) => ({ +})(({ theme }) => ({ zIndex: (theme.vars || theme).zIndex.modal, - ...(ownerState.disablePortal && { - position: 'absolute', - }), + variants: [ + { + props: { disablePortal: true }, + style: { + position: 'absolute', + }, + }, + ], })); const AutocompletePaper = styled(Paper, { @@ -975,7 +999,7 @@ Autocomplete.propTypes /* remove-proptypes */ = { ListboxProps: PropTypes.object, /** * If `true`, the component is in a loading state. - * This shows the `loadingText` in place of suggestions (only if there are no suggestions to show, e.g. `options` are empty). + * This shows the `loadingText` in place of suggestions (only if there are no suggestions to show, for example `options` are empty). * @default false */ loading: PropTypes.bool, diff --git a/packages/mui-material/src/Autocomplete/autocompleteClasses.ts b/packages/mui-material/src/Autocomplete/autocompleteClasses.ts index 3e937604ab1bf9..863a1719e46490 100644 --- a/packages/mui-material/src/Autocomplete/autocompleteClasses.ts +++ b/packages/mui-material/src/Autocomplete/autocompleteClasses.ts @@ -12,11 +12,11 @@ export interface AutocompleteClasses { focused: string; /** Styles applied to the option elements if they are keyboard focused. */ focusVisible: string; - /** Styles applied to the tag elements, e.g. the chips. */ + /** Styles applied to the tag elements, for example the chips. */ tag: string; - /** Styles applied to the tag elements, e.g. the chips if `size="small"`. */ + /** Styles applied to the tag elements, for example the chips if `size="small"`. */ tagSizeSmall: string; - /** Styles applied to the tag elements, e.g. the chips if `size="medium"`. */ + /** Styles applied to the tag elements, for example the chips if `size="medium"`. */ tagSizeMedium: string; /** Styles applied when the popup icon is rendered. */ hasPopupIcon: string; diff --git a/packages/mui-material/src/Breadcrumbs/Breadcrumbs.js b/packages/mui-material/src/Breadcrumbs/Breadcrumbs.js index 6d9ffc22b684d4..ae105db224e248 100644 --- a/packages/mui-material/src/Breadcrumbs/Breadcrumbs.js +++ b/packages/mui-material/src/Breadcrumbs/Breadcrumbs.js @@ -6,12 +6,13 @@ import clsx from 'clsx'; import integerPropType from '@mui/utils/integerPropType'; import { useSlotProps } from '@mui/base/utils'; import composeClasses from '@mui/utils/composeClasses'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; +import { styled, createUseThemeProps } from '../zero-styled'; import Typography from '../Typography'; import BreadcrumbCollapsed from './BreadcrumbCollapsed'; import breadcrumbsClasses, { getBreadcrumbsUtilityClass } from './breadcrumbsClasses'; +const useThemeProps = createUseThemeProps('MuiBreadcrumbs'); + const useUtilityClasses = (ownerState) => { const { classes } = ownerState; diff --git a/packages/mui-material/src/CircularProgress/CircularProgress.d.ts b/packages/mui-material/src/CircularProgress/CircularProgress.d.ts index ba54f738dfaed0..c0046a5b247e2b 100644 --- a/packages/mui-material/src/CircularProgress/CircularProgress.d.ts +++ b/packages/mui-material/src/CircularProgress/CircularProgress.d.ts @@ -31,7 +31,7 @@ export interface CircularProgressProps /** * The size of the component. * If using a number, the pixel unit is assumed. - * If using a string, you need to provide the CSS unit, e.g. '3rem'. + * If using a string, you need to provide the CSS unit, for example '3rem'. * @default 40 */ size?: number | string; diff --git a/packages/mui-material/src/CircularProgress/CircularProgress.js b/packages/mui-material/src/CircularProgress/CircularProgress.js index e50517d07ec2d8..183b589359da0b 100644 --- a/packages/mui-material/src/CircularProgress/CircularProgress.js +++ b/packages/mui-material/src/CircularProgress/CircularProgress.js @@ -238,7 +238,7 @@ CircularProgress.propTypes /* remove-proptypes */ = { /** * The size of the component. * If using a number, the pixel unit is assumed. - * If using a string, you need to provide the CSS unit, e.g. '3rem'. + * If using a string, you need to provide the CSS unit, for example '3rem'. * @default 40 */ size: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), diff --git a/packages/mui-material/src/Icon/Icon.d.ts b/packages/mui-material/src/Icon/Icon.d.ts index 80ca632a70a7ad..643e8a9667a12d 100644 --- a/packages/mui-material/src/Icon/Icon.d.ts +++ b/packages/mui-material/src/Icon/Icon.d.ts @@ -12,7 +12,7 @@ export interface IconPropsColorOverrides {} export interface IconOwnProps { /** * The base class applied to the icon. Defaults to 'material-icons', but can be changed to any - * other base class that suits the icon font you're using (e.g. material-icons-rounded, fas, etc). + * other base class that suits the icon font you're using (for example material-icons-rounded, fas, etc). * @default 'material-icons' */ baseClassName?: string; diff --git a/packages/mui-material/src/Icon/Icon.js b/packages/mui-material/src/Icon/Icon.js index c98665f504ca04..8a8dbbc9344e4e 100644 --- a/packages/mui-material/src/Icon/Icon.js +++ b/packages/mui-material/src/Icon/Icon.js @@ -111,7 +111,7 @@ Icon.propTypes /* remove-proptypes */ = { // └─────────────────────────────────────────────────────────────────────┘ /** * The base class applied to the icon. Defaults to 'material-icons', but can be changed to any - * other base class that suits the icon font you're using (e.g. material-icons-rounded, fas, etc). + * other base class that suits the icon font you're using (for example material-icons-rounded, fas, etc). * @default 'material-icons' */ baseClassName: PropTypes.string, diff --git a/packages/mui-material/src/Modal/Modal.js b/packages/mui-material/src/Modal/Modal.js index 69b85c5bf6d7b4..801585fcca45c1 100644 --- a/packages/mui-material/src/Modal/Modal.js +++ b/packages/mui-material/src/Modal/Modal.js @@ -9,11 +9,12 @@ import { unstable_useModal as useModal } from '@mui/base/unstable_useModal'; import composeClasses from '@mui/utils/composeClasses'; import FocusTrap from '../Unstable_TrapFocus'; import Portal from '../Portal'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; +import { styled, createUseThemeProps } from '../zero-styled'; import Backdrop from '../Backdrop'; import { getModalUtilityClass } from './modalClasses'; +const useThemeProps = createUseThemeProps('MuiModal'); + const useUtilityClasses = (ownerState) => { const { open, exited, classes } = ownerState; @@ -33,17 +34,21 @@ const ModalRoot = styled('div', { return [styles.root, !ownerState.open && ownerState.exited && styles.hidden]; }, -})(({ theme, ownerState }) => ({ +})(({ theme }) => ({ position: 'fixed', zIndex: (theme.vars || theme).zIndex.modal, right: 0, bottom: 0, top: 0, left: 0, - ...(!ownerState.open && - ownerState.exited && { - visibility: 'hidden', - }), + variants: [ + { + props: ({ ownerState }) => !ownerState.open && ownerState.exited, + style: { + visibility: 'hidden', + }, + }, + ], })); const ModalBackdrop = styled(Backdrop, { diff --git a/packages/mui-material/src/OverridableComponent.d.ts b/packages/mui-material/src/OverridableComponent.d.ts index b84410ef0bc004..1a65de60e6c1f5 100644 --- a/packages/mui-material/src/OverridableComponent.d.ts +++ b/packages/mui-material/src/OverridableComponent.d.ts @@ -11,7 +11,7 @@ export interface OverridableComponent<TypeMap extends OverridableTypeMap> { // If you make any changes to this interface, please make sure to update the // `OverridableComponent` type in `mui-types/index.d.ts` as well. // Also, there are types in Base UI that have a similar shape to this interface - // (e.g. SelectType, OptionType, etc.). + // (for example SelectType, OptionType, etc.). <RootComponent extends React.ElementType>( props: { /** diff --git a/packages/mui-material/src/Popper/Popper.tsx b/packages/mui-material/src/Popper/Popper.tsx index 6609e81a78b2a1..3d946e9e32adf8 100644 --- a/packages/mui-material/src/Popper/Popper.tsx +++ b/packages/mui-material/src/Popper/Popper.tsx @@ -8,7 +8,7 @@ import PropTypes from 'prop-types'; import * as React from 'react'; import { styled, Theme, useThemeProps } from '../styles'; -export type PopperProps = Omit<BasePopperProps, 'direction'> & { +export interface PopperProps extends Omit<BasePopperProps, 'direction'> { /** * The component used for the root node. * Either a string to use a HTML element or a component. @@ -31,7 +31,7 @@ export type PopperProps = Omit<BasePopperProps, 'direction'> & { * The system prop that allows defining system overrides as well as additional CSS styles. */ sx?: SxProps<Theme>; -}; +} const PopperRoot = styled(BasePopper, { name: 'MuiPopper', diff --git a/packages/mui-material/src/Select/Select.d.ts b/packages/mui-material/src/Select/Select.d.ts index cbf55494ba697f..c39944e1e0ded8 100644 --- a/packages/mui-material/src/Select/Select.d.ts +++ b/packages/mui-material/src/Select/Select.d.ts @@ -148,7 +148,7 @@ export interface BaseSelectProps<Value = unknown> * The variant to use. * @default 'outlined' */ - variant?: 'filled' | 'standard' | 'outlined'; + variant?: SelectVariants; } export interface FilledSelectProps extends Omit<FilledInputProps, 'value' | 'onChange'> { @@ -172,20 +172,15 @@ export interface OutlinedSelectProps extends Omit<OutlinedInputProps, 'value' | * The variant to use. * @default 'outlined' */ - variant: 'outlined'; + variant?: 'outlined'; } export type SelectVariants = 'outlined' | 'standard' | 'filled'; -export type SelectProps< - Value = unknown, - Variant extends SelectVariants = SelectVariants, -> = BaseSelectProps<Value> & - (Variant extends 'filled' - ? FilledSelectProps - : Variant extends 'standard' - ? StandardSelectProps - : OutlinedSelectProps); +export type SelectProps<Value = unknown> = + | (FilledSelectProps & BaseSelectProps<Value>) + | (StandardSelectProps & BaseSelectProps<Value>) + | (OutlinedSelectProps & BaseSelectProps<Value>); /** * @@ -198,15 +193,8 @@ export type SelectProps< * - [Select API](https://mui.com/material-ui/api/select/) * - inherits [OutlinedInput API](https://mui.com/material-ui/api/outlined-input/) */ - -export default function Select<Value = unknown, Variant extends SelectVariants = 'outlined'>( - props: { - /** - * The variant to use. - * @default 'outlined' - */ - variant?: Variant; - } & Omit<SelectProps<Value, Variant>, 'variant'>, +export default function Select<Value = unknown>( + props: SelectProps<Value>, ): JSX.Element & { muiName: string; }; diff --git a/packages/mui-material/src/Select/Select.spec.tsx b/packages/mui-material/src/Select/Select.spec.tsx index 25283d8a874054..78f69c76c606d0 100644 --- a/packages/mui-material/src/Select/Select.spec.tsx +++ b/packages/mui-material/src/Select/Select.spec.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import Select, { SelectChangeEvent } from '@mui/material/Select'; +import Select, { SelectChangeEvent, SelectProps } from '@mui/material/Select'; import MenuItem from '@mui/material/MenuItem'; import { createTheme } from '@mui/material/styles'; @@ -75,4 +75,124 @@ function genericValueTest() { }, }} />; + + <Select variant="filled" />; + <Select variant="standard" />; + <Select variant="outlined" />; + <Select />; + + <Select variant="filled" hiddenLabel />; + // @ts-expect-error hiddenLabel is not present in standard variant + <Select variant="standard" hiddenLabel />; + // @ts-expect-error hiddenLabel is not present in outlined variant + <Select variant="outlined" hiddenLabel />; + // @ts-expect-error hiddenLabel is not present in outlined variant + <Select hiddenLabel />; + + const defaultProps: SelectProps<number> = {}; + const outlinedProps: SelectProps<number> = { + variant: 'outlined', + }; + const filledProps: SelectProps<number> = { + variant: 'filled', + }; + const standardProps: SelectProps<number> = { + variant: 'standard', + }; + + <Select {...defaultProps} />; + <Select {...outlinedProps} />; + <Select {...filledProps} />; + <Select {...standardProps} />; + <Select<number> {...outlinedProps} />; + <Select<number> {...defaultProps} />; + <Select<number> {...filledProps} />; + + const rawDefaultProps: SelectProps = {}; + const rawOutlinedProps: SelectProps = { + variant: 'outlined', + }; + const rawFilledProps: SelectProps = { + variant: 'filled', + }; + + <Select {...rawDefaultProps} />; + <Select {...rawOutlinedProps} />; + <Select {...rawFilledProps} />; + + // @ts-expect-error hiddenLabel is not present in outlined variant + <Select {...defaultProps} hiddenLabel />; + // @ts-expect-error hiddenLabel is not present in outlined variant + <Select {...outlinedProps} hiddenLabel />; + <Select {...filledProps} hiddenLabel />; + // @ts-expect-error hiddenLabel is not present in standard variant + <Select {...standardProps} hiddenLabel />; + + interface OtherProps { + otherProp?: number; + } + + const SelectWrapper1 = <Value,>(props: SelectProps<Value> & OtherProps) => { + const { otherProp, ...materialSelectProps } = props; + + return <Select<Value> {...materialSelectProps} />; + }; + + <SelectWrapper1 />; + <SelectWrapper1<number> variant="filled" hiddenLabel />; + <SelectWrapper1 variant="filled" hiddenLabel />; } + +type Options<T> = { text: string; value: T } | T; + +type Props<T> = ( + | { + value: T; + multiple?: false; + onChange: (value: T) => void; + } + | { + value: T[]; + multiple: true; + onChange: (value: T[]) => void; + } +) & { + options: Options<T>[]; +}; + +// test for https://github.com/mui/material-ui/issues/41375 +const AppSelect = <T extends string>(props: Props<T>) => { + const getOptionText = (option: Options<T>) => { + if (typeof option === 'object') { + return option.text; + } + return option; + }; + + const getOptionValue = (option: Options<T>) => { + if (typeof option === 'object') { + return option.value; + } + return option; + }; + + return ( + <Select + value={props.value} + multiple={props.multiple} + onChange={(event) => { + if (props.multiple) { + props.onChange(event.target.value as T[]); + } else { + props.onChange(event.target.value as T); + } + }} + > + {props.options.map((option, index) => ( + <MenuItem key={index} value={getOptionValue(option)}> + {getOptionText(option)} + </MenuItem> + ))} + </Select> + ); +}; diff --git a/packages/mui-material/src/Slider/Slider.js b/packages/mui-material/src/Slider/Slider.js index 096e13f1c25555..5b5ed8c9e97f61 100644 --- a/packages/mui-material/src/Slider/Slider.js +++ b/packages/mui-material/src/Slider/Slider.js @@ -8,13 +8,15 @@ import composeClasses from '@mui/utils/composeClasses'; import { useSlider, valueToPercent } from '@mui/base/useSlider'; import { alpha, lighten, darken } from '@mui/system/colorManipulator'; import { useRtl } from '@mui/system/RtlProvider'; -import useThemeProps from '../styles/useThemeProps'; -import styled, { slotShouldForwardProp } from '../styles/styled'; +import { styled, createUseThemeProps } from '../zero-styled'; +import slotShouldForwardProp from '../styles/slotShouldForwardProp'; import shouldSpreadAdditionalProps from '../utils/shouldSpreadAdditionalProps'; import capitalize from '../utils/capitalize'; import BaseSliderValueLabel from './SliderValueLabel'; import sliderClasses, { getSliderUtilityClass } from './sliderClasses'; +const useThemeProps = createUseThemeProps('MuiSlider'); + function Identity(x) { return x; } @@ -35,47 +37,14 @@ export const SliderRoot = styled('span', { ownerState.track === false && styles.trackFalse, ]; }, -})(({ theme, ownerState }) => ({ +})(({ theme }) => ({ borderRadius: 12, boxSizing: 'content-box', display: 'inline-block', position: 'relative', cursor: 'pointer', touchAction: 'none', - color: (theme.vars || theme).palette[ownerState.color].main, WebkitTapHighlightColor: 'transparent', - ...(ownerState.orientation === 'horizontal' && { - height: 4, - width: '100%', - padding: '13px 0', - // The primary input mechanism of the device includes a pointing device of limited accuracy. - '@media (pointer: coarse)': { - // Reach 42px touch target, about ~8mm on screen. - padding: '20px 0', - }, - ...(ownerState.size === 'small' && { - height: 2, - }), - ...(ownerState.marked && { - marginBottom: 20, - }), - }), - ...(ownerState.orientation === 'vertical' && { - height: '100%', - width: 4, - padding: '0 13px', - // The primary input mechanism of the device includes a pointing device of limited accuracy. - '@media (pointer: coarse)': { - // Reach 42px touch target, about ~8mm on screen. - padding: '0 20px', - }, - ...(ownerState.size === 'small' && { - width: 2, - }), - ...(ownerState.marked && { - marginRight: 44, - }), - }), '@media print': { colorAdjust: 'exact', }, @@ -89,44 +58,111 @@ export const SliderRoot = styled('span', { transition: 'none', }, }, + variants: [ + ...Object.keys((theme.vars ?? theme).palette) + .filter((key) => (theme.vars ?? theme).palette[key].main) + .map((color) => ({ + props: { color }, + style: { + color: (theme.vars || theme).palette[color].main, + }, + })), + { + props: { orientation: 'horizontal' }, + style: { + height: 4, + width: '100%', + padding: '13px 0', + // The primary input mechanism of the device includes a pointing device of limited accuracy. + '@media (pointer: coarse)': { + // Reach 42px touch target, about ~8mm on screen. + padding: '20px 0', + }, + }, + }, + { + props: { orientation: 'horizontal', size: 'small' }, + style: { + height: 2, + }, + }, + { + props: { orientation: 'horizontal', marked: true }, + style: { + marginBottom: 20, + }, + }, + { + props: { orientation: 'vertical' }, + style: { + height: '100%', + width: 4, + padding: '0 13px', + // The primary input mechanism of the device includes a pointing device of limited accuracy. + '@media (pointer: coarse)': { + // Reach 42px touch target, about ~8mm on screen. + padding: '0 20px', + }, + }, + }, + { + props: { orientation: 'vertical', size: 'small' }, + style: { + width: 2, + }, + }, + { + props: { orientation: 'vertical', marked: true }, + style: { + marginRight: 44, + }, + }, + ], })); export const SliderRail = styled('span', { name: 'MuiSlider', slot: 'Rail', overridesResolver: (props, styles) => styles.rail, -})(({ ownerState }) => ({ +})({ display: 'block', position: 'absolute', borderRadius: 'inherit', backgroundColor: 'currentColor', opacity: 0.38, - ...(ownerState.orientation === 'horizontal' && { - width: '100%', - height: 'inherit', - top: '50%', - transform: 'translateY(-50%)', - }), - ...(ownerState.orientation === 'vertical' && { - height: '100%', - width: 'inherit', - left: '50%', - transform: 'translateX(-50%)', - }), - ...(ownerState.track === 'inverted' && { - opacity: 1, - }), -})); + variants: [ + { + props: { orientation: 'horizontal' }, + style: { + width: '100%', + height: 'inherit', + top: '50%', + transform: 'translateY(-50%)', + }, + }, + { + props: { orientation: 'vertical' }, + style: { + height: '100%', + width: 'inherit', + left: '50%', + transform: 'translateX(-50%)', + }, + }, + { + props: { track: 'inverted' }, + style: { + opacity: 1, + }, + }, + ], +}); export const SliderTrack = styled('span', { name: 'MuiSlider', slot: 'Track', overridesResolver: (props, styles) => styles.track, -})(({ theme, ownerState }) => { - const color = // Same logic as the LinearProgress track color - theme.palette.mode === 'light' - ? lighten(theme.palette[ownerState.color].main, 0.62) - : darken(theme.palette[ownerState.color].main, 0.5); +})(({ theme }) => { return { display: 'block', position: 'absolute', @@ -136,26 +172,58 @@ export const SliderTrack = styled('span', { transition: theme.transitions.create(['left', 'width', 'bottom', 'height'], { duration: theme.transitions.duration.shortest, }), - ...(ownerState.size === 'small' && { - border: 'none', - }), - ...(ownerState.orientation === 'horizontal' && { - height: 'inherit', - top: '50%', - transform: 'translateY(-50%)', - }), - ...(ownerState.orientation === 'vertical' && { - width: 'inherit', - left: '50%', - transform: 'translateX(-50%)', - }), - ...(ownerState.track === false && { - display: 'none', - }), - ...(ownerState.track === 'inverted' && { - backgroundColor: theme.vars ? theme.vars.palette.Slider[`${ownerState.color}Track`] : color, - borderColor: theme.vars ? theme.vars.palette.Slider[`${ownerState.color}Track`] : color, - }), + variants: [ + { + props: { size: 'small' }, + style: { + border: 'none', + }, + }, + { + props: { orientation: 'horizontal' }, + style: { + height: 'inherit', + top: '50%', + transform: 'translateY(-50%)', + }, + }, + { + props: { orientation: 'vertical' }, + style: { + width: 'inherit', + left: '50%', + transform: 'translateX(-50%)', + }, + }, + { + props: { track: false }, + style: { + display: 'none', + }, + }, + ...Object.keys((theme.vars ?? theme).palette) + .filter((key) => (theme.vars ?? theme).palette[key].main) + .map((color) => ({ + props: { color, track: 'inverted' }, + style: { + ...(theme.vars + ? { + backgroundColor: theme.vars.palette.Slider[`${color}Track`], + borderColor: theme.vars.palette.Slider[`${color}Track`], + } + : { + backgroundColor: lighten(theme.palette[color].main, 0.62), + borderColor: lighten(theme.palette[color].main, 0.62), + ...theme.applyStyles('dark', { + backgroundColor: darken(theme.palette[color].main, 0.5), + }), + ...theme.applyStyles('dark', { + borderColor: darken(theme.palette[color].main, 0.5), + }), + }), + }, + })), + ], }; }); @@ -170,7 +238,7 @@ export const SliderThumb = styled('span', { ownerState.size !== 'medium' && styles[`thumbSize${capitalize(ownerState.size)}`], ]; }, -})(({ theme, ownerState }) => ({ +})(({ theme }) => ({ position: 'absolute', width: 20, height: 20, @@ -184,18 +252,6 @@ export const SliderThumb = styled('span', { transition: theme.transitions.create(['box-shadow', 'left', 'bottom'], { duration: theme.transitions.duration.shortest, }), - ...(ownerState.size === 'small' && { - width: 12, - height: 12, - }), - ...(ownerState.orientation === 'horizontal' && { - top: '50%', - transform: 'translate(-50%, -50%)', - }), - ...(ownerState.orientation === 'vertical' && { - left: '50%', - transform: 'translate(-50%, 50%)', - }), '&::before': { position: 'absolute', content: '""', @@ -203,9 +259,6 @@ export const SliderThumb = styled('span', { width: '100%', height: '100%', boxShadow: (theme.vars || theme).shadows[2], - ...(ownerState.size === 'small' && { - boxShadow: 'none', - }), }, '&::after': { position: 'absolute', @@ -218,40 +271,72 @@ export const SliderThumb = styled('span', { left: '50%', transform: 'translate(-50%, -50%)', }, - [`&:hover, &.${sliderClasses.focusVisible}`]: { - boxShadow: `0px 0px 0px 8px ${ - theme.vars - ? `rgba(${theme.vars.palette[ownerState.color].mainChannel} / 0.16)` - : alpha(theme.palette[ownerState.color].main, 0.16) - }`, - '@media (hover: none)': { - boxShadow: 'none', - }, - }, - [`&.${sliderClasses.active}`]: { - boxShadow: `0px 0px 0px 14px ${ - theme.vars - ? `rgba(${theme.vars.palette[ownerState.color].mainChannel} / 0.16)` - : alpha(theme.palette[ownerState.color].main, 0.16) - }`, - }, [`&.${sliderClasses.disabled}`]: { '&:hover': { boxShadow: 'none', }, }, + variants: [ + ...Object.keys((theme.vars ?? theme).palette) + .filter((key) => (theme.vars ?? theme).palette[key].main) + .map((color) => ({ + props: { color }, + style: { + [`&:hover, &.${sliderClasses.focusVisible}`]: { + ...(theme.vars + ? { + boxShadow: `0px 0px 0px 8px rgba(${theme.vars.palette[color].mainChannel} / 0.16)`, + } + : { + boxShadow: `0px 0px 0px 8px ${alpha(theme.palette[color].main, 0.16)}`, + }), + '@media (hover: none)': { + boxShadow: 'none', + }, + }, + [`&.${sliderClasses.active}`]: { + ...(theme.vars + ? { + boxShadow: `0px 0px 0px 14px rgba(${theme.vars.palette[color].mainChannel} / 0.16)}`, + } + : { + boxShadow: `0px 0px 0px 14px ${alpha(theme.palette[color].main, 0.16)}`, + }), + }, + }, + })), + { + props: { size: 'small' }, + style: { + width: 12, + height: 12, + '&::before': { + boxShadow: 'none', + }, + }, + }, + { + props: { orientation: 'horizontal' }, + style: { + top: '50%', + transform: 'translate(-50%, -50%)', + }, + }, + { + props: { orientation: 'vertical' }, + style: { + left: '50%', + transform: 'translate(-50%, 50%)', + }, + }, + ], })); export const SliderValueLabel = styled(BaseSliderValueLabel, { name: 'MuiSlider', slot: 'ValueLabel', overridesResolver: (props, styles) => styles.valueLabel, -})(({ theme, ownerState }) => ({ - [`&.${sliderClasses.valueLabelOpen}`]: { - transform: `${ - ownerState.orientation === 'vertical' ? 'translateY(-50%)' : 'translateY(-100%)' - } scale(1)`, - }, +})(({ theme }) => ({ zIndex: 1, whiteSpace: 'nowrap', ...theme.typography.body2, @@ -259,9 +344,6 @@ export const SliderValueLabel = styled(BaseSliderValueLabel, { transition: theme.transitions.create(['transform'], { duration: theme.transitions.duration.shortest, }), - transform: `${ - ownerState.orientation === 'vertical' ? 'translateY(-50%)' : 'translateY(-100%)' - } scale(0)`, position: 'absolute', backgroundColor: (theme.vars || theme).palette.grey[600], borderRadius: 2, @@ -270,39 +352,64 @@ export const SliderValueLabel = styled(BaseSliderValueLabel, { alignItems: 'center', justifyContent: 'center', padding: '0.25rem 0.75rem', - ...(ownerState.orientation === 'horizontal' && { - top: '-10px', - transformOrigin: 'bottom center', - '&::before': { - position: 'absolute', - content: '""', - width: 8, - height: 8, - transform: 'translate(-50%, 50%) rotate(45deg)', - backgroundColor: 'inherit', - bottom: 0, - left: '50%', + variants: [ + { + props: { orientation: 'horizontal' }, + style: { + transform: 'translateY(-100%) scale(0)', + top: '-10px', + transformOrigin: 'bottom center', + '&::before': { + position: 'absolute', + content: '""', + width: 8, + height: 8, + transform: 'translate(-50%, 50%) rotate(45deg)', + backgroundColor: 'inherit', + bottom: 0, + left: '50%', + }, + [`&.${sliderClasses.valueLabelOpen}`]: { + transform: 'translateY(-100%) scale(1)', + }, + }, }, - }), - ...(ownerState.orientation === 'vertical' && { - right: ownerState.size === 'small' ? '20px' : '30px', - top: '50%', - transformOrigin: 'right center', - '&::before': { - position: 'absolute', - content: '""', - width: 8, - height: 8, - transform: 'translate(-50%, -50%) rotate(45deg)', - backgroundColor: 'inherit', - right: -8, - top: '50%', + { + props: { orientation: 'vertical' }, + style: { + transform: 'translateY(-50%) scale(0)', + right: '30px', + top: '50%', + transformOrigin: 'right center', + '&::before': { + position: 'absolute', + content: '""', + width: 8, + height: 8, + transform: 'translate(-50%, -50%) rotate(45deg)', + backgroundColor: 'inherit', + right: -8, + top: '50%', + }, + [`&.${sliderClasses.valueLabelOpen}`]: { + transform: 'translateY(-50%) scale(1)', + }, + }, }, - }), - ...(ownerState.size === 'small' && { - fontSize: theme.typography.pxToRem(12), - padding: '0.25rem 0.5rem', - }), + { + props: { size: 'small' }, + style: { + fontSize: theme.typography.pxToRem(12), + padding: '0.25rem 0.5rem', + }, + }, + { + props: { orientation: 'vertical', size: 'small' }, + style: { + right: '20px', + }, + }, + ], })); export const SliderMark = styled('span', { @@ -314,24 +421,35 @@ export const SliderMark = styled('span', { return [styles.mark, markActive && styles.markActive]; }, -})(({ theme, ownerState, markActive }) => ({ +})(({ theme }) => ({ position: 'absolute', width: 2, height: 2, borderRadius: 1, backgroundColor: 'currentColor', - ...(ownerState.orientation === 'horizontal' && { - top: '50%', - transform: 'translate(-1px, -50%)', - }), - ...(ownerState.orientation === 'vertical' && { - left: '50%', - transform: 'translate(-50%, 1px)', - }), - ...(markActive && { - backgroundColor: (theme.vars || theme).palette.background.paper, - opacity: 0.8, - }), + variants: [ + { + props: { orientation: 'horizontal' }, + style: { + top: '50%', + transform: 'translate(-1px, -50%)', + }, + }, + { + props: { orientation: 'vertical' }, + style: { + left: '50%', + transform: 'translate(-50%, 1px)', + }, + }, + { + props: { markActive: true }, + style: { + backgroundColor: (theme.vars || theme).palette.background.paper, + opacity: 0.8, + }, + }, + ], })); export const SliderMarkLabel = styled('span', { @@ -339,28 +457,39 @@ export const SliderMarkLabel = styled('span', { slot: 'MarkLabel', shouldForwardProp: (prop) => slotShouldForwardProp(prop) && prop !== 'markLabelActive', overridesResolver: (props, styles) => styles.markLabel, -})(({ theme, ownerState, markLabelActive }) => ({ +})(({ theme }) => ({ ...theme.typography.body2, color: (theme.vars || theme).palette.text.secondary, position: 'absolute', whiteSpace: 'nowrap', - ...(ownerState.orientation === 'horizontal' && { - top: 30, - transform: 'translateX(-50%)', - '@media (pointer: coarse)': { - top: 40, + variants: [ + { + props: { orientation: 'horizontal' }, + style: { + top: 30, + transform: 'translateX(-50%)', + '@media (pointer: coarse)': { + top: 40, + }, + }, }, - }), - ...(ownerState.orientation === 'vertical' && { - left: 36, - transform: 'translateY(50%)', - '@media (pointer: coarse)': { - left: 44, + { + props: { orientation: 'vertical' }, + style: { + left: 36, + transform: 'translateY(50%)', + '@media (pointer: coarse)': { + left: 44, + }, + }, }, - }), - ...(markLabelActive && { - color: (theme.vars || theme).palette.text.primary, - }), + { + props: { markLabelActive: true }, + style: { + color: (theme.vars || theme).palette.text.primary, + }, + }, + ], })); const useUtilityClasses = (ownerState) => { diff --git a/packages/mui-material/src/SvgIcon/SvgIcon.js b/packages/mui-material/src/SvgIcon/SvgIcon.js index 3548612a6b256d..e99c51e720f9a9 100644 --- a/packages/mui-material/src/SvgIcon/SvgIcon.js +++ b/packages/mui-material/src/SvgIcon/SvgIcon.js @@ -40,7 +40,7 @@ const SvgIconRoot = styled('svg', { height: '1em', display: 'inline-block', // the <svg> will define the property that has `currentColor` - // e.g. heroicons uses fill="none" and stroke="currentColor" + // for example heroicons uses fill="none" and stroke="currentColor" fill: ownerState.hasSvgAsChild ? undefined : 'currentColor', flexShrink: 0, transition: theme.transitions?.create?.('fill', { diff --git a/packages/mui-material/src/Tooltip/Tooltip.js b/packages/mui-material/src/Tooltip/Tooltip.js index fa17187fdbb82d..05cea653d79888 100644 --- a/packages/mui-material/src/Tooltip/Tooltip.js +++ b/packages/mui-material/src/Tooltip/Tooltip.js @@ -224,11 +224,11 @@ export function testReset() { } function composeEventHandler(handler, eventHandler) { - return (event) => { + return (event, ...params) => { if (eventHandler) { - eventHandler(event); + eventHandler(event, ...params); } - handler(event); + handler(event, ...params); }; } diff --git a/packages/mui-material/src/Tooltip/Tooltip.test.js b/packages/mui-material/src/Tooltip/Tooltip.test.js index 83ea7d886991a5..f63ab0555bdfdb 100644 --- a/packages/mui-material/src/Tooltip/Tooltip.test.js +++ b/packages/mui-material/src/Tooltip/Tooltip.test.js @@ -963,6 +963,45 @@ describe('<Tooltip />', () => { expect(handleFocus.callCount).to.equal(1); expect(handleFocus.returned(input)).to.equal(true); }); + + // https://github.com/mui/mui-x/issues/12248 + it('should support event handlers with extra parameters', () => { + const handleFocus = spy((event, extra) => extra); + const handleBlur = spy((event, ...params) => params); + + const TextField = React.forwardRef(function TextField(props, ref) { + const { onFocus, onBlur, ...other } = props; + return ( + <div ref={ref} {...other}> + <input + type="text" + onFocus={(event) => onFocus(event, 'focus')} + onBlur={(event) => onBlur(event, 'blur', 1)} + /> + </div> + ); + }); + render( + <Tooltip open title="test"> + <TextField onFocus={handleFocus} onBlur={handleBlur} variant="standard" /> + </Tooltip>, + ); + const input = screen.getByRole('textbox'); + + act(() => { + input.focus(); + }); + + expect(handleFocus.callCount).to.equal(1); + expect(handleFocus.returnValues[0]).to.equal('focus'); + + act(() => { + input.blur(); + }); + + expect(handleBlur.callCount).to.equal(1); + expect(handleBlur.returnValues[0]).to.deep.equal(['blur', 1]); + }); }); describe('warnings', () => { diff --git a/packages/mui-material/src/styles/CssVarsProvider.test.js b/packages/mui-material/src/styles/CssVarsProvider.test.js index 31c1aa8f733b10..8b34b5b546cdee 100644 --- a/packages/mui-material/src/styles/CssVarsProvider.test.js +++ b/packages/mui-material/src/styles/CssVarsProvider.test.js @@ -153,6 +153,7 @@ describe('[Material UI] CssVarsProvider', () => { paper: 'var(--mui-palette-background-paper)', default: 'var(--mui-palette-background-default)', defaultChannel: 'var(--mui-palette-background-defaultChannel)', + paperChannel: 'var(--mui-palette-background-paperChannel)', }), ); expect(screen.getByTestId('palette-action').textContent).to.equal( diff --git a/packages/mui-material/src/styles/CssVarsProvider.tsx b/packages/mui-material/src/styles/CssVarsProvider.tsx index 90cd0733dd72a7..75f5c6e6f18154 100644 --- a/packages/mui-material/src/styles/CssVarsProvider.tsx +++ b/packages/mui-material/src/styles/CssVarsProvider.tsx @@ -35,10 +35,7 @@ const { CssVarsProvider, useColorScheme, getInitColorSchemeScript } = createCssV }; newTheme.unstable_sx = function sx(props: SxProps<CssVarsTheme>) { - return styleFunctionSx({ - sx: props, - theme: this, - }); + return styleFunctionSx({ sx: props, theme: this }); }; return newTheme; diff --git a/packages/mui-material/src/styles/experimental_extendTheme.d.ts b/packages/mui-material/src/styles/experimental_extendTheme.d.ts index fa42f2a605957a..e2d45afbbc7b93 100644 --- a/packages/mui-material/src/styles/experimental_extendTheme.d.ts +++ b/packages/mui-material/src/styles/experimental_extendTheme.d.ts @@ -69,6 +69,7 @@ export type Overlays = [ export interface PaletteBackgroundChannel { defaultChannel: string; + paperChannel: string; } export interface PaletteCommonChannel { diff --git a/packages/mui-material/src/styles/experimental_extendTheme.js b/packages/mui-material/src/styles/experimental_extendTheme.js index 3d8c2a31f3f357..10bfdb80e22920 100644 --- a/packages/mui-material/src/styles/experimental_extendTheme.js +++ b/packages/mui-material/src/styles/experimental_extendTheme.js @@ -57,7 +57,7 @@ function setColorChannel(obj, key) { toRgb(obj[key]), `MUI: Can't create \`palette.${key}Channel\` because \`palette.${key}\` is not one of these formats: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla(), color().` + '\n' + - `To suppress this warning, you need to explicitly provide the \`palette.${key}Channel\` as a string (in rgb format, e.g. "12 12 12") or undefined if you want to remove the channel token.`, + `To suppress this warning, you need to explicitly provide the \`palette.${key}Channel\` as a string (in rgb format, for example "12 12 12") or undefined if you want to remove the channel token.`, ); } } @@ -344,6 +344,9 @@ export default function extendTheme(options = {}, ...args) { // MUI X - DataGrid needs this token. setColorChannel(palette.background, 'default'); + // added for consistency with the `background.default` token + setColorChannel(palette.background, 'paper'); + setColorChannel(palette.common, 'background'); setColorChannel(palette.common, 'onBackground'); diff --git a/packages/mui-material/src/styles/experimental_extendTheme.test.js b/packages/mui-material/src/styles/experimental_extendTheme.test.js index c7990a0043b46b..d90f899199d140 100644 --- a/packages/mui-material/src/styles/experimental_extendTheme.test.js +++ b/packages/mui-material/src/styles/experimental_extendTheme.test.js @@ -59,6 +59,9 @@ describe('experimental_extendTheme', () => { expect(theme.colorSchemes.dark.palette.background.defaultChannel).to.equal('18 18 18'); expect(theme.colorSchemes.light.palette.background.defaultChannel).to.equal('255 255 255'); + expect(theme.colorSchemes.dark.palette.background.paperChannel).to.equal('18 18 18'); + expect(theme.colorSchemes.light.palette.background.paperChannel).to.equal('255 255 255'); + expect(theme.colorSchemes.dark.palette.primary.mainChannel).to.equal('144 202 249'); expect(theme.colorSchemes.dark.palette.primary.darkChannel).to.equal('66 165 245'); expect(theme.colorSchemes.dark.palette.primary.lightChannel).to.equal('227 242 253'); @@ -428,7 +431,7 @@ describe('experimental_extendTheme', () => { ).toWarnDev( "MUI: Can't create `palette.dividerChannel` because `palette.divider` is not one of these formats: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla(), color()." + '\n' + - 'To suppress this warning, you need to explicitly provide the `palette.dividerChannel` as a string (in rgb format, e.g. "12 12 12") or undefined if you want to remove the channel token.', + 'To suppress this warning, you need to explicitly provide the `palette.dividerChannel` as a string (in rgb format, for example "12 12 12") or undefined if you want to remove the channel token.', ); }); diff --git a/packages/mui-material/src/styles/rootShouldForwardProp.ts b/packages/mui-material/src/styles/rootShouldForwardProp.ts new file mode 100644 index 00000000000000..92a781135a85a2 --- /dev/null +++ b/packages/mui-material/src/styles/rootShouldForwardProp.ts @@ -0,0 +1,5 @@ +import slotShouldForwardProp from './slotShouldForwardProp'; + +const rootShouldForwardProp = (prop: string) => slotShouldForwardProp(prop) && prop !== 'classes'; + +export default rootShouldForwardProp; diff --git a/packages/mui-material/src/styles/slotShouldForwardProp.ts b/packages/mui-material/src/styles/slotShouldForwardProp.ts new file mode 100644 index 00000000000000..8b674ade2bd0cd --- /dev/null +++ b/packages/mui-material/src/styles/slotShouldForwardProp.ts @@ -0,0 +1,6 @@ +// copied from @mui/system/createStyled +function slotShouldForwardProp(prop: string) { + return prop !== 'ownerState' && prop !== 'theme' && prop !== 'sx' && prop !== 'as'; +} + +export default slotShouldForwardProp; diff --git a/packages/mui-material/src/styles/styled.d.ts b/packages/mui-material/src/styles/styled.d.ts index a5977f53ac9977..3c7fa4938bf0d4 100644 --- a/packages/mui-material/src/styles/styled.d.ts +++ b/packages/mui-material/src/styles/styled.d.ts @@ -1,9 +1,8 @@ import { CreateMUIStyled } from '@mui/system'; import { Theme } from './createTheme'; -export function rootShouldForwardProp(prop: string): boolean; - -export function slotShouldForwardProp(prop: string): boolean; +export { default as slotShouldForwardProp } from './slotShouldForwardProp'; +export { default as rootShouldForwardProp } from './rootShouldForwardProp'; /** * Custom styled utility that has a default MUI theme. diff --git a/packages/mui-material/src/styles/styled.js b/packages/mui-material/src/styles/styled.js index 4e68a6ab48eaef..3d690d470ed672 100644 --- a/packages/mui-material/src/styles/styled.js +++ b/packages/mui-material/src/styles/styled.js @@ -1,11 +1,11 @@ 'use client'; -import createStyled, { shouldForwardProp } from '@mui/system/createStyled'; +import createStyled from '@mui/system/createStyled'; import defaultTheme from './defaultTheme'; import THEME_ID from './identifier'; +import rootShouldForwardProp from './rootShouldForwardProp'; -export const rootShouldForwardProp = (prop) => shouldForwardProp(prop) && prop !== 'classes'; - -export const slotShouldForwardProp = shouldForwardProp; +export { default as slotShouldForwardProp } from './slotShouldForwardProp'; +export { default as rootShouldForwardProp } from './rootShouldForwardProp'; const styled = createStyled({ themeId: THEME_ID, diff --git a/packages/mui-material/src/usePagination/usePagination.js b/packages/mui-material/src/usePagination/usePagination.js index c77a737e0ebbe3..63cd2d10cc4c0e 100644 --- a/packages/mui-material/src/usePagination/usePagination.js +++ b/packages/mui-material/src/usePagination/usePagination.js @@ -67,7 +67,7 @@ export default function usePagination(props = {}) { ); // Basic list of items to render - // e.g. itemList = ['first', 'previous', 1, 'ellipsis', 4, 5, 6, 'ellipsis', 10, 'next', 'last'] + // for example itemList = ['first', 'previous', 1, 'ellipsis', 4, 5, 6, 'ellipsis', 10, 'next', 'last'] const itemList = [ ...(showFirstButton ? ['first'] : []), ...(hidePrevButton ? [] : ['previous']), diff --git a/packages/mui-material/test/integration/Menu.test.js b/packages/mui-material/test/integration/Menu.test.js index 1f36ee815e6114..77f6e6542b9cc7 100644 --- a/packages/mui-material/test/integration/Menu.test.js +++ b/packages/mui-material/test/integration/Menu.test.js @@ -239,7 +239,7 @@ describe('<Menu /> integration', () => { }); // falling back to the menu immediately so that we don't have to come up - // with custom fallbacks (e.g. what happens if the first item is also selected) + // with custom fallbacks (for example what happens if the first item is also selected) // it's debatable whether disabled items should still be focusable specify( '[variant=selectedMenu] focuses the first non-disabled item if the selected menuitem is disabled', diff --git a/packages/mui-private-theming/package.json b/packages/mui-private-theming/package.json index d81038d164a087..a34d5e95b3f86a 100644 --- a/packages/mui-private-theming/package.json +++ b/packages/mui-private-theming/package.json @@ -1,6 +1,6 @@ { "name": "@mui/private-theming", - "version": "5.15.12", + "version": "5.15.14", "private": false, "author": "MUI Team", "description": "Private - The React theme context to be shared between `@mui/styles` and `@mui/material`.", diff --git a/packages/mui-styled-engine-sc/README.md b/packages/mui-styled-engine-sc/README.md index 871bccbd80d746..3ad474dbc2e264 100644 --- a/packages/mui-styled-engine-sc/README.md +++ b/packages/mui-styled-engine-sc/README.md @@ -7,4 +7,4 @@ It's designed for developers who would like to use `styled-components` as the ma <!-- #default-branch-switch --> -Visit [https://mui.com/material-ui/integrations/styled-components/](https://mui.com/material-ui/integrations/styled-components/) to view the full documentation. +Visit [https://next.mui.com/material-ui/integrations/styled-components/](https://next.mui.com/material-ui/integrations/styled-components/) to view the full documentation. diff --git a/packages/mui-styled-engine-sc/package.json b/packages/mui-styled-engine-sc/package.json index 6d26c546fe7b07..9309fb33afce07 100644 --- a/packages/mui-styled-engine-sc/package.json +++ b/packages/mui-styled-engine-sc/package.json @@ -1,6 +1,6 @@ { "name": "@mui/styled-engine-sc", - "version": "6.0.0-alpha.17", + "version": "6.0.0-alpha.18", "private": false, "author": "MUI Team", "description": "styled() API wrapper package for styled-components.", diff --git a/packages/mui-styled-engine/README.md b/packages/mui-styled-engine/README.md index ab3d9fd2a968da..05a621f948f910 100644 --- a/packages/mui-styled-engine/README.md +++ b/packages/mui-styled-engine/README.md @@ -8,4 +8,4 @@ It is used internally in the `@mui/system` package. <!-- #default-branch-switch --> -Visit [https://mui.com/material-ui/integrations/styled-components/](https://mui.com/material-ui/integrations/styled-components/) to view the full documentation. +Visit [https://next.mui.com/material-ui/integrations/styled-components/](https://next.mui.com/material-ui/integrations/styled-components/) to view the full documentation. diff --git a/packages/mui-styled-engine/package.json b/packages/mui-styled-engine/package.json index 78ef3bc80309b3..578216b8dbf9a9 100644 --- a/packages/mui-styled-engine/package.json +++ b/packages/mui-styled-engine/package.json @@ -1,6 +1,6 @@ { "name": "@mui/styled-engine", - "version": "5.15.11", + "version": "5.15.14", "private": false, "author": "MUI Team", "description": "styled() API wrapper package for emotion.", diff --git a/packages/mui-styles/package.json b/packages/mui-styles/package.json index 915855c36f9ce1..d25cd3c3c15e49 100644 --- a/packages/mui-styles/package.json +++ b/packages/mui-styles/package.json @@ -1,6 +1,6 @@ { "name": "@mui/styles", - "version": "5.15.12", + "version": "5.15.14", "private": false, "author": "MUI Team", "description": "MUI Styles - The legacy JSS-based styling solution of Material UI.", diff --git a/packages/mui-system/README.md b/packages/mui-system/README.md index a3f99ef5eff975..66f439ddcb3298 100644 --- a/packages/mui-system/README.md +++ b/packages/mui-system/README.md @@ -9,11 +9,11 @@ Install the package in your project directory with: <!-- #default-branch-switch --> ```bash -npm install @mui/system @emotion/react @emotion/styled +npm install @mui/system@next @emotion/react @emotion/styled ``` ## Documentation <!-- #default-branch-switch --> -Visit [https://mui.com/system/getting-started/](https://mui.com/system/getting-started/) to view the full documentation. +Visit [https://next.mui.com/system/getting-started/](https://next.mui.com/system/getting-started/) to view the full documentation. diff --git a/packages/mui-system/package.json b/packages/mui-system/package.json index 8c61f0ca70f2a4..079119e5131574 100644 --- a/packages/mui-system/package.json +++ b/packages/mui-system/package.json @@ -1,6 +1,6 @@ { "name": "@mui/system", - "version": "5.15.12", + "version": "5.15.14", "private": false, "author": "MUI Team", "description": "MUI System is a set of CSS utilities to help you build custom designs more efficiently. It makes it possible to rapidly lay out custom designs.", diff --git a/packages/mui-system/src/createTheme/createBreakpoints.d.ts b/packages/mui-system/src/createTheme/createBreakpoints.d.ts index 4404933d0e9b5e..9486f3b68aa224 100644 --- a/packages/mui-system/src/createTheme/createBreakpoints.d.ts +++ b/packages/mui-system/src/createTheme/createBreakpoints.d.ts @@ -31,13 +31,13 @@ export interface Breakpoints { /** * @param key - A breakpoint key (`xs`, `sm`, etc.) or a screen width number in px. * @returns A media query string ready to be used with most styling solutions, which matches screen widths greater than the screen size given by the breakpoint key (inclusive). - * @see [API documentation](https://mui.com/material-ui/customization/breakpoints/#theme-breakpoints-up-key-media-query) + * @see [API documentation](https://next.mui.com/material-ui/customization/breakpoints/#theme-breakpoints-up-key-media-query) */ up: (key: Breakpoint | number) => string; /** * @param key - A breakpoint key (`xs`, `sm`, etc.) or a screen width number in px. * @returns A media query string ready to be used with most styling solutions, which matches screen widths less than the screen size given by the breakpoint key (exclusive). - * @see [API documentation](https://mui.com/material-ui/customization/breakpoints/#theme-breakpoints-down-key-media-query) + * @see [API documentation](https://next.mui.com/material-ui/customization/breakpoints/#theme-breakpoints-down-key-media-query) */ down: (key: Breakpoint | number) => string; /** @@ -45,14 +45,14 @@ export interface Breakpoints { * @param end - A breakpoint key (`xs`, `sm`, etc.) or a screen width number in px. * @returns A media query string ready to be used with most styling solutions, which matches screen widths greater than * the screen size given by the breakpoint key in the first argument (inclusive) and less than the screen size given by the breakpoint key in the second argument (exclusive). - * @see [API documentation](https://mui.com/material-ui/customization/breakpoints/#theme-breakpoints-between-start-end-media-query) + * @see [API documentation](https://next.mui.com/material-ui/customization/breakpoints/#theme-breakpoints-between-start-end-media-query) */ between: (start: Breakpoint | number, end: Breakpoint | number) => string; /** * @param key - A breakpoint key (`xs`, `sm`, etc.) or a screen width number in px. * @returns A media query string ready to be used with most styling solutions, which matches screen widths starting from * the screen size given by the breakpoint key (inclusive) and stopping at the screen size given by the next breakpoint key (exclusive). - * @see [API documentation](https://mui.com/material-ui/customization/breakpoints/#theme-breakpoints-only-key-media-query) + * @see [API documentation](https://next.mui.com/material-ui/customization/breakpoints/#theme-breakpoints-only-key-media-query) */ only: (key: Breakpoint) => string; /** diff --git a/packages/mui-system/src/cssVars/createCssVarsProvider.js b/packages/mui-system/src/cssVars/createCssVarsProvider.js index dc6882ab204cb3..e4969c4ad5b44e 100644 --- a/packages/mui-system/src/cssVars/createCssVarsProvider.js +++ b/packages/mui-system/src/cssVars/createCssVarsProvider.js @@ -60,22 +60,23 @@ export default function createCssVarsProvider(options) { return value; }; - function CssVarsProvider({ - children, - theme: themeProp = defaultTheme, - modeStorageKey = defaultModeStorageKey, - colorSchemeStorageKey = defaultColorSchemeStorageKey, - attribute = defaultAttribute, - defaultMode = designSystemMode, - defaultColorScheme = designSystemColorScheme, - disableTransitionOnChange = designSystemTransitionOnChange, - storageWindow = typeof window === 'undefined' ? undefined : window, - documentNode = typeof document === 'undefined' ? undefined : document, - colorSchemeNode = typeof document === 'undefined' ? undefined : document.documentElement, - colorSchemeSelector = ':root', - disableNestedContext = false, - disableStyleSheetGeneration = false, - }) { + function CssVarsProvider(props) { + const { + children, + theme: themeProp = defaultTheme, + modeStorageKey = defaultModeStorageKey, + colorSchemeStorageKey = defaultColorSchemeStorageKey, + attribute = defaultAttribute, + defaultMode = designSystemMode, + defaultColorScheme = designSystemColorScheme, + disableTransitionOnChange = designSystemTransitionOnChange, + storageWindow = typeof window === 'undefined' ? undefined : window, + documentNode = typeof document === 'undefined' ? undefined : document, + colorSchemeNode = typeof document === 'undefined' ? undefined : document.documentElement, + colorSchemeSelector = ':root', + disableNestedContext = false, + disableStyleSheetGeneration = false, + } = props; const hasMounted = React.useRef(false); const upperTheme = muiUseTheme(); const ctx = React.useContext(ColorSchemeContext); @@ -248,14 +249,14 @@ export default function createCssVarsProvider(options) { const contextValue = React.useMemo( () => ({ - mode, - systemMode, - setMode, - lightColorScheme, - darkColorScheme, + allColorSchemes, colorScheme, + darkColorScheme, + lightColorScheme, + mode, setColorScheme, - allColorSchemes, + setMode, + systemMode, }), [ allColorSchemes, diff --git a/packages/mui-system/src/cssVars/useCurrentColorScheme.ts b/packages/mui-system/src/cssVars/useCurrentColorScheme.ts index a44c4790419d77..f9f46bbfdc9dbf 100644 --- a/packages/mui-system/src/cssVars/useCurrentColorScheme.ts +++ b/packages/mui-system/src/cssVars/useCurrentColorScheme.ts @@ -160,7 +160,7 @@ export default function useCurrentColorScheme<SupportedColorScheme extends strin // do nothing if mode does not change return currentState; } - const newMode = !mode ? defaultMode : mode; + const newMode = mode ?? defaultMode; try { localStorage.setItem(modeStorageKey, newMode); } catch (e) { @@ -254,12 +254,17 @@ export default function useCurrentColorScheme<SupportedColorScheme extends strin ); const handleMediaQuery = React.useCallback( - (e?: MediaQueryListEvent) => { + (event?: MediaQueryListEvent) => { if (state.mode === 'system') { - setState((currentState) => ({ - ...currentState, - systemMode: e?.matches ? 'dark' : 'light', - })); + setState((currentState) => { + const systemMode = event?.matches ? 'dark' : 'light'; + + // Early exit, nothing changed. + if (currentState.systemMode === systemMode) { + return currentState; + } + return { ...currentState, systemMode }; + }); } }, [state.mode], @@ -278,35 +283,41 @@ export default function useCurrentColorScheme<SupportedColorScheme extends strin // Intentionally use deprecated listener methods to support iOS & old browsers media.addListener(handler); handler(media); - - return () => media.removeListener(handler); + return () => { + media.removeListener(handler); + }; }, []); // Handle when localStorage has changed React.useEffect(() => { - const handleStorage = (event: StorageEvent) => { - const value = event.newValue; - if ( - typeof event.key === 'string' && - event.key.startsWith(colorSchemeStorageKey) && - (!value || joinedColorSchemes.match(value)) - ) { - // If the key is deleted, value will be null then reset color scheme to the default one. - if (event.key.endsWith('light')) { - setColorScheme({ light: value as SupportedColorScheme | null }); + if (storageWindow) { + const handleStorage = (event: StorageEvent) => { + const value = event.newValue; + if ( + typeof event.key === 'string' && + event.key.startsWith(colorSchemeStorageKey) && + (!value || joinedColorSchemes.match(value)) + ) { + // If the key is deleted, value will be null then reset color scheme to the default one. + if (event.key.endsWith('light')) { + setColorScheme({ light: value as SupportedColorScheme | null }); + } + if (event.key.endsWith('dark')) { + setColorScheme({ dark: value as SupportedColorScheme | null }); + } } - if (event.key.endsWith('dark')) { - setColorScheme({ dark: value as SupportedColorScheme | null }); + if ( + event.key === modeStorageKey && + (!value || ['light', 'dark', 'system'].includes(value)) + ) { + setMode((value as Mode) || defaultMode); } - } - if (event.key === modeStorageKey && (!value || ['light', 'dark', 'system'].includes(value))) { - setMode((value as Mode) || defaultMode); - } - }; - if (storageWindow) { + }; // For syncing color-scheme changes between iframes storageWindow.addEventListener('storage', handleStorage); - return () => storageWindow.removeEventListener('storage', handleStorage); + return () => { + storageWindow.removeEventListener('storage', handleStorage); + }; } return undefined; }, [ diff --git a/packages/mui-types/index.d.ts b/packages/mui-types/index.d.ts index 77963ef07b94a0..64daa8cb6bb7bc 100644 --- a/packages/mui-types/index.d.ts +++ b/packages/mui-types/index.d.ts @@ -92,7 +92,7 @@ export interface OverridableComponent<M extends OverridableTypeMap> { // If you make any changes to this interface, please make sure to update the // `OverridableComponent` type in `mui-material/src/OverridableComponent.d.ts` as well. // Also, there are types in Base UI that have a similar shape to this interface - // (e.g. SelectType, OptionType, etc.). + // (for example SelectType, OptionType, etc.). <C extends React.ElementType>( props: { /** diff --git a/packages/mui-types/package.json b/packages/mui-types/package.json index f8404d25f51085..b19ec8a59fc4f1 100644 --- a/packages/mui-types/package.json +++ b/packages/mui-types/package.json @@ -1,6 +1,6 @@ { "name": "@mui/types", - "version": "7.2.13", + "version": "7.2.14", "private": false, "author": "MUI Team", "description": "Utility types for MUI.", diff --git a/packages/mui-utils/package.json b/packages/mui-utils/package.json index a7c8d9f1d1becc..1ec2250902f92c 100644 --- a/packages/mui-utils/package.json +++ b/packages/mui-utils/package.json @@ -1,6 +1,6 @@ { "name": "@mui/utils", - "version": "5.15.12", + "version": "5.15.14", "private": false, "author": "MUI Team", "description": "Utility functions for React components.", @@ -50,7 +50,7 @@ "@mui/types": "workspace:^", "@types/chai": "^4.3.12", "@types/mocha": "^10.0.6", - "@types/node": "^18.19.21", + "@types/node": "^18.19.25", "@types/react": "^18.2.55", "@types/react-dom": "^18.2.19", "@types/react-is": "^18.2.4", diff --git a/packages/mui-utils/src/useLocalStorageState/index.ts b/packages/mui-utils/src/useLocalStorageState/index.ts new file mode 100644 index 00000000000000..33ff661f99ed20 --- /dev/null +++ b/packages/mui-utils/src/useLocalStorageState/index.ts @@ -0,0 +1 @@ +export { default } from './useLocalStorageState'; diff --git a/packages/mui-utils/src/useLocalStorageState/useLocalStorageState.ts b/packages/mui-utils/src/useLocalStorageState/useLocalStorageState.ts new file mode 100644 index 00000000000000..2d785fc5512f05 --- /dev/null +++ b/packages/mui-utils/src/useLocalStorageState/useLocalStorageState.ts @@ -0,0 +1,150 @@ +'use client'; +import * as React from 'react'; + +// storage events only work across tabs, we'll use an event emitter to announce within the current tab +const currentTabChangeListeners = new Map<string, Set<() => void>>(); + +function onCurrentTabStorageChange(key: string, handler: () => void) { + let listeners = currentTabChangeListeners.get(key); + + if (!listeners) { + listeners = new Set(); + currentTabChangeListeners.set(key, listeners); + } + + listeners.add(handler); +} + +function offCurrentTabStorageChange(key: string, handler: () => void) { + const listeners = currentTabChangeListeners.get(key); + if (!listeners) { + return; + } + + listeners.delete(handler); + + if (listeners.size === 0) { + currentTabChangeListeners.delete(key); + } +} + +function emitCurrentTabStorageChange(key: string) { + const listeners = currentTabChangeListeners.get(key); + if (listeners) { + listeners.forEach((listener) => listener()); + } +} + +function subscribe(area: Storage, key: string | null, callbark: () => void): () => void { + if (!key) { + return () => {}; + } + const storageHandler = (event: StorageEvent) => { + if (event.storageArea === area && event.key === key) { + callbark(); + } + }; + window.addEventListener('storage', storageHandler); + onCurrentTabStorageChange(key, callbark); + return () => { + window.removeEventListener('storage', storageHandler); + offCurrentTabStorageChange(key, callbark); + }; +} + +function getSnapshot(area: Storage, key: string | null): string | null { + if (!key) { + return null; + } + try { + return area.getItem(key); + } catch { + // ignore + // See https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#feature-detecting_localstorage + return null; + } +} + +function setValue(area: Storage, key: string | null, value: string | null) { + if (!key) { + return; + } + try { + if (value === null) { + area.removeItem(key); + } else { + area.setItem(key, String(value)); + } + } catch { + // ignore + // See https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#feature-detecting_localstorage + return; + } + emitCurrentTabStorageChange(key); +} + +type Initializer = () => string | null; + +type UseStorageStateHookResult = [ + string | null, + React.Dispatch<React.SetStateAction<string | null>>, +]; + +const serverValue: UseStorageStateHookResult = [null, () => {}]; + +function useLocalStorageStateServer(): UseStorageStateHookResult { + return serverValue; +} + +/** + * Sync state to local storage so that it persists through a page refresh. Usage is + * similar to useState except we pass in a storage key so that we can default + * to that value on page load instead of the specified initial value. + * + * Since the storage API isn't available in server-rendering environments, we + * return null during SSR and hydration. + */ +function useLocalStorageStateBrowser( + key: string | null, + initializer: string | null | Initializer = null, +): UseStorageStateHookResult { + const [initialValue] = React.useState(initializer); + const area = window.localStorage; + const subscribeKey = React.useCallback( + (callbark: () => void) => subscribe(area, key, callbark), + [area, key], + ); + const getKeySnapshot = React.useCallback( + () => getSnapshot(area, key) ?? initialValue, + [area, initialValue, key], + ); + + // Start with null for the hydration, and then switch to the actual value. + const getKeyServerSnapshot = () => null; + + const storedValue = React.useSyncExternalStore( + subscribeKey, + getKeySnapshot, + getKeyServerSnapshot, + ); + + const setStoredValue = React.useCallback( + (value: React.SetStateAction<string | null>) => { + const valueToStore = value instanceof Function ? value(storedValue) : value; + setValue(area, key, valueToStore); + }, + [area, key, storedValue], + ); + + const [nonStoredValue, setNonStoredValue] = React.useState(initialValue); + + if (!key) { + return [nonStoredValue, setNonStoredValue]; + } + + return [storedValue, setStoredValue]; +} + +export default typeof window === 'undefined' + ? useLocalStorageStateServer + : useLocalStorageStateBrowser; diff --git a/packages/mui-utils/src/visuallyHidden/visuallyHidden.ts b/packages/mui-utils/src/visuallyHidden/visuallyHidden.ts index 5d4145de6911dc..703c335112b678 100644 --- a/packages/mui-utils/src/visuallyHidden/visuallyHidden.ts +++ b/packages/mui-utils/src/visuallyHidden/visuallyHidden.ts @@ -2,7 +2,7 @@ const visuallyHidden: import('react').CSSProperties = { border: 0, clip: 'rect(0 0 0 0)', height: '1px', - margin: -1, + margin: '-1px', overflow: 'hidden', padding: 0, position: 'absolute', diff --git a/packages/pigment-nextjs-plugin/.eslintrc b/packages/pigment-css-nextjs-plugin/.eslintrc similarity index 100% rename from packages/pigment-nextjs-plugin/.eslintrc rename to packages/pigment-css-nextjs-plugin/.eslintrc diff --git a/packages/pigment-nextjs-plugin/.gitignore b/packages/pigment-css-nextjs-plugin/.gitignore similarity index 100% rename from packages/pigment-nextjs-plugin/.gitignore rename to packages/pigment-css-nextjs-plugin/.gitignore diff --git a/packages/pigment-nextjs-plugin/loader.js b/packages/pigment-css-nextjs-plugin/loader.js similarity index 100% rename from packages/pigment-nextjs-plugin/loader.js rename to packages/pigment-css-nextjs-plugin/loader.js diff --git a/packages/pigment-nextjs-plugin/next-font.js b/packages/pigment-css-nextjs-plugin/next-font.js similarity index 100% rename from packages/pigment-nextjs-plugin/next-font.js rename to packages/pigment-css-nextjs-plugin/next-font.js diff --git a/packages/pigment-nextjs-plugin/next-image.js b/packages/pigment-css-nextjs-plugin/next-image.js similarity index 100% rename from packages/pigment-nextjs-plugin/next-image.js rename to packages/pigment-css-nextjs-plugin/next-image.js diff --git a/packages/pigment-nextjs-plugin/package.json b/packages/pigment-css-nextjs-plugin/package.json similarity index 92% rename from packages/pigment-nextjs-plugin/package.json rename to packages/pigment-css-nextjs-plugin/package.json index 9bd71bd251699a..e7c718e1f3d5ee 100644 --- a/packages/pigment-nextjs-plugin/package.json +++ b/packages/pigment-css-nextjs-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@pigment-css/nextjs-plugin", - "version": "0.0.1", + "version": "0.0.3", "main": "build/index.js", "module": "build/index.mjs", "types": "build/index.d.ts", @@ -9,13 +9,13 @@ "repository": { "type": "git", "url": "https://github.com/mui/material-ui.git", - "directory": "packages/pigment-nextjs-plugin" + "directory": "packages/pigment-css-nextjs-plugin" }, "license": "MIT", "bugs": { "url": "https://github.com/mui/material-ui/issues" }, - "homepage": "https://github.com/mui/material-ui/tree/master/packages/pigment-react", + "homepage": "https://github.com/mui/material-ui/tree/master/packages/pigment-css-react", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" diff --git a/packages/pigment-nextjs-plugin/src/index.ts b/packages/pigment-css-nextjs-plugin/src/index.ts similarity index 64% rename from packages/pigment-nextjs-plugin/src/index.ts rename to packages/pigment-css-nextjs-plugin/src/index.ts index 7950f160799981..ecf7b1598687b2 100644 --- a/packages/pigment-nextjs-plugin/src/index.ts +++ b/packages/pigment-css-nextjs-plugin/src/index.ts @@ -1,23 +1,25 @@ import * as path from 'node:path'; import type { NextConfig } from 'next'; import { findPagesDir } from 'next/dist/lib/find-pages-dir'; -import { - webpack as webpackPlugin, - extendTheme, - type PigmentOptions as BasePigmentOptions, -} from '@pigment-css/unplugin'; +import { webpack as webpackPlugin, extendTheme, type PigmentOptions } from '@pigment-css/unplugin'; -export type PigmentOptions = BasePigmentOptions & { - asyncResolve?: (what: string) => string | null; -}; +export { type PigmentOptions }; const extractionFile = path.join( path.dirname(require.resolve('../package.json')), 'zero-virtual.css', ); -export function withPigment(nextConfig: NextConfig, pigmentConfig: PigmentOptions) { - const { babelOptions, asyncResolve, ...rest } = pigmentConfig; +export function withPigment(nextConfig: NextConfig, pigmentConfig?: PigmentOptions) { + const { babelOptions = {}, asyncResolve, ...rest } = pigmentConfig ?? {}; + if (process.env.TURBOPACK === '1') { + // eslint-disable-next-line no-console + console.log( + `\x1B[33m${process.env.PACKAGE_NAME}: Turbo mode is not supported yet. Please disable it by removing the "--turbo" flag from your "next dev" command to use Pigment CSS.\x1B[39m`, + ); + return nextConfig; + } + const webpack: Exclude<NextConfig['webpack'], undefined> = (config, context) => { const { dir, dev, isServer, config: resolvedNextConfig } = context; @@ -52,14 +54,23 @@ export function withPigment(nextConfig: NextConfig, pigmentConfig: PigmentOption outputCss: dev || hasAppDir || !isServer, placeholderCssFile: extractionFile, }, - asyncResolve(what) { + async asyncResolve(what: string, importer: string, stack: string[]) { + // Need to point to the react from node_modules during eval time. + // Otherwise, next makes it point to its own version of react that + // has a lot of RSC specific logic which is not actually needed. + if (what.startsWith('react') || what.startsWith('next')) { + return require.resolve(what); + } if (what === 'next/image') { return require.resolve('../next-image'); } if (what.startsWith('next/font')) { return require.resolve('../next-font'); } - return asyncResolve?.(what) ?? null; + if (asyncResolve) { + return asyncResolve(what, importer, stack); + } + return null; }, babelOptions: { ...babelOptions, @@ -73,7 +84,7 @@ export function withPigment(nextConfig: NextConfig, pigmentConfig: PigmentOption } config.ignoreWarnings = config.ignoreWarnings ?? []; config.ignoreWarnings.push({ - module: /(zero-virtual\.css)|(runtime\/styles\.css)/, + module: /(zero-virtual\.css)|(react\/styles\.css)/, }); return config; }; diff --git a/packages/pigment-nextjs-plugin/src/virtual-css-loader.js b/packages/pigment-css-nextjs-plugin/src/virtual-css-loader.js similarity index 100% rename from packages/pigment-nextjs-plugin/src/virtual-css-loader.js rename to packages/pigment-css-nextjs-plugin/src/virtual-css-loader.js diff --git a/packages/pigment-nextjs-plugin/tsconfig.build.json b/packages/pigment-css-nextjs-plugin/tsconfig.build.json similarity index 100% rename from packages/pigment-nextjs-plugin/tsconfig.build.json rename to packages/pigment-css-nextjs-plugin/tsconfig.build.json diff --git a/packages/pigment-nextjs-plugin/tsconfig.json b/packages/pigment-css-nextjs-plugin/tsconfig.json similarity index 100% rename from packages/pigment-nextjs-plugin/tsconfig.json rename to packages/pigment-css-nextjs-plugin/tsconfig.json diff --git a/packages/pigment-nextjs-plugin/tsup.config.ts b/packages/pigment-css-nextjs-plugin/tsup.config.ts similarity index 100% rename from packages/pigment-nextjs-plugin/tsup.config.ts rename to packages/pigment-css-nextjs-plugin/tsup.config.ts diff --git a/packages/pigment-nextjs-plugin/zero-virtual.css b/packages/pigment-css-nextjs-plugin/zero-virtual.css similarity index 100% rename from packages/pigment-nextjs-plugin/zero-virtual.css rename to packages/pigment-css-nextjs-plugin/zero-virtual.css diff --git a/packages/pigment-react/.eslintignore b/packages/pigment-css-react/.eslintignore similarity index 100% rename from packages/pigment-react/.eslintignore rename to packages/pigment-css-react/.eslintignore diff --git a/packages/pigment-react/.eslintrc b/packages/pigment-css-react/.eslintrc similarity index 100% rename from packages/pigment-react/.eslintrc rename to packages/pigment-css-react/.eslintrc diff --git a/packages/pigment-react/.gitignore b/packages/pigment-css-react/.gitignore similarity index 100% rename from packages/pigment-react/.gitignore rename to packages/pigment-css-react/.gitignore diff --git a/packages/pigment-css-react/README.md b/packages/pigment-css-react/README.md new file mode 100644 index 00000000000000..6092ba58dfb061 --- /dev/null +++ b/packages/pigment-css-react/README.md @@ -0,0 +1,767 @@ +# Pigment CSS + +Pigment CSS is a zero-runtime CSS-in-JS library that extracts the colocated styles to their own CSS files at build time. + +- [Getting started](#getting-started) + - [Why this project exists?](#why-choose-pigment-css) + - [Start with Next.js](#start-with-nextjs) + - [Start with Vite](#start-with-vite) +- [Basic usage](#basic-usage) + - [Creating styles](#creating-styles) + - [Creating components](#creating-components) + - [Styling based on props](#styling-based-on-props) + - [Styling based on runtime values](#styling-based-on-runtime-values) + - [Styled component as a CSS selector](#styled-component-as-a-css-selector) + - [Typing props](#typing-props) +- [Theming](#theming) + - [Accesing theme values](#accesing-theme-values) + - [CSS variables support](#css-variables-support) + - [Color schemes](#color-schemes) + - [Switching color schemes](#switching-color-schemes) + - [TypeScript](#typescript) +- [How-to guides](#how-to-guides) + - [Coming from Emotion or styled-components](#coming-from-emotion-or-styled-components) + +## Getting started + +Pigment CSS supports Next.js and Vite with support for more bundlers in the future. +You must install the corresponding plugin, as shown below. + +### Why choose Pigment CSS + +Thanks to recent advancements in CSS (like CSS variables and `color-mix()`), "traditional" CSS-in-JS solutions that process styles at runtime are no longer required for unlocking features like color transformations and theme variables which are necessary for maintaining a sophisticated design system. + +Pigment CSS addresses the needs of the modern React developer by providing a zero-runtime CSS-in-JS styling solution as a successor to tools like Emotion and styled-components. + +Compared to its predecessors, Pigment CSS offers improved DX and runtime performance (though at the cost of increased build time) while also being compatible with React Server Components. +Pigment CSS is built on top of [WyW-in-JS](https://wyw-in-js.dev/), enabling to provide the smoothest possible experience for Material UI users when migrating from Emotion in v5 to Pigment CSS in v6. + +### Start with Next.js + +Use the following commands to create a new Next.js project with Pigment CSS set up: + +```bash +curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/pigment-css-nextjs-ts +cd pigment-css-nextjs-ts +``` + +#### Manual installation + +```bash +npm install @pigment-css/react +npm install --save-dev @pigment-css/nextjs-plugin +``` + +Then, in your `next.config.js` file, import the plugin and wrap the exported config object: + +```js +const { withPigment } = require('@pigment-css/nextjs-plugin'); + +module.exports = withPigment({ + // ... Your nextjs config. +}); +``` + +Finally, import the stylesheet in the root `layout.tsx` file: + +```diff + import type { Metadata } from 'next'; ++import '@pigment-css/react/styles.css'; + + export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', + }; + + export default function RootLayout(props: { children: React.ReactNode }) { + return ( + <html lang="en"> + <body>{props.children}</body> + </html> + ); + } +``` + +### Start with Vite + +Use the following commands to create a new Vite project with Pigment CSS set up: + +```bash +curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/pigment-css-vite-ts +cd pigment-css-vite-ts +``` + +#### Manual installation + +```bash +npm install @pigment-css/react +npm install --save-dev @pigment-css/vite-plugin +``` + +Then, in your Vite config file, import the plugin and pass it to the `plugins` array as shown: + +```js +import { pigment } from '@pigment-css/vite-plugin'; + +export default defineConfig({ + plugins: [ + pigment(), + // ... Your other plugins. + ], +}); +``` + +Finally, import the stylesheet in the root `main.tsx` file: + +```diff + import * as React from 'react'; + import { createRoot } from 'react-dom/client'; ++import '@pigment-css/react/styles.css'; + import App from './App'; + + const rootElement = document.getElementById('root'); + const root = createRoot(rootElement); + + root.render( + <React.StrictMode> + <App /> + </React.StrictMode>, + ); +``` + +## Basic usage + +**You must configure Pigment CSS with [Next.js](#nextjs) or [Vite](#vite) first.** + +### Creating styles + +Use the `css` API to create reusable styles: + +```js +import { css } from '@pigment-css/react'; + +const visuallyHidden = css({ + border: 0, + clip: 'rect(0 0 0 0)', + height: '1px', + margin: -1, + overflow: 'hidden', + padding: 0, + position: 'absolute', + whiteSpace: 'nowrap', + width: '1px', +}); + +function App() { + return <div className={visuallyHidden}>I am invisible</div>; +} +``` + +The call to the `css` function is replaced with a unique string that represents the CSS class name for the generated styles. + +Use a callback function to get access to the [theme](#theming) values: + +```js +const title = css(({ theme }) => ({ + color: theme.colors.primary, + fontSize: theme.spacing.unit * 4, + fontFamily: theme.typography.fontFamily, +})); +``` + +### Creating components + +Use the `styled` API to create a component by passing styles at the end. The usage should be familiar if you've worked with Emotion or styled-components: + +```js +import { styled } from '@pigment-css/react'; + +const Heading = styled('div')({ + fontSize: '4rem', + fontWeight: 'bold', + padding: '10px 0px', +}); + +function App() { + return <Heading>Hello</Heading>; +} +``` + +The Pigment CSS library differs from "standard" runtime CSS-in-JS libraries in a few ways: + +1. You never get direct access to props in your styled declarations. This is because prop values are only available at runtime, but the CSS is extracted at build time. See [Styling based on runtime values](#styling-based-on-runtime-values) for a workaround. +2. Your styles must be declarative and must account for all combinations of props that you want to style. +3. The theme lets you declare CSS tokens that become part of the CSS bundle after the build. Any other values and methods that it might have are only available during build time—not at runtime. This leads to smaller bundle sizes. + +#### Styling based on props + +> 💡 This approach is recommended when the value of the prop is known at build time (finite values). + +Use the `variants` key to define styles for a combination of the component's props. + +Each variant is an object with `props` and `style` keys. The styles are applied when the component's props match the `props` object. + +**Example 1** — A button component with `small` and `large` sizes: + +```jsx +const Button = styled('button')({ + border: 'none', + padding: '0.75rem', + // ...other base styles + variants: [ + { + props: { size: 'large' }, + style: { padding: '1rem' }, + }, + { + props: { size: 'small' }, + style: { padding: '0.5rem' }, + }, + ], +}); + +<Button>Normal button</Button>; // padding: 0.75rem +<Button size="large">Large button</Button>; // padding: 1rem +<Button size="small">Small button</Button>; // padding: 0.5rem +``` + +**Example 2** — A button component with variants and colors: + +```jsx +const Button = styled('button')({ + border: 'none', + padding: '0.75rem', + // ...other base styles + variants: [ + { + props: { variant: 'contained', color: 'primary' }, + style: { backgroundColor: 'tomato', color: 'white' }, + }, + ], +}); + +// `backgroundColor: 'tomato', color: 'white'` +<Button variant="contained" color="primary"> + Submit +</Button>; +``` + +**Example 3** — Apply styles based on a condition: + +The value of the `props` can be a function that returns a boolean. If the function returns `true`, the styles are applied. + +```jsx +const Button = styled('button')({ + border: 'none', + padding: '0.75rem', + // ...other base styles + variants: [ + { + props: (props) => props.variant !== 'contained', + style: { backgroundColor: 'transparent' }, + }, + ], +}); +``` + +Note that the `props` function doesn't work if it is inside another closure, for example, inside an `array.map`: + +```jsx +const Button = styled('button')({ + border: 'none', + padding: '0.75rem', + // ...other base styles + variants: ['red', 'blue', 'green'].map((item) => ({ + props: (props) => { + // ❌ Cannot access `item` in this closure + return props.color === item && !props.disabled; + }, + style: { backgroundColor: 'tomato' }, + })), +}); +``` + +Instead, use plain objects to define the variants: + +```jsx +const Button = styled('button')({ + border: 'none', + padding: '0.75rem', + // ...other base styles + variants: ['red', 'blue', 'green'].map((item) => ({ + props: { color: item, disabled: false }, + style: { backgroundColor: 'tomato' }, + })), +}); +``` + +#### Styling based on runtime values + +> 💡 This approach is recommended when the value of a prop is **unknown** ahead of time or possibly unlimited values, for example styling based on the user's input. + +There are two ways to acheive this (both involve using a CSS variable): + +1. Declare a CSS variable directly in the styles and set its value using inline styles: + +```jsx +const Heading = styled('h1')({ + color: 'var(--color)', +}); + +function Heading() { + const [color, setColor] = React.useState('red'); + + return <Heading style={{ '--color': color }} />; +} +``` + +2. Use a callback function as a value to create a dynamic style for the specific CSS property: + +```jsx +const Heading = styled('h1')({ + color: ({ isError }) => (isError ? 'red' : 'black'), +}); +``` + +Pigment CSS replaces the callback with a CSS variable and inject the value through inline style. This makes it possible to create a static CSS file while still allowing dynamic styles. + +```css +.Heading_class_akjsdfb { + color: var(--Heading_class_akjsdfb-0); +} +``` + +```jsx +<h1 + style={{ + '--Heading_class_akjsdfb-0': ({ isError }) => (isError ? 'red' : 'black'), + }} +> + Hello +</h1> +``` + +#### Styled component as a CSS selector + +All of the components that you create are also available as CSS selectors. For example, for the `Heading` component described in the previous section, you can target that component inside another styled component like this: + +```jsx +const Wrapper = styled.div({ + [`& ${Heading}`]: { + color: 'blue', + }, +}); +``` + +This enables you to override the default `color` of the Heading component rendered inside the Wrapper: + +```tsx +<Wrapper> + <Heading>Hello</Heading> +</Wrapper> +``` + +You can also export any styled component you create and use it as the base for additional components: + +```jsx +const ExtraHeading = styled(Heading)({ + // ... overridden styled +}); +``` + +#### Media and Container queries + +Pigment CSS APIs have built-in support for writing media queries and container queries. Use the `@media` and `@container` keys to define styles for different screen and container sizes. + +```jsx +import { css, styled } from '@pigment-css/react'; + +const styles = css({ + fontSize: '2rem', + '@media (min-width: 768px)': { + fontSize: '3rem', + }, + '@container (max-width: 768px)': { + fontSize: '1.5rem', + }, +}); + +const Heading = styled('h1')({ + fontSize: '2rem', + '@media (min-width: 768px)': { + fontSize: '3rem', + }, + '@container (max-width: 768px)': { + fontSize: '1.5rem', + }, +}); +``` + +> 💡 **Good to know**: +> +> Pigment CSS uses Emotion behind the scenes for turning tagged templates and objects into CSS strings. + +#### Typing props + +If you use TypeScript, add the props typing before the styles to get the type checking: + +```tsx +const Heading = styled('h1')<{ isError?: boolean }>({ + color: ({ isError }) => (isError ? 'red' : 'black'), +}); +``` + +### Creating animation keyframes + +Use the `keyframes` API to create reusable [animation keyframes](https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes): + +```js +import { keyframes } from '@pigment-css/react'; + +const fadeIn = keyframes` + from { + opacity: 0; + } + to { + opacity: 1; + } +`; + +function Example1() { + return <div style={{ animation: `${fadeIn} 0.5s` }}>I am invisible</div>; +} +``` + +The call to the `keyframes` function is replaced with a unique string that represents the CSS animation name. It can be used with `css` or `styled` too. + +```js +import { css, styled, keyframes } from '@pigment-css/react'; + +const fadeIn = keyframes(...); + +const Example2 = styled('div')({ + animation: `${fadeIn} 0.5s`, +}); + +function App() { + return ( + <> + <Example1 /> + <div + className={css` + animation: ${fadeIn} 0.5s; + `} + /> + </> + ) +} +``` + +### Theming + +Theming is an **optional** feature that lets you reuse the same values, such as colors, spacing, and typography, across your application. It is a plain object of any structure that you can define in your config file. + +> **💡 Good to know**: +> +> The **theme** object is used at build time and does not exist in the final JavaScript bundle. This means that components created using Pigment CSS's `styled` can be used with React Server Components by default while still getting the benefits of theming. + +For example, in Next.js, you can define a theme in the `next.config.js` file like this: + +```js +const { withPigment } = require('@pigment-css/nextjs-plugin'); + +module.exports = withPigment( + { + // ...other nextConfig + }, + { + theme: { + colors: { + primary: 'tomato', + secondary: 'cyan', + }, + spacing: { + unit: 8, + }, + typography: { + fontFamily: 'Inter, sans-serif', + }, + // ...more keys and values, it's free style! + }, + }, +); +``` + +#### Accessing theme values + +A callback can be used with **styled()** and **css()** APIs to access the theme values: + +```js +const Heading = styled('h1')(({ theme }) => ({ + color: theme.colors.primary, + fontSize: theme.spacing.unit * 4, + fontFamily: theme.typography.fontFamily, +})); +``` + +#### CSS variables support + +Pigment CSS can generate CSS variables from the theme values when you wrap your theme with `extendTheme` utility. For example, in a `next.config.js` file: + +```js +const { withPigment, extendTheme } = require('@pigment-css/nextjs-plugin'); + +module.exports = withPigment( + { + // ...nextConfig + }, + { + theme: extendTheme({ + colors: { + primary: 'tomato', + secondary: 'cyan', + }, + spacing: { + unit: 8, + }, + typography: { + fontFamily: 'Inter, sans-serif', + }, + }), + }, +); +``` + +The `extendTheme` utility goes through the theme and create a `vars` object which represents the tokens that refer to CSS variables. + +```jsx +const theme = extendTheme({ + colors: { + primary: 'tomato', + secondary: 'cyan', + }, +}); + +console.log(theme.colors.primary); // 'tomato' +console.log(theme.vars.colors.primary); // 'var(--colors-primary)' +``` + +#### Adding a prefix to CSS variables + +You can add a prefix to the generated CSS variables by providing a `cssVarPrefix` option to the `extendTheme` utility: + +```jsx +extendTheme({ + cssVarPrefix: 'pigment', +}); +``` + +The generated CSS variables has the `pigment` prefix: + +```css +:root { + --pigment-colors-background: #f9f9f9; + --pigment-colors-foreground: #121212; +} +``` + +#### Color schemes + +Some tokens, especially color-related tokens, can have different values for different scenarios. For example in a daylight condition, the background color might be white, but in a dark condition, it might be black. + +The `extendTheme` utility lets you define a theme with a special `colorSchemes` key: + +```jsx +extendTheme({ + colorSchemes: { + light: { + colors: { + background: '#f9f9f9', + foreground: '#121212', + }, + }, + dark: { + colors: { + background: '#212121', + foreground: '#fff', + }, + }, + }, +}); +``` + +In the above example, `light` (default) and `dark` color schemes are defined. The structure of each color scheme must be a plain object with keys and values. + +#### Switching color schemes + +By default, when `colorSchemes` is defined, Pigment CSS uses the [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) media query to switch between color schemes based on the user's system settings. + +However, if you want to control the color scheme based on application logic, for example, using a button to switch between light and dark mode, you can customize the behavior by providing a `getSelector` function: + +```diff + extendTheme({ + colorSchemes: { + light: { ... }, + dark: { ... }, + }, ++ getSelector: (colorScheme) => colorScheme ? `.theme-${colorScheme}` : ':root', + }); +``` + +Note that you need to add the logic to a button by yourself. Here is an example of how to do it: + +```jsx +function App() { + return ( + <button + onClick={() => { + document.documentElement.classList.toggle('theme-dark'); + }} + > + Toggle color scheme + </button> + ); +} +``` + +#### Styling based on color scheme + +The `extendTheme` utility attaches a function called `applyStyles` to the theme object. It receives a color scheme as the first argument followed by a style object. +It returns a proper CSS selector based on the theme configuration. + +```jsx +const Heading = styled('h1')(({ theme }) => ({ + color: theme.colors.primary, + fontSize: theme.spacing.unit * 4, + fontFamily: theme.typography.fontFamily, + ...theme.applyStyles('dark', { + color: theme.colors.primaryLight, + }), +})); +``` + +#### TypeScript + +To get the type checking for the theme, you need to augment the theme type: + +```ts +// any file that is included in your tsconfig.json +import type { ExtendTheme } from '@pigment-css/react/theme'; + +declare module '@pigment-css/react/theme' { + interface ThemeTokens { + // the structure of your theme + } + + interface ThemeArgs { + theme: ExtendTheme<{ + colorScheme: 'light' | 'dark'; + tokens: ThemeTokens; + }>; + } +} +``` + +## How-to guides + +### Coming from Emotion or styled-components + +Emotion and styled-components are runtime CSS-in-JS libraries. What you write in your styles is what you get in the final bundle, which means the styles can be as dynamic as you want with bundle size and performance overhead trade-offs. + +On the other hand, Pigment CSS extracts CSS at build time and replaces the JavaScript code with hashed class names and some CSS variables. This means that it has to know all of the styles to be extracted ahead of time, so there are rules and limitations that you need to be aware of when using JavaScript callbacks or variables in Pigment CSS's APIs. + +Here are some common patterns and how to achieve them with Pigment CSS: + +1. **Fixed set of styles** + +In Emotion or styled-components, you can use props to create styles conditionally: + +```js +const Flex = styled('div')((props) => ({ + display: 'flex', + ...(props.vertical // ❌ Pigment CSS will throw an error + ? { + flexDirection: 'column', + paddingBlock: '1rem', + } + : { + paddingInline: '1rem', + }), +})); +``` + +But in Pigment CSS, you need to define all of the styles ahead of time using the `variants` key: + +```js +const Flex = styled('div')((props) => ({ + display: 'flex', + variants: [ + { + props: { vertical: true }, + style: { + flexDirection: 'column', + paddingBlock: '1rem', + }, + }, + { + props: { vertical: false }, + style: { + paddingInline: '1rem', + }, + }, + ], +})); +``` + +> 💡 Keep in mind that the `variants` key is for fixed values of props, for example, a component's colors, sizes, and states. + +2. **Programatically generated styles** + +For Emotion and styled-components, the styles is different on each render and instance because the styles are generated at runtime: + +```js +function randomBetween(min: number, max: number) { + return Math.floor(Math.random() * (max - min + 1) + min); +} +function generateBubbleVars() { + return ` + --x: ${randomBetween(10, 90)}%; + --y: ${randomBetween(15, 85)}%; + `; +} + +function App() { + return ( + <div> + {[...Array(10)].map((_, index) => ( + <div key={index} className={css`${generateBubbleVars()}`} /> + ))} + </div> + ) +} +``` + +However, in Pigment CSS with the same code as above, all instances have the same styles and won't change between renders because the styles are extracted at build time. + +To achieve the same result, you need to move the dynamic logic to props and pass the value to CSS variables instead: + +```js +function randomBetween(min: number, max: number) { + return Math.floor(Math.random() * (max - min + 1) + min); +} + +const Bubble = styled('div')({ + '--x': props => props.x, + '--y': props => props.y, +}); + +function App() { + return ( + <div> + {[...Array(10)].map((_, index) => ( + <Bubble key={index} x={`${randomBetween(10, 90)}%`} y={`${randomBetween(15, 85)}%`} /> + ))} + </div> + ) +} +``` diff --git a/packages/pigment-react/exports/createUseThemeProps.js b/packages/pigment-css-react/exports/createUseThemeProps.js similarity index 100% rename from packages/pigment-react/exports/createUseThemeProps.js rename to packages/pigment-css-react/exports/createUseThemeProps.js diff --git a/packages/pigment-react/exports/css.js b/packages/pigment-css-react/exports/css.js similarity index 100% rename from packages/pigment-react/exports/css.js rename to packages/pigment-css-react/exports/css.js diff --git a/packages/pigment-react/exports/generateAtomics.js b/packages/pigment-css-react/exports/generateAtomics.js similarity index 100% rename from packages/pigment-react/exports/generateAtomics.js rename to packages/pigment-css-react/exports/generateAtomics.js diff --git a/packages/pigment-react/exports/keyframes.js b/packages/pigment-css-react/exports/keyframes.js similarity index 100% rename from packages/pigment-react/exports/keyframes.js rename to packages/pigment-css-react/exports/keyframes.js diff --git a/packages/pigment-react/exports/styled.js b/packages/pigment-css-react/exports/styled.js similarity index 100% rename from packages/pigment-react/exports/styled.js rename to packages/pigment-css-react/exports/styled.js diff --git a/packages/pigment-react/exports/sx-plugin.js b/packages/pigment-css-react/exports/sx-plugin.js similarity index 100% rename from packages/pigment-react/exports/sx-plugin.js rename to packages/pigment-css-react/exports/sx-plugin.js diff --git a/packages/pigment-react/exports/sx.js b/packages/pigment-css-react/exports/sx.js similarity index 100% rename from packages/pigment-react/exports/sx.js rename to packages/pigment-css-react/exports/sx.js diff --git a/packages/pigment-react/package.json b/packages/pigment-css-react/package.json similarity index 85% rename from packages/pigment-react/package.json rename to packages/pigment-css-react/package.json index ad575146312e8d..a86699f7bb56c7 100644 --- a/packages/pigment-react/package.json +++ b/packages/pigment-css-react/package.json @@ -1,6 +1,6 @@ { "name": "@pigment-css/react", - "version": "0.0.1", + "version": "0.0.3", "main": "build/index.js", "module": "build/index.mjs", "types": "build/index.d.ts", @@ -9,13 +9,13 @@ "repository": { "type": "git", "url": "https://github.com/mui/material-ui.git", - "directory": "packages/pigment-react" + "directory": "packages/pigment-css-react" }, "license": "MIT", "bugs": { "url": "https://github.com/mui/material-ui/issues" }, - "homepage": "https://github.com/mui/material-ui/tree/master/packages/pigment-react", + "homepage": "https://github.com/mui/material-ui/tree/master/packages/pigment-css-react", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" @@ -25,9 +25,9 @@ "watch": "tsup --watch --clean false", "copy-license": "node ../../scripts/pigment-license.mjs", "build": "tsup", - "test": "cd ../../ && cross-env NODE_ENV=test mocha 'packages/pigment-react/**/*.test.{js,ts,tsx}'", - "test:update": "cd ../../ && cross-env NODE_ENV=test UPDATE_FIXTURES=true mocha 'packages/pigment-react/**/*.test.{js,ts,tsx}'", - "test:ci": "cd ../../ && cross-env NODE_ENV=test BABEL_ENV=coverage nyc --reporter=lcov --report-dir=./coverage/pigment-react mocha 'packages/pigment-react/**/*.test.{js,ts,tsx}'", + "test": "cd ../../ && cross-env NODE_ENV=test mocha 'packages/pigment-css-react/**/*.test.{js,ts,tsx}'", + "test:update": "cd ../../ && cross-env NODE_ENV=test UPDATE_FIXTURES=true mocha 'packages/pigment-css-react/**/*.test.{js,ts,tsx}'", + "test:ci": "cd ../../ && cross-env NODE_ENV=test BABEL_ENV=coverage nyc --reporter=lcov --report-dir=./coverage/pigment-css-react mocha 'packages/pigment-css-react/**/*.test.{js,ts,tsx}'", "typecheck": "tsc --noEmit -p ." }, "dependencies": { @@ -57,9 +57,9 @@ "@types/babel__helper-plugin-utils": "^7.10.3", "@types/chai": "^4.3.12", "@types/cssesc": "^3.0.2", - "@types/lodash": "^4.14.202", + "@types/lodash": "^4.17.0", "@types/mocha": "^10.0.6", - "@types/node": "^18.19.21", + "@types/node": "^18.19.25", "@types/react": "^18.2.55", "@types/stylis": "^4.2.5", "chai": "^4.4.1", @@ -132,6 +132,12 @@ }, "./exports/createUseThemeProps": { "default": "./exports/createUseThemeProps.js" + }, + "./Box": { + "types": "./build/Box.d.ts", + "import": "./build/Box.mjs", + "require": "./build/Box.js", + "default": "./build/Box.js" } }, "nx": { @@ -142,6 +148,12 @@ "build" ] }, + "test:update": { + "cache": false, + "dependsOn": [ + "build" + ] + }, "test:ci": { "cache": false, "dependsOn": [ diff --git a/packages/pigment-css-react/src/Box.d.ts b/packages/pigment-css-react/src/Box.d.ts new file mode 100644 index 00000000000000..8cfb27534aa6d4 --- /dev/null +++ b/packages/pigment-css-react/src/Box.d.ts @@ -0,0 +1,26 @@ +import type { BaseDefaultProps, Substitute, NoInfer } from './base'; +import type { SxProp } from './sx'; + +export type PolymorphicComponentProps< + BaseProps extends object, + AsTarget extends React.ElementType | undefined, + AsTargetProps extends object = AsTarget extends React.ElementType + ? React.ComponentPropsWithRef<AsTarget> + : BaseDefaultProps, +> = NoInfer<Omit<Substitute<BaseProps, AsTargetProps>, 'as' | 'component'>> & { + as?: AsTarget; + component?: AsTarget; + sx?: SxProp; + children?: React.ReactNode; +}; + +export interface PolymorphicComponent<BaseProps extends BaseDefaultProps> + extends React.ForwardRefExoticComponent<BaseProps> { + <AsTarget extends React.ElementType | undefined = undefined>( + props: PolymorphicComponentProps<BaseProps, AsTarget>, + ): JSX.Element; +} + +declare const Box: PolymorphicComponent<{}>; + +export { Box }; diff --git a/packages/pigment-css-react/src/Box.jsx b/packages/pigment-css-react/src/Box.jsx new file mode 100644 index 00000000000000..be87c121f7a3d4 --- /dev/null +++ b/packages/pigment-css-react/src/Box.jsx @@ -0,0 +1,51 @@ +import * as React from 'react'; + +// eslint-disable-next-line react/prop-types +export const Box = React.forwardRef( + ( + { + as = 'div', + // Added to support compatibility with @mui/system + component, + /** + * The type of the transformed sx prop is either a + * "string" if the css passed was fully static or an + * object with the following shape: + * { + * className: string, + * vars: Record<string, [string | number, boolean]> + * } + */ + sx, + className, + style, + ...rest + }, + ref, + ) => { + const Component = component ?? as; + // eslint-disable-next-line react/prop-types + const sxClass = typeof sx === 'string' ? sx : sx?.className; + const classes = [className, sxClass].filter(Boolean).join(' '); + // eslint-disable-next-line react/prop-types + const sxVars = sx && typeof sx !== 'string' ? sx?.vars : {}; + const varStyles = {}; + + if (sxVars) { + Object.entries(sxVars).forEach(([cssVariable, [value, isUnitLess]]) => { + if (typeof value === 'string' || isUnitLess) { + varStyles[`--${cssVariable}`] = value; + } else { + varStyles[`--${cssVariable}`] = `${value}px`; + } + }); + } + + const styles = { + ...style, + ...varStyles, + }; + + return <Component ref={ref} className={classes} style={styles} {...rest} />; + }, +); diff --git a/packages/pigment-react/src/base.d.ts b/packages/pigment-css-react/src/base.d.ts similarity index 51% rename from packages/pigment-react/src/base.d.ts rename to packages/pigment-css-react/src/base.d.ts index 0aae8b1e5adf86..c524bd4a3f7e43 100644 --- a/packages/pigment-react/src/base.d.ts +++ b/packages/pigment-css-react/src/base.d.ts @@ -35,3 +35,32 @@ export type CSSObjectNoCallback = | CSSPropertiesMultiValue | CSSPseudosNoCallback | CSSOthersObjectNoCallback; + +export type BaseDefaultProps = object; + +export type NoInfer<T> = [T][T extends any ? 0 : never]; +type FastOmit<T extends object, U extends string | number | symbol> = { + [K in keyof T as K extends U ? never : K]: T[K]; +}; + +export type Substitute<A extends object, B extends object> = FastOmit<A, keyof B> & B; + +export type PolymorphicComponentProps< + SxProp, + BaseProps extends object, + AsTarget extends React.ElementType | undefined, + AsTargetProps extends object = AsTarget extends React.ElementType + ? React.ComponentPropsWithRef<AsTarget> + : BaseDefaultProps, +> = NoInfer<Omit<Substitute<BaseProps, AsTargetProps>, 'as'>> & { + as?: AsTarget; + sx?: SxProp; + children?: React.ReactNode; +}; + +export interface PolymorphicComponent<SxProp, BaseProps extends BaseDefaultProps> + extends React.ForwardRefExoticComponent<BaseProps> { + <AsTarget extends React.ElementType | undefined = undefined>( + props: PolymorphicComponentProps<SxProp, BaseProps, AsTarget>, + ): JSX.Element; +} diff --git a/packages/pigment-react/src/createUseThemeProps.d.ts b/packages/pigment-css-react/src/createUseThemeProps.d.ts similarity index 100% rename from packages/pigment-react/src/createUseThemeProps.d.ts rename to packages/pigment-css-react/src/createUseThemeProps.d.ts diff --git a/packages/pigment-react/src/createUseThemeProps.js b/packages/pigment-css-react/src/createUseThemeProps.js similarity index 100% rename from packages/pigment-react/src/createUseThemeProps.js rename to packages/pigment-css-react/src/createUseThemeProps.js diff --git a/packages/pigment-react/src/css.d.ts b/packages/pigment-css-react/src/css.d.ts similarity index 100% rename from packages/pigment-react/src/css.d.ts rename to packages/pigment-css-react/src/css.d.ts diff --git a/packages/pigment-react/src/css.js b/packages/pigment-css-react/src/css.js similarity index 100% rename from packages/pigment-react/src/css.js rename to packages/pigment-css-react/src/css.js diff --git a/packages/pigment-react/src/generateAtomics.d.ts b/packages/pigment-css-react/src/generateAtomics.d.ts similarity index 100% rename from packages/pigment-react/src/generateAtomics.d.ts rename to packages/pigment-css-react/src/generateAtomics.d.ts diff --git a/packages/pigment-react/src/generateAtomics.js b/packages/pigment-css-react/src/generateAtomics.js similarity index 100% rename from packages/pigment-react/src/generateAtomics.js rename to packages/pigment-css-react/src/generateAtomics.js diff --git a/packages/pigment-react/src/index.ts b/packages/pigment-css-react/src/index.ts similarity index 100% rename from packages/pigment-react/src/index.ts rename to packages/pigment-css-react/src/index.ts diff --git a/packages/pigment-react/src/keyframes.d.ts b/packages/pigment-css-react/src/keyframes.d.ts similarity index 100% rename from packages/pigment-react/src/keyframes.d.ts rename to packages/pigment-css-react/src/keyframes.d.ts diff --git a/packages/pigment-react/src/keyframes.js b/packages/pigment-css-react/src/keyframes.js similarity index 100% rename from packages/pigment-react/src/keyframes.js rename to packages/pigment-css-react/src/keyframes.js diff --git a/packages/pigment-react/src/processors/base-processor.ts b/packages/pigment-css-react/src/processors/base-processor.ts similarity index 100% rename from packages/pigment-react/src/processors/base-processor.ts rename to packages/pigment-css-react/src/processors/base-processor.ts diff --git a/packages/pigment-react/src/processors/createUseThemeProps.ts b/packages/pigment-css-react/src/processors/createUseThemeProps.ts similarity index 100% rename from packages/pigment-react/src/processors/createUseThemeProps.ts rename to packages/pigment-css-react/src/processors/createUseThemeProps.ts diff --git a/packages/pigment-react/src/processors/css.ts b/packages/pigment-css-react/src/processors/css.ts similarity index 100% rename from packages/pigment-react/src/processors/css.ts rename to packages/pigment-css-react/src/processors/css.ts diff --git a/packages/pigment-react/src/processors/generateAtomics.ts b/packages/pigment-css-react/src/processors/generateAtomics.ts similarity index 100% rename from packages/pigment-react/src/processors/generateAtomics.ts rename to packages/pigment-css-react/src/processors/generateAtomics.ts diff --git a/packages/pigment-react/src/processors/keyframes.ts b/packages/pigment-css-react/src/processors/keyframes.ts similarity index 97% rename from packages/pigment-react/src/processors/keyframes.ts rename to packages/pigment-css-react/src/processors/keyframes.ts index ded71900ad0c75..98b55419336d96 100644 --- a/packages/pigment-react/src/processors/keyframes.ts +++ b/packages/pigment-css-react/src/processors/keyframes.ts @@ -100,7 +100,9 @@ export class KeyframesProcessor extends BaseProcessor { generateArtifacts(styleObjOrTaggged: CSSInterpolation | string[], ...args: Primitive[]) { const { styles } = serializeStyles( - [styleObjOrTaggged as Interpolation<{}>, args], + args.length > 0 + ? [styleObjOrTaggged as Interpolation<{}>, ...args] + : [styleObjOrTaggged as Interpolation<{}>], cache.registered, ); const cssText = `@keyframes {${styles}}`; diff --git a/packages/pigment-react/src/processors/styled.ts b/packages/pigment-css-react/src/processors/styled.ts similarity index 99% rename from packages/pigment-react/src/processors/styled.ts rename to packages/pigment-css-react/src/processors/styled.ts index 82f91f9775e47f..f9258edea8121e 100644 --- a/packages/pigment-react/src/processors/styled.ts +++ b/packages/pigment-css-react/src/processors/styled.ts @@ -292,7 +292,7 @@ export class StyledProcessor extends BaseProcessor { * ```js * const Component = styled(...)(...) * const Component2 = styled()({ - * [`.${Component} &`]: { + * [`${Component} &`]: { * color: 'red' * } * }) @@ -300,7 +300,7 @@ export class StyledProcessor extends BaseProcessor { * to further target `Component` rendered inside `Component2`. */ doEvaltimeReplacement() { - this.replacer(this.value, false); + this.replacer(this.astService.stringLiteral(this.asSelector), false); } /** diff --git a/packages/pigment-react/src/processors/sx.ts b/packages/pigment-css-react/src/processors/sx.ts similarity index 96% rename from packages/pigment-react/src/processors/sx.ts rename to packages/pigment-css-react/src/processors/sx.ts index 37bdc88ac59ec0..b0293aa8c58f93 100644 --- a/packages/pigment-react/src/processors/sx.ts +++ b/packages/pigment-css-react/src/processors/sx.ts @@ -41,7 +41,7 @@ export class SxProcessor extends BaseProcessor { } } - if (!this.elementClassName) { + if (!this.elementClassName || this.elementClassName[0] !== '.') { return; } @@ -55,7 +55,7 @@ export class SxProcessor extends BaseProcessor { cssText = this.processCss(styleObjOrFn, sxStyle); } const selector = this.elementClassName - ? `.${this.elementClassName}${this.asSelector}` + ? `${this.elementClassName}${this.asSelector}` : this.asSelector; if (!cssText) { @@ -94,7 +94,7 @@ export class SxProcessor extends BaseProcessor { doRuntimeReplacement() { const t = this.astService; - // do not replace if sx prop is not on zero-runtime styled component + // do not replace if sx prop is not a Pigment styled component if (!this.elementClassName) { return; } diff --git a/packages/pigment-react/src/styled.d.ts b/packages/pigment-css-react/src/styled.d.ts similarity index 70% rename from packages/pigment-react/src/styled.d.ts rename to packages/pigment-css-react/src/styled.d.ts index b518a903161460..d0fd15efb7351a 100644 --- a/packages/pigment-react/src/styled.d.ts +++ b/packages/pigment-css-react/src/styled.d.ts @@ -1,19 +1,11 @@ import type * as React from 'react'; -import type { CSSObject } from './base'; +import type { BaseDefaultProps, CSSObject, PolymorphicComponent, Substitute } from './base'; import type { ThemeArgs } from './theme'; import type { SxProp } from './sx'; import { Primitve } from './keyframes'; type Falsy = false | 0 | '' | null | undefined; -type BaseDefaultProps = object; - -export type NoInfer<T> = [T][T extends any ? 0 : never]; -type FastOmit<T extends object, U extends string | number | symbol> = { - [K in keyof T as K extends U ? never : K]: T[K]; -}; -export type Substitute<A extends object, B extends object> = FastOmit<A, keyof B> & B; - export interface StyledVariants<Props extends BaseDefaultProps> { props: Partial<Props> | ((props: Props) => boolean); style: CSSObject<Props>; @@ -31,26 +23,8 @@ export type StyledArgument<Props extends BaseDefaultProps> = | StyledCssArgument<Props> | StyledCallback<Props>; -export type PolymorphicComponentProps< - BaseProps extends object, - AsTarget extends React.ElementType | undefined, - AsTargetProps extends object = AsTarget extends React.ElementType - ? React.ComponentPropsWithRef<AsTarget> - : BaseDefaultProps, -> = NoInfer<Omit<Substitute<BaseProps, AsTargetProps>, 'as'>> & { - as?: AsTarget; - sx?: SxProp; -}; - -export interface PolymorphicComponent<BaseProps extends BaseDefaultProps> - extends React.ForwardRefExoticComponent<BaseProps> { - <AsTarget extends React.ElementType | undefined = undefined>( - props: PolymorphicComponentProps<BaseProps, AsTarget>, - ): JSX.Element; -} - export interface StyledComponent<Props extends BaseDefaultProps = BaseDefaultProps> - extends PolymorphicComponent<Props> { + extends PolymorphicComponent<SxProp, Props> { defaultProps?: Partial<Props> | undefined; toString: () => string; } diff --git a/packages/pigment-react/src/styled.jsx b/packages/pigment-css-react/src/styled.jsx similarity index 100% rename from packages/pigment-react/src/styled.jsx rename to packages/pigment-css-react/src/styled.jsx diff --git a/packages/pigment-react/src/sx.d.ts b/packages/pigment-css-react/src/sx.d.ts similarity index 100% rename from packages/pigment-react/src/sx.d.ts rename to packages/pigment-css-react/src/sx.d.ts diff --git a/packages/pigment-react/src/sx.js b/packages/pigment-css-react/src/sx.js similarity index 100% rename from packages/pigment-react/src/sx.js rename to packages/pigment-css-react/src/sx.js diff --git a/packages/pigment-react/src/theme.ts b/packages/pigment-css-react/src/theme.ts similarity index 100% rename from packages/pigment-react/src/theme.ts rename to packages/pigment-css-react/src/theme.ts diff --git a/packages/pigment-react/src/utils/checkStaticObjectOrArray.ts b/packages/pigment-css-react/src/utils/checkStaticObjectOrArray.ts similarity index 100% rename from packages/pigment-react/src/utils/checkStaticObjectOrArray.ts rename to packages/pigment-css-react/src/utils/checkStaticObjectOrArray.ts diff --git a/packages/pigment-react/src/utils/convertAtomicsToCss.ts b/packages/pigment-css-react/src/utils/convertAtomicsToCss.ts similarity index 100% rename from packages/pigment-react/src/utils/convertAtomicsToCss.ts rename to packages/pigment-css-react/src/utils/convertAtomicsToCss.ts diff --git a/packages/pigment-react/src/utils/cssFnValueToVariable.ts b/packages/pigment-css-react/src/utils/cssFnValueToVariable.ts similarity index 100% rename from packages/pigment-react/src/utils/cssFnValueToVariable.ts rename to packages/pigment-css-react/src/utils/cssFnValueToVariable.ts diff --git a/packages/pigment-react/src/utils/cssFunctionTransformerPlugin.ts b/packages/pigment-css-react/src/utils/cssFunctionTransformerPlugin.ts similarity index 100% rename from packages/pigment-react/src/utils/cssFunctionTransformerPlugin.ts rename to packages/pigment-css-react/src/utils/cssFunctionTransformerPlugin.ts diff --git a/packages/pigment-react/src/utils/emotion.ts b/packages/pigment-css-react/src/utils/emotion.ts similarity index 100% rename from packages/pigment-react/src/utils/emotion.ts rename to packages/pigment-css-react/src/utils/emotion.ts diff --git a/packages/pigment-react/src/utils/extendTheme.ts b/packages/pigment-css-react/src/utils/extendTheme.ts similarity index 100% rename from packages/pigment-react/src/utils/extendTheme.ts rename to packages/pigment-css-react/src/utils/extendTheme.ts diff --git a/packages/pigment-react/src/utils/generateCss.ts b/packages/pigment-css-react/src/utils/generateCss.ts similarity index 100% rename from packages/pigment-react/src/utils/generateCss.ts rename to packages/pigment-css-react/src/utils/generateCss.ts diff --git a/packages/pigment-react/src/utils/index.ts b/packages/pigment-css-react/src/utils/index.ts similarity index 100% rename from packages/pigment-react/src/utils/index.ts rename to packages/pigment-css-react/src/utils/index.ts diff --git a/packages/pigment-react/src/utils/isUnitLess.ts b/packages/pigment-css-react/src/utils/isUnitLess.ts similarity index 100% rename from packages/pigment-react/src/utils/isUnitLess.ts rename to packages/pigment-css-react/src/utils/isUnitLess.ts diff --git a/packages/pigment-react/src/utils/pre-linaria-plugin.ts b/packages/pigment-css-react/src/utils/pre-linaria-plugin.ts similarity index 100% rename from packages/pigment-react/src/utils/pre-linaria-plugin.ts rename to packages/pigment-css-react/src/utils/pre-linaria-plugin.ts diff --git a/packages/pigment-react/src/utils/preprocessor.ts b/packages/pigment-css-react/src/utils/preprocessor.ts similarity index 100% rename from packages/pigment-react/src/utils/preprocessor.ts rename to packages/pigment-css-react/src/utils/preprocessor.ts diff --git a/packages/pigment-react/src/utils/processCssObject.ts b/packages/pigment-css-react/src/utils/processCssObject.ts similarity index 100% rename from packages/pigment-react/src/utils/processCssObject.ts rename to packages/pigment-css-react/src/utils/processCssObject.ts diff --git a/packages/pigment-react/src/utils/sxObjectExtractor.ts b/packages/pigment-css-react/src/utils/sxObjectExtractor.ts similarity index 97% rename from packages/pigment-react/src/utils/sxObjectExtractor.ts rename to packages/pigment-css-react/src/utils/sxObjectExtractor.ts index 28f5866781f3e5..c232e41b8fc572 100644 --- a/packages/pigment-react/src/utils/sxObjectExtractor.ts +++ b/packages/pigment-css-react/src/utils/sxObjectExtractor.ts @@ -133,7 +133,7 @@ export function sxObjectExtractor(nodePath: NodePath<ObjectExpression | ArrowFun const body = nodePath.get('body'); if (!body.isObjectExpression()) { throw body.buildCodeFrameError( - `${process.env.PACKAGE_NAME}: sx prop only supports arrow functions directly returning an object, e.g. () => ({color: 'red'}). You can accept theme object in the params if required.`, + `${process.env.PACKAGE_NAME}: sx prop only supports arrow functions directly returning an object, for example () => ({color: 'red'}). You can accept theme object in the params if required.`, ); } traverseObjectExpression(body, nodePath); diff --git a/packages/pigment-react/src/utils/sxPropConverter.ts b/packages/pigment-css-react/src/utils/sxPropConverter.ts similarity index 100% rename from packages/pigment-react/src/utils/sxPropConverter.ts rename to packages/pigment-css-react/src/utils/sxPropConverter.ts diff --git a/packages/pigment-react/src/utils/valueToLiteral.ts b/packages/pigment-css-react/src/utils/valueToLiteral.ts similarity index 97% rename from packages/pigment-react/src/utils/valueToLiteral.ts rename to packages/pigment-css-react/src/utils/valueToLiteral.ts index 52a30e97298d98..8f737b17ade2ff 100644 --- a/packages/pigment-react/src/utils/valueToLiteral.ts +++ b/packages/pigment-css-react/src/utils/valueToLiteral.ts @@ -122,7 +122,7 @@ export function valueToLiteral(value: unknown, expression?: ExpressionValue): t. throw ( expression?.buildCodeFrameError( - `The expression evaluated to '${value}', which is probably a mistake. If you want it to be inserted into CSS, explicitly cast or transform the value to a string, e.g. - 'String(${expression.source})'.`, + `The expression evaluated to '${value}', which is probably a mistake. If you want it to be inserted into CSS, explicitly cast or transform the value to a string, for example - 'String(${expression.source})'.`, ) ?? new Error(`Could not convert value: "${value}" to literal.`) ); } diff --git a/packages/pigment-react/styles.css b/packages/pigment-css-react/styles.css similarity index 100% rename from packages/pigment-react/styles.css rename to packages/pigment-css-react/styles.css diff --git a/packages/pigment-css-react/tests/Box.spec.tsx b/packages/pigment-css-react/tests/Box.spec.tsx new file mode 100644 index 00000000000000..8b28efe600b1be --- /dev/null +++ b/packages/pigment-css-react/tests/Box.spec.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import { Box } from '@pigment-css/react/Box'; + +export function App() { + return ( + <Box as="div" sx={{ display: 'flex' }}> + <Box as="p" sx={() => ({ color: 'primary' })}> + Hello{' '} + <Box as="a" href="https://mui.com" download> + Link + </Box> + <Box component="dialog" open> + Dialog + </Box> + {/* @ts-expect-error */} + <Box component="dialog" as="button" open> + Dialog 2 + </Box> + </Box> + </Box> + ); +} diff --git a/packages/pigment-react/tests/README.md b/packages/pigment-css-react/tests/README.md similarity index 100% rename from packages/pigment-react/tests/README.md rename to packages/pigment-css-react/tests/README.md diff --git a/packages/pigment-react/tests/css/css.test.ts b/packages/pigment-css-react/tests/css/css.test.ts similarity index 100% rename from packages/pigment-react/tests/css/css.test.ts rename to packages/pigment-css-react/tests/css/css.test.ts diff --git a/packages/pigment-react/tests/css/fixtures/css.input.js b/packages/pigment-css-react/tests/css/fixtures/css.input.js similarity index 100% rename from packages/pigment-react/tests/css/fixtures/css.input.js rename to packages/pigment-css-react/tests/css/fixtures/css.input.js diff --git a/packages/pigment-react/tests/css/fixtures/css.output.css b/packages/pigment-css-react/tests/css/fixtures/css.output.css similarity index 74% rename from packages/pigment-react/tests/css/fixtures/css.output.css rename to packages/pigment-css-react/tests/css/fixtures/css.output.css index e34d404861da6d..7b2e7bc2675ee9 100644 --- a/packages/pigment-react/tests/css/fixtures/css.output.css +++ b/packages/pigment-css-react/tests/css/fixtures/css.output.css @@ -1,4 +1,4 @@ -.c1wr0t7p { +.c1pj8glw { color: red; font-size: 3rem; } diff --git a/packages/pigment-css-react/tests/css/fixtures/css.output.js b/packages/pigment-css-react/tests/css/fixtures/css.output.js new file mode 100644 index 00000000000000..351cb0daa5eb94 --- /dev/null +++ b/packages/pigment-css-react/tests/css/fixtures/css.output.js @@ -0,0 +1 @@ +const cls1 = 'c1pj8glw'; diff --git a/packages/pigment-css-react/tests/keyframes/fixtures/keyframes-theme.input.js b/packages/pigment-css-react/tests/keyframes/fixtures/keyframes-theme.input.js new file mode 100644 index 00000000000000..d1c52344896f2d --- /dev/null +++ b/packages/pigment-css-react/tests/keyframes/fixtures/keyframes-theme.input.js @@ -0,0 +1,29 @@ +import { keyframes } from '@pigment-css/react'; + +const green = 'green'; + +const gradientKeyframe = keyframes(({ theme }) => ({ + '0%': { + background: theme.palette.primary.main, + }, + '50%': { + background: green, + }, + '100%': { + background: theme.palette.secondary.main, + }, +})); + +const gradientKeyframe2 = keyframes` + 0% { + background: ${({ theme }) => theme.palette.primary.main}; + } + + 50% { + background: ${green}; + } + + 100% { + background: ${({ theme }) => theme.palette.secondary.main}; + } +`; diff --git a/packages/pigment-css-react/tests/keyframes/fixtures/keyframes-theme.output.css b/packages/pigment-css-react/tests/keyframes/fixtures/keyframes-theme.output.css new file mode 100644 index 00000000000000..491c737fd38365 --- /dev/null +++ b/packages/pigment-css-react/tests/keyframes/fixtures/keyframes-theme.output.css @@ -0,0 +1,22 @@ +@keyframes gpsum18 { + 0% { + background: red; + } + 50% { + background: green; + } + 100% { + background: blue; + } +} +@keyframes g1t1dgxp { + 0% { + background: red; + } + 50% { + background: green; + } + 100% { + background: blue; + } +} diff --git a/packages/pigment-css-react/tests/keyframes/fixtures/keyframes-theme.output.js b/packages/pigment-css-react/tests/keyframes/fixtures/keyframes-theme.output.js new file mode 100644 index 00000000000000..9674335eb2d8fd --- /dev/null +++ b/packages/pigment-css-react/tests/keyframes/fixtures/keyframes-theme.output.js @@ -0,0 +1,2 @@ +const gradientKeyframe = 'gpsum18'; +const gradientKeyframe2 = 'g1t1dgxp'; diff --git a/packages/pigment-react/tests/keyframes/fixtures/keyframes.input.js b/packages/pigment-css-react/tests/keyframes/fixtures/keyframes.input.js similarity index 58% rename from packages/pigment-react/tests/keyframes/fixtures/keyframes.input.js rename to packages/pigment-css-react/tests/keyframes/fixtures/keyframes.input.js index 8ff26f9d247b2b..4c4103c6f66c3a 100644 --- a/packages/pigment-react/tests/keyframes/fixtures/keyframes.input.js +++ b/packages/pigment-css-react/tests/keyframes/fixtures/keyframes.input.js @@ -8,3 +8,13 @@ const rotateKeyframe = keyframes({ transform: 'rotate(0deg)', }, }); + +const rotateKeyframe2 = keyframes` + from { + transform: rotate(360deg); + } + + to { + transform: rotate(0deg); + } +`; diff --git a/packages/pigment-css-react/tests/keyframes/fixtures/keyframes.output.css b/packages/pigment-css-react/tests/keyframes/fixtures/keyframes.output.css new file mode 100644 index 00000000000000..255fbfb069242c --- /dev/null +++ b/packages/pigment-css-react/tests/keyframes/fixtures/keyframes.output.css @@ -0,0 +1,16 @@ +@keyframes rr2uptz { + from { + transform: rotate(360deg); + } + to { + transform: rotate(0deg); + } +} +@keyframes r1dngkim { + from { + transform: rotate(360deg); + } + to { + transform: rotate(0deg); + } +} diff --git a/packages/pigment-css-react/tests/keyframes/fixtures/keyframes.output.js b/packages/pigment-css-react/tests/keyframes/fixtures/keyframes.output.js new file mode 100644 index 00000000000000..bab4d4fc9bd619 --- /dev/null +++ b/packages/pigment-css-react/tests/keyframes/fixtures/keyframes.output.js @@ -0,0 +1,2 @@ +const rotateKeyframe = 'rr2uptz'; +const rotateKeyframe2 = 'r1dngkim'; diff --git a/packages/pigment-css-react/tests/keyframes/keyframes.test.ts b/packages/pigment-css-react/tests/keyframes/keyframes.test.ts new file mode 100644 index 00000000000000..87f956f4c0f17b --- /dev/null +++ b/packages/pigment-css-react/tests/keyframes/keyframes.test.ts @@ -0,0 +1,36 @@ +import path from 'node:path'; +import { runTransformation, expect } from '../testUtils'; + +describe('Pigment CSS - keyframes', () => { + it('basics', async () => { + const { output, fixture } = await runTransformation( + path.join(__dirname, 'fixtures/keyframes.input.js'), + ); + + expect(output.js).to.equal(fixture.js); + expect(output.css).to.equal(fixture.css); + }); + + it('should transform correctly with theme', async () => { + const { output, fixture } = await runTransformation( + path.join(__dirname, 'fixtures/keyframes-theme.input.js'), + { + themeArgs: { + theme: { + palette: { + primary: { + main: 'red', + }, + secondary: { + main: 'blue', + }, + }, + }, + }, + }, + ); + + expect(output.js).to.equal(fixture.js); + expect(output.css).to.equal(fixture.css); + }); +}); diff --git a/packages/pigment-css-react/tests/styled/fixtures/styled-theme.input.js b/packages/pigment-css-react/tests/styled/fixtures/styled-theme.input.js new file mode 100644 index 00000000000000..909306c646fd71 --- /dev/null +++ b/packages/pigment-css-react/tests/styled/fixtures/styled-theme.input.js @@ -0,0 +1,36 @@ +import { styled, keyframes } from '@pigment-css/react'; + +const rotateKeyframe = keyframes({ + from: { + transform: 'rotate(360deg)', + }, + to: { + transform: 'rotate(0deg)', + }, +}); + +const Component = styled.div(({ theme }) => ({ + color: (theme.vars ?? theme).palette.primary.main, + animation: `${rotateKeyframe} 2s ease-out 0s infinite`, +})); + +const SliderRail = styled('span', { + name: 'MuiSlider', + slot: 'Rail', +})` + display: block; + position: absolute; + border-radius: inherit; + background-color: currentColor; + opacity: 0.38; + font-size: ${({ theme }) => (theme.vars ?? theme).size.font.h1}; +`; + +const SliderRail2 = styled.span` + display: block; + opacity: 0.38; + font-size: ${({ theme }) => (theme.vars ?? theme).size.font.h1}; + ${SliderRail} { + display: none; + } +`; diff --git a/packages/pigment-react/tests/styled/fixtures/styled.output.css b/packages/pigment-css-react/tests/styled/fixtures/styled-theme.output.css similarity index 62% rename from packages/pigment-react/tests/styled/fixtures/styled.output.css rename to packages/pigment-css-react/tests/styled/fixtures/styled-theme.output.css index ff22b6cc6cb58d..2d6da6559ab971 100644 --- a/packages/pigment-react/tests/styled/fixtures/styled.output.css +++ b/packages/pigment-css-react/tests/styled/fixtures/styled-theme.output.css @@ -1,4 +1,4 @@ -@keyframes r1419f2q { +@keyframes rnit1sq { from { transform: rotate(360deg); } @@ -6,11 +6,11 @@ transform: rotate(0deg); } } -.c1vtarpi { +.c1h7nuob { color: red; - animation: r1419f2q 2s ease-out 0s infinite; + animation: rnit1sq 2s ease-out 0s infinite; } -.s1sjy0ja { +.s13xim6i { display: block; position: absolute; border-radius: inherit; @@ -18,11 +18,14 @@ opacity: 0.38; font-size: 3rem; } -.s1sjy0ja-1 { - font-size: 3rem; +.s13xim6i-1 { + font-size: 1.5rem; } -.s6hrafw { +.s1emg10t { display: block; opacity: 0.38; font-size: 3rem; } +.s1emg10t .s13xim6i { + display: none; +} diff --git a/packages/pigment-css-react/tests/styled/fixtures/styled-theme.output.js b/packages/pigment-css-react/tests/styled/fixtures/styled-theme.output.js new file mode 100644 index 00000000000000..5344df17cb9649 --- /dev/null +++ b/packages/pigment-css-react/tests/styled/fixtures/styled-theme.output.js @@ -0,0 +1,10 @@ +import { styled as _styled3 } from '@pigment-css/react'; +import { styled as _styled2 } from '@pigment-css/react'; +import { styled as _styled } from '@pigment-css/react'; +import _theme from '@pigment-css/react/theme'; +const Component = /*#__PURE__*/ _styled('div')({ + classes: ['c1h7nuob'], +}); +const SliderRail2 = /*#__PURE__*/ _styled3('span')({ + classes: ['s1emg10t'], +}); diff --git a/packages/pigment-react/tests/styled/fixtures/styled.input.js b/packages/pigment-css-react/tests/styled/fixtures/styled.input.js similarity index 75% rename from packages/pigment-react/tests/styled/fixtures/styled.input.js rename to packages/pigment-css-react/tests/styled/fixtures/styled.input.js index fb6173d1406689..92126ff9efa283 100644 --- a/packages/pigment-react/tests/styled/fixtures/styled.input.js +++ b/packages/pigment-css-react/tests/styled/fixtures/styled.input.js @@ -10,7 +10,7 @@ const rotateKeyframe = keyframes({ }); const Component = styled.div(({ theme }) => ({ - color: (theme.vars ?? theme).palette.primary.main, + color: '#ff5252', animation: `${rotateKeyframe} 2s ease-out 0s infinite`, })); @@ -23,11 +23,12 @@ export const SliderRail = styled('span', { border-radius: inherit; background-color: currentColor; opacity: 0.38; - font-size: ${({ theme }) => (theme.vars ?? theme).size.font.h1}; `; const SliderRail2 = styled.span` display: block; opacity: 0.38; - font-size: ${({ theme }) => (theme.vars ?? theme).size.font.h1}; + ${SliderRail} { + display: none; + } `; diff --git a/packages/pigment-css-react/tests/styled/fixtures/styled.output.css b/packages/pigment-css-react/tests/styled/fixtures/styled.output.css new file mode 100644 index 00000000000000..f21339761843bf --- /dev/null +++ b/packages/pigment-css-react/tests/styled/fixtures/styled.output.css @@ -0,0 +1,26 @@ +@keyframes r1sz5zcv { + from { + transform: rotate(360deg); + } + to { + transform: rotate(0deg); + } +} +.c1aiqtje { + color: #ff5252; + animation: r1sz5zcv 2s ease-out 0s infinite; +} +.sj0zd45 { + display: block; + position: absolute; + border-radius: inherit; + background-color: currentColor; + opacity: 0.38; +} +.shdkmm7 { + display: block; + opacity: 0.38; +} +.shdkmm7 .sj0zd45 { + display: none; +} diff --git a/packages/pigment-react/tests/styled/fixtures/styled.output.js b/packages/pigment-css-react/tests/styled/fixtures/styled.output.js similarity index 83% rename from packages/pigment-react/tests/styled/fixtures/styled.output.js rename to packages/pigment-css-react/tests/styled/fixtures/styled.output.js index dbe0d964a69688..0d81f13859b120 100644 --- a/packages/pigment-react/tests/styled/fixtures/styled.output.js +++ b/packages/pigment-css-react/tests/styled/fixtures/styled.output.js @@ -3,14 +3,14 @@ import { styled as _styled2 } from '@pigment-css/react'; import { styled as _styled } from '@pigment-css/react'; import _theme from '@pigment-css/react/theme'; const Component = /*#__PURE__*/ _styled('div')({ - classes: ['c1vtarpi'], + classes: ['c1aiqtje'], }); export const SliderRail = /*#__PURE__*/ _styled2('span', { name: 'MuiSlider', slot: 'Rail', })({ - classes: ['s1sjy0ja', 's1sjy0ja-1'], + classes: ['sj0zd45'], }); const SliderRail2 = /*#__PURE__*/ _styled3('span')({ - classes: ['s6hrafw'], + classes: ['shdkmm7'], }); diff --git a/packages/pigment-css-react/tests/styled/styled.test.ts b/packages/pigment-css-react/tests/styled/styled.test.ts new file mode 100644 index 00000000000000..664fa0d06abdb9 --- /dev/null +++ b/packages/pigment-css-react/tests/styled/styled.test.ts @@ -0,0 +1,47 @@ +import path from 'node:path'; +import { runTransformation, expect } from '../testUtils'; + +describe('Pigment CSS - styled', () => { + it('basics', async () => { + const { output, fixture } = await runTransformation( + path.join(__dirname, 'fixtures/styled.input.js'), + ); + + expect(output.js).to.equal(fixture.js); + expect(output.css).to.equal(fixture.css); + }); + + it('should work with theme', async () => { + const { output, fixture } = await runTransformation( + path.join(__dirname, 'fixtures/styled-theme.input.js'), + { + themeArgs: { + theme: { + palette: { + primary: { + main: 'red', + }, + }, + size: { + font: { + h1: '3rem', + }, + }, + components: { + MuiSlider: { + styleOverrides: { + rail: { + fontSize: '1.5rem', + }, + }, + }, + }, + }, + }, + }, + ); + + expect(output.js).to.equal(fixture.js); + expect(output.css).to.equal(fixture.css); + }); +}); diff --git a/packages/pigment-react/tests/sx/fixtures/sxProps.input.js b/packages/pigment-css-react/tests/sx/fixtures/sxProps.input.js similarity index 100% rename from packages/pigment-react/tests/sx/fixtures/sxProps.input.js rename to packages/pigment-css-react/tests/sx/fixtures/sxProps.input.js diff --git a/packages/pigment-react/tests/sx/fixtures/sxProps.output.css b/packages/pigment-css-react/tests/sx/fixtures/sxProps.output.css similarity index 60% rename from packages/pigment-react/tests/sx/fixtures/sxProps.output.css rename to packages/pigment-css-react/tests/sx/fixtures/sxProps.output.css index 2da9b96ba1de38..c7f2c2d52a85d5 100644 --- a/packages/pigment-react/tests/sx/fixtures/sxProps.output.css +++ b/packages/pigment-css-react/tests/sx/fixtures/sxProps.output.css @@ -1,4 +1,4 @@ -.s4jxdij { +.sjfloo5 { display: block; position: absolute; border-radius: inherit; @@ -6,23 +6,23 @@ opacity: 0.38; font-size: 3rem; } -.s4jxdij-1 { +.sjfloo5-1 { font-size: 3rem; } -.s4jxdij.s6g18tg { +.sjfloo5.s1o8xp19 { color: red; } -.s4jxdij.sqa8j32 { - color: var(--sqa8j32-0); +.sjfloo5.s1xbsywq { + color: var(--s1xbsywq-0); } -.s4jxdij.s1vuaneo { +.sjfloo5.s1wnk6s5 { background-color: blue; color: white; } -.s4jxdij.sihzw1t { - color: var(--sihzw1t-0); +.sjfloo5.stzaibv { + color: var(--stzaibv-0); } -.s4jxdij.s1cb6hjd { +.sjfloo5.sazg8ol { margin-bottom: 8px; text-align: center; } diff --git a/packages/pigment-react/tests/sx/fixtures/sxProps.output.js b/packages/pigment-css-react/tests/sx/fixtures/sxProps.output.js similarity index 61% rename from packages/pigment-react/tests/sx/fixtures/sxProps.output.js rename to packages/pigment-css-react/tests/sx/fixtures/sxProps.output.js index 365853e2ea51e5..1f60cb6e1b5aa9 100644 --- a/packages/pigment-react/tests/sx/fixtures/sxProps.output.js +++ b/packages/pigment-css-react/tests/sx/fixtures/sxProps.output.js @@ -3,10 +3,10 @@ export const SliderRail = /*#__PURE__*/ _styled('span', { name: 'MuiSlider', slot: 'Rail', })({ - classes: ['s4jxdij', 's4jxdij-1'], + classes: ['sjfloo5', 'sjfloo5-1'], }); function App() { - return <SliderRail sx={'s6g18tg'} />; + return <SliderRail sx={'s1o8xp19'} />; } function App2(props) { return ( @@ -14,12 +14,12 @@ function App2(props) { sx={ props.variant === 'secondary' ? { - className: 'sqa8j32', + className: 's1xbsywq', vars: { - 'sqa8j32-0': [props.isRed ? 'red' : 'blue', false], + 's1xbsywq-0': [props.isRed ? 'red' : 'blue', false], }, } - : 's1vuaneo' + : 's1wnk6s5' } /> ); @@ -29,9 +29,9 @@ function App3(props) { <SliderRail sx={ props.variant === 'secondary' && { - className: 'sihzw1t', + className: 'stzaibv', vars: { - 'sihzw1t-0': [props.isRed ? 'red' : 'blue', false], + 'stzaibv-0': [props.isRed ? 'red' : 'blue', false], }, } } @@ -39,5 +39,5 @@ function App3(props) { ); } function App4(props) { - return <SliderRail sx={'s1cb6hjd'} />; + return <SliderRail sx={'sazg8ol'} />; } diff --git a/packages/pigment-react/tests/sx/fixtures/sxProps2.input.js b/packages/pigment-css-react/tests/sx/fixtures/sxProps2.input.js similarity index 100% rename from packages/pigment-react/tests/sx/fixtures/sxProps2.input.js rename to packages/pigment-css-react/tests/sx/fixtures/sxProps2.input.js diff --git a/packages/pigment-react/tests/sx/fixtures/sxProps2.output.css b/packages/pigment-css-react/tests/sx/fixtures/sxProps2.output.css similarity index 79% rename from packages/pigment-react/tests/sx/fixtures/sxProps2.output.css rename to packages/pigment-css-react/tests/sx/fixtures/sxProps2.output.css index e406adf7a04e85..b059356454be53 100644 --- a/packages/pigment-react/tests/sx/fixtures/sxProps2.output.css +++ b/packages/pigment-css-react/tests/sx/fixtures/sxProps2.output.css @@ -1,4 +1,4 @@ -.sex8rxh { +.sdbmcs3 { display: block; position: absolute; border-radius: inherit; @@ -6,14 +6,14 @@ opacity: 0.38; font-size: 3rem; } -.sex8rxh-1 { +.sdbmcs3-1 { font-size: 3rem; } -.sex8rxh.suhmzz0 { +.sdbmcs3.si7ulc4 { margin-bottom: 8px; } @media (prefers-color-scheme: dark) { - .sex8rxh.suhmzz0 { + .sdbmcs3.si7ulc4 { color: white; } } diff --git a/packages/pigment-react/tests/sx/fixtures/sxProps2.output.js b/packages/pigment-css-react/tests/sx/fixtures/sxProps2.output.js similarity index 69% rename from packages/pigment-react/tests/sx/fixtures/sxProps2.output.js rename to packages/pigment-css-react/tests/sx/fixtures/sxProps2.output.js index efe0be355aba42..f38b3f8d753633 100644 --- a/packages/pigment-react/tests/sx/fixtures/sxProps2.output.js +++ b/packages/pigment-css-react/tests/sx/fixtures/sxProps2.output.js @@ -3,8 +3,8 @@ const SliderRail = /*#__PURE__*/ _styled('span', { name: 'MuiSlider', slot: 'Rail', })({ - classes: ['sex8rxh', 'sex8rxh-1'], + classes: ['sdbmcs3', 'sdbmcs3-1'], }); function App2(props) { - return <SliderRail sx={'suhmzz0'} />; + return <SliderRail sx={'si7ulc4'} />; } diff --git a/packages/pigment-react/tests/sx/sx.test.ts b/packages/pigment-css-react/tests/sx/sx.test.ts similarity index 100% rename from packages/pigment-react/tests/sx/sx.test.ts rename to packages/pigment-css-react/tests/sx/sx.test.ts diff --git a/packages/pigment-react/tests/testUtils.ts b/packages/pigment-css-react/tests/testUtils.ts similarity index 100% rename from packages/pigment-react/tests/testUtils.ts rename to packages/pigment-css-react/tests/testUtils.ts diff --git a/packages/pigment-react/theme/index.d.ts b/packages/pigment-css-react/theme/index.d.ts similarity index 100% rename from packages/pigment-react/theme/index.d.ts rename to packages/pigment-css-react/theme/index.d.ts diff --git a/packages/pigment-react/theme/index.js b/packages/pigment-css-react/theme/index.js similarity index 100% rename from packages/pigment-react/theme/index.js rename to packages/pigment-css-react/theme/index.js diff --git a/packages/pigment-react/theme/index.mjs b/packages/pigment-css-react/theme/index.mjs similarity index 100% rename from packages/pigment-react/theme/index.mjs rename to packages/pigment-css-react/theme/index.mjs diff --git a/packages/pigment-react/tsconfig.build.json b/packages/pigment-css-react/tsconfig.build.json similarity index 100% rename from packages/pigment-react/tsconfig.build.json rename to packages/pigment-css-react/tsconfig.build.json diff --git a/packages/pigment-react/tsconfig.json b/packages/pigment-css-react/tsconfig.json similarity index 100% rename from packages/pigment-react/tsconfig.json rename to packages/pigment-css-react/tsconfig.json diff --git a/packages/pigment-react/tsup.config.ts b/packages/pigment-css-react/tsup.config.ts similarity index 82% rename from packages/pigment-react/tsup.config.ts rename to packages/pigment-css-react/tsup.config.ts index 635ac6fa9b2f64..4e7f5ee79cd81b 100644 --- a/packages/pigment-react/tsup.config.ts +++ b/packages/pigment-css-react/tsup.config.ts @@ -1,6 +1,5 @@ import { Options, defineConfig } from 'tsup'; import config from '../../tsup.config'; -import packageJson from './package.json'; const processors = ['styled', 'sx', 'keyframes', 'generateAtomics', 'css', 'createUseThemeProps']; const external = ['react', 'react-is', 'prop-types']; @@ -9,15 +8,12 @@ const baseConfig: Options = { ...(config as Options), tsconfig: './tsconfig.build.json', external, - env: { - PACKAGE_NAME: packageJson.name, - }, }; export default defineConfig([ { ...baseConfig, - entry: ['./src/index.ts', './src/theme.ts'], + entry: ['./src/index.ts', './src/theme.ts', './src/Box.jsx'], }, { ...baseConfig, diff --git a/packages/pigment-unplugin/.gitignore b/packages/pigment-css-unplugin/.gitignore similarity index 100% rename from packages/pigment-unplugin/.gitignore rename to packages/pigment-css-unplugin/.gitignore diff --git a/packages/pigment-unplugin/package.json b/packages/pigment-css-unplugin/package.json similarity index 92% rename from packages/pigment-unplugin/package.json rename to packages/pigment-css-unplugin/package.json index b42ca85edcb7e1..4ffc19b889726d 100644 --- a/packages/pigment-unplugin/package.json +++ b/packages/pigment-css-unplugin/package.json @@ -1,11 +1,11 @@ { "name": "@pigment-css/unplugin", - "version": "0.0.1", + "version": "0.0.3", "main": "build/index.js", "module": "build/index.mjs", "types": "build/index.d.ts", "author": "MUI Team", - "description": "Webpack integration for Pigment CSS.", + "description": "Webpack integration for Pigment CSS.", "repository": { "type": "git", "url": "https://github.com/mui/material-ui.git", @@ -15,7 +15,7 @@ "bugs": { "url": "https://github.com/mui/material-ui/issues" }, - "homepage": "https://github.com/mui/material-ui/tree/master/packages/pigment-react", + "homepage": "https://github.com/mui/material-ui/tree/master/packages/pigment-css-react", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" diff --git a/packages/pigment-unplugin/src/index.ts b/packages/pigment-css-unplugin/src/index.ts similarity index 84% rename from packages/pigment-unplugin/src/index.ts rename to packages/pigment-css-unplugin/src/index.ts index e4c11e4c4b2c72..262577e8056808 100644 --- a/packages/pigment-unplugin/src/index.ts +++ b/packages/pigment-css-unplugin/src/index.ts @@ -1,3 +1,4 @@ +import * as path from 'node:path'; import { transformAsync } from '@babel/core'; import { type Preprocessor, @@ -21,6 +22,7 @@ import { extendTheme, type Theme as BaseTheme, } from '@pigment-css/react/utils'; +import type { ResolvePluginInstance } from 'webpack'; type NextMeta = { type: 'next'; @@ -39,6 +41,7 @@ type WebpackMeta = { }; type Meta = NextMeta | ViteMeta | WebpackMeta; +export type AsyncResolver = (what: string, importer: string, stack: string[]) => Promise<string>; export type PigmentOptions<Theme extends BaseTheme = BaseTheme> = { theme?: Theme; @@ -47,7 +50,7 @@ export type PigmentOptions<Theme extends BaseTheme = BaseTheme> = { debug?: IFileReporterOptions | false; sourceMap?: boolean; meta?: Meta; - asyncResolve?: (what: string) => string | null; + asyncResolve?: (...args: Parameters<AsyncResolver>) => Promise<string | null>; transformSx?: boolean; } & Partial<WywInJsPluginOptions>; @@ -134,6 +137,20 @@ export const plugin = createUnplugin<PigmentOptions, true>((options) => { }; }, }; + + let webpackResolver: AsyncResolver; + + const asyncResolve: AsyncResolver = async (what, importer, stack) => { + const result = await asyncResolveOpt?.(what, importer, stack); + if (typeof result === 'string') { + return result; + } + if (webpackResolver) { + return webpackResolver(what, importer, stack); + } + return asyncResolveFallback(what, importer, stack); + }; + const linariaTransformPlugin: UnpluginOptions = { name: 'zero-plugin-transform-linaria', enforce: 'post', @@ -143,14 +160,35 @@ export const plugin = createUnplugin<PigmentOptions, true>((options) => { transformInclude(id) { return isZeroRuntimeProcessableFile(id, transformLibraries); }, - async transform(code, id) { - const asyncResolve: typeof asyncResolveFallback = async (what, importer, stack) => { - const result = asyncResolveOpt?.(what); - if (typeof result === 'string') { - return result; - } - return asyncResolveFallback(what, importer, stack); + webpack(compiler) { + const resolverPlugin: ResolvePluginInstance = { + apply(resolver) { + webpackResolver = function webpackAsyncResolve( + what: string, + importer: string, + stack: string[], + ) { + const context = path.isAbsolute(importer) + ? path.dirname(importer) + : path.join(process.cwd(), path.dirname(importer)); + return new Promise((resolve, reject) => { + resolver.resolve({}, context, what, { stack: new Set(stack) }, (err, result) => { + if (err) { + reject(err); + } else if (result) { + resolve(result); + } else { + reject(new Error(`${process.env.PACKAGE_NAME}: Cannot resolve ${what}`)); + } + }); + }); + }; + }, }; + compiler.options.resolve.plugins = compiler.options.resolve.plugins || []; + compiler.options.resolve.plugins.push(resolverPlugin); + }, + async transform(code, id) { const transformServices = { options: { filename: id, @@ -269,16 +307,16 @@ export const plugin = createUnplugin<PigmentOptions, true>((options) => { return ( // this file should exist in the package id.endsWith(`${process.env.RUNTIME_PACKAGE_NAME}/styles.css`) || - id.endsWith('/pigment-react/styles.css') || + id.endsWith('/pigment-css-react/styles.css') || id.includes(`${process.env.RUNTIME_PACKAGE_NAME}/theme`) || - id.includes('/pigment-react/theme') + id.includes('/pigment-css-react/theme') ); }, transform(_code, id) { if (id.endsWith('styles.css')) { return theme ? generateTokenCss(theme) : _code; } - if (id.includes('pigment-react/theme')) { + if (id.includes('pigment-css-react/theme')) { return `export default ${ theme ? JSON.stringify(generateThemeTokens(theme)) : '{}' };`; diff --git a/packages/pigment-unplugin/tsconfig.build.json b/packages/pigment-css-unplugin/tsconfig.build.json similarity index 100% rename from packages/pigment-unplugin/tsconfig.build.json rename to packages/pigment-css-unplugin/tsconfig.build.json diff --git a/packages/pigment-unplugin/tsconfig.json b/packages/pigment-css-unplugin/tsconfig.json similarity index 77% rename from packages/pigment-unplugin/tsconfig.json rename to packages/pigment-css-unplugin/tsconfig.json index 1a850071c7483e..aa7c7cb0fab5b0 100644 --- a/packages/pigment-unplugin/tsconfig.json +++ b/packages/pigment-css-unplugin/tsconfig.json @@ -9,8 +9,8 @@ "@mui/system/*": ["./packages/mui-system/src/*"], "@mui/utils": ["./packages/mui-utils/src"], "@mui/utils/*": ["./packages/mui-utils/src/*"], - "@pigment-css/react": ["./packages/pigment-react/src"], - "@pigment-css/react/*": ["./packages/pigment-react/src/*"] + "@pigment-css/react": ["./packages/pigment-css-react/src"], + "@pigment-css/react/*": ["./packages/pigment-css-react/src/*"] } }, "include": ["src/**/*.ts"], diff --git a/packages/pigment-unplugin/tsup.config.ts b/packages/pigment-css-unplugin/tsup.config.ts similarity index 81% rename from packages/pigment-unplugin/tsup.config.ts rename to packages/pigment-css-unplugin/tsup.config.ts index ee75314ee863c4..4f207fb16765e1 100644 --- a/packages/pigment-unplugin/tsup.config.ts +++ b/packages/pigment-css-unplugin/tsup.config.ts @@ -1,6 +1,6 @@ import { Options, defineConfig } from 'tsup'; import config from '../../tsup.config'; -import runtimePackageJson from '../pigment-react/package.json'; +import runtimePackageJson from '../pigment-css-react/package.json'; const baseConfig: Options = { ...(config as Options), diff --git a/packages/pigment-vite-plugin/.gitignore b/packages/pigment-css-vite-plugin/.gitignore similarity index 100% rename from packages/pigment-vite-plugin/.gitignore rename to packages/pigment-css-vite-plugin/.gitignore diff --git a/packages/pigment-vite-plugin/package.json b/packages/pigment-css-vite-plugin/package.json similarity index 89% rename from packages/pigment-vite-plugin/package.json rename to packages/pigment-css-vite-plugin/package.json index d91e7618636110..e59fa61ef19b46 100644 --- a/packages/pigment-vite-plugin/package.json +++ b/packages/pigment-css-vite-plugin/package.json @@ -1,21 +1,21 @@ { "name": "@pigment-css/vite-plugin", - "version": "0.0.1", + "version": "0.0.3", "main": "build/index.js", "module": "build/index.mjs", "types": "build/index.d.ts", "author": "MUI Team", - "description": "Vite integration for Pigment CSS.", + "description": "Vite integration for Pigment CSS.", "repository": { "type": "git", "url": "https://github.com/mui/material-ui.git", - "directory": "packages/pigment-vite-plugin" + "directory": "packages/pigment-css-vite-plugin" }, "license": "MIT", "bugs": { "url": "https://github.com/mui/material-ui/issues" }, - "homepage": "https://github.com/mui/material-ui/tree/master/packages/pigment-react", + "homepage": "https://github.com/mui/material-ui/tree/master/packages/pigment-css-react", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" diff --git a/packages/pigment-vite-plugin/src/index.ts b/packages/pigment-css-vite-plugin/src/index.ts similarity index 100% rename from packages/pigment-vite-plugin/src/index.ts rename to packages/pigment-css-vite-plugin/src/index.ts diff --git a/packages/pigment-vite-plugin/src/vite-plugin.ts b/packages/pigment-css-vite-plugin/src/vite-plugin.ts similarity index 100% rename from packages/pigment-vite-plugin/src/vite-plugin.ts rename to packages/pigment-css-vite-plugin/src/vite-plugin.ts diff --git a/packages/pigment-vite-plugin/tsconfig.build.json b/packages/pigment-css-vite-plugin/tsconfig.build.json similarity index 100% rename from packages/pigment-vite-plugin/tsconfig.build.json rename to packages/pigment-css-vite-plugin/tsconfig.build.json diff --git a/packages/pigment-vite-plugin/tsconfig.json b/packages/pigment-css-vite-plugin/tsconfig.json similarity index 76% rename from packages/pigment-vite-plugin/tsconfig.json rename to packages/pigment-css-vite-plugin/tsconfig.json index 33f1d4469a3274..bd8a7238963d0f 100644 --- a/packages/pigment-vite-plugin/tsconfig.json +++ b/packages/pigment-css-vite-plugin/tsconfig.json @@ -8,8 +8,8 @@ "@mui/system/*": ["./packages/mui-system/src/*"], "@mui/utils": ["./packages/mui-utils/src"], "@mui/utils/*": ["./packages/mui-utils/src/*"], - "@pigment-css/react": ["./packages/pigment-react/src"], - "@pigment-css/react/*": ["./packages/pigment-react/src/*"] + "@pigment-css/react": ["./packages/pigment-css-react/src"], + "@pigment-css/react/*": ["./packages/pigment-css-react/src/*"] } }, "include": ["src/**/*"], diff --git a/packages/pigment-vite-plugin/tsup.config.ts b/packages/pigment-css-vite-plugin/tsup.config.ts similarity index 86% rename from packages/pigment-vite-plugin/tsup.config.ts rename to packages/pigment-css-vite-plugin/tsup.config.ts index df6f5ec24d6e7f..49c643a8293478 100644 --- a/packages/pigment-vite-plugin/tsup.config.ts +++ b/packages/pigment-css-vite-plugin/tsup.config.ts @@ -1,6 +1,6 @@ import { Options, defineConfig } from 'tsup'; import config from '../../tsup.config'; -import zeroPkgJson from '../pigment-react/package.json'; +import zeroPkgJson from '../pigment-css-react/package.json'; const external = [`${zeroPkgJson.name}/utils`]; diff --git a/packages/pigment-nextjs-plugin/LICENSE b/packages/pigment-nextjs-plugin/LICENSE new file mode 100644 index 00000000000000..af1beaff7f5645 --- /dev/null +++ b/packages/pigment-nextjs-plugin/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Call-Em-All + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/pigment-react/LICENSE b/packages/pigment-react/LICENSE new file mode 100644 index 00000000000000..af1beaff7f5645 --- /dev/null +++ b/packages/pigment-react/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Call-Em-All + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/pigment-react/README.md b/packages/pigment-react/README.md index cf0335f0530f20..1c9613bff8f608 100644 --- a/packages/pigment-react/README.md +++ b/packages/pigment-react/README.md @@ -1,719 +1,3 @@ # Pigment CSS -Pigment CSS is a zero-runtime CSS-in-JS library that extracts the colocated styles to their own CSS files at build time. - -- [Getting started](#getting-started) - - [Why this project exists?](#why-choose-pigment-css) - - [Start with Next.js](#start-with-nextjs) - - [Start with Vite](#start-with-vite) -- [Basic usage](#basic-usage) - - [Creating styles](#creating-styles) - - [Creating components](#creating-components) - - [Styling based on props](#styling-based-on-props) - - [Styling based on runtime values](#styling-based-on-runtime-values) - - [Styled component as a CSS selector](#styled-component-as-a-css-selector) - - [Typing props](#typing-props) -- [Theming](#theming) - - [Accesing theme values](#accesing-theme-values) - - [CSS variables support](#css-variables-support) - - [Color schemes](#color-schemes) - - [Switching color schemes](#switching-color-schemes) - - [TypeScript](#typescript) -- [How-to guides](#how-to-guides) - - [Coming from Emotion or styled-components](#coming-from-emotion-or-styled-components) - -## Getting started - -Pigment CSS supports Next.js and Vite with support for more bundlers in the future. -You must install the corresponding plugin, as shown below. - -### Why choose Pigment CSS - -Thanks to recent advancements in CSS (like CSS variables and `color-mix()`), "traditional" CSS-in-JS solutions that process styles at runtime are no longer required for unlocking features like color transformations and theme variables which are necessary for maintaining a sophisticated design system. - -Pigment CSS addresses the needs of the modern React developer by providing a zero-runtime CSS-in-JS styling solution as a successor to tools like Emotion and styled-components. - -Compared to its predecessors, Pigment CSS offers improved DX and runtime performance (though at the cost of increased build time) while also being compatible with React Server Components. -Pigment CSS is built on top of [WyW-in-JS](https://wyw-in-js.dev/), enabling us to provide the smoothest possible experience for Material UI users when migrating from Emotion in v5 to Pigment CSS in v6. - -### Start with Next.js - -Use the following commands to create a new Next.js project with Pigment CSS set up: - -```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/pigment-css-nextjs-ts -cd pigment-css-nextjs-ts -``` - -#### Manual installation - -```bash -npm install @pigment-css/react -npm install --save-dev @pigment-css/nextjs-plugin -``` - -Then, in your `next.config.js` file, import the plugin and wrap the exported config object: - -```js -const { withPigment } = require('@pigment-css/nextjs-plugin'); - -module.exports = withPigment({ - // ... Your nextjs config. -}); -``` - -Finally, import the stylesheet in the root `layout.tsx` file: - -```diff - import type { Metadata } from 'next'; -+import '@pigment-css/react/styles.css'; - - export const metadata: Metadata = { - title: 'Create Next App', - description: 'Generated by create next app', - }; - - export default function RootLayout(props: { children: React.ReactNode }) { - return ( - <html lang="en"> - <body>{props.children}</body> - </html> - ); - } -``` - -### Start with Vite - -Use the following commands to create a new Vite project with Pigment CSS set up: - -```bash -curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/pigment-css-vite-ts -cd pigment-css-vite-ts -``` - -#### Manual installation - -```bash -npm install @pigment-css/react -npm install --save-dev @pigment-css/vite-plugin -``` - -Then, in your Vite config file, import the plugin and pass it to the `plugins` array as shown: - -```js -import { pigment } from '@pigment-css/vite-plugin'; - -export default defineConfig({ - plugins: [ - pigment(), - // ... Your other plugins. - ], -}); -``` - -Finally, import the stylesheet in the root `main.tsx` file: - -```diff - import * as React from 'react'; - import { createRoot } from 'react-dom/client'; -+import '@pigment-css/react/styles.css'; - import App from './App'; - - const rootElement = document.getElementById('root'); - const root = createRoot(rootElement); - - root.render( - <React.StrictMode> - <App /> - </React.StrictMode>, - ); -``` - -## Basic usage - -**You must configure Pigment CSS with [Next.js](#nextjs) or [Vite](#vite) first.** - -### Creating styles - -Use the `css` API to create reusable styles: - -```js -import { css } from '@pigment-css/react'; - -const visuallyHidden = css({ - border: 0, - clip: 'rect(0 0 0 0)', - height: '1px', - margin: -1, - overflow: 'hidden', - padding: 0, - position: 'absolute', - whiteSpace: 'nowrap', - width: '1px', -}); - -function App() { - return <div className={visuallyHidden}>I am invisible</div>; -} -``` - -The call to the `css` function will be replaced with a unique string that represents the CSS class name for the generated styles. - -Use a callback function to get access to the [theme](#theming) values: - -```js -const title = css(({ theme }) => ({ - color: theme.colors.primary, - fontSize: theme.spacing.unit * 4, - fontFamily: theme.typography.fontFamily, -})); -``` - -### Creating components - -Use the `styled` API to create a component by passing styles at the end. The usage should be familiar if you've worked with Emotion or styled-components: - -```js -import { styled } from '@pigment-css/react'; - -const Heading = styled('div')({ - fontSize: '4rem', - fontWeight: 'bold', - padding: '10px 0px', -}); - -function App() { - return <Heading>Hello</Heading>; -} -``` - -The Pigment CSS library differs from "standard" runtime CSS-in-JS libraries in a few ways: - -1. You never get direct access to props in your styled declarations. This is because prop values are only available at runtime, but the CSS is extracted at build time. See [Styling based on runtime values](#styling-based-on-runtime-values) for a workaround. -2. Your styles must be declarative and must account for all combinations of props that you want to style. -3. The theme lets you declare CSS tokens that become part of the CSS bundle after the build. Any other values and methods that it might have are only available during build time—not at runtime. This leads to smaller bundle sizes. - -#### Styling based on props - -> 💡 This approach is recommended when the value of the prop is known at build time (finite values). - -Use the `variants` key to define styles for a combination of the component's props. - -Each variant is an object with `props` and `style` keys. The styles are applied when the component's props match the `props` object. - -**Example 1** — A button component with `small` and `large` sizes: - -```jsx -const Button = styled('button')({ - border: 'none', - padding: '0.75rem', - // ...other base styles - variants: [ - { - props: { size: 'large' }, - style: { padding: '1rem' }, - }, - { - props: { size: 'small' }, - style: { padding: '0.5rem' }, - }, - ], -}); - -<Button>Normal button</Button>; // padding: 0.75rem -<Button size="large">Large button</Button>; // padding: 1rem -<Button size="small">Small button</Button>; // padding: 0.5rem -``` - -**Example 2** — A button component with variants and colors: - -```jsx -const Button = styled('button')({ - border: 'none', - padding: '0.75rem', - // ...other base styles - variants: [ - { - props: { variant: 'contained', color: 'primary' }, - style: { backgroundColor: 'tomato', color: 'white' }, - }, - ], -}); - -// `backgroundColor: 'tomato', color: 'white'` -<Button variant="contained" color="primary"> - Submit -</Button>; -``` - -**Example 3** — Apply styles based on a condition: - -The value of the `props` can be a function that returns a boolean. If the function returns `true`, the styles are applied. - -```jsx -const Button = styled('button')({ - border: 'none', - padding: '0.75rem', - // ...other base styles - variants: [ - { - props: (props) => props.variant !== 'contained', - style: { backgroundColor: 'transparent' }, - }, - ], -}); -``` - -Note that the `props` function will not work if it is inside another closure, for example, inside an `array.map`: - -```jsx -const Button = styled('button')({ - border: 'none', - padding: '0.75rem', - // ...other base styles - variants: ['red', 'blue', 'green'].map((item) => ({ - props: (props) => { - // ❌ Cannot access `item` in this closure - return props.color === item && !props.disabled; - }, - style: { backgroundColor: 'tomato' }, - })), -}); -``` - -Instead, use plain objects to define the variants: - -```jsx -const Button = styled('button')({ - border: 'none', - padding: '0.75rem', - // ...other base styles - variants: ['red', 'blue', 'green'].map((item) => ({ - props: { color: item, disabled: false }, - style: { backgroundColor: 'tomato' }, - })), -}); -``` - -#### Styling based on runtime values - -> 💡 This approach is recommended when the value of a prop is **unknown** ahead of time or possibly unlimited values, for example styling based on the user's input. - -Use a callback function as a value to create a dynamic style for the specific CSS property: - -```jsx -const Heading = styled('h1')({ - color: ({ isError }) => (isError ? 'red' : 'black'), -}); -``` - -Pigment CSS will replace the callback with a CSS variable and inject the value through inline style. This makes it possible to create a static CSS file while still allowing dynamic styles. - -```css -.Heading_class_akjsdfb { - color: var(--Heading_class_akjsdfb-0); -} -``` - -```jsx -<h1 - style={{ - '--Heading_class_akjsdfb-0': ({ isError }) => (isError ? 'red' : 'black'), - }} -> - Hello -</h1> -``` - -#### Styled component as a CSS selector - -All of the components that you create are also available as CSS selectors. For example, for the `Heading` component described in the previous section, you can target that component inside another styled component like this: - -```jsx -const Wrapper = styled.div({ - [`& .${Heading}`]: { - color: 'blue', - }, -}); -``` - -This enables you to override the default `color` of the Heading component rendered inside the Wrapper: - -```tsx -<Wrapper> - <Heading>Hello</Heading> -</Wrapper> -``` - -You can also export any styled component you create and use it as the base for additional components: - -```tsx -const ExtraHeading = styled(Heading)({ - // ... overridden styled -}); -``` - -#### Typing props - -If you use TypeScript, add the props typing before the styles to get the type checking: - -```tsx -const Heading = styled('h1')<{ isError?: boolean }>({ - color: ({ isError }) => (isError ? 'red' : 'black'), -}); -``` - -### Creating animation keyframes - -Use the `keyframes` API to create reusable [animation keyframes](https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes): - -```js -import { keyframes } from '@pigment-css/react'; - -const fadeIn = keyframes` - from { - opacity: 0; - } - to { - opacity: 1; - } -`; - -function Example1() { - return <div style={{ animation: `${fadeIn} 0.5s` }}>I am invisible</div>; -} -``` - -The call to the `keyframes` function will be replaced with a unique string that represents the CSS animation name. It can be used with `css` or `styled` too. - -```js -import { css, styled, keyframes } from '@pigment-css/react'; - -const fadeIn = keyframes(...); - -const Example2 = styled('div')({ - animation: `${fadeIn} 0.5s`, -}); - -function App() { - return ( - <> - <Example1 /> - <div - className={css` - animation: ${fadeIn} 0.5s; - `} - /> - </> - ) -} -``` - -### Theming - -Theming is an **optional** feature that lets you reuse the same values, such as colors, spacing, and typography, across your application. It is a plain object of any structure that you can define in your config file. - -> **💡 Good to know**: -> -> The **theme** object is used at build time and does not exist in the final JS bundle. This means that components created using Pigment's `styled` can be used with React Server Components by default while still getting the benefits of theming. - -For example, in Next.js, you can define a theme in the `next.config.js` file like this: - -```js -const { withPigment } = require('@pigment-css/nextjs-plugin'); - -module.exports = withPigment( - { - // ...other nextConfig - }, - { - theme: { - colors: { - primary: 'tomato', - secondary: 'cyan', - }, - spacing: { - unit: 8, - }, - typography: { - fontFamily: 'Inter, sans-serif', - }, - // ...more keys and values, it's free style! - }, - }, -); -``` - -#### Accessing theme values - -A callback can be used with **styled** and **css** APIs to access the theme values: - -```js -const Heading = styled('h1')(({ theme }) => ({ - color: theme.colors.primary, - fontSize: theme.spacing.unit * 4, - fontFamily: theme.typography.fontFamily, -})); -``` - -#### CSS variables support - -Pigment CSS can generate CSS variables from the theme values when you wrap your theme with `extendTheme` utility. For example, in a `next.config.js` file: - -```js -const { withPigment, extendTheme } = require('@pigment-css/nextjs-plugin'); - -module.exports = withPigment( - { - // ...nextConfig - }, - { - theme: extendTheme({ - colors: { - primary: 'tomato', - secondary: 'cyan', - }, - spacing: { - unit: 8, - }, - typography: { - fontFamily: 'Inter, sans-serif', - }, - }), - }, -); -``` - -The `extendTheme` utility will go through the theme and create a `vars` object which represents the tokens that refer to CSS variables. - -```jsx -const theme = extendTheme({ - colors: { - primary: 'tomato', - secondary: 'cyan', - }, -}); - -console.log(theme.colors.primary); // 'tomato' -console.log(theme.vars.colors.primary); // 'var(--colors-primary)' -``` - -#### Color schemes - -Some tokens, especially color-related tokens, can have different values for different scenarios. For example in a daylight condition, the background color might be white, but in a dark condition, it might be black. - -The `extendTheme` utility lets you define a theme with a special `colorSchemes` key: - -```jsx -extendTheme({ - colorSchemes: { - light: { - colors: { - background: '#f9f9f9', - foreground: '#121212', - }, - }, - dark: { - colors: { - background: '#212121', - foreground: '#fff', - }, - }, - }, -}); -``` - -In the above example, `light` (default) and `dark` color schemes are defined. The structure of each color scheme must be a plain object with keys and values. - -#### Switching color schemes - -By default, when `colorSchemes` is defined, Pigment uses the [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) media query to switch between color schemes based on user's system settings. - -However, if you want to control the color scheme based on application logic, for example, using a button to switch between light and dark mode, you can customize the behavior by providing a `getSelector` function: - -```diff - extendTheme({ - colorSchemes: { - light: { ... }, - dark: { ... }, - }, -+ getSelector: (colorScheme) => colorScheme ? `.theme-${colorScheme}` : ':root', - }); -``` - -Note that you need to add the logic to a button by yourself. Here is an example of how to do it: - -```jsx -function App() { - return ( - <button - onClick={() => { - document.documentElement.classList.toggle('theme-dark'); - }} - > - Toggle color scheme - </button> - ); -} -``` - -#### Styling based on color scheme - -The `extendTheme` utility attaches a function called `applyStyles` to the theme object. It receives a color scheme as the first argument followed by a style object. -It will return a proper CSS selector based on the theme configuration. - -```jsx -const Heading = styled('h1')(({ theme }) => ({ - color: theme.colors.primary, - fontSize: theme.spacing.unit * 4, - fontFamily: theme.typography.fontFamily, - ...theme.applyStyles('dark', { - color: theme.colors.primaryLight, - }), -})); -``` - -#### CSS variables prefix - -You can add a prefix to the generated CSS variables by providing a `cssVarPrefix` option to the `extendTheme` utility: - -```jsx -extendTheme({ - cssVarPrefix: 'pigment', -}); -``` - -The generated CSS variables will have the `pigment` prefix: - -```css -:root { - --pigment-colors-background: #f9f9f9; - --pigment-colors-foreground: #121212; -} -``` - -#### TypeScript - -To get the type checking for the theme, you need to augment the theme type: - -```ts -// any file that is included in your tsconfig.json -import type { ExtendTheme } from '@pigment-css/react'; - -declare module '@pigment-css/react/theme' { - interface ThemeTokens { - // the structure of your theme - } - - interface ThemeArgs { - theme: ExtendTheme<{ - colorScheme: 'light' | 'dark'; - tokens: ThemeTokens; - }>; - } -} -``` - -## How-to guides - -### Coming from Emotion or styled-components - -Emotion and styled-components are runtime CSS-in-JS libraries. What you write in your styles is what you get in the final bundle, which means the styles can be as dynamic as you want with bundle size and performance overhead trade-offs. - -On the other hand, Pigment CSS extracts CSS at build time and replaces the JS code with hashed class names and some CSS variables. This means that it has to know all of the styles to be extracted ahead of time, so there are rules and limitations that you need to be aware of when using JavaScript callbacks or variables in Pigment CSS's APIs. - -Here are some common patterns and how to achieve them with Pigment CSS: - -1. **Fixed set of styles** - -In Emotion or styled-components, you can use props to create styles conditionally: - -```js -const Flex = styled('div')((props) => ({ - display: 'flex', - ...(props.vertical // ❌ Pigment CSS will throw an error - ? { - flexDirection: 'column', - paddingBlock: '1rem', - } - : { - paddingInline: '1rem', - }), -})); -``` - -But in Pigment CSS, you need to define all of the styles ahead of time using the `variants` key: - -```js -const Flex = styled('div')((props) => ({ - display: 'flex', - variants: [ - { - props: { vertical: true }, - style: { - flexDirection: 'column', - paddingBlock: '1rem', - }, - }, - { - props: { vertical: false }, - style: { - paddingInline: '1rem', - }, - }, - ], -})); -``` - -> 💡 Keep in mind that the `variants` key is for fixed values of props, for example, a component's colors, sizes, and states. - -2. **Programatically generated styles** - -For Emotion and styled-components, the styles will be different on each render and instance because the styles are generated at runtime: - -```js -function randomBetween(min: number, max: number) { - return Math.floor(Math.random() * (max - min + 1) + min); -} -function generateBubbleVars() { - return ` - --x: ${randomBetween(10, 90)}%; - --y: ${randomBetween(15, 85)}%; - `; -} - -function App() { - return ( - <div> - {[...Array(10)].map((_, index) => ( - <div key={index} className={css`${generateBubbleVars()}`} /> - ))} - </div> - ) -} -``` - -However, in Pigment CSS with the same code as above, all instances will have the same styles and won't change between renders because the styles are extracted at build time. - -To achieve the same result, you need to move the dynamic logic to props and pass the value to CSS variables instead: - -```js -function randomBetween(min: number, max: number) { - return Math.floor(Math.random() * (max - min + 1) + min); -} - -const Bubble = styled('div')({ - '--x': props => props.x, - '--y': props => props.y, -}); - -function App() { - return ( - <div> - {[...Array(10)].map((_, index) => ( - <Bubble key={index} x={`${randomBetween(10, 90)}%`} y={`${randomBetween(15, 85)}%`} /> - ))} - </div> - ) -} -``` +The package has moved [here](../pigment-css-react/). diff --git a/packages/pigment-react/processors/base-processor-Bkjcr8yc.d.mts b/packages/pigment-react/processors/base-processor-Bkjcr8yc.d.mts new file mode 100644 index 00000000000000..bc1829e1cfc2ff --- /dev/null +++ b/packages/pigment-react/processors/base-processor-Bkjcr8yc.d.mts @@ -0,0 +1,10 @@ +import { IVariableContext } from '@wyw-in-js/shared'; +import { BaseProcessor as BaseProcessor$1 } from '@wyw-in-js/processor-utils'; + +declare abstract class BaseProcessor extends BaseProcessor$1 { + variableIdx: number; + protected getCustomVariableId(cssKey: string, source: string, hasUnit: boolean): string; + protected getVariableContext(cssKey: string, source: string, hasUnit: boolean): IVariableContext; +} + +export { BaseProcessor as B }; diff --git a/packages/pigment-react/processors/base-processor-Bkjcr8yc.d.ts b/packages/pigment-react/processors/base-processor-Bkjcr8yc.d.ts new file mode 100644 index 00000000000000..bc1829e1cfc2ff --- /dev/null +++ b/packages/pigment-react/processors/base-processor-Bkjcr8yc.d.ts @@ -0,0 +1,10 @@ +import { IVariableContext } from '@wyw-in-js/shared'; +import { BaseProcessor as BaseProcessor$1 } from '@wyw-in-js/processor-utils'; + +declare abstract class BaseProcessor extends BaseProcessor$1 { + variableIdx: number; + protected getCustomVariableId(cssKey: string, source: string, hasUnit: boolean): string; + protected getVariableContext(cssKey: string, source: string, hasUnit: boolean): IVariableContext; +} + +export { BaseProcessor as B }; diff --git a/packages/pigment-react/processors/chunk-3NOOOXUY.js b/packages/pigment-react/processors/chunk-3NOOOXUY.js new file mode 100644 index 00000000000000..3af363095a38e0 --- /dev/null +++ b/packages/pigment-react/processors/chunk-3NOOOXUY.js @@ -0,0 +1,25 @@ +'use strict'; + +var createEmotion = require('@emotion/css/create-instance'); + +function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } + +var createEmotion__default = /*#__PURE__*/_interopDefault(createEmotion); + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var { keyframes, cache, css } = createEmotion__default.default({ + stylisPlugins: [], + key: "zero" +}); + +exports.cache = cache; +exports.css = css; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=chunk-3NOOOXUY.js.map \ No newline at end of file diff --git a/packages/pigment-react/processors/chunk-3NOOOXUY.js.map b/packages/pigment-react/processors/chunk-3NOOOXUY.js.map new file mode 100644 index 00000000000000..c780a0e52b11a9 --- /dev/null +++ b/packages/pigment-react/processors/chunk-3NOOOXUY.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/utils/emotion.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,OAAO,mBAAmB;AAE1B,IAAM,EAAE,WAAW,OAAO,IAAI,IAAI,cAAc;AAAA,EAC9C,eAAe,CAAC;AAAA,EAChB,KAAK;AACP,CAAC","sourcesContent":["import createEmotion from '@emotion/css/create-instance';\n\nconst { keyframes, cache, css } = createEmotion({\n stylisPlugins: [],\n key: 'zero',\n});\n\nexport { keyframes, cache, css };\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/chunk-7L3GVF7S.js b/packages/pigment-react/processors/chunk-7L3GVF7S.js new file mode 100644 index 00000000000000..a6c84effb67552 --- /dev/null +++ b/packages/pigment-react/processors/chunk-7L3GVF7S.js @@ -0,0 +1,73 @@ +'use strict'; + +var shared = require('@wyw-in-js/shared'); +var processorUtils = require('@wyw-in-js/processor-utils'); + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var __defProp = Object.defineProperty; +var __defProps = Object.defineProperties; +var __getOwnPropDescs = Object.getOwnPropertyDescriptors; +var __getOwnPropSymbols = Object.getOwnPropertySymbols; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __propIsEnum = Object.prototype.propertyIsEnumerable; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __spreadValues = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + if (__getOwnPropSymbols) + for (var prop of __getOwnPropSymbols(b)) { + if (__propIsEnum.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + } + return a; +}; +var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); +var BaseProcessor = class extends processorUtils.BaseProcessor { + constructor() { + super(...arguments); + this.variableIdx = 0; + } + // Implementation taken from Linaria - https://github.com/callstack/linaria/blob/master/packages/react/src/processors/styled.ts#L284 + getCustomVariableId(cssKey, source, hasUnit) { + const context = this.getVariableContext(cssKey, source, hasUnit); + const customSlugFn = this.options.variableNameSlug; + if (!customSlugFn) { + return processorUtils.toValidCSSIdentifier(`${this.slug}-${context.index}`); + } + return typeof customSlugFn === "function" ? customSlugFn(context) : processorUtils.buildSlug(customSlugFn, __spreadValues({}, context)); + } + // Implementation taken from Linaria - https://github.com/callstack/linaria/blob/master/packages/react/src/processors/styled.ts#L362 + getVariableContext(cssKey, source, hasUnit) { + const getIndex = () => { + const id = this.variableIdx; + this.variableIdx += 1; + return id; + }; + return { + componentName: this.displayName, + componentSlug: this.slug, + get index() { + return getIndex(); + }, + precedingCss: cssKey, + processor: this.constructor.name, + source: "", + unit: "", + valueSlug: shared.slugify(`${source}${hasUnit}`) + }; + } +}; + +exports.BaseProcessor = BaseProcessor; +exports.__spreadProps = __spreadProps; +exports.__spreadValues = __spreadValues; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=chunk-7L3GVF7S.js.map \ No newline at end of file diff --git a/packages/pigment-react/processors/chunk-7L3GVF7S.js.map b/packages/pigment-react/processors/chunk-7L3GVF7S.js.map new file mode 100644 index 00000000000000..2bbbb6f196aebc --- /dev/null +++ b/packages/pigment-react/processors/chunk-7L3GVF7S.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/processors/base-processor.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,eAAiC;AAC1C;AAAA,EACE;AAAA,EACA,iBAAiB;AAAA,EACjB;AAAA,OACK;AAEP,IAA8B,gBAA9B,cAAoD,iBAAiB;AAAA,EAArE;AAAA;AACE,uBAAc;AAAA;AAAA;AAAA,EAGJ,oBAAoB,QAAgB,QAAgB,SAAkB;AAC9E,UAAM,UAAU,KAAK,mBAAmB,QAAQ,QAAQ,OAAO;AAC/D,UAAM,eAAe,KAAK,QAAQ;AAClC,QAAI,CAAC,cAAc;AACjB,aAAO,qBAAqB,GAAG,KAAK,IAAI,IAAI,QAAQ,KAAK,EAAE;AAAA,IAC7D;AAEA,WAAO,OAAO,iBAAiB,aAC3B,aAAa,OAAO,IACpB,UAAU,cAAc,mBAAK,QAAS;AAAA,EAC5C;AAAA;AAAA,EAGU,mBAAmB,QAAgB,QAAgB,SAAoC;AAC/F,UAAM,WAAW,MAAM;AACrB,YAAM,KAAK,KAAK;AAChB,WAAK,eAAe;AACpB,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL,eAAe,KAAK;AAAA,MACpB,eAAe,KAAK;AAAA,MACpB,IAAI,QAAQ;AACV,eAAO,SAAS;AAAA,MAClB;AAAA,MACA,cAAc;AAAA,MACd,WAAW,KAAK,YAAY;AAAA,MAC5B,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,WAAW,QAAQ,GAAG,MAAM,GAAG,OAAO,EAAE;AAAA,IAC1C;AAAA,EACF;AACF","sourcesContent":["import { slugify, IVariableContext } from '@wyw-in-js/shared';\nimport {\n buildSlug,\n BaseProcessor as WywBaseProcessor,\n toValidCSSIdentifier,\n} from '@wyw-in-js/processor-utils';\n\nexport default abstract class BaseProcessor extends WywBaseProcessor {\n variableIdx = 0;\n\n // Implementation taken from Linaria - https://github.com/callstack/linaria/blob/master/packages/react/src/processors/styled.ts#L284\n protected getCustomVariableId(cssKey: string, source: string, hasUnit: boolean) {\n const context = this.getVariableContext(cssKey, source, hasUnit);\n const customSlugFn = this.options.variableNameSlug;\n if (!customSlugFn) {\n return toValidCSSIdentifier(`${this.slug}-${context.index}`);\n }\n\n return typeof customSlugFn === 'function'\n ? customSlugFn(context)\n : buildSlug(customSlugFn, { ...context });\n }\n\n // Implementation taken from Linaria - https://github.com/callstack/linaria/blob/master/packages/react/src/processors/styled.ts#L362\n protected getVariableContext(cssKey: string, source: string, hasUnit: boolean): IVariableContext {\n const getIndex = () => {\n const id = this.variableIdx;\n this.variableIdx += 1;\n return id;\n };\n\n return {\n componentName: this.displayName,\n componentSlug: this.slug,\n get index() {\n return getIndex();\n },\n precedingCss: cssKey,\n processor: this.constructor.name,\n source: '',\n unit: '',\n valueSlug: slugify(`${source}${hasUnit}`),\n };\n }\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/chunk-DTNT7PFI.mjs b/packages/pigment-react/processors/chunk-DTNT7PFI.mjs new file mode 100644 index 00000000000000..1a01d70aef36fc --- /dev/null +++ b/packages/pigment-react/processors/chunk-DTNT7PFI.mjs @@ -0,0 +1,111 @@ +import { types } from '@babel/core'; +import { parseExpression } from '@babel/parser'; +import { isBoxedPrimitive } from '@wyw-in-js/shared'; + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +function isSerializable(o) { + if (Array.isArray(o)) { + return o.every(isSerializable); + } + if (o === null) { + return true; + } + if (isBoxedPrimitive(o)) { + return true; + } + if (typeof o === "object") { + return Object.values(o).every(isSerializable); + } + return typeof o === "function" || typeof o === "string" || typeof o === "number" || typeof o === "boolean"; +} +function parseAndGenerateFunction(fnString, expression) { + var _a; + try { + const exp = parseExpression(fnString); + return exp; + } catch (ex) { + try { + const exp = parseExpression(`{${fnString}}`); + if (exp.type !== "ObjectExpression") { + throw new Error("MUI: The expression must be an object literal."); + } + const propMethod = exp.properties[0]; + return types.arrowFunctionExpression(propMethod.params, propMethod.body); + } catch (ex2) { + throw (_a = expression == null ? void 0 : expression.buildCodeFrameError(`MUI: Could not parse the expression '${fnString}'.`)) != null ? _a : new Error(`MUI: Could not parse the given expression ${fnString}`); + } + } +} +function valueToLiteral(value, expression) { + var _a; + if (value === void 0) { + return { + type: "Identifier", + name: "undefined" + }; + } + if (typeof value === "function") { + return parseAndGenerateFunction(value.toString(), expression); + } + if (isSerializable(value)) { + if (value === null) { + return { + type: "NullLiteral" + }; + } + if (typeof value === "string") { + return { + type: "StringLiteral", + value + }; + } + if (typeof value === "number") { + return { + type: "NumericLiteral", + value + }; + } + if (typeof value === "boolean") { + return { + type: "BooleanLiteral", + value + }; + } + if (Array.isArray(value)) { + return { + type: "ArrayExpression", + elements: value.map((v) => valueToLiteral(v, expression)) + }; + } + return { + type: "ObjectExpression", + properties: Object.entries(value).map(([key, v]) => ({ + type: "ObjectProperty", + key: key.match(/^[a-zA-Z]\w*$/) ? { + type: "Identifier", + name: key + } : { + type: "StringLiteral", + value: key + }, + value: valueToLiteral(v, expression), + computed: false, + shorthand: false + })) + }; + } + throw (_a = expression == null ? void 0 : expression.buildCodeFrameError( + `The expression evaluated to '${value}', which is probably a mistake. If you want it to be inserted into CSS, explicitly cast or transform the value to a string, e.g. - 'String(${expression.source})'.` + )) != null ? _a : new Error(`Could not convert value: "${value}" to literal.`); +} + +export { valueToLiteral }; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=chunk-DTNT7PFI.mjs.map \ No newline at end of file diff --git a/packages/pigment-react/processors/chunk-DTNT7PFI.mjs.map b/packages/pigment-react/processors/chunk-DTNT7PFI.mjs.map new file mode 100644 index 00000000000000..b0aacb5640ef72 --- /dev/null +++ b/packages/pigment-react/processors/chunk-DTNT7PFI.mjs.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/utils/valueToLiteral.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,SAAS,SAAS,SAAS;AAC3B,SAAS,uBAAuB;AAChC,SAA0B,wBAAwB;AAG3C,SAAS,eAAe,GAA+B;AAC5D,MAAI,MAAM,QAAQ,CAAC,GAAG;AACpB,WAAO,EAAE,MAAM,cAAc;AAAA,EAC/B;AAEA,MAAI,MAAM,MAAM;AACd,WAAO;AAAA,EACT;AAEA,MAAI,iBAAiB,CAAC,GAAG;AACvB,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,MAAM,UAAU;AACzB,WAAO,OAAO,OAAO,CAAC,EAAE,MAAM,cAAc;AAAA,EAC9C;AAEA,SACE,OAAO,MAAM,cACb,OAAO,MAAM,YACb,OAAO,MAAM,YACb,OAAO,MAAM;AAEjB;AAEO,SAAS,yBAAyB,UAAkB,YAA8B;AA9BzF;AA+BE,MAAI;AACF,UAAM,MAAM,gBAAgB,QAAQ;AAEpC,WAAO;AAAA,EACT,SAAS,IAAI;AACX,QAAI;AACF,YAAM,MAAM,gBAAgB,IAAI,QAAQ,GAAG;AAC3C,UAAI,IAAI,SAAS,oBAAoB;AACnC,cAAM,IAAI,MAAM,gDAAgD;AAAA,MAClE;AACA,YAAM,aAAa,IAAI,WAAW,CAAC;AACnC,aAAO,EAAE,wBAAwB,WAAW,QAAQ,WAAW,IAAI;AAAA,IACrE,SAAS,KAAK;AACZ,aACE,8CAAY,oBAAoB,wCAAwC,QAAQ,UAAhF,YACA,IAAI,MAAM,6CAA6C,QAAQ,EAAE;AAAA,IAErE;AAAA,EACF;AACF;AAKO,SAAS,eAAe,OAAgB,YAA4C;AAvD3F;AAwDE,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AAAA,EACF;AAEA,MAAI,OAAO,UAAU,YAAY;AAC/B,WAAO,yBAAyB,MAAM,SAAS,GAAG,UAAU;AAAA,EAC9D;AAEA,MAAI,eAAe,KAAK,GAAG;AACzB,QAAI,UAAU,MAAM;AAClB,aAAO;AAAA,QACL,MAAM;AAAA,MACR;AAAA,IACF;AAEA,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO;AAAA,QACL,MAAM;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAEA,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO;AAAA,QACL,MAAM;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAEA,QAAI,OAAO,UAAU,WAAW;AAC9B,aAAO;AAAA,QACL,MAAM;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAEA,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,MAAM,IAAI,CAAC,MAAM,eAAe,GAAG,UAAU,CAAC;AAAA,MAC1D;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,YAAY,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO;AAAA,QACnD,MAAM;AAAA,QACN,KAAK,IAAI,MAAM,eAAe,IAC1B;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,QACR,IACA;AAAA,UACE,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,QACJ,OAAO,eAAe,GAAG,UAAU;AAAA,QACnC,UAAU;AAAA,QACV,WAAW;AAAA,MACb,EAAE;AAAA,IACJ;AAAA,EACF;AAEA,SACE,8CAAY;AAAA,IACV,gCAAgC,KAAK,8IAA8I,WAAW,MAAM;AAAA,QADtM,YAEK,IAAI,MAAM,6BAA6B,KAAK,eAAe;AAEpE","sourcesContent":["import { types as t } from '@babel/core';\nimport { parseExpression } from '@babel/parser';\nimport { ExpressionValue, isBoxedPrimitive } from '@wyw-in-js/shared';\nimport { Serializable } from '@wyw-in-js/transform';\n\nexport function isSerializable(o: unknown): o is Serializable {\n if (Array.isArray(o)) {\n return o.every(isSerializable);\n }\n\n if (o === null) {\n return true;\n }\n\n if (isBoxedPrimitive(o)) {\n return true;\n }\n\n if (typeof o === 'object') {\n return Object.values(o).every(isSerializable);\n }\n\n return (\n typeof o === 'function' ||\n typeof o === 'string' ||\n typeof o === 'number' ||\n typeof o === 'boolean'\n );\n}\n\nexport function parseAndGenerateFunction(fnString: string, expression?: ExpressionValue) {\n try {\n const exp = parseExpression(fnString);\n // a function or an arrow function expression\n return exp as t.FunctionExpression | t.ArrowFunctionExpression;\n } catch (ex) {\n try {\n const exp = parseExpression(`{${fnString}}`);\n if (exp.type !== 'ObjectExpression') {\n throw new Error('MUI: The expression must be an object literal.');\n }\n const propMethod = exp.properties[0] as t.ObjectMethod;\n return t.arrowFunctionExpression(propMethod.params, propMethod.body);\n } catch (ex2) {\n throw (\n expression?.buildCodeFrameError(`MUI: Could not parse the expression '${fnString}'.`) ??\n new Error(`MUI: Could not parse the given expression ${fnString}`)\n );\n }\n }\n}\n\n/**\n * Converts a javascript primitive to its Babel AST node representation.\n */\nexport function valueToLiteral(value: unknown, expression?: ExpressionValue): t.Expression {\n if (value === undefined) {\n return {\n type: 'Identifier',\n name: 'undefined',\n };\n }\n\n if (typeof value === 'function') {\n return parseAndGenerateFunction(value.toString(), expression);\n }\n\n if (isSerializable(value)) {\n if (value === null) {\n return {\n type: 'NullLiteral',\n };\n }\n\n if (typeof value === 'string') {\n return {\n type: 'StringLiteral',\n value,\n };\n }\n\n if (typeof value === 'number') {\n return {\n type: 'NumericLiteral',\n value,\n };\n }\n\n if (typeof value === 'boolean') {\n return {\n type: 'BooleanLiteral',\n value,\n };\n }\n\n if (Array.isArray(value)) {\n return {\n type: 'ArrayExpression',\n elements: value.map((v) => valueToLiteral(v, expression)),\n };\n }\n\n return {\n type: 'ObjectExpression',\n properties: Object.entries(value).map(([key, v]) => ({\n type: 'ObjectProperty',\n key: key.match(/^[a-zA-Z]\\w*$/)\n ? {\n type: 'Identifier',\n name: key,\n }\n : {\n type: 'StringLiteral',\n value: key,\n },\n value: valueToLiteral(v, expression),\n computed: false,\n shorthand: false,\n })),\n };\n }\n\n throw (\n expression?.buildCodeFrameError(\n `The expression evaluated to '${value}', which is probably a mistake. If you want it to be inserted into CSS, explicitly cast or transform the value to a string, e.g. - 'String(${expression.source})'.`,\n ) ?? new Error(`Could not convert value: \"${value}\" to literal.`)\n );\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/chunk-HIMMIWAJ.mjs b/packages/pigment-react/processors/chunk-HIMMIWAJ.mjs new file mode 100644 index 00000000000000..75e79506c144a9 --- /dev/null +++ b/packages/pigment-react/processors/chunk-HIMMIWAJ.mjs @@ -0,0 +1,69 @@ +import { slugify } from '@wyw-in-js/shared'; +import { BaseProcessor as BaseProcessor$1, toValidCSSIdentifier, buildSlug } from '@wyw-in-js/processor-utils'; + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var __defProp = Object.defineProperty; +var __defProps = Object.defineProperties; +var __getOwnPropDescs = Object.getOwnPropertyDescriptors; +var __getOwnPropSymbols = Object.getOwnPropertySymbols; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __propIsEnum = Object.prototype.propertyIsEnumerable; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __spreadValues = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + if (__getOwnPropSymbols) + for (var prop of __getOwnPropSymbols(b)) { + if (__propIsEnum.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + } + return a; +}; +var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); +var BaseProcessor = class extends BaseProcessor$1 { + constructor() { + super(...arguments); + this.variableIdx = 0; + } + // Implementation taken from Linaria - https://github.com/callstack/linaria/blob/master/packages/react/src/processors/styled.ts#L284 + getCustomVariableId(cssKey, source, hasUnit) { + const context = this.getVariableContext(cssKey, source, hasUnit); + const customSlugFn = this.options.variableNameSlug; + if (!customSlugFn) { + return toValidCSSIdentifier(`${this.slug}-${context.index}`); + } + return typeof customSlugFn === "function" ? customSlugFn(context) : buildSlug(customSlugFn, __spreadValues({}, context)); + } + // Implementation taken from Linaria - https://github.com/callstack/linaria/blob/master/packages/react/src/processors/styled.ts#L362 + getVariableContext(cssKey, source, hasUnit) { + const getIndex = () => { + const id = this.variableIdx; + this.variableIdx += 1; + return id; + }; + return { + componentName: this.displayName, + componentSlug: this.slug, + get index() { + return getIndex(); + }, + precedingCss: cssKey, + processor: this.constructor.name, + source: "", + unit: "", + valueSlug: slugify(`${source}${hasUnit}`) + }; + } +}; + +export { BaseProcessor, __spreadProps, __spreadValues }; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=chunk-HIMMIWAJ.mjs.map \ No newline at end of file diff --git a/packages/pigment-react/processors/chunk-HIMMIWAJ.mjs.map b/packages/pigment-react/processors/chunk-HIMMIWAJ.mjs.map new file mode 100644 index 00000000000000..2bbbb6f196aebc --- /dev/null +++ b/packages/pigment-react/processors/chunk-HIMMIWAJ.mjs.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/processors/base-processor.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,eAAiC;AAC1C;AAAA,EACE;AAAA,EACA,iBAAiB;AAAA,EACjB;AAAA,OACK;AAEP,IAA8B,gBAA9B,cAAoD,iBAAiB;AAAA,EAArE;AAAA;AACE,uBAAc;AAAA;AAAA;AAAA,EAGJ,oBAAoB,QAAgB,QAAgB,SAAkB;AAC9E,UAAM,UAAU,KAAK,mBAAmB,QAAQ,QAAQ,OAAO;AAC/D,UAAM,eAAe,KAAK,QAAQ;AAClC,QAAI,CAAC,cAAc;AACjB,aAAO,qBAAqB,GAAG,KAAK,IAAI,IAAI,QAAQ,KAAK,EAAE;AAAA,IAC7D;AAEA,WAAO,OAAO,iBAAiB,aAC3B,aAAa,OAAO,IACpB,UAAU,cAAc,mBAAK,QAAS;AAAA,EAC5C;AAAA;AAAA,EAGU,mBAAmB,QAAgB,QAAgB,SAAoC;AAC/F,UAAM,WAAW,MAAM;AACrB,YAAM,KAAK,KAAK;AAChB,WAAK,eAAe;AACpB,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL,eAAe,KAAK;AAAA,MACpB,eAAe,KAAK;AAAA,MACpB,IAAI,QAAQ;AACV,eAAO,SAAS;AAAA,MAClB;AAAA,MACA,cAAc;AAAA,MACd,WAAW,KAAK,YAAY;AAAA,MAC5B,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,WAAW,QAAQ,GAAG,MAAM,GAAG,OAAO,EAAE;AAAA,IAC1C;AAAA,EACF;AACF","sourcesContent":["import { slugify, IVariableContext } from '@wyw-in-js/shared';\nimport {\n buildSlug,\n BaseProcessor as WywBaseProcessor,\n toValidCSSIdentifier,\n} from '@wyw-in-js/processor-utils';\n\nexport default abstract class BaseProcessor extends WywBaseProcessor {\n variableIdx = 0;\n\n // Implementation taken from Linaria - https://github.com/callstack/linaria/blob/master/packages/react/src/processors/styled.ts#L284\n protected getCustomVariableId(cssKey: string, source: string, hasUnit: boolean) {\n const context = this.getVariableContext(cssKey, source, hasUnit);\n const customSlugFn = this.options.variableNameSlug;\n if (!customSlugFn) {\n return toValidCSSIdentifier(`${this.slug}-${context.index}`);\n }\n\n return typeof customSlugFn === 'function'\n ? customSlugFn(context)\n : buildSlug(customSlugFn, { ...context });\n }\n\n // Implementation taken from Linaria - https://github.com/callstack/linaria/blob/master/packages/react/src/processors/styled.ts#L362\n protected getVariableContext(cssKey: string, source: string, hasUnit: boolean): IVariableContext {\n const getIndex = () => {\n const id = this.variableIdx;\n this.variableIdx += 1;\n return id;\n };\n\n return {\n componentName: this.displayName,\n componentSlug: this.slug,\n get index() {\n return getIndex();\n },\n precedingCss: cssKey,\n processor: this.constructor.name,\n source: '',\n unit: '',\n valueSlug: slugify(`${source}${hasUnit}`),\n };\n }\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/chunk-IUHN7KUC.mjs b/packages/pigment-react/processors/chunk-IUHN7KUC.mjs new file mode 100644 index 00000000000000..df91dba42f1688 --- /dev/null +++ b/packages/pigment-react/processors/chunk-IUHN7KUC.mjs @@ -0,0 +1,18 @@ +import createEmotion from '@emotion/css/create-instance'; + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var { keyframes, cache, css } = createEmotion({ + stylisPlugins: [], + key: "zero" +}); + +export { cache, css }; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=chunk-IUHN7KUC.mjs.map \ No newline at end of file diff --git a/packages/pigment-react/processors/chunk-IUHN7KUC.mjs.map b/packages/pigment-react/processors/chunk-IUHN7KUC.mjs.map new file mode 100644 index 00000000000000..c780a0e52b11a9 --- /dev/null +++ b/packages/pigment-react/processors/chunk-IUHN7KUC.mjs.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/utils/emotion.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,OAAO,mBAAmB;AAE1B,IAAM,EAAE,WAAW,OAAO,IAAI,IAAI,cAAc;AAAA,EAC9C,eAAe,CAAC;AAAA,EAChB,KAAK;AACP,CAAC","sourcesContent":["import createEmotion from '@emotion/css/create-instance';\n\nconst { keyframes, cache, css } = createEmotion({\n stylisPlugins: [],\n key: 'zero',\n});\n\nexport { keyframes, cache, css };\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/chunk-MRLAB7WR.js b/packages/pigment-react/processors/chunk-MRLAB7WR.js new file mode 100644 index 00000000000000..e8a519137e13ac --- /dev/null +++ b/packages/pigment-react/processors/chunk-MRLAB7WR.js @@ -0,0 +1,113 @@ +'use strict'; + +var core = require('@babel/core'); +var parser = require('@babel/parser'); +var shared = require('@wyw-in-js/shared'); + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +function isSerializable(o) { + if (Array.isArray(o)) { + return o.every(isSerializable); + } + if (o === null) { + return true; + } + if (shared.isBoxedPrimitive(o)) { + return true; + } + if (typeof o === "object") { + return Object.values(o).every(isSerializable); + } + return typeof o === "function" || typeof o === "string" || typeof o === "number" || typeof o === "boolean"; +} +function parseAndGenerateFunction(fnString, expression) { + var _a; + try { + const exp = parser.parseExpression(fnString); + return exp; + } catch (ex) { + try { + const exp = parser.parseExpression(`{${fnString}}`); + if (exp.type !== "ObjectExpression") { + throw new Error("MUI: The expression must be an object literal."); + } + const propMethod = exp.properties[0]; + return core.types.arrowFunctionExpression(propMethod.params, propMethod.body); + } catch (ex2) { + throw (_a = expression == null ? void 0 : expression.buildCodeFrameError(`MUI: Could not parse the expression '${fnString}'.`)) != null ? _a : new Error(`MUI: Could not parse the given expression ${fnString}`); + } + } +} +function valueToLiteral(value, expression) { + var _a; + if (value === void 0) { + return { + type: "Identifier", + name: "undefined" + }; + } + if (typeof value === "function") { + return parseAndGenerateFunction(value.toString(), expression); + } + if (isSerializable(value)) { + if (value === null) { + return { + type: "NullLiteral" + }; + } + if (typeof value === "string") { + return { + type: "StringLiteral", + value + }; + } + if (typeof value === "number") { + return { + type: "NumericLiteral", + value + }; + } + if (typeof value === "boolean") { + return { + type: "BooleanLiteral", + value + }; + } + if (Array.isArray(value)) { + return { + type: "ArrayExpression", + elements: value.map((v) => valueToLiteral(v, expression)) + }; + } + return { + type: "ObjectExpression", + properties: Object.entries(value).map(([key, v]) => ({ + type: "ObjectProperty", + key: key.match(/^[a-zA-Z]\w*$/) ? { + type: "Identifier", + name: key + } : { + type: "StringLiteral", + value: key + }, + value: valueToLiteral(v, expression), + computed: false, + shorthand: false + })) + }; + } + throw (_a = expression == null ? void 0 : expression.buildCodeFrameError( + `The expression evaluated to '${value}', which is probably a mistake. If you want it to be inserted into CSS, explicitly cast or transform the value to a string, e.g. - 'String(${expression.source})'.` + )) != null ? _a : new Error(`Could not convert value: "${value}" to literal.`); +} + +exports.valueToLiteral = valueToLiteral; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=chunk-MRLAB7WR.js.map \ No newline at end of file diff --git a/packages/pigment-react/processors/chunk-MRLAB7WR.js.map b/packages/pigment-react/processors/chunk-MRLAB7WR.js.map new file mode 100644 index 00000000000000..b0aacb5640ef72 --- /dev/null +++ b/packages/pigment-react/processors/chunk-MRLAB7WR.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/utils/valueToLiteral.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,SAAS,SAAS,SAAS;AAC3B,SAAS,uBAAuB;AAChC,SAA0B,wBAAwB;AAG3C,SAAS,eAAe,GAA+B;AAC5D,MAAI,MAAM,QAAQ,CAAC,GAAG;AACpB,WAAO,EAAE,MAAM,cAAc;AAAA,EAC/B;AAEA,MAAI,MAAM,MAAM;AACd,WAAO;AAAA,EACT;AAEA,MAAI,iBAAiB,CAAC,GAAG;AACvB,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,MAAM,UAAU;AACzB,WAAO,OAAO,OAAO,CAAC,EAAE,MAAM,cAAc;AAAA,EAC9C;AAEA,SACE,OAAO,MAAM,cACb,OAAO,MAAM,YACb,OAAO,MAAM,YACb,OAAO,MAAM;AAEjB;AAEO,SAAS,yBAAyB,UAAkB,YAA8B;AA9BzF;AA+BE,MAAI;AACF,UAAM,MAAM,gBAAgB,QAAQ;AAEpC,WAAO;AAAA,EACT,SAAS,IAAI;AACX,QAAI;AACF,YAAM,MAAM,gBAAgB,IAAI,QAAQ,GAAG;AAC3C,UAAI,IAAI,SAAS,oBAAoB;AACnC,cAAM,IAAI,MAAM,gDAAgD;AAAA,MAClE;AACA,YAAM,aAAa,IAAI,WAAW,CAAC;AACnC,aAAO,EAAE,wBAAwB,WAAW,QAAQ,WAAW,IAAI;AAAA,IACrE,SAAS,KAAK;AACZ,aACE,8CAAY,oBAAoB,wCAAwC,QAAQ,UAAhF,YACA,IAAI,MAAM,6CAA6C,QAAQ,EAAE;AAAA,IAErE;AAAA,EACF;AACF;AAKO,SAAS,eAAe,OAAgB,YAA4C;AAvD3F;AAwDE,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AAAA,EACF;AAEA,MAAI,OAAO,UAAU,YAAY;AAC/B,WAAO,yBAAyB,MAAM,SAAS,GAAG,UAAU;AAAA,EAC9D;AAEA,MAAI,eAAe,KAAK,GAAG;AACzB,QAAI,UAAU,MAAM;AAClB,aAAO;AAAA,QACL,MAAM;AAAA,MACR;AAAA,IACF;AAEA,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO;AAAA,QACL,MAAM;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAEA,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO;AAAA,QACL,MAAM;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAEA,QAAI,OAAO,UAAU,WAAW;AAC9B,aAAO;AAAA,QACL,MAAM;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAEA,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,MAAM,IAAI,CAAC,MAAM,eAAe,GAAG,UAAU,CAAC;AAAA,MAC1D;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,YAAY,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO;AAAA,QACnD,MAAM;AAAA,QACN,KAAK,IAAI,MAAM,eAAe,IAC1B;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,QACR,IACA;AAAA,UACE,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,QACJ,OAAO,eAAe,GAAG,UAAU;AAAA,QACnC,UAAU;AAAA,QACV,WAAW;AAAA,MACb,EAAE;AAAA,IACJ;AAAA,EACF;AAEA,SACE,8CAAY;AAAA,IACV,gCAAgC,KAAK,8IAA8I,WAAW,MAAM;AAAA,QADtM,YAEK,IAAI,MAAM,6BAA6B,KAAK,eAAe;AAEpE","sourcesContent":["import { types as t } from '@babel/core';\nimport { parseExpression } from '@babel/parser';\nimport { ExpressionValue, isBoxedPrimitive } from '@wyw-in-js/shared';\nimport { Serializable } from '@wyw-in-js/transform';\n\nexport function isSerializable(o: unknown): o is Serializable {\n if (Array.isArray(o)) {\n return o.every(isSerializable);\n }\n\n if (o === null) {\n return true;\n }\n\n if (isBoxedPrimitive(o)) {\n return true;\n }\n\n if (typeof o === 'object') {\n return Object.values(o).every(isSerializable);\n }\n\n return (\n typeof o === 'function' ||\n typeof o === 'string' ||\n typeof o === 'number' ||\n typeof o === 'boolean'\n );\n}\n\nexport function parseAndGenerateFunction(fnString: string, expression?: ExpressionValue) {\n try {\n const exp = parseExpression(fnString);\n // a function or an arrow function expression\n return exp as t.FunctionExpression | t.ArrowFunctionExpression;\n } catch (ex) {\n try {\n const exp = parseExpression(`{${fnString}}`);\n if (exp.type !== 'ObjectExpression') {\n throw new Error('MUI: The expression must be an object literal.');\n }\n const propMethod = exp.properties[0] as t.ObjectMethod;\n return t.arrowFunctionExpression(propMethod.params, propMethod.body);\n } catch (ex2) {\n throw (\n expression?.buildCodeFrameError(`MUI: Could not parse the expression '${fnString}'.`) ??\n new Error(`MUI: Could not parse the given expression ${fnString}`)\n );\n }\n }\n}\n\n/**\n * Converts a javascript primitive to its Babel AST node representation.\n */\nexport function valueToLiteral(value: unknown, expression?: ExpressionValue): t.Expression {\n if (value === undefined) {\n return {\n type: 'Identifier',\n name: 'undefined',\n };\n }\n\n if (typeof value === 'function') {\n return parseAndGenerateFunction(value.toString(), expression);\n }\n\n if (isSerializable(value)) {\n if (value === null) {\n return {\n type: 'NullLiteral',\n };\n }\n\n if (typeof value === 'string') {\n return {\n type: 'StringLiteral',\n value,\n };\n }\n\n if (typeof value === 'number') {\n return {\n type: 'NumericLiteral',\n value,\n };\n }\n\n if (typeof value === 'boolean') {\n return {\n type: 'BooleanLiteral',\n value,\n };\n }\n\n if (Array.isArray(value)) {\n return {\n type: 'ArrayExpression',\n elements: value.map((v) => valueToLiteral(v, expression)),\n };\n }\n\n return {\n type: 'ObjectExpression',\n properties: Object.entries(value).map(([key, v]) => ({\n type: 'ObjectProperty',\n key: key.match(/^[a-zA-Z]\\w*$/)\n ? {\n type: 'Identifier',\n name: key,\n }\n : {\n type: 'StringLiteral',\n value: key,\n },\n value: valueToLiteral(v, expression),\n computed: false,\n shorthand: false,\n })),\n };\n }\n\n throw (\n expression?.buildCodeFrameError(\n `The expression evaluated to '${value}', which is probably a mistake. If you want it to be inserted into CSS, explicitly cast or transform the value to a string, e.g. - 'String(${expression.source})'.`,\n ) ?? new Error(`Could not convert value: \"${value}\" to literal.`)\n );\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/chunk-QVEBVMS6.mjs b/packages/pigment-react/processors/chunk-QVEBVMS6.mjs new file mode 100644 index 00000000000000..a6e457e69dd5e0 --- /dev/null +++ b/packages/pigment-react/processors/chunk-QVEBVMS6.mjs @@ -0,0 +1,251 @@ +import { css, cache } from './chunk-IUHN7KUC.mjs'; +import { __spreadValues } from './chunk-HIMMIWAJ.mjs'; +import { transformSync } from '@babel/core'; +import { parseExpression } from '@babel/parser'; +import * as t from '@babel/types'; +import { declare } from '@babel/helper-plugin-utils'; +import styleFunctionSx, { unstable_defaultSxConfig } from '@mui/system/styleFunctionSx'; +import get from 'lodash/get'; + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + + +// src/utils/isUnitLess.ts +var unitlessKeys = { + animationIterationCount: 1, + aspectRatio: 1, + borderImageOutset: 1, + borderImageSlice: 1, + borderImageWidth: 1, + boxFlex: 1, + boxFlexGroup: 1, + boxOrdinalGroup: 1, + columnCount: 1, + columns: 1, + flex: 1, + flexGrow: 1, + flexPositive: 1, + flexShrink: 1, + flexNegative: 1, + flexOrder: 1, + gridRow: 1, + gridRowEnd: 1, + gridRowSpan: 1, + gridRowStart: 1, + gridColumn: 1, + gridColumnEnd: 1, + gridColumnSpan: 1, + gridColumnStart: 1, + msGridRow: 1, + msGridRowSpan: 1, + msGridColumn: 1, + msGridColumnSpan: 1, + fontWeight: 1, + lineHeight: 1, + opacity: 1, + order: 1, + orphans: 1, + tabSize: 1, + widows: 1, + zIndex: 1, + zoom: 1, + WebkitLineClamp: 1, + // SVG-related properties + fillOpacity: 1, + floodOpacity: 1, + stopOpacity: 1, + strokeDasharray: 1, + strokeDashoffset: 1, + strokeMiterlimit: 1, + strokeOpacity: 1, + strokeWidth: 1 +}; +function isUnitLess(cssKey) { + return unitlessKeys[cssKey] === 1 || cssKey.startsWith("--"); +} +var cssFunctionTransformerPlugin = declare((api, pluginOptions) => { + var _a; + const { types: t2 } = api; + const { + options: { themeArgs: { theme } = {} }, + styleKey + } = pluginOptions; + const config = (_a = theme == null ? void 0 : theme.unstable_sxConfig) != null ? _a : unstable_defaultSxConfig; + const cssPropOptions = config[styleKey]; + const themeKey = cssPropOptions == null ? void 0 : cssPropOptions.themeKey; + const finalPrefix = (theme == null ? void 0 : theme.cssVarPrefix) || ""; + return { + name: "@pigmentcss/zero-internal/cssFunctionTransformerPlugin", + visitor: { + // @TODO - Maybe add support for plain strings in template + // literals as well. + StringLiteral(path) { + var _a2; + const val = path.node.value; + if (val.startsWith("var(") || !val.includes(".")) { + return; + } + if (themeKey === "typography" && val === "inherit") { + return; + } + const propertyThemeKey = themeKey != null ? themeKey : val.split(".")[0]; + const themeValue = (_a2 = get(theme, `${propertyThemeKey}.${val}`)) != null ? _a2 : (theme == null ? void 0 : theme.vars) ? get(theme.vars, `${propertyThemeKey}.${val}`) : void 0; + if (!themeValue) { + console.warn( + `MUI: Value for key: ${val} does not exist in "theme.${propertyThemeKey}" or "theme.vars.${propertyThemeKey}"` + ); + } + const themeKeyArr = val.split(".").join("-"); + path.replaceWith( + t2.stringLiteral(`var(--${finalPrefix}${propertyThemeKey}-${themeKeyArr})`) + ); + } + } + }; +}); + +// src/utils/cssFnValueToVariable.ts +function parseAndWrapExpression(functionString, expressionValue, themeImportIdentifier) { + if (!expressionValue) { + return parseExpression(functionString); + } + const expression = parseExpression(functionString); + if (expression.type === "FunctionExpression" || expression.type === "ArrowFunctionExpression") { + expression.params.push( + t.assignmentPattern(t.identifier("theme"), t.identifier(themeImportIdentifier != null ? themeImportIdentifier : "theme")) + ); + } + return expression; +} +function transformThemeKeysInFn(styleKey, functionString, options, filename, expressionValue, themeImportIdentifier) { + var _a; + const { themeArgs: { theme } = {} } = options; + if (!theme) { + return parseAndWrapExpression(functionString, expressionValue, themeImportIdentifier); + } + const result = transformSync(functionString, { + plugins: [ + [ + cssFunctionTransformerPlugin, + { + styleKey, + options + } + ] + ], + filename: filename != null ? filename : "intermediate-fn.ts", + ast: true, + configFile: false, + babelrc: false + }); + const firstItem = (_a = result == null ? void 0 : result.ast) == null ? void 0 : _a.program.body[0]; + if (!firstItem) { + return parseAndWrapExpression(functionString, expressionValue, themeImportIdentifier); + } + const defaultThemeParam = t.assignmentPattern( + t.identifier("theme"), + t.identifier(themeImportIdentifier != null ? themeImportIdentifier : "theme") + ); + if (firstItem.type === "ExpressionStatement") { + const { expression } = firstItem; + if (expression.type === "ArrowFunctionExpression" || expression.type === "FunctionExpression") { + expression.params.push(defaultThemeParam); + } + return expression; + } + if (firstItem.type === "FunctionDeclaration") { + return t.functionExpression(null, [...firstItem.params, defaultThemeParam], firstItem.body); + } + return parseAndWrapExpression(functionString, expressionValue, themeImportIdentifier); +} +function iterateAndReplaceFunctions(styleObj, expressionValue, getVariableName, options, acc, filename, themeImportIdentifier, includeThemeArg = false) { + const css2 = styleObj; + Object.keys(css2).forEach((key) => { + const value = css2[key]; + if (typeof value === "object") { + if (!Array.isArray(value)) { + iterateAndReplaceFunctions( + value, + expressionValue, + getVariableName, + options, + acc, + filename, + themeImportIdentifier, + includeThemeArg + ); + } + return; + } + if (typeof value !== "function") { + return; + } + try { + const fnString = value.toString(); + const expression = transformThemeKeysInFn( + key, + fnString, + options, + filename, + includeThemeArg && expressionValue ? expressionValue : void 0, + themeImportIdentifier + ); + const unitLess = isUnitLess(key); + const variableId = getVariableName(key, fnString, unitLess); + acc.push([variableId, expression, unitLess]); + css2[key] = `var(--${variableId})`; + } catch (ex) { + const err = expressionValue == null ? void 0 : expressionValue.buildCodeFrameError( + ex.message || "Could not parse function expression." + ); + if (!err) { + throw ex; + } + if ("cause" in err) { + err.cause = ex; + } + throw err; + } + }); +} +function cssFnValueToVariable({ + styleObj, + expressionValue, + getVariableName, + filename, + options, + themeImportIdentifier, + includeThemeArg = false +}) { + const acc = []; + iterateAndReplaceFunctions( + styleObj, + expressionValue, + getVariableName, + options, + acc, + filename != null ? filename : void 0, + themeImportIdentifier, + includeThemeArg + ); + return acc; +} +function processCssObject(cssObj, themeArgs, skipSx = true) { + const processedObj = skipSx ? cssObj : styleFunctionSx(__spreadValues({ + // Does not support shorthand as of now because + // it also adds the spacing multiplier + sx: () => cssObj + }, themeArgs)); + const className = css(processedObj); + return cache.registered[className]; +} + +export { cssFnValueToVariable, processCssObject }; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=chunk-QVEBVMS6.mjs.map \ No newline at end of file diff --git a/packages/pigment-react/processors/chunk-QVEBVMS6.mjs.map b/packages/pigment-react/processors/chunk-QVEBVMS6.mjs.map new file mode 100644 index 00000000000000..23f1587b7abd49 --- /dev/null +++ b/packages/pigment-react/processors/chunk-QVEBVMS6.mjs.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/utils/cssFnValueToVariable.ts","../src/utils/isUnitLess.ts","../src/utils/cssFunctionTransformerPlugin.ts","../src/utils/processCssObject.ts"],"names":["t","_a","css"],"mappings":"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","sourcesContent":["import type { ExpressionValue, FunctionValue } from '@wyw-in-js/shared';\nimport { transformSync, type Node } from '@babel/core';\nimport { parseExpression } from '@babel/parser';\nimport * as t from '@babel/types';\nimport type { Expression } from '@babel/types';\nimport { isUnitLess } from './isUnitLess';\nimport { cssFunctionTransformerPlugin } from './cssFunctionTransformerPlugin';\nimport type { Theme } from './extendTheme';\n\ninterface StyleObj {\n [key: string]: string | number | (() => void) | StyleObj;\n}\n\nexport type PluginCustomOptions = {\n /**\n * Object to pass as parameter to the styled css callback functions.\n */\n themeArgs?: { theme?: Theme };\n};\n\ntype CssFnValueToVariableParams = {\n styleObj: unknown;\n expressionValue: ExpressionValue | null;\n getVariableName: (cssKey: string, source: string, hasUnit: boolean) => string;\n filename?: string | null;\n options: PluginCustomOptions;\n includeThemeArg?: boolean;\n themeImportIdentifier?: string;\n};\n\n// const expressionCache = new WeakMap<ExpressionValue, Expression>();\n\n// @TODO - Implement default theme argument for non-theme config as well.\nfunction parseAndWrapExpression(\n functionString: string,\n expressionValue?: FunctionValue,\n themeImportIdentifier?: string,\n) {\n if (!expressionValue) {\n return parseExpression(functionString);\n }\n const expression = parseExpression(functionString);\n if (expression.type === 'FunctionExpression' || expression.type === 'ArrowFunctionExpression') {\n // let parsedParentExpression = expressionCache.get(expressionValue);\n // if (!parsedParentExpression) {\n // parsedParentExpression = parseExpression(expressionValue.source);\n // if (!parsedParentExpression) {\n // throw new Error(\"MUI: Could not parse styled function's source.\");\n // }\n // }\n expression.params.push(\n t.assignmentPattern(t.identifier('theme'), t.identifier(themeImportIdentifier ?? 'theme')),\n );\n }\n return expression;\n}\n\nfunction transformThemeKeysInFn(\n styleKey: string,\n functionString: string,\n options: PluginCustomOptions,\n filename?: string,\n expressionValue?: FunctionValue,\n themeImportIdentifier?: string,\n) {\n const { themeArgs: { theme } = {} } = options;\n\n // return the function as-is if sxConfig does not contain\n // this css key\n if (!theme) {\n return parseAndWrapExpression(functionString, expressionValue, themeImportIdentifier);\n }\n\n const result = transformSync(functionString, {\n plugins: [\n [\n cssFunctionTransformerPlugin,\n {\n styleKey,\n options,\n },\n ],\n ],\n filename: filename ?? 'intermediate-fn.ts',\n ast: true,\n configFile: false,\n babelrc: false,\n });\n const firstItem = result?.ast?.program.body[0];\n if (!firstItem) {\n return parseAndWrapExpression(functionString, expressionValue, themeImportIdentifier);\n }\n const defaultThemeParam = t.assignmentPattern(\n t.identifier('theme'),\n t.identifier(themeImportIdentifier ?? 'theme'),\n );\n if (firstItem.type === 'ExpressionStatement') {\n const { expression } = firstItem;\n if (expression.type === 'ArrowFunctionExpression' || expression.type === 'FunctionExpression') {\n expression.params.push(defaultThemeParam);\n }\n return expression;\n }\n if (firstItem.type === 'FunctionDeclaration') {\n return t.functionExpression(null, [...firstItem.params, defaultThemeParam], firstItem.body);\n }\n return parseAndWrapExpression(functionString, expressionValue, themeImportIdentifier);\n}\n\nfunction iterateAndReplaceFunctions(\n styleObj: unknown,\n expressionValue: ExpressionValue | null,\n getVariableName: (cssKey: string, source: string, hasUnit: boolean) => string,\n options: PluginCustomOptions,\n acc: [string, Node, boolean][],\n filename?: string,\n themeImportIdentifier?: string,\n includeThemeArg = false,\n) {\n const css = styleObj as StyleObj;\n Object.keys(css).forEach((key) => {\n const value = css[key];\n\n if (typeof value === 'object') {\n if (!Array.isArray(value)) {\n iterateAndReplaceFunctions(\n value,\n expressionValue,\n getVariableName,\n options,\n acc,\n filename,\n themeImportIdentifier,\n includeThemeArg,\n );\n }\n return;\n }\n\n if (typeof value !== 'function') {\n return;\n }\n\n try {\n const fnString = value.toString();\n const expression = transformThemeKeysInFn(\n key,\n fnString,\n options,\n filename,\n includeThemeArg && expressionValue ? (expressionValue as FunctionValue) : undefined,\n themeImportIdentifier,\n );\n const unitLess = isUnitLess(key);\n const variableId = getVariableName(key, fnString, unitLess);\n acc.push([variableId, expression, unitLess]);\n css[key] = `var(--${variableId})`;\n } catch (ex) {\n const err = expressionValue?.buildCodeFrameError(\n (ex as Error).message || 'Could not parse function expression.',\n ) as Error;\n if (!err) {\n throw ex;\n }\n if ('cause' in err) {\n err.cause = ex;\n }\n throw err;\n }\n });\n}\n\n/**\n * Goes through the css object and identifies any keys where the value is a function and replaces the function with a variable id.\n */\nexport function cssFnValueToVariable({\n styleObj,\n expressionValue,\n getVariableName,\n filename,\n options,\n themeImportIdentifier,\n includeThemeArg = false,\n}: CssFnValueToVariableParams) {\n const acc: [string, Expression, boolean][] = [];\n iterateAndReplaceFunctions(\n styleObj,\n expressionValue,\n getVariableName,\n options,\n acc,\n filename ?? undefined,\n themeImportIdentifier,\n includeThemeArg,\n );\n return acc;\n}\n","// Copied over from https://github.com/emotion-js/emotion/blob/main/packages/unitless/src/index.js\n// since typings of @emotion/unitless are broken. PR to fix here - https://github.com/emotion-js/emotion/pull/3088\nconst unitlessKeys: { [key: string]: 1 } = {\n animationIterationCount: 1,\n aspectRatio: 1,\n borderImageOutset: 1,\n borderImageSlice: 1,\n borderImageWidth: 1,\n boxFlex: 1,\n boxFlexGroup: 1,\n boxOrdinalGroup: 1,\n columnCount: 1,\n columns: 1,\n flex: 1,\n flexGrow: 1,\n flexPositive: 1,\n flexShrink: 1,\n flexNegative: 1,\n flexOrder: 1,\n gridRow: 1,\n gridRowEnd: 1,\n gridRowSpan: 1,\n gridRowStart: 1,\n gridColumn: 1,\n gridColumnEnd: 1,\n gridColumnSpan: 1,\n gridColumnStart: 1,\n msGridRow: 1,\n msGridRowSpan: 1,\n msGridColumn: 1,\n msGridColumnSpan: 1,\n fontWeight: 1,\n lineHeight: 1,\n opacity: 1,\n order: 1,\n orphans: 1,\n tabSize: 1,\n widows: 1,\n zIndex: 1,\n zoom: 1,\n WebkitLineClamp: 1,\n\n // SVG-related properties\n fillOpacity: 1,\n floodOpacity: 1,\n stopOpacity: 1,\n strokeDasharray: 1,\n strokeDashoffset: 1,\n strokeMiterlimit: 1,\n strokeOpacity: 1,\n strokeWidth: 1,\n};\n\nexport function isUnitLess(cssKey: string) {\n return unitlessKeys[cssKey] === 1 || cssKey.startsWith('--');\n}\n","import { declare } from '@babel/helper-plugin-utils';\nimport { unstable_defaultSxConfig as defaultSxConfig } from '@mui/system/styleFunctionSx';\nimport get from 'lodash/get';\nimport type { PluginCustomOptions } from './cssFnValueToVariable';\n\ntype BabelPluginOptions = {\n styleKey: string;\n options: PluginCustomOptions;\n};\n\n/**\n * Replaces all usage of theme key strings inside runtime functions with it's equivalent css variable.\n * For ex, for this function\n * ```ts\n * (props: any) => (props.isRed ? 'primary.main' : 'secondary.main')\n * ```\n * The output will be\n * ```ts\n * (props) => (props.isRed ? 'var(--mui-palette-primary-main)' : 'var(--mui-palette-secondary-main)')\n * ```\n */\nconst cssFunctionTransformerPlugin = declare<BabelPluginOptions>((api, pluginOptions) => {\n const { types: t } = api;\n const {\n options: { themeArgs: { theme } = {} },\n styleKey,\n } = pluginOptions;\n const config = theme?.unstable_sxConfig ?? defaultSxConfig;\n const cssPropOptions = config[styleKey];\n const themeKey = cssPropOptions?.themeKey;\n const finalPrefix = theme?.cssVarPrefix || '';\n\n return {\n name: '@pigmentcss/zero-internal/cssFunctionTransformerPlugin',\n visitor: {\n // @TODO - Maybe add support for plain strings in template\n // literals as well.\n StringLiteral(path) {\n const val = path.node.value;\n if (val.startsWith('var(') || !val.includes('.')) {\n return;\n }\n if (themeKey === 'typography' && val === 'inherit') {\n return;\n }\n const propertyThemeKey = themeKey ?? val.split('.')[0];\n const themeValue =\n get(theme, `${propertyThemeKey}.${val}`) ??\n (theme?.vars ? get(theme.vars, `${propertyThemeKey}.${val}`) : undefined);\n if (!themeValue) {\n console.warn(\n `MUI: Value for key: ${val} does not exist in \"theme.${propertyThemeKey}\" or \"theme.vars.${propertyThemeKey}\"`,\n );\n }\n const themeKeyArr = val.split('.').join('-');\n path.replaceWith(\n t.stringLiteral(`var(--${finalPrefix}${propertyThemeKey}-${themeKeyArr})`),\n );\n },\n },\n };\n});\n\nexport { cssFunctionTransformerPlugin };\n","import type { CSSObject } from '@emotion/css';\n// @TODO - Ideally, this should be replicated here instead of importing.\nimport styleFunctionSx from '@mui/system/styleFunctionSx';\nimport { css, cache } from './emotion';\nimport type { PluginCustomOptions } from './cssFnValueToVariable';\n\nexport function processCssObject(\n cssObj: object,\n themeArgs?: PluginCustomOptions['themeArgs'],\n skipSx = true,\n) {\n const processedObj = (\n skipSx\n ? cssObj\n : styleFunctionSx({\n // Does not support shorthand as of now because\n // it also adds the spacing multiplier\n sx: () => cssObj,\n ...themeArgs,\n })\n ) as CSSObject;\n const className = css(processedObj);\n return cache.registered[className];\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/chunk-SBKEDKMF.js b/packages/pigment-react/processors/chunk-SBKEDKMF.js new file mode 100644 index 00000000000000..1291db6d1dd03b --- /dev/null +++ b/packages/pigment-react/processors/chunk-SBKEDKMF.js @@ -0,0 +1,278 @@ +'use strict'; + +var chunk3NOOOXUY_js = require('./chunk-3NOOOXUY.js'); +var chunk7L3GVF7S_js = require('./chunk-7L3GVF7S.js'); +var core = require('@babel/core'); +var parser = require('@babel/parser'); +var t = require('@babel/types'); +var helperPluginUtils = require('@babel/helper-plugin-utils'); +var styleFunctionSx = require('@mui/system/styleFunctionSx'); +var get = require('lodash/get'); + +function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } + +function _interopNamespace(e) { + if (e && e.__esModule) return e; + var n = Object.create(null); + if (e) { + Object.keys(e).forEach(function (k) { + if (k !== 'default') { + var d = Object.getOwnPropertyDescriptor(e, k); + Object.defineProperty(n, k, d.get ? d : { + enumerable: true, + get: function () { return e[k]; } + }); + } + }); + } + n.default = e; + return Object.freeze(n); +} + +var t__namespace = /*#__PURE__*/_interopNamespace(t); +var styleFunctionSx__default = /*#__PURE__*/_interopDefault(styleFunctionSx); +var get__default = /*#__PURE__*/_interopDefault(get); + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + + +// src/utils/isUnitLess.ts +var unitlessKeys = { + animationIterationCount: 1, + aspectRatio: 1, + borderImageOutset: 1, + borderImageSlice: 1, + borderImageWidth: 1, + boxFlex: 1, + boxFlexGroup: 1, + boxOrdinalGroup: 1, + columnCount: 1, + columns: 1, + flex: 1, + flexGrow: 1, + flexPositive: 1, + flexShrink: 1, + flexNegative: 1, + flexOrder: 1, + gridRow: 1, + gridRowEnd: 1, + gridRowSpan: 1, + gridRowStart: 1, + gridColumn: 1, + gridColumnEnd: 1, + gridColumnSpan: 1, + gridColumnStart: 1, + msGridRow: 1, + msGridRowSpan: 1, + msGridColumn: 1, + msGridColumnSpan: 1, + fontWeight: 1, + lineHeight: 1, + opacity: 1, + order: 1, + orphans: 1, + tabSize: 1, + widows: 1, + zIndex: 1, + zoom: 1, + WebkitLineClamp: 1, + // SVG-related properties + fillOpacity: 1, + floodOpacity: 1, + stopOpacity: 1, + strokeDasharray: 1, + strokeDashoffset: 1, + strokeMiterlimit: 1, + strokeOpacity: 1, + strokeWidth: 1 +}; +function isUnitLess(cssKey) { + return unitlessKeys[cssKey] === 1 || cssKey.startsWith("--"); +} +var cssFunctionTransformerPlugin = helperPluginUtils.declare((api, pluginOptions) => { + var _a; + const { types: t2 } = api; + const { + options: { themeArgs: { theme } = {} }, + styleKey + } = pluginOptions; + const config = (_a = theme == null ? void 0 : theme.unstable_sxConfig) != null ? _a : styleFunctionSx.unstable_defaultSxConfig; + const cssPropOptions = config[styleKey]; + const themeKey = cssPropOptions == null ? void 0 : cssPropOptions.themeKey; + const finalPrefix = (theme == null ? void 0 : theme.cssVarPrefix) || ""; + return { + name: "@pigmentcss/zero-internal/cssFunctionTransformerPlugin", + visitor: { + // @TODO - Maybe add support for plain strings in template + // literals as well. + StringLiteral(path) { + var _a2; + const val = path.node.value; + if (val.startsWith("var(") || !val.includes(".")) { + return; + } + if (themeKey === "typography" && val === "inherit") { + return; + } + const propertyThemeKey = themeKey != null ? themeKey : val.split(".")[0]; + const themeValue = (_a2 = get__default.default(theme, `${propertyThemeKey}.${val}`)) != null ? _a2 : (theme == null ? void 0 : theme.vars) ? get__default.default(theme.vars, `${propertyThemeKey}.${val}`) : void 0; + if (!themeValue) { + console.warn( + `MUI: Value for key: ${val} does not exist in "theme.${propertyThemeKey}" or "theme.vars.${propertyThemeKey}"` + ); + } + const themeKeyArr = val.split(".").join("-"); + path.replaceWith( + t2.stringLiteral(`var(--${finalPrefix}${propertyThemeKey}-${themeKeyArr})`) + ); + } + } + }; +}); + +// src/utils/cssFnValueToVariable.ts +function parseAndWrapExpression(functionString, expressionValue, themeImportIdentifier) { + if (!expressionValue) { + return parser.parseExpression(functionString); + } + const expression = parser.parseExpression(functionString); + if (expression.type === "FunctionExpression" || expression.type === "ArrowFunctionExpression") { + expression.params.push( + t__namespace.assignmentPattern(t__namespace.identifier("theme"), t__namespace.identifier(themeImportIdentifier != null ? themeImportIdentifier : "theme")) + ); + } + return expression; +} +function transformThemeKeysInFn(styleKey, functionString, options, filename, expressionValue, themeImportIdentifier) { + var _a; + const { themeArgs: { theme } = {} } = options; + if (!theme) { + return parseAndWrapExpression(functionString, expressionValue, themeImportIdentifier); + } + const result = core.transformSync(functionString, { + plugins: [ + [ + cssFunctionTransformerPlugin, + { + styleKey, + options + } + ] + ], + filename: filename != null ? filename : "intermediate-fn.ts", + ast: true, + configFile: false, + babelrc: false + }); + const firstItem = (_a = result == null ? void 0 : result.ast) == null ? void 0 : _a.program.body[0]; + if (!firstItem) { + return parseAndWrapExpression(functionString, expressionValue, themeImportIdentifier); + } + const defaultThemeParam = t__namespace.assignmentPattern( + t__namespace.identifier("theme"), + t__namespace.identifier(themeImportIdentifier != null ? themeImportIdentifier : "theme") + ); + if (firstItem.type === "ExpressionStatement") { + const { expression } = firstItem; + if (expression.type === "ArrowFunctionExpression" || expression.type === "FunctionExpression") { + expression.params.push(defaultThemeParam); + } + return expression; + } + if (firstItem.type === "FunctionDeclaration") { + return t__namespace.functionExpression(null, [...firstItem.params, defaultThemeParam], firstItem.body); + } + return parseAndWrapExpression(functionString, expressionValue, themeImportIdentifier); +} +function iterateAndReplaceFunctions(styleObj, expressionValue, getVariableName, options, acc, filename, themeImportIdentifier, includeThemeArg = false) { + const css2 = styleObj; + Object.keys(css2).forEach((key) => { + const value = css2[key]; + if (typeof value === "object") { + if (!Array.isArray(value)) { + iterateAndReplaceFunctions( + value, + expressionValue, + getVariableName, + options, + acc, + filename, + themeImportIdentifier, + includeThemeArg + ); + } + return; + } + if (typeof value !== "function") { + return; + } + try { + const fnString = value.toString(); + const expression = transformThemeKeysInFn( + key, + fnString, + options, + filename, + includeThemeArg && expressionValue ? expressionValue : void 0, + themeImportIdentifier + ); + const unitLess = isUnitLess(key); + const variableId = getVariableName(key, fnString, unitLess); + acc.push([variableId, expression, unitLess]); + css2[key] = `var(--${variableId})`; + } catch (ex) { + const err = expressionValue == null ? void 0 : expressionValue.buildCodeFrameError( + ex.message || "Could not parse function expression." + ); + if (!err) { + throw ex; + } + if ("cause" in err) { + err.cause = ex; + } + throw err; + } + }); +} +function cssFnValueToVariable({ + styleObj, + expressionValue, + getVariableName, + filename, + options, + themeImportIdentifier, + includeThemeArg = false +}) { + const acc = []; + iterateAndReplaceFunctions( + styleObj, + expressionValue, + getVariableName, + options, + acc, + filename != null ? filename : void 0, + themeImportIdentifier, + includeThemeArg + ); + return acc; +} +function processCssObject(cssObj, themeArgs, skipSx = true) { + const processedObj = skipSx ? cssObj : styleFunctionSx__default.default(chunk7L3GVF7S_js.__spreadValues({ + // Does not support shorthand as of now because + // it also adds the spacing multiplier + sx: () => cssObj + }, themeArgs)); + const className = chunk3NOOOXUY_js.css(processedObj); + return chunk3NOOOXUY_js.cache.registered[className]; +} + +exports.cssFnValueToVariable = cssFnValueToVariable; +exports.processCssObject = processCssObject; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=chunk-SBKEDKMF.js.map \ No newline at end of file diff --git a/packages/pigment-react/processors/chunk-SBKEDKMF.js.map b/packages/pigment-react/processors/chunk-SBKEDKMF.js.map new file mode 100644 index 00000000000000..23f1587b7abd49 --- /dev/null +++ b/packages/pigment-react/processors/chunk-SBKEDKMF.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/utils/cssFnValueToVariable.ts","../src/utils/isUnitLess.ts","../src/utils/cssFunctionTransformerPlugin.ts","../src/utils/processCssObject.ts"],"names":["t","_a","css"],"mappings":"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","sourcesContent":["import type { ExpressionValue, FunctionValue } from '@wyw-in-js/shared';\nimport { transformSync, type Node } from '@babel/core';\nimport { parseExpression } from '@babel/parser';\nimport * as t from '@babel/types';\nimport type { Expression } from '@babel/types';\nimport { isUnitLess } from './isUnitLess';\nimport { cssFunctionTransformerPlugin } from './cssFunctionTransformerPlugin';\nimport type { Theme } from './extendTheme';\n\ninterface StyleObj {\n [key: string]: string | number | (() => void) | StyleObj;\n}\n\nexport type PluginCustomOptions = {\n /**\n * Object to pass as parameter to the styled css callback functions.\n */\n themeArgs?: { theme?: Theme };\n};\n\ntype CssFnValueToVariableParams = {\n styleObj: unknown;\n expressionValue: ExpressionValue | null;\n getVariableName: (cssKey: string, source: string, hasUnit: boolean) => string;\n filename?: string | null;\n options: PluginCustomOptions;\n includeThemeArg?: boolean;\n themeImportIdentifier?: string;\n};\n\n// const expressionCache = new WeakMap<ExpressionValue, Expression>();\n\n// @TODO - Implement default theme argument for non-theme config as well.\nfunction parseAndWrapExpression(\n functionString: string,\n expressionValue?: FunctionValue,\n themeImportIdentifier?: string,\n) {\n if (!expressionValue) {\n return parseExpression(functionString);\n }\n const expression = parseExpression(functionString);\n if (expression.type === 'FunctionExpression' || expression.type === 'ArrowFunctionExpression') {\n // let parsedParentExpression = expressionCache.get(expressionValue);\n // if (!parsedParentExpression) {\n // parsedParentExpression = parseExpression(expressionValue.source);\n // if (!parsedParentExpression) {\n // throw new Error(\"MUI: Could not parse styled function's source.\");\n // }\n // }\n expression.params.push(\n t.assignmentPattern(t.identifier('theme'), t.identifier(themeImportIdentifier ?? 'theme')),\n );\n }\n return expression;\n}\n\nfunction transformThemeKeysInFn(\n styleKey: string,\n functionString: string,\n options: PluginCustomOptions,\n filename?: string,\n expressionValue?: FunctionValue,\n themeImportIdentifier?: string,\n) {\n const { themeArgs: { theme } = {} } = options;\n\n // return the function as-is if sxConfig does not contain\n // this css key\n if (!theme) {\n return parseAndWrapExpression(functionString, expressionValue, themeImportIdentifier);\n }\n\n const result = transformSync(functionString, {\n plugins: [\n [\n cssFunctionTransformerPlugin,\n {\n styleKey,\n options,\n },\n ],\n ],\n filename: filename ?? 'intermediate-fn.ts',\n ast: true,\n configFile: false,\n babelrc: false,\n });\n const firstItem = result?.ast?.program.body[0];\n if (!firstItem) {\n return parseAndWrapExpression(functionString, expressionValue, themeImportIdentifier);\n }\n const defaultThemeParam = t.assignmentPattern(\n t.identifier('theme'),\n t.identifier(themeImportIdentifier ?? 'theme'),\n );\n if (firstItem.type === 'ExpressionStatement') {\n const { expression } = firstItem;\n if (expression.type === 'ArrowFunctionExpression' || expression.type === 'FunctionExpression') {\n expression.params.push(defaultThemeParam);\n }\n return expression;\n }\n if (firstItem.type === 'FunctionDeclaration') {\n return t.functionExpression(null, [...firstItem.params, defaultThemeParam], firstItem.body);\n }\n return parseAndWrapExpression(functionString, expressionValue, themeImportIdentifier);\n}\n\nfunction iterateAndReplaceFunctions(\n styleObj: unknown,\n expressionValue: ExpressionValue | null,\n getVariableName: (cssKey: string, source: string, hasUnit: boolean) => string,\n options: PluginCustomOptions,\n acc: [string, Node, boolean][],\n filename?: string,\n themeImportIdentifier?: string,\n includeThemeArg = false,\n) {\n const css = styleObj as StyleObj;\n Object.keys(css).forEach((key) => {\n const value = css[key];\n\n if (typeof value === 'object') {\n if (!Array.isArray(value)) {\n iterateAndReplaceFunctions(\n value,\n expressionValue,\n getVariableName,\n options,\n acc,\n filename,\n themeImportIdentifier,\n includeThemeArg,\n );\n }\n return;\n }\n\n if (typeof value !== 'function') {\n return;\n }\n\n try {\n const fnString = value.toString();\n const expression = transformThemeKeysInFn(\n key,\n fnString,\n options,\n filename,\n includeThemeArg && expressionValue ? (expressionValue as FunctionValue) : undefined,\n themeImportIdentifier,\n );\n const unitLess = isUnitLess(key);\n const variableId = getVariableName(key, fnString, unitLess);\n acc.push([variableId, expression, unitLess]);\n css[key] = `var(--${variableId})`;\n } catch (ex) {\n const err = expressionValue?.buildCodeFrameError(\n (ex as Error).message || 'Could not parse function expression.',\n ) as Error;\n if (!err) {\n throw ex;\n }\n if ('cause' in err) {\n err.cause = ex;\n }\n throw err;\n }\n });\n}\n\n/**\n * Goes through the css object and identifies any keys where the value is a function and replaces the function with a variable id.\n */\nexport function cssFnValueToVariable({\n styleObj,\n expressionValue,\n getVariableName,\n filename,\n options,\n themeImportIdentifier,\n includeThemeArg = false,\n}: CssFnValueToVariableParams) {\n const acc: [string, Expression, boolean][] = [];\n iterateAndReplaceFunctions(\n styleObj,\n expressionValue,\n getVariableName,\n options,\n acc,\n filename ?? undefined,\n themeImportIdentifier,\n includeThemeArg,\n );\n return acc;\n}\n","// Copied over from https://github.com/emotion-js/emotion/blob/main/packages/unitless/src/index.js\n// since typings of @emotion/unitless are broken. PR to fix here - https://github.com/emotion-js/emotion/pull/3088\nconst unitlessKeys: { [key: string]: 1 } = {\n animationIterationCount: 1,\n aspectRatio: 1,\n borderImageOutset: 1,\n borderImageSlice: 1,\n borderImageWidth: 1,\n boxFlex: 1,\n boxFlexGroup: 1,\n boxOrdinalGroup: 1,\n columnCount: 1,\n columns: 1,\n flex: 1,\n flexGrow: 1,\n flexPositive: 1,\n flexShrink: 1,\n flexNegative: 1,\n flexOrder: 1,\n gridRow: 1,\n gridRowEnd: 1,\n gridRowSpan: 1,\n gridRowStart: 1,\n gridColumn: 1,\n gridColumnEnd: 1,\n gridColumnSpan: 1,\n gridColumnStart: 1,\n msGridRow: 1,\n msGridRowSpan: 1,\n msGridColumn: 1,\n msGridColumnSpan: 1,\n fontWeight: 1,\n lineHeight: 1,\n opacity: 1,\n order: 1,\n orphans: 1,\n tabSize: 1,\n widows: 1,\n zIndex: 1,\n zoom: 1,\n WebkitLineClamp: 1,\n\n // SVG-related properties\n fillOpacity: 1,\n floodOpacity: 1,\n stopOpacity: 1,\n strokeDasharray: 1,\n strokeDashoffset: 1,\n strokeMiterlimit: 1,\n strokeOpacity: 1,\n strokeWidth: 1,\n};\n\nexport function isUnitLess(cssKey: string) {\n return unitlessKeys[cssKey] === 1 || cssKey.startsWith('--');\n}\n","import { declare } from '@babel/helper-plugin-utils';\nimport { unstable_defaultSxConfig as defaultSxConfig } from '@mui/system/styleFunctionSx';\nimport get from 'lodash/get';\nimport type { PluginCustomOptions } from './cssFnValueToVariable';\n\ntype BabelPluginOptions = {\n styleKey: string;\n options: PluginCustomOptions;\n};\n\n/**\n * Replaces all usage of theme key strings inside runtime functions with it's equivalent css variable.\n * For ex, for this function\n * ```ts\n * (props: any) => (props.isRed ? 'primary.main' : 'secondary.main')\n * ```\n * The output will be\n * ```ts\n * (props) => (props.isRed ? 'var(--mui-palette-primary-main)' : 'var(--mui-palette-secondary-main)')\n * ```\n */\nconst cssFunctionTransformerPlugin = declare<BabelPluginOptions>((api, pluginOptions) => {\n const { types: t } = api;\n const {\n options: { themeArgs: { theme } = {} },\n styleKey,\n } = pluginOptions;\n const config = theme?.unstable_sxConfig ?? defaultSxConfig;\n const cssPropOptions = config[styleKey];\n const themeKey = cssPropOptions?.themeKey;\n const finalPrefix = theme?.cssVarPrefix || '';\n\n return {\n name: '@pigmentcss/zero-internal/cssFunctionTransformerPlugin',\n visitor: {\n // @TODO - Maybe add support for plain strings in template\n // literals as well.\n StringLiteral(path) {\n const val = path.node.value;\n if (val.startsWith('var(') || !val.includes('.')) {\n return;\n }\n if (themeKey === 'typography' && val === 'inherit') {\n return;\n }\n const propertyThemeKey = themeKey ?? val.split('.')[0];\n const themeValue =\n get(theme, `${propertyThemeKey}.${val}`) ??\n (theme?.vars ? get(theme.vars, `${propertyThemeKey}.${val}`) : undefined);\n if (!themeValue) {\n console.warn(\n `MUI: Value for key: ${val} does not exist in \"theme.${propertyThemeKey}\" or \"theme.vars.${propertyThemeKey}\"`,\n );\n }\n const themeKeyArr = val.split('.').join('-');\n path.replaceWith(\n t.stringLiteral(`var(--${finalPrefix}${propertyThemeKey}-${themeKeyArr})`),\n );\n },\n },\n };\n});\n\nexport { cssFunctionTransformerPlugin };\n","import type { CSSObject } from '@emotion/css';\n// @TODO - Ideally, this should be replicated here instead of importing.\nimport styleFunctionSx from '@mui/system/styleFunctionSx';\nimport { css, cache } from './emotion';\nimport type { PluginCustomOptions } from './cssFnValueToVariable';\n\nexport function processCssObject(\n cssObj: object,\n themeArgs?: PluginCustomOptions['themeArgs'],\n skipSx = true,\n) {\n const processedObj = (\n skipSx\n ? cssObj\n : styleFunctionSx({\n // Does not support shorthand as of now because\n // it also adds the spacing multiplier\n sx: () => cssObj,\n ...themeArgs,\n })\n ) as CSSObject;\n const className = css(processedObj);\n return cache.registered[className];\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/createUseThemeProps.d.mts b/packages/pigment-react/processors/createUseThemeProps.d.mts new file mode 100644 index 00000000000000..62ec2d51a3bd13 --- /dev/null +++ b/packages/pigment-react/processors/createUseThemeProps.d.mts @@ -0,0 +1,15 @@ +import { Params, TailProcessorParams, Expression } from '@wyw-in-js/processor-utils'; +import { B as BaseProcessor } from './base-processor-Bkjcr8yc.mjs'; +import '@wyw-in-js/shared'; + +declare class CreateUseThemePropsProcessor extends BaseProcessor { + componentName: string; + constructor(params: Params, ...args: TailProcessorParams); + build(): void; + doEvaltimeReplacement(): void; + get value(): Expression; + doRuntimeReplacement(): void; + get asSelector(): string; +} + +export { CreateUseThemePropsProcessor }; diff --git a/packages/pigment-react/processors/createUseThemeProps.d.ts b/packages/pigment-react/processors/createUseThemeProps.d.ts new file mode 100644 index 00000000000000..ba494453297eb7 --- /dev/null +++ b/packages/pigment-react/processors/createUseThemeProps.d.ts @@ -0,0 +1,15 @@ +import { Params, TailProcessorParams, Expression } from '@wyw-in-js/processor-utils'; +import { B as BaseProcessor } from './base-processor-Bkjcr8yc.js'; +import '@wyw-in-js/shared'; + +declare class CreateUseThemePropsProcessor extends BaseProcessor { + componentName: string; + constructor(params: Params, ...args: TailProcessorParams); + build(): void; + doEvaltimeReplacement(): void; + get value(): Expression; + doRuntimeReplacement(): void; + get asSelector(): string; +} + +export { CreateUseThemePropsProcessor }; diff --git a/packages/pigment-react/processors/createUseThemeProps.js b/packages/pigment-react/processors/createUseThemeProps.js new file mode 100644 index 00000000000000..f5ab0524cc7fda --- /dev/null +++ b/packages/pigment-react/processors/createUseThemeProps.js @@ -0,0 +1,61 @@ +'use strict'; + +var chunkMRLAB7WR_js = require('./chunk-MRLAB7WR.js'); +var chunk7L3GVF7S_js = require('./chunk-7L3GVF7S.js'); +var processorUtils = require('@wyw-in-js/processor-utils'); + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var CreateUseThemePropsProcessor = class extends chunk7L3GVF7S_js.BaseProcessor { + constructor(params, ...args) { + if (params.length > 2) { + throw chunk7L3GVF7S_js.BaseProcessor.SKIP; + } + processorUtils.validateParams(params, ["callee", "call"], "Invalid use of createUseThemeProps tag."); + super([params[0]], ...args); + const [, callParam] = params; + const [, callArg] = callParam; + if (!callArg || callArg.ex.type !== "StringLiteral") { + throw new Error( + `Invalid usage of \`createUseThemeProps\` tag, expected one string literal argument but got ${callArg == null ? void 0 : callArg.ex.type}.` + ); + } + this.componentName = callArg.ex.value; + } + // eslint-disable-next-line class-methods-use-this + build() { + } + doEvaltimeReplacement() { + this.replacer(this.value, false); + } + get value() { + return this.astService.nullLiteral(); + } + doRuntimeReplacement() { + var _a, _b; + const t = this.astService; + const { themeArgs: { theme } = {} } = this.options; + const useThemePropsImportIdentifier = t.addNamedImport( + this.tagSource.imported, + "@pigment-css/react" + ); + let replacement = t.stringLiteral(this.componentName); + if ((_b = (_a = theme == null ? void 0 : theme.components) == null ? void 0 : _a[this.componentName]) == null ? void 0 : _b.defaultProps) { + replacement = chunkMRLAB7WR_js.valueToLiteral(theme.components[this.componentName].defaultProps); + } + this.replacer(t.callExpression(useThemePropsImportIdentifier, [replacement]), true); + } + get asSelector() { + return `.${this.className}`; + } +}; + +exports.CreateUseThemePropsProcessor = CreateUseThemePropsProcessor; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=createUseThemeProps.js.map \ No newline at end of file diff --git a/packages/pigment-react/processors/createUseThemeProps.js.map b/packages/pigment-react/processors/createUseThemeProps.js.map new file mode 100644 index 00000000000000..db60c46191fb13 --- /dev/null +++ b/packages/pigment-react/processors/createUseThemeProps.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/processors/createUseThemeProps.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,SAAS,sBAAgD;AAWlD,IAAM,+BAAN,cAA2C,cAAc;AAAA,EAG9D,YAAY,WAAmB,MAA2B;AAExD,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,cAAc;AAAA,IACtB;AACA,mBAAe,QAAQ,CAAC,UAAU,MAAM,GAAG,yCAAyC;AAEpF,UAAM,CAAC,OAAO,CAAC,CAAC,GAAG,GAAG,IAAI;AAC1B,UAAM,CAAC,EAAE,SAAS,IAAI;AACtB,UAAM,CAAC,EAAE,OAAO,IAAI;AACpB,QAAI,CAAC,WAAW,QAAQ,GAAG,SAAS,iBAAiB;AACnD,YAAM,IAAI;AAAA,QACR,8FAA8F,mCAAS,GAAG,IAAI;AAAA,MAChH;AAAA,IACF;AACA,SAAK,gBAAgB,QAAQ,GAAG;AAAA,EAClC;AAAA;AAAA,EAGA,QAAc;AAAA,EAAC;AAAA,EAEf,wBAA8B;AAC5B,SAAK,SAAS,KAAK,OAAO,KAAK;AAAA,EACjC;AAAA,EAEA,IAAI,QAAoB;AACtB,WAAO,KAAK,WAAW,YAAY;AAAA,EACrC;AAAA,EAEA,uBAA6B;AA3C/B;AA4CI,UAAM,IAAI,KAAK;AAEf,UAAM,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC,EAAE,IAAI,KAAK;AAE3C,UAAM,gCAAgC,EAAE;AAAA,MACtC,KAAK,UAAU;AAAA,MACf;AAAA,IACF;AAEA,QAAI,cAA0B,EAAE,cAAc,KAAK,aAAa;AAChE,SAAI,0CAAO,eAAP,mBAAoB,KAAK,mBAAzB,mBAAyC,cAAc;AACzD,oBAAc,eAAe,MAAM,WAAW,KAAK,aAAa,EAAE,YAAY;AAAA,IAChF;AACA,SAAK,SAAS,EAAE,eAAe,+BAA+B,CAAC,WAAW,CAAC,GAAG,IAAI;AAAA,EACpF;AAAA,EAEA,IAAoB,aAAqB;AAEvC,WAAO,IAAI,KAAK,SAAS;AAAA,EAC3B;AACF","sourcesContent":["import { validateParams, IOptions as IBaseOptions } from '@wyw-in-js/processor-utils';\nimport type { Expression, Params, TailProcessorParams } from '@wyw-in-js/processor-utils';\nimport BaseProcessor from './base-processor';\nimport { valueToLiteral } from '../utils/valueToLiteral';\n\ntype IOptions = IBaseOptions & {\n themeArgs: {\n theme: { components?: Record<string, { defaultProps?: Record<string, unknown> }> };\n };\n};\n\nexport class CreateUseThemePropsProcessor extends BaseProcessor {\n componentName: string;\n\n constructor(params: Params, ...args: TailProcessorParams) {\n // this is already transformed if there is an extra argument\n if (params.length > 2) {\n throw BaseProcessor.SKIP;\n }\n validateParams(params, ['callee', 'call'], 'Invalid use of createUseThemeProps tag.');\n\n super([params[0]], ...args);\n const [, callParam] = params;\n const [, callArg] = callParam;\n if (!callArg || callArg.ex.type !== 'StringLiteral') {\n throw new Error(\n `Invalid usage of \\`createUseThemeProps\\` tag, expected one string literal argument but got ${callArg?.ex.type}.`,\n );\n }\n this.componentName = callArg.ex.value;\n }\n\n // eslint-disable-next-line class-methods-use-this\n build(): void {}\n\n doEvaltimeReplacement(): void {\n this.replacer(this.value, false);\n }\n\n get value(): Expression {\n return this.astService.nullLiteral();\n }\n\n doRuntimeReplacement(): void {\n const t = this.astService;\n\n const { themeArgs: { theme } = {} } = this.options as IOptions;\n\n const useThemePropsImportIdentifier = t.addNamedImport(\n this.tagSource.imported,\n process.env.PACKAGE_NAME as string,\n );\n\n let replacement: Expression = t.stringLiteral(this.componentName);\n if (theme?.components?.[this.componentName]?.defaultProps) {\n replacement = valueToLiteral(theme.components[this.componentName].defaultProps);\n }\n this.replacer(t.callExpression(useThemePropsImportIdentifier, [replacement]), true);\n }\n\n public override get asSelector(): string {\n // For completeness, this is not intended to be used.\n return `.${this.className}`;\n }\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/createUseThemeProps.mjs b/packages/pigment-react/processors/createUseThemeProps.mjs new file mode 100644 index 00000000000000..48cc35834f7520 --- /dev/null +++ b/packages/pigment-react/processors/createUseThemeProps.mjs @@ -0,0 +1,59 @@ +import { valueToLiteral } from './chunk-DTNT7PFI.mjs'; +import { BaseProcessor } from './chunk-HIMMIWAJ.mjs'; +import { validateParams } from '@wyw-in-js/processor-utils'; + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var CreateUseThemePropsProcessor = class extends BaseProcessor { + constructor(params, ...args) { + if (params.length > 2) { + throw BaseProcessor.SKIP; + } + validateParams(params, ["callee", "call"], "Invalid use of createUseThemeProps tag."); + super([params[0]], ...args); + const [, callParam] = params; + const [, callArg] = callParam; + if (!callArg || callArg.ex.type !== "StringLiteral") { + throw new Error( + `Invalid usage of \`createUseThemeProps\` tag, expected one string literal argument but got ${callArg == null ? void 0 : callArg.ex.type}.` + ); + } + this.componentName = callArg.ex.value; + } + // eslint-disable-next-line class-methods-use-this + build() { + } + doEvaltimeReplacement() { + this.replacer(this.value, false); + } + get value() { + return this.astService.nullLiteral(); + } + doRuntimeReplacement() { + var _a, _b; + const t = this.astService; + const { themeArgs: { theme } = {} } = this.options; + const useThemePropsImportIdentifier = t.addNamedImport( + this.tagSource.imported, + "@pigment-css/react" + ); + let replacement = t.stringLiteral(this.componentName); + if ((_b = (_a = theme == null ? void 0 : theme.components) == null ? void 0 : _a[this.componentName]) == null ? void 0 : _b.defaultProps) { + replacement = valueToLiteral(theme.components[this.componentName].defaultProps); + } + this.replacer(t.callExpression(useThemePropsImportIdentifier, [replacement]), true); + } + get asSelector() { + return `.${this.className}`; + } +}; + +export { CreateUseThemePropsProcessor }; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=createUseThemeProps.mjs.map \ No newline at end of file diff --git a/packages/pigment-react/processors/createUseThemeProps.mjs.map b/packages/pigment-react/processors/createUseThemeProps.mjs.map new file mode 100644 index 00000000000000..db60c46191fb13 --- /dev/null +++ b/packages/pigment-react/processors/createUseThemeProps.mjs.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/processors/createUseThemeProps.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,SAAS,sBAAgD;AAWlD,IAAM,+BAAN,cAA2C,cAAc;AAAA,EAG9D,YAAY,WAAmB,MAA2B;AAExD,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,cAAc;AAAA,IACtB;AACA,mBAAe,QAAQ,CAAC,UAAU,MAAM,GAAG,yCAAyC;AAEpF,UAAM,CAAC,OAAO,CAAC,CAAC,GAAG,GAAG,IAAI;AAC1B,UAAM,CAAC,EAAE,SAAS,IAAI;AACtB,UAAM,CAAC,EAAE,OAAO,IAAI;AACpB,QAAI,CAAC,WAAW,QAAQ,GAAG,SAAS,iBAAiB;AACnD,YAAM,IAAI;AAAA,QACR,8FAA8F,mCAAS,GAAG,IAAI;AAAA,MAChH;AAAA,IACF;AACA,SAAK,gBAAgB,QAAQ,GAAG;AAAA,EAClC;AAAA;AAAA,EAGA,QAAc;AAAA,EAAC;AAAA,EAEf,wBAA8B;AAC5B,SAAK,SAAS,KAAK,OAAO,KAAK;AAAA,EACjC;AAAA,EAEA,IAAI,QAAoB;AACtB,WAAO,KAAK,WAAW,YAAY;AAAA,EACrC;AAAA,EAEA,uBAA6B;AA3C/B;AA4CI,UAAM,IAAI,KAAK;AAEf,UAAM,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC,EAAE,IAAI,KAAK;AAE3C,UAAM,gCAAgC,EAAE;AAAA,MACtC,KAAK,UAAU;AAAA,MACf;AAAA,IACF;AAEA,QAAI,cAA0B,EAAE,cAAc,KAAK,aAAa;AAChE,SAAI,0CAAO,eAAP,mBAAoB,KAAK,mBAAzB,mBAAyC,cAAc;AACzD,oBAAc,eAAe,MAAM,WAAW,KAAK,aAAa,EAAE,YAAY;AAAA,IAChF;AACA,SAAK,SAAS,EAAE,eAAe,+BAA+B,CAAC,WAAW,CAAC,GAAG,IAAI;AAAA,EACpF;AAAA,EAEA,IAAoB,aAAqB;AAEvC,WAAO,IAAI,KAAK,SAAS;AAAA,EAC3B;AACF","sourcesContent":["import { validateParams, IOptions as IBaseOptions } from '@wyw-in-js/processor-utils';\nimport type { Expression, Params, TailProcessorParams } from '@wyw-in-js/processor-utils';\nimport BaseProcessor from './base-processor';\nimport { valueToLiteral } from '../utils/valueToLiteral';\n\ntype IOptions = IBaseOptions & {\n themeArgs: {\n theme: { components?: Record<string, { defaultProps?: Record<string, unknown> }> };\n };\n};\n\nexport class CreateUseThemePropsProcessor extends BaseProcessor {\n componentName: string;\n\n constructor(params: Params, ...args: TailProcessorParams) {\n // this is already transformed if there is an extra argument\n if (params.length > 2) {\n throw BaseProcessor.SKIP;\n }\n validateParams(params, ['callee', 'call'], 'Invalid use of createUseThemeProps tag.');\n\n super([params[0]], ...args);\n const [, callParam] = params;\n const [, callArg] = callParam;\n if (!callArg || callArg.ex.type !== 'StringLiteral') {\n throw new Error(\n `Invalid usage of \\`createUseThemeProps\\` tag, expected one string literal argument but got ${callArg?.ex.type}.`,\n );\n }\n this.componentName = callArg.ex.value;\n }\n\n // eslint-disable-next-line class-methods-use-this\n build(): void {}\n\n doEvaltimeReplacement(): void {\n this.replacer(this.value, false);\n }\n\n get value(): Expression {\n return this.astService.nullLiteral();\n }\n\n doRuntimeReplacement(): void {\n const t = this.astService;\n\n const { themeArgs: { theme } = {} } = this.options as IOptions;\n\n const useThemePropsImportIdentifier = t.addNamedImport(\n this.tagSource.imported,\n process.env.PACKAGE_NAME as string,\n );\n\n let replacement: Expression = t.stringLiteral(this.componentName);\n if (theme?.components?.[this.componentName]?.defaultProps) {\n replacement = valueToLiteral(theme.components[this.componentName].defaultProps);\n }\n this.replacer(t.callExpression(useThemePropsImportIdentifier, [replacement]), true);\n }\n\n public override get asSelector(): string {\n // For completeness, this is not intended to be used.\n return `.${this.className}`;\n }\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/css.d.mts b/packages/pigment-react/processors/css.d.mts new file mode 100644 index 00000000000000..7b85aa691ee4f9 --- /dev/null +++ b/packages/pigment-react/processors/css.d.mts @@ -0,0 +1,35 @@ +import { Expression } from '@babel/types'; +import { CallParam, TemplateParam, Params, TailProcessorParams, ValueCache } from '@wyw-in-js/processor-utils'; +import { CSSInterpolation } from '@emotion/css'; +import { B as BaseProcessor } from './base-processor-Bkjcr8yc.mjs'; +import { Primitive } from './keyframes.mjs'; +import '@wyw-in-js/shared'; + +/** + * @description Scope css class generation similar to css from emotion. + * + * @example + * ```ts + * import { css } from '@pigment-css/react'; + * + * const class1 = css(({theme}) => ({ + * color: (theme.vars || theme).palette.primary.main, + * })) + * ``` + * + * <html className={class1} /> + */ +declare class CssProcessor extends BaseProcessor { + callParam: CallParam | TemplateParam; + constructor(params: Params, ...args: TailProcessorParams); + build(values: ValueCache): void; + private handleTemplate; + generateArtifacts(styleObjOrTaggged: CSSInterpolation | string[], ...args: Primitive[]): void; + private handleCall; + doEvaltimeReplacement(): void; + doRuntimeReplacement(): void; + get asSelector(): string; + get value(): Expression; +} + +export { CssProcessor }; diff --git a/packages/pigment-react/processors/css.d.ts b/packages/pigment-react/processors/css.d.ts new file mode 100644 index 00000000000000..decf215350e7d3 --- /dev/null +++ b/packages/pigment-react/processors/css.d.ts @@ -0,0 +1,35 @@ +import { Expression } from '@babel/types'; +import { CallParam, TemplateParam, Params, TailProcessorParams, ValueCache } from '@wyw-in-js/processor-utils'; +import { CSSInterpolation } from '@emotion/css'; +import { B as BaseProcessor } from './base-processor-Bkjcr8yc.js'; +import { Primitive } from './keyframes.js'; +import '@wyw-in-js/shared'; + +/** + * @description Scope css class generation similar to css from emotion. + * + * @example + * ```ts + * import { css } from '@pigment-css/react'; + * + * const class1 = css(({theme}) => ({ + * color: (theme.vars || theme).palette.primary.main, + * })) + * ``` + * + * <html className={class1} /> + */ +declare class CssProcessor extends BaseProcessor { + callParam: CallParam | TemplateParam; + constructor(params: Params, ...args: TailProcessorParams); + build(values: ValueCache): void; + private handleTemplate; + generateArtifacts(styleObjOrTaggged: CSSInterpolation | string[], ...args: Primitive[]): void; + private handleCall; + doEvaltimeReplacement(): void; + doRuntimeReplacement(): void; + get asSelector(): string; + get value(): Expression; +} + +export { CssProcessor }; diff --git a/packages/pigment-react/processors/css.js b/packages/pigment-react/processors/css.js new file mode 100644 index 00000000000000..1490544882e9aa --- /dev/null +++ b/packages/pigment-react/processors/css.js @@ -0,0 +1,153 @@ +'use strict'; + +var chunk3NOOOXUY_js = require('./chunk-3NOOOXUY.js'); +var chunk7L3GVF7S_js = require('./chunk-7L3GVF7S.js'); +var processorUtils = require('@wyw-in-js/processor-utils'); +var shared = require('@wyw-in-js/shared'); +var deepMerge = require('lodash/merge'); + +function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } + +var deepMerge__default = /*#__PURE__*/_interopDefault(deepMerge); + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var CssProcessor = class extends chunk7L3GVF7S_js.BaseProcessor { + constructor(params, ...args) { + if (params.length < 2) { + throw chunk7L3GVF7S_js.BaseProcessor.SKIP; + } + super([params[0]], ...args); + processorUtils.validateParams( + params, + ["callee", ["call", "template"]], + `Invalid use of ${this.tagSource.imported} tag.` + ); + const [, callParams] = params; + if (callParams[0] === "call") { + const [, ...callArgs] = callParams; + this.dependencies.push(...callArgs); + } else if (callParams[0] === "template") { + callParams[1].forEach((element) => { + if ("kind" in element && element.kind !== shared.ValueType.CONST) { + this.dependencies.push(element); + } + }); + } + this.callParam = callParams; + } + build(values) { + if (this.artifacts.length > 0) { + throw new Error(`MUI: "${this.tagSource.imported}" is already built`); + } + const [callType] = this.callParam; + if (callType === "template") { + this.handleTemplate(this.callParam, values); + } else { + this.handleCall(this.callParam, values); + } + } + handleTemplate([, callArgs], values) { + const templateStrs = []; + templateStrs.raw = []; + const templateExpressions = []; + const { themeArgs } = this.options; + callArgs.forEach((item) => { + if ("kind" in item) { + switch (item.kind) { + case shared.ValueType.FUNCTION: { + const value = values.get(item.ex.name); + templateExpressions.push(value(themeArgs)); + break; + } + case shared.ValueType.CONST: + templateExpressions.push(item.value); + break; + case shared.ValueType.LAZY: { + const evaluatedValue = values.get(item.ex.name); + if (typeof evaluatedValue === "function") { + templateExpressions.push(evaluatedValue(themeArgs)); + } else { + templateExpressions.push(evaluatedValue); + } + break; + } + } + } else if (item.type === "TemplateElement") { + templateStrs.push(item.value.cooked); + templateStrs.raw.push(item.value.raw); + } + }); + this.generateArtifacts(templateStrs, ...templateExpressions); + } + generateArtifacts(styleObjOrTaggged, ...args) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; + const cssClassName = chunk3NOOOXUY_js.css(styleObjOrTaggged, ...args); + const cssText = chunk3NOOOXUY_js.cache.registered[cssClassName]; + const rules = { + [this.asSelector]: { + className: this.className, + cssText, + displayName: this.displayName, + start: (_b = (_a = this.location) == null ? void 0 : _a.start) != null ? _b : null + } + }; + const sourceMapReplacements = [ + { + length: cssText.length, + original: { + start: { + column: (_d = (_c = this.location) == null ? void 0 : _c.start.column) != null ? _d : 0, + line: (_f = (_e = this.location) == null ? void 0 : _e.start.line) != null ? _f : 0 + }, + end: { + column: (_h = (_g = this.location) == null ? void 0 : _g.end.column) != null ? _h : 0, + line: (_j = (_i = this.location) == null ? void 0 : _i.end.line) != null ? _j : 0 + } + } + } + ]; + this.artifacts.push(["css", [rules, sourceMapReplacements]]); + } + handleCall([, ...callArgs], values) { + const mergedStyleObj = {}; + callArgs.forEach((callArg) => { + let styleObj; + if (callArg.kind === shared.ValueType.LAZY) { + styleObj = values.get(callArg.ex.name); + } else if (callArg.kind === shared.ValueType.FUNCTION) { + const { themeArgs } = this.options; + const value = values.get(callArg.ex.name); + styleObj = value(themeArgs); + } + if (styleObj) { + deepMerge__default.default(mergedStyleObj, styleObj); + } + }); + if (Object.keys(mergedStyleObj).length > 0) { + this.generateArtifacts(mergedStyleObj); + } + } + doEvaltimeReplacement() { + this.replacer(this.value, false); + } + doRuntimeReplacement() { + this.doEvaltimeReplacement(); + } + get asSelector() { + return `.${this.className}`; + } + get value() { + return this.astService.stringLiteral(this.className); + } +}; + +exports.CssProcessor = CssProcessor; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=css.js.map \ No newline at end of file diff --git a/packages/pigment-react/processors/css.js.map b/packages/pigment-react/processors/css.js.map new file mode 100644 index 00000000000000..f1e963d3ca01ee --- /dev/null +++ b/packages/pigment-react/processors/css.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/processors/css.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AACA,SAAS,sBAAsB;AAS/B,SAAS,iBAAiB;AAE1B,OAAO,eAAe;AAoBf,IAAM,eAAN,cAA2B,cAAc;AAAA,EAG9C,YAAY,WAAmB,MAA2B;AACxD,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,cAAc;AAAA,IACtB;AACA,UAAM,CAAC,OAAO,CAAC,CAAC,GAAG,GAAG,IAAI;AAC1B;AAAA,MACE;AAAA,MACA,CAAC,UAAU,CAAC,QAAQ,UAAU,CAAC;AAAA,MAC/B,kBAAkB,KAAK,UAAU,QAAQ;AAAA,IAC3C;AAEA,UAAM,CAAC,EAAE,UAAU,IAAI;AACvB,QAAI,WAAW,CAAC,MAAM,QAAQ;AAC5B,YAAM,CAAC,EAAE,GAAG,QAAQ,IAAI;AACxB,WAAK,aAAa,KAAK,GAAG,QAAQ;AAAA,IACpC,WAAW,WAAW,CAAC,MAAM,YAAY;AACvC,iBAAW,CAAC,EAAE,QAAQ,CAAC,YAAY;AACjC,YAAI,UAAU,WAAW,QAAQ,SAAS,UAAU,OAAO;AACzD,eAAK,aAAa,KAAK,OAAO;AAAA,QAChC;AAAA,MACF,CAAC;AAAA,IACH;AACA,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,QAAoB;AACxB,QAAI,KAAK,UAAU,SAAS,GAAG;AAC7B,YAAM,IAAI,MAAM,SAAS,KAAK,UAAU,QAAQ,oBAAoB;AAAA,IACtE;AAEA,UAAM,CAAC,QAAQ,IAAI,KAAK;AAExB,QAAI,aAAa,YAAY;AAC3B,WAAK,eAAe,KAAK,WAAW,MAAM;AAAA,IAC5C,OAAO;AACL,WAAK,WAAW,KAAK,WAAW,MAAM;AAAA,IACxC;AAAA,EACF;AAAA,EAEQ,eAAe,CAAC,EAAE,QAAQ,GAAkB,QAAoB;AACtE,UAAM,eAAyB,CAAC;AAEhC,iBAAa,MAAM,CAAC;AACpB,UAAM,sBAAmC,CAAC;AAC1C,UAAM,EAAE,UAAU,IAAI,KAAK;AAE3B,aAAS,QAAQ,CAAC,SAAS;AACzB,UAAI,UAAU,MAAM;AAClB,gBAAQ,KAAK,MAAM;AAAA,UACjB,KAAK,UAAU,UAAU;AACvB,kBAAM,QAAQ,OAAO,IAAI,KAAK,GAAG,IAAI;AACrC,gCAAoB,KAAK,MAAM,SAAS,CAAC;AACzC;AAAA,UACF;AAAA,UACA,KAAK,UAAU;AACb,gCAAoB,KAAK,KAAK,KAAK;AACnC;AAAA,UACF,KAAK,UAAU,MAAM;AACnB,kBAAM,iBAAiB,OAAO,IAAI,KAAK,GAAG,IAAI;AAC9C,gBAAI,OAAO,mBAAmB,YAAY;AACxC,kCAAoB,KAAK,eAAe,SAAS,CAAC;AAAA,YACpD,OAAO;AACL,kCAAoB,KAAK,cAA2B;AAAA,YACtD;AACA;AAAA,UACF;AAAA,UACA;AACE;AAAA,QACJ;AAAA,MACF,WAAW,KAAK,SAAS,mBAAmB;AAC1C,qBAAa,KAAK,KAAK,MAAM,MAAgB;AAE7C,qBAAa,IAAI,KAAK,KAAK,MAAM,GAAG;AAAA,MACtC;AAAA,IACF,CAAC;AACD,SAAK,kBAAkB,cAAc,GAAG,mBAAmB;AAAA,EAC7D;AAAA,EAEA,kBAAkB,sBAAmD,MAAmB;AAjH1F;AAkHI,UAAM,eAAe,IAAI,mBAAmB,GAAG,IAAI;AACnD,UAAM,UAAU,MAAM,WAAW,YAAY;AAE7C,UAAM,QAAe;AAAA,MACnB,CAAC,KAAK,UAAU,GAAG;AAAA,QACjB,WAAW,KAAK;AAAA,QAChB;AAAA,QACA,aAAa,KAAK;AAAA,QAClB,QAAO,gBAAK,aAAL,mBAAe,UAAf,YAAwB;AAAA,MACjC;AAAA,IACF;AACA,UAAM,wBAAsC;AAAA,MAC1C;AAAA,QACE,QAAQ,QAAQ;AAAA,QAChB,UAAU;AAAA,UACR,OAAO;AAAA,YACL,SAAQ,gBAAK,aAAL,mBAAe,MAAM,WAArB,YAA+B;AAAA,YACvC,OAAM,gBAAK,aAAL,mBAAe,MAAM,SAArB,YAA6B;AAAA,UACrC;AAAA,UACA,KAAK;AAAA,YACH,SAAQ,gBAAK,aAAL,mBAAe,IAAI,WAAnB,YAA6B;AAAA,YACrC,OAAM,gBAAK,aAAL,mBAAe,IAAI,SAAnB,YAA2B;AAAA,UACnC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,SAAK,UAAU,KAAK,CAAC,OAAO,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAAA,EAC7D;AAAA,EAEQ,WAAW,CAAC,EAAE,GAAG,QAAQ,GAAc,QAAoB;AACjE,UAAM,iBAAmC,CAAC;AAE1C,aAAS,QAAQ,CAAC,YAAY;AAC5B,UAAI;AACJ,UAAI,QAAQ,SAAS,UAAU,MAAM;AACnC,mBAAW,OAAO,IAAI,QAAQ,GAAG,IAAI;AAAA,MACvC,WAAW,QAAQ,SAAS,UAAU,UAAU;AAC9C,cAAM,EAAE,UAAU,IAAI,KAAK;AAC3B,cAAM,QAAQ,OAAO,IAAI,QAAQ,GAAG,IAAI;AAGxC,mBAAW,MAAM,SAAS;AAAA,MAC5B;AAEA,UAAI,UAAU;AACZ,kBAAU,gBAAgB,QAAQ;AAAA,MACpC;AAAA,IACF,CAAC;AACD,QAAI,OAAO,KAAK,cAAc,EAAE,SAAS,GAAG;AAC1C,WAAK,kBAAkB,cAAc;AAAA,IACvC;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,SAAK,SAAS,KAAK,OAAO,KAAK;AAAA,EACjC;AAAA,EAEA,uBAAuB;AACrB,SAAK,sBAAsB;AAAA,EAC7B;AAAA,EAEA,IAAI,aAAa;AACf,WAAO,IAAI,KAAK,SAAS;AAAA,EAC3B;AAAA,EAEA,IAAI,QAAoB;AACtB,WAAO,KAAK,WAAW,cAAc,KAAK,SAAS;AAAA,EACrD;AACF","sourcesContent":["import type { Expression } from '@babel/types';\nimport { validateParams } from '@wyw-in-js/processor-utils';\nimport type {\n CallParam,\n TemplateParam,\n Params,\n TailProcessorParams,\n ValueCache,\n} from '@wyw-in-js/processor-utils';\nimport type { Replacements, Rules } from '@wyw-in-js/shared';\nimport { ValueType } from '@wyw-in-js/shared';\nimport type { CSSInterpolation } from '@emotion/css';\nimport deepMerge from 'lodash/merge';\nimport BaseProcessor from './base-processor';\nimport type { IOptions } from './styled';\nimport { cache, css } from '../utils/emotion';\nimport type { Primitive, TemplateCallback } from './keyframes';\n\n/**\n * @description Scope css class generation similar to css from emotion.\n *\n * @example\n * ```ts\n * import { css } from '@pigment-css/react';\n *\n * const class1 = css(({theme}) => ({\n * color: (theme.vars || theme).palette.primary.main,\n * }))\n * ```\n *\n * <html className={class1} />\n */\nexport class CssProcessor extends BaseProcessor {\n callParam: CallParam | TemplateParam;\n\n constructor(params: Params, ...args: TailProcessorParams) {\n if (params.length < 2) {\n throw BaseProcessor.SKIP;\n }\n super([params[0]], ...args);\n validateParams(\n params,\n ['callee', ['call', 'template']],\n `Invalid use of ${this.tagSource.imported} tag.`,\n );\n\n const [, callParams] = params;\n if (callParams[0] === 'call') {\n const [, ...callArgs] = callParams;\n this.dependencies.push(...callArgs);\n } else if (callParams[0] === 'template') {\n callParams[1].forEach((element) => {\n if ('kind' in element && element.kind !== ValueType.CONST) {\n this.dependencies.push(element);\n }\n });\n }\n this.callParam = callParams;\n }\n\n build(values: ValueCache) {\n if (this.artifacts.length > 0) {\n throw new Error(`MUI: \"${this.tagSource.imported}\" is already built`);\n }\n\n const [callType] = this.callParam;\n\n if (callType === 'template') {\n this.handleTemplate(this.callParam, values);\n } else {\n this.handleCall(this.callParam, values);\n }\n }\n\n private handleTemplate([, callArgs]: TemplateParam, values: ValueCache) {\n const templateStrs: string[] = [];\n // @ts-ignore @TODO - Fix this. No idea how to initialize a Tagged String array.\n templateStrs.raw = [];\n const templateExpressions: Primitive[] = [];\n const { themeArgs } = this.options as IOptions;\n\n callArgs.forEach((item) => {\n if ('kind' in item) {\n switch (item.kind) {\n case ValueType.FUNCTION: {\n const value = values.get(item.ex.name) as TemplateCallback;\n templateExpressions.push(value(themeArgs));\n break;\n }\n case ValueType.CONST:\n templateExpressions.push(item.value);\n break;\n case ValueType.LAZY: {\n const evaluatedValue = values.get(item.ex.name);\n if (typeof evaluatedValue === 'function') {\n templateExpressions.push(evaluatedValue(themeArgs));\n } else {\n templateExpressions.push(evaluatedValue as Primitive);\n }\n break;\n }\n default:\n break;\n }\n } else if (item.type === 'TemplateElement') {\n templateStrs.push(item.value.cooked as string);\n // @ts-ignore\n templateStrs.raw.push(item.value.raw);\n }\n });\n this.generateArtifacts(templateStrs, ...templateExpressions);\n }\n\n generateArtifacts(styleObjOrTaggged: CSSInterpolation | string[], ...args: Primitive[]) {\n const cssClassName = css(styleObjOrTaggged, ...args);\n const cssText = cache.registered[cssClassName] as string;\n\n const rules: Rules = {\n [this.asSelector]: {\n className: this.className,\n cssText,\n displayName: this.displayName,\n start: this.location?.start ?? null,\n },\n };\n const sourceMapReplacements: Replacements = [\n {\n length: cssText.length,\n original: {\n start: {\n column: this.location?.start.column ?? 0,\n line: this.location?.start.line ?? 0,\n },\n end: {\n column: this.location?.end.column ?? 0,\n line: this.location?.end.line ?? 0,\n },\n },\n },\n ];\n this.artifacts.push(['css', [rules, sourceMapReplacements]]);\n }\n\n private handleCall([, ...callArgs]: CallParam, values: ValueCache) {\n const mergedStyleObj: CSSInterpolation = {};\n\n callArgs.forEach((callArg) => {\n let styleObj: CSSInterpolation;\n if (callArg.kind === ValueType.LAZY) {\n styleObj = values.get(callArg.ex.name) as CSSInterpolation;\n } else if (callArg.kind === ValueType.FUNCTION) {\n const { themeArgs } = this.options as IOptions;\n const value = values.get(callArg.ex.name) as (\n args: Record<string, unknown> | undefined,\n ) => CSSInterpolation;\n styleObj = value(themeArgs);\n }\n\n if (styleObj) {\n deepMerge(mergedStyleObj, styleObj);\n }\n });\n if (Object.keys(mergedStyleObj).length > 0) {\n this.generateArtifacts(mergedStyleObj);\n }\n }\n\n doEvaltimeReplacement() {\n this.replacer(this.value, false);\n }\n\n doRuntimeReplacement() {\n this.doEvaltimeReplacement();\n }\n\n get asSelector() {\n return `.${this.className}`;\n }\n\n get value(): Expression {\n return this.astService.stringLiteral(this.className);\n }\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/css.mjs b/packages/pigment-react/processors/css.mjs new file mode 100644 index 00000000000000..9362570dc16288 --- /dev/null +++ b/packages/pigment-react/processors/css.mjs @@ -0,0 +1,147 @@ +import { css, cache } from './chunk-IUHN7KUC.mjs'; +import { BaseProcessor } from './chunk-HIMMIWAJ.mjs'; +import { validateParams } from '@wyw-in-js/processor-utils'; +import { ValueType } from '@wyw-in-js/shared'; +import deepMerge from 'lodash/merge'; + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var CssProcessor = class extends BaseProcessor { + constructor(params, ...args) { + if (params.length < 2) { + throw BaseProcessor.SKIP; + } + super([params[0]], ...args); + validateParams( + params, + ["callee", ["call", "template"]], + `Invalid use of ${this.tagSource.imported} tag.` + ); + const [, callParams] = params; + if (callParams[0] === "call") { + const [, ...callArgs] = callParams; + this.dependencies.push(...callArgs); + } else if (callParams[0] === "template") { + callParams[1].forEach((element) => { + if ("kind" in element && element.kind !== ValueType.CONST) { + this.dependencies.push(element); + } + }); + } + this.callParam = callParams; + } + build(values) { + if (this.artifacts.length > 0) { + throw new Error(`MUI: "${this.tagSource.imported}" is already built`); + } + const [callType] = this.callParam; + if (callType === "template") { + this.handleTemplate(this.callParam, values); + } else { + this.handleCall(this.callParam, values); + } + } + handleTemplate([, callArgs], values) { + const templateStrs = []; + templateStrs.raw = []; + const templateExpressions = []; + const { themeArgs } = this.options; + callArgs.forEach((item) => { + if ("kind" in item) { + switch (item.kind) { + case ValueType.FUNCTION: { + const value = values.get(item.ex.name); + templateExpressions.push(value(themeArgs)); + break; + } + case ValueType.CONST: + templateExpressions.push(item.value); + break; + case ValueType.LAZY: { + const evaluatedValue = values.get(item.ex.name); + if (typeof evaluatedValue === "function") { + templateExpressions.push(evaluatedValue(themeArgs)); + } else { + templateExpressions.push(evaluatedValue); + } + break; + } + } + } else if (item.type === "TemplateElement") { + templateStrs.push(item.value.cooked); + templateStrs.raw.push(item.value.raw); + } + }); + this.generateArtifacts(templateStrs, ...templateExpressions); + } + generateArtifacts(styleObjOrTaggged, ...args) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; + const cssClassName = css(styleObjOrTaggged, ...args); + const cssText = cache.registered[cssClassName]; + const rules = { + [this.asSelector]: { + className: this.className, + cssText, + displayName: this.displayName, + start: (_b = (_a = this.location) == null ? void 0 : _a.start) != null ? _b : null + } + }; + const sourceMapReplacements = [ + { + length: cssText.length, + original: { + start: { + column: (_d = (_c = this.location) == null ? void 0 : _c.start.column) != null ? _d : 0, + line: (_f = (_e = this.location) == null ? void 0 : _e.start.line) != null ? _f : 0 + }, + end: { + column: (_h = (_g = this.location) == null ? void 0 : _g.end.column) != null ? _h : 0, + line: (_j = (_i = this.location) == null ? void 0 : _i.end.line) != null ? _j : 0 + } + } + } + ]; + this.artifacts.push(["css", [rules, sourceMapReplacements]]); + } + handleCall([, ...callArgs], values) { + const mergedStyleObj = {}; + callArgs.forEach((callArg) => { + let styleObj; + if (callArg.kind === ValueType.LAZY) { + styleObj = values.get(callArg.ex.name); + } else if (callArg.kind === ValueType.FUNCTION) { + const { themeArgs } = this.options; + const value = values.get(callArg.ex.name); + styleObj = value(themeArgs); + } + if (styleObj) { + deepMerge(mergedStyleObj, styleObj); + } + }); + if (Object.keys(mergedStyleObj).length > 0) { + this.generateArtifacts(mergedStyleObj); + } + } + doEvaltimeReplacement() { + this.replacer(this.value, false); + } + doRuntimeReplacement() { + this.doEvaltimeReplacement(); + } + get asSelector() { + return `.${this.className}`; + } + get value() { + return this.astService.stringLiteral(this.className); + } +}; + +export { CssProcessor }; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=css.mjs.map \ No newline at end of file diff --git a/packages/pigment-react/processors/css.mjs.map b/packages/pigment-react/processors/css.mjs.map new file mode 100644 index 00000000000000..f1e963d3ca01ee --- /dev/null +++ b/packages/pigment-react/processors/css.mjs.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/processors/css.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Expression } from '@babel/types';\nimport { validateParams } from '@wyw-in-js/processor-utils';\nimport type {\n CallParam,\n TemplateParam,\n Params,\n TailProcessorParams,\n ValueCache,\n} from '@wyw-in-js/processor-utils';\nimport type { Replacements, Rules } from '@wyw-in-js/shared';\nimport { ValueType } from '@wyw-in-js/shared';\nimport type { CSSInterpolation } from '@emotion/css';\nimport deepMerge from 'lodash/merge';\nimport BaseProcessor from './base-processor';\nimport type { IOptions } from './styled';\nimport { cache, css } from '../utils/emotion';\nimport type { Primitive, TemplateCallback } from './keyframes';\n\n/**\n * @description Scope css class generation similar to css from emotion.\n *\n * @example\n * ```ts\n * import { css } from '@pigment-css/react';\n *\n * const class1 = css(({theme}) => ({\n * color: (theme.vars || theme).palette.primary.main,\n * }))\n * ```\n *\n * <html className={class1} />\n */\nexport class CssProcessor extends BaseProcessor {\n callParam: CallParam | TemplateParam;\n\n constructor(params: Params, ...args: TailProcessorParams) {\n if (params.length < 2) {\n throw BaseProcessor.SKIP;\n }\n super([params[0]], ...args);\n validateParams(\n params,\n ['callee', ['call', 'template']],\n `Invalid use of ${this.tagSource.imported} tag.`,\n );\n\n const [, callParams] = params;\n if (callParams[0] === 'call') {\n const [, ...callArgs] = callParams;\n this.dependencies.push(...callArgs);\n } else if (callParams[0] === 'template') {\n callParams[1].forEach((element) => {\n if ('kind' in element && element.kind !== ValueType.CONST) {\n this.dependencies.push(element);\n }\n });\n }\n this.callParam = callParams;\n }\n\n build(values: ValueCache) {\n if (this.artifacts.length > 0) {\n throw new Error(`MUI: \"${this.tagSource.imported}\" is already built`);\n }\n\n const [callType] = this.callParam;\n\n if (callType === 'template') {\n this.handleTemplate(this.callParam, values);\n } else {\n this.handleCall(this.callParam, values);\n }\n }\n\n private handleTemplate([, callArgs]: TemplateParam, values: ValueCache) {\n const templateStrs: string[] = [];\n // @ts-ignore @TODO - Fix this. No idea how to initialize a Tagged String array.\n templateStrs.raw = [];\n const templateExpressions: Primitive[] = [];\n const { themeArgs } = this.options as IOptions;\n\n callArgs.forEach((item) => {\n if ('kind' in item) {\n switch (item.kind) {\n case ValueType.FUNCTION: {\n const value = values.get(item.ex.name) as TemplateCallback;\n templateExpressions.push(value(themeArgs));\n break;\n }\n case ValueType.CONST:\n templateExpressions.push(item.value);\n break;\n case ValueType.LAZY: {\n const evaluatedValue = values.get(item.ex.name);\n if (typeof evaluatedValue === 'function') {\n templateExpressions.push(evaluatedValue(themeArgs));\n } else {\n templateExpressions.push(evaluatedValue as Primitive);\n }\n break;\n }\n default:\n break;\n }\n } else if (item.type === 'TemplateElement') {\n templateStrs.push(item.value.cooked as string);\n // @ts-ignore\n templateStrs.raw.push(item.value.raw);\n }\n });\n this.generateArtifacts(templateStrs, ...templateExpressions);\n }\n\n generateArtifacts(styleObjOrTaggged: CSSInterpolation | string[], ...args: Primitive[]) {\n const cssClassName = css(styleObjOrTaggged, ...args);\n const cssText = cache.registered[cssClassName] as string;\n\n const rules: Rules = {\n [this.asSelector]: {\n className: this.className,\n cssText,\n displayName: this.displayName,\n start: this.location?.start ?? null,\n },\n };\n const sourceMapReplacements: Replacements = [\n {\n length: cssText.length,\n original: {\n start: {\n column: this.location?.start.column ?? 0,\n line: this.location?.start.line ?? 0,\n },\n end: {\n column: this.location?.end.column ?? 0,\n line: this.location?.end.line ?? 0,\n },\n },\n },\n ];\n this.artifacts.push(['css', [rules, sourceMapReplacements]]);\n }\n\n private handleCall([, ...callArgs]: CallParam, values: ValueCache) {\n const mergedStyleObj: CSSInterpolation = {};\n\n callArgs.forEach((callArg) => {\n let styleObj: CSSInterpolation;\n if (callArg.kind === ValueType.LAZY) {\n styleObj = values.get(callArg.ex.name) as CSSInterpolation;\n } else if (callArg.kind === ValueType.FUNCTION) {\n const { themeArgs } = this.options as IOptions;\n const value = values.get(callArg.ex.name) as (\n args: Record<string, unknown> | undefined,\n ) => CSSInterpolation;\n styleObj = value(themeArgs);\n }\n\n if (styleObj) {\n deepMerge(mergedStyleObj, styleObj);\n }\n });\n if (Object.keys(mergedStyleObj).length > 0) {\n this.generateArtifacts(mergedStyleObj);\n }\n }\n\n doEvaltimeReplacement() {\n this.replacer(this.value, false);\n }\n\n doRuntimeReplacement() {\n this.doEvaltimeReplacement();\n }\n\n get asSelector() {\n return `.${this.className}`;\n }\n\n get value(): Expression {\n return this.astService.stringLiteral(this.className);\n }\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/generateAtomics.d.mts b/packages/pigment-react/processors/generateAtomics.d.mts new file mode 100644 index 00000000000000..090bdef8ed5d2f --- /dev/null +++ b/packages/pigment-react/processors/generateAtomics.d.mts @@ -0,0 +1,31 @@ +import { Expression } from '@babel/types'; +import { Params, TailProcessorParams, ValueCache } from '@wyw-in-js/processor-utils'; +import { ExpressionValue } from '@wyw-in-js/shared'; +import { B as BaseProcessor } from './base-processor-Bkjcr8yc.mjs'; + +type Atomics = { + conditions: Record<string, string>; + defaultCondition: string; + properties: { + [key: string]: string[]; + }; + shorthands: Record<string, string[]>; +}; +type RuntimeConfig = { + conditions: string[]; + styles: Record<string, Record<string, Record<string, string>>>; + shorthands: Atomics['shorthands']; +}; + +declare class GenerateAtomicsProcessor extends BaseProcessor { + callParam: ExpressionValue; + runtimeConfig?: RuntimeConfig; + constructor(params: Params, ...args: TailProcessorParams); + get asSelector(): string; + get value(): Expression; + doEvaltimeReplacement(): void; + build(values: ValueCache): void; + doRuntimeReplacement(): void; +} + +export { GenerateAtomicsProcessor }; diff --git a/packages/pigment-react/processors/generateAtomics.d.ts b/packages/pigment-react/processors/generateAtomics.d.ts new file mode 100644 index 00000000000000..8883fb7539f0da --- /dev/null +++ b/packages/pigment-react/processors/generateAtomics.d.ts @@ -0,0 +1,31 @@ +import { Expression } from '@babel/types'; +import { Params, TailProcessorParams, ValueCache } from '@wyw-in-js/processor-utils'; +import { ExpressionValue } from '@wyw-in-js/shared'; +import { B as BaseProcessor } from './base-processor-Bkjcr8yc.js'; + +type Atomics = { + conditions: Record<string, string>; + defaultCondition: string; + properties: { + [key: string]: string[]; + }; + shorthands: Record<string, string[]>; +}; +type RuntimeConfig = { + conditions: string[]; + styles: Record<string, Record<string, Record<string, string>>>; + shorthands: Atomics['shorthands']; +}; + +declare class GenerateAtomicsProcessor extends BaseProcessor { + callParam: ExpressionValue; + runtimeConfig?: RuntimeConfig; + constructor(params: Params, ...args: TailProcessorParams); + get asSelector(): string; + get value(): Expression; + doEvaltimeReplacement(): void; + build(values: ValueCache): void; + doRuntimeReplacement(): void; +} + +export { GenerateAtomicsProcessor }; diff --git a/packages/pigment-react/processors/generateAtomics.js b/packages/pigment-react/processors/generateAtomics.js new file mode 100644 index 00000000000000..97481f786b79f8 --- /dev/null +++ b/packages/pigment-react/processors/generateAtomics.js @@ -0,0 +1,160 @@ +'use strict'; + +var chunk3NOOOXUY_js = require('./chunk-3NOOOXUY.js'); +var chunkMRLAB7WR_js = require('./chunk-MRLAB7WR.js'); +var chunk7L3GVF7S_js = require('./chunk-7L3GVF7S.js'); +var processorUtils = require('@wyw-in-js/processor-utils'); +var shared = require('@wyw-in-js/shared'); +var cssesc = require('cssesc'); + +function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } + +var cssesc__default = /*#__PURE__*/_interopDefault(cssesc); + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +function getClassName(...items) { + return cssesc__default.default(items.filter(Boolean).join("_")); +} +function convertAtomicsToCss({ conditions = {}, defaultCondition, properties, shorthands = {} }, mainClassName, isGlobal = false, debug = false, prefix = "Mui") { + const runtimeConfig = { + styles: {}, + shorthands, + conditions: Object.keys(conditions) + }; + let count = 1; + function getCount() { + const val = count; + count += 1; + return val; + } + const classes = []; + Object.entries(conditions).forEach(([conditionName, mediaQueryStr]) => { + Object.entries(properties).forEach(([cssPropertyName, propertyValues]) => { + propertyValues.forEach((propertyValue) => { + var _a, _b; + const className = isGlobal || debug ? getClassName( + prefix, + cssPropertyName, + conditionName != null ? conditionName : "default", + propertyValue, + !isGlobal ? mainClassName : "" + ) : `${mainClassName}${getCount()}`; + if (defaultCondition === conditionName || !mediaQueryStr) { + classes.push({ + className, + css: { + [cssPropertyName]: propertyValue + } + }); + } else { + classes.push({ + className, + css: { + [mediaQueryStr]: { + [cssPropertyName]: propertyValue + } + } + }); + } + const classMap = (_a = runtimeConfig.styles[cssPropertyName]) != null ? _a : {}; + const conditionClassMap = (_b = classMap[propertyValue]) != null ? _b : {}; + conditionClassMap[conditionName] = className; + if (conditionName === defaultCondition) { + conditionClassMap.$$default = className; + } + classMap[propertyValue] = conditionClassMap; + runtimeConfig.styles[cssPropertyName] = classMap; + }); + }); + }); + return { + classes, + runtimeConfig + }; +} + +// src/processors/generateAtomics.ts +var GenerateAtomicsProcessor = class extends chunk7L3GVF7S_js.BaseProcessor { + constructor(params, ...args) { + super([params[0]], ...args); + processorUtils.validateParams(params, ["callee", ["call"]], `Invalid use of ${this.tagSource.imported} tag.`); + const [, callParam] = params; + const [, callParamArgument] = callParam; + this.dependencies.push(callParamArgument); + this.callParam = callParamArgument; + } + // eslint-disable-next-line class-methods-use-this + get asSelector() { + throw new Error("Method not implemented."); + } + // eslint-disable-next-line class-methods-use-this + get value() { + throw new Error("Method not implemented."); + } + doEvaltimeReplacement() { + this.replacer(this.astService.nullLiteral(), true); + } + build(values) { + const { themeArgs = {} } = this.options; + const param = this.callParam; + if (param.kind !== shared.ValueType.CONST) { + const value = param.kind === shared.ValueType.FUNCTION ? values.get(param.ex.name)(themeArgs) : values.get(param.ex.name); + const { classes, runtimeConfig } = convertAtomicsToCss( + value, + this.className, + false, + this.options.displayName + ); + this.runtimeConfig = runtimeConfig; + classes.forEach(({ className, css: cssObject }) => { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; + const emotionClass = chunk3NOOOXUY_js.css(cssObject); + const cssText = chunk3NOOOXUY_js.cache.registered[emotionClass]; + const rules = { + [`.${className}`]: { + cssText, + className: this.className, + displayName: this.displayName, + start: (_b = (_a = this.location) == null ? void 0 : _a.start) != null ? _b : null + } + }; + const sourceMapReplacements = [ + { + length: cssText.length, + original: { + start: { + column: (_d = (_c = this.location) == null ? void 0 : _c.start.column) != null ? _d : 0, + line: (_f = (_e = this.location) == null ? void 0 : _e.start.line) != null ? _f : 0 + }, + end: { + column: (_h = (_g = this.location) == null ? void 0 : _g.end.column) != null ? _h : 0, + line: (_j = (_i = this.location) == null ? void 0 : _i.end.line) != null ? _j : 0 + } + } + } + ]; + this.artifacts.push(["css", [rules, sourceMapReplacements]]); + }); + } + } + doRuntimeReplacement() { + if (!this.runtimeConfig) { + this.doEvaltimeReplacement(); + return; + } + const { astService: t } = this; + const importName = t.addNamedImport("atomics", "@pigment-css/react"); + this.replacer(t.callExpression(importName, [chunkMRLAB7WR_js.valueToLiteral(this.runtimeConfig)]), true); + } +}; + +exports.GenerateAtomicsProcessor = GenerateAtomicsProcessor; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=generateAtomics.js.map \ No newline at end of file diff --git a/packages/pigment-react/processors/generateAtomics.js.map b/packages/pigment-react/processors/generateAtomics.js.map new file mode 100644 index 00000000000000..0ba40d6aacc440 --- /dev/null +++ b/packages/pigment-react/processors/generateAtomics.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/processors/generateAtomics.ts","../src/utils/convertAtomicsToCss.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AACA;AAAA,EAIE;AAAA,OACK;AACP,SAAS,iBAAsE;;;ACP/E,OAAO,YAAY;AAiBnB,SAAS,gBAAgB,OAAiB;AACxC,SAAO,OAAO,MAAM,OAAO,OAAO,EAAE,KAAK,GAAG,CAAC;AAC/C;AAEO,SAAS,oBACd,EAAE,aAAa,CAAC,GAAG,kBAAkB,YAAY,aAAa,CAAC,EAAE,GACjE,eACA,WAAW,OACX,QAAQ,OACR,SAAS,OACT;AACA,QAAM,gBAA+B;AAAA,IACnC,QAAQ,CAAC;AAAA,IACT;AAAA,IACA,YAAY,OAAO,KAAK,UAAU;AAAA,EACpC;AACA,MAAI,QAAQ;AACZ,WAAS,WAAW;AAClB,UAAM,MAAM;AACZ,aAAS;AACT,WAAO;AAAA,EACT;AAEA,QAAM,UAGA,CAAC;AAEP,SAAO,QAAQ,UAAU,EAAE,QAAQ,CAAC,CAAC,eAAe,aAAa,MAAM;AACrE,WAAO,QAAQ,UAAU,EAAE,QAAQ,CAAC,CAAC,iBAAiB,cAAc,MAAM;AACxE,qBAAe,QAAQ,CAAC,kBAAkB;AA/ChD;AAgDQ,cAAM,YACJ,YAAY,QACR;AAAA,UACE;AAAA,UACA;AAAA,UACA,wCAAiB;AAAA,UACjB;AAAA,UACA,CAAC,WAAW,gBAAgB;AAAA,QAC9B,IACA,GAAG,aAAa,GAAG,SAAS,CAAC;AACnC,YAAI,qBAAqB,iBAAiB,CAAC,eAAe;AACxD,kBAAQ,KAAK;AAAA,YACX;AAAA,YACA,KAAK;AAAA,cACH,CAAC,eAAe,GAAG;AAAA,YACrB;AAAA,UACF,CAAC;AAAA,QACH,OAAO;AACL,kBAAQ,KAAK;AAAA,YACX;AAAA,YACA,KAAK;AAAA,cACH,CAAC,aAAa,GAAG;AAAA,gBACf,CAAC,eAAe,GAAG;AAAA,cACrB;AAAA,YACF;AAAA,UACF,CAAC;AAAA,QACH;AACA,cAAM,YAAW,mBAAc,OAAO,eAAe,MAApC,YAAyC,CAAC;AAC3D,cAAM,qBAAoB,cAAS,aAAa,MAAtB,YAA2B,CAAC;AACtD,0BAAkB,aAAa,IAAI;AACnC,YAAI,kBAAkB,kBAAkB;AACtC,4BAAkB,YAAY;AAAA,QAChC;AACA,iBAAS,aAAa,IAAI;AAC1B,sBAAc,OAAO,eAAe,IAAI;AAAA,MAC1C,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;;;ADvEO,IAAM,2BAAN,cAAuC,cAAc;AAAA,EAK1D,YAAY,WAAmB,MAA2B;AACxD,UAAM,CAAC,OAAO,CAAC,CAAC,GAAG,GAAG,IAAI;AAC1B,mBAAe,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,kBAAkB,KAAK,UAAU,QAAQ,OAAO;AAC7F,UAAM,CAAC,EAAE,SAAS,IAAI;AACtB,UAAM,CAAC,EAAE,iBAAiB,IAAI;AAC9B,SAAK,aAAa,KAAK,iBAAiB;AACxC,SAAK,YAAY;AAAA,EACnB;AAAA;AAAA,EAGA,IAAI,aAAqB;AACvB,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AAAA;AAAA,EAGA,IAAI,QAAoB;AACtB,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AAAA,EAEA,wBAA8B;AAC5B,SAAK,SAAS,KAAK,WAAW,YAAY,GAAG,IAAI;AAAA,EACnD;AAAA,EAEA,MAAM,QAA0B;AAC9B,UAAM,EAAE,YAAY,CAAC,EAAE,IAAI,KAAK;AAChC,UAAM,QAAQ,KAAK;AACnB,QAAI,MAAM,SAAS,UAAU,OAAO;AAClC,YAAM,QACJ,MAAM,SAAS,UAAU,WACpB,OAAO,IAAI,MAAM,GAAG,IAAI,EAAmC,SAAS,IACrE,OAAO,IAAI,MAAM,GAAG,IAAI;AAC9B,YAAM,EAAE,SAAS,cAAc,IAAI;AAAA,QACjC;AAAA,QACA,KAAK;AAAA,QACL;AAAA,QACA,KAAK,QAAQ;AAAA,MACf;AACA,WAAK,gBAAgB;AAErB,cAAQ,QAAQ,CAAC,EAAE,WAAW,KAAK,UAAU,MAAM;AAhEzD;AAiEQ,cAAM,eAAe,IAAI,SAA6B;AACtD,cAAM,UAAU,MAAM,WAAW,YAAY;AAE7C,cAAM,QAAe;AAAA,UACnB,CAAC,IAAI,SAAS,EAAE,GAAG;AAAA,YACjB;AAAA,YACA,WAAW,KAAK;AAAA,YAChB,aAAa,KAAK;AAAA,YAClB,QAAO,gBAAK,aAAL,mBAAe,UAAf,YAAwB;AAAA,UACjC;AAAA,QACF;AACA,cAAM,wBAAsC;AAAA,UAC1C;AAAA,YACE,QAAQ,QAAQ;AAAA,YAChB,UAAU;AAAA,cACR,OAAO;AAAA,gBACL,SAAQ,gBAAK,aAAL,mBAAe,MAAM,WAArB,YAA+B;AAAA,gBACvC,OAAM,gBAAK,aAAL,mBAAe,MAAM,SAArB,YAA6B;AAAA,cACrC;AAAA,cACA,KAAK;AAAA,gBACH,SAAQ,gBAAK,aAAL,mBAAe,IAAI,WAAnB,YAA6B;AAAA,gBACrC,OAAM,gBAAK,aAAL,mBAAe,IAAI,SAAnB,YAA2B;AAAA,cACnC;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,aAAK,UAAU,KAAK,CAAC,OAAO,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAAA,MAC7D,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,uBAA6B;AAC3B,QAAI,CAAC,KAAK,eAAe;AACvB,WAAK,sBAAsB;AAC3B;AAAA,IACF;AACA,UAAM,EAAE,YAAY,EAAE,IAAI;AAC1B,UAAM,aAAa,EAAE,eAAe,WAAW,oBAAkC;AACjF,SAAK,SAAS,EAAE,eAAe,YAAY,CAAC,eAAe,KAAK,aAAa,CAAC,CAAC,GAAG,IAAI;AAAA,EACxF;AACF","sourcesContent":["import { Expression } from '@babel/types';\nimport {\n type Params,\n type TailProcessorParams,\n type ValueCache,\n validateParams,\n} from '@wyw-in-js/processor-utils';\nimport { ValueType, type ExpressionValue, type Replacements, type Rules } from '@wyw-in-js/shared';\n\nimport { CSSInterpolation } from '@emotion/css';\nimport BaseProcessor from './base-processor';\nimport type { IOptions } from './styled';\nimport {\n convertAtomicsToCss,\n type Atomics,\n type RuntimeConfig,\n} from '../utils/convertAtomicsToCss';\nimport { css, cache } from '../utils/emotion';\nimport { valueToLiteral } from '../utils/valueToLiteral';\n\nexport class GenerateAtomicsProcessor extends BaseProcessor {\n callParam: ExpressionValue;\n\n runtimeConfig?: RuntimeConfig;\n\n constructor(params: Params, ...args: TailProcessorParams) {\n super([params[0]], ...args);\n validateParams(params, ['callee', ['call']], `Invalid use of ${this.tagSource.imported} tag.`);\n const [, callParam] = params;\n const [, callParamArgument] = callParam;\n this.dependencies.push(callParamArgument);\n this.callParam = callParamArgument;\n }\n\n // eslint-disable-next-line class-methods-use-this\n get asSelector(): string {\n throw new Error('Method not implemented.');\n }\n\n // eslint-disable-next-line class-methods-use-this\n get value(): Expression {\n throw new Error('Method not implemented.');\n }\n\n doEvaltimeReplacement(): void {\n this.replacer(this.astService.nullLiteral(), true);\n }\n\n build(values: ValueCache): void {\n const { themeArgs = {} } = this.options as IOptions;\n const param = this.callParam;\n if (param.kind !== ValueType.CONST) {\n const value =\n param.kind === ValueType.FUNCTION\n ? (values.get(param.ex.name) as (config: unknown) => unknown)(themeArgs)\n : values.get(param.ex.name);\n const { classes, runtimeConfig } = convertAtomicsToCss(\n value as Atomics,\n this.className,\n false,\n this.options.displayName,\n );\n this.runtimeConfig = runtimeConfig;\n\n classes.forEach(({ className, css: cssObject }) => {\n const emotionClass = css(cssObject as CSSInterpolation);\n const cssText = cache.registered[emotionClass];\n\n const rules: Rules = {\n [`.${className}`]: {\n cssText,\n className: this.className,\n displayName: this.displayName,\n start: this.location?.start ?? null,\n },\n };\n const sourceMapReplacements: Replacements = [\n {\n length: cssText.length,\n original: {\n start: {\n column: this.location?.start.column ?? 0,\n line: this.location?.start.line ?? 0,\n },\n end: {\n column: this.location?.end.column ?? 0,\n line: this.location?.end.line ?? 0,\n },\n },\n },\n ];\n this.artifacts.push(['css', [rules, sourceMapReplacements]]);\n });\n }\n }\n\n doRuntimeReplacement(): void {\n if (!this.runtimeConfig) {\n this.doEvaltimeReplacement();\n return;\n }\n const { astService: t } = this;\n const importName = t.addNamedImport('atomics', process.env.PACKAGE_NAME as string);\n this.replacer(t.callExpression(importName, [valueToLiteral(this.runtimeConfig)]), true);\n }\n}\n","import cssesc from 'cssesc';\n\nexport type Atomics = {\n conditions: Record<string, string>;\n defaultCondition: string;\n properties: {\n [key: string]: string[];\n };\n shorthands: Record<string, string[]>;\n};\n\nexport type RuntimeConfig = {\n conditions: string[];\n styles: Record<string, Record<string, Record<string, string>>>;\n shorthands: Atomics['shorthands'];\n};\n\nfunction getClassName(...items: string[]) {\n return cssesc(items.filter(Boolean).join('_'));\n}\n\nexport function convertAtomicsToCss(\n { conditions = {}, defaultCondition, properties, shorthands = {} }: Atomics,\n mainClassName: string,\n isGlobal = false,\n debug = false,\n prefix = 'Mui',\n) {\n const runtimeConfig: RuntimeConfig = {\n styles: {},\n shorthands,\n conditions: Object.keys(conditions),\n };\n let count = 1;\n function getCount() {\n const val = count;\n count += 1;\n return val;\n }\n\n const classes: {\n className: string;\n css: object;\n }[] = [];\n\n Object.entries(conditions).forEach(([conditionName, mediaQueryStr]) => {\n Object.entries(properties).forEach(([cssPropertyName, propertyValues]) => {\n propertyValues.forEach((propertyValue) => {\n const className =\n isGlobal || debug\n ? getClassName(\n prefix,\n cssPropertyName,\n conditionName ?? 'default',\n propertyValue,\n !isGlobal ? mainClassName : '',\n )\n : `${mainClassName}${getCount()}`;\n if (defaultCondition === conditionName || !mediaQueryStr) {\n classes.push({\n className,\n css: {\n [cssPropertyName]: propertyValue,\n },\n });\n } else {\n classes.push({\n className,\n css: {\n [mediaQueryStr]: {\n [cssPropertyName]: propertyValue,\n },\n },\n });\n }\n const classMap = runtimeConfig.styles[cssPropertyName] ?? {};\n const conditionClassMap = classMap[propertyValue] ?? {};\n conditionClassMap[conditionName] = className;\n if (conditionName === defaultCondition) {\n conditionClassMap.$$default = className;\n }\n classMap[propertyValue] = conditionClassMap;\n runtimeConfig.styles[cssPropertyName] = classMap;\n });\n });\n });\n\n return {\n classes,\n runtimeConfig,\n };\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/generateAtomics.mjs b/packages/pigment-react/processors/generateAtomics.mjs new file mode 100644 index 00000000000000..2df20ed7677faa --- /dev/null +++ b/packages/pigment-react/processors/generateAtomics.mjs @@ -0,0 +1,154 @@ +import { css, cache } from './chunk-IUHN7KUC.mjs'; +import { valueToLiteral } from './chunk-DTNT7PFI.mjs'; +import { BaseProcessor } from './chunk-HIMMIWAJ.mjs'; +import { validateParams } from '@wyw-in-js/processor-utils'; +import { ValueType } from '@wyw-in-js/shared'; +import cssesc from 'cssesc'; + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +function getClassName(...items) { + return cssesc(items.filter(Boolean).join("_")); +} +function convertAtomicsToCss({ conditions = {}, defaultCondition, properties, shorthands = {} }, mainClassName, isGlobal = false, debug = false, prefix = "Mui") { + const runtimeConfig = { + styles: {}, + shorthands, + conditions: Object.keys(conditions) + }; + let count = 1; + function getCount() { + const val = count; + count += 1; + return val; + } + const classes = []; + Object.entries(conditions).forEach(([conditionName, mediaQueryStr]) => { + Object.entries(properties).forEach(([cssPropertyName, propertyValues]) => { + propertyValues.forEach((propertyValue) => { + var _a, _b; + const className = isGlobal || debug ? getClassName( + prefix, + cssPropertyName, + conditionName != null ? conditionName : "default", + propertyValue, + !isGlobal ? mainClassName : "" + ) : `${mainClassName}${getCount()}`; + if (defaultCondition === conditionName || !mediaQueryStr) { + classes.push({ + className, + css: { + [cssPropertyName]: propertyValue + } + }); + } else { + classes.push({ + className, + css: { + [mediaQueryStr]: { + [cssPropertyName]: propertyValue + } + } + }); + } + const classMap = (_a = runtimeConfig.styles[cssPropertyName]) != null ? _a : {}; + const conditionClassMap = (_b = classMap[propertyValue]) != null ? _b : {}; + conditionClassMap[conditionName] = className; + if (conditionName === defaultCondition) { + conditionClassMap.$$default = className; + } + classMap[propertyValue] = conditionClassMap; + runtimeConfig.styles[cssPropertyName] = classMap; + }); + }); + }); + return { + classes, + runtimeConfig + }; +} + +// src/processors/generateAtomics.ts +var GenerateAtomicsProcessor = class extends BaseProcessor { + constructor(params, ...args) { + super([params[0]], ...args); + validateParams(params, ["callee", ["call"]], `Invalid use of ${this.tagSource.imported} tag.`); + const [, callParam] = params; + const [, callParamArgument] = callParam; + this.dependencies.push(callParamArgument); + this.callParam = callParamArgument; + } + // eslint-disable-next-line class-methods-use-this + get asSelector() { + throw new Error("Method not implemented."); + } + // eslint-disable-next-line class-methods-use-this + get value() { + throw new Error("Method not implemented."); + } + doEvaltimeReplacement() { + this.replacer(this.astService.nullLiteral(), true); + } + build(values) { + const { themeArgs = {} } = this.options; + const param = this.callParam; + if (param.kind !== ValueType.CONST) { + const value = param.kind === ValueType.FUNCTION ? values.get(param.ex.name)(themeArgs) : values.get(param.ex.name); + const { classes, runtimeConfig } = convertAtomicsToCss( + value, + this.className, + false, + this.options.displayName + ); + this.runtimeConfig = runtimeConfig; + classes.forEach(({ className, css: cssObject }) => { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; + const emotionClass = css(cssObject); + const cssText = cache.registered[emotionClass]; + const rules = { + [`.${className}`]: { + cssText, + className: this.className, + displayName: this.displayName, + start: (_b = (_a = this.location) == null ? void 0 : _a.start) != null ? _b : null + } + }; + const sourceMapReplacements = [ + { + length: cssText.length, + original: { + start: { + column: (_d = (_c = this.location) == null ? void 0 : _c.start.column) != null ? _d : 0, + line: (_f = (_e = this.location) == null ? void 0 : _e.start.line) != null ? _f : 0 + }, + end: { + column: (_h = (_g = this.location) == null ? void 0 : _g.end.column) != null ? _h : 0, + line: (_j = (_i = this.location) == null ? void 0 : _i.end.line) != null ? _j : 0 + } + } + } + ]; + this.artifacts.push(["css", [rules, sourceMapReplacements]]); + }); + } + } + doRuntimeReplacement() { + if (!this.runtimeConfig) { + this.doEvaltimeReplacement(); + return; + } + const { astService: t } = this; + const importName = t.addNamedImport("atomics", "@pigment-css/react"); + this.replacer(t.callExpression(importName, [valueToLiteral(this.runtimeConfig)]), true); + } +}; + +export { GenerateAtomicsProcessor }; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=generateAtomics.mjs.map \ No newline at end of file diff --git a/packages/pigment-react/processors/generateAtomics.mjs.map b/packages/pigment-react/processors/generateAtomics.mjs.map new file mode 100644 index 00000000000000..0ba40d6aacc440 --- /dev/null +++ b/packages/pigment-react/processors/generateAtomics.mjs.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/processors/generateAtomics.ts","../src/utils/convertAtomicsToCss.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AACA;AAAA,EAIE;AAAA,OACK;AACP,SAAS,iBAAsE;;;ACP/E,OAAO,YAAY;AAiBnB,SAAS,gBAAgB,OAAiB;AACxC,SAAO,OAAO,MAAM,OAAO,OAAO,EAAE,KAAK,GAAG,CAAC;AAC/C;AAEO,SAAS,oBACd,EAAE,aAAa,CAAC,GAAG,kBAAkB,YAAY,aAAa,CAAC,EAAE,GACjE,eACA,WAAW,OACX,QAAQ,OACR,SAAS,OACT;AACA,QAAM,gBAA+B;AAAA,IACnC,QAAQ,CAAC;AAAA,IACT;AAAA,IACA,YAAY,OAAO,KAAK,UAAU;AAAA,EACpC;AACA,MAAI,QAAQ;AACZ,WAAS,WAAW;AAClB,UAAM,MAAM;AACZ,aAAS;AACT,WAAO;AAAA,EACT;AAEA,QAAM,UAGA,CAAC;AAEP,SAAO,QAAQ,UAAU,EAAE,QAAQ,CAAC,CAAC,eAAe,aAAa,MAAM;AACrE,WAAO,QAAQ,UAAU,EAAE,QAAQ,CAAC,CAAC,iBAAiB,cAAc,MAAM;AACxE,qBAAe,QAAQ,CAAC,kBAAkB;AA/ChD;AAgDQ,cAAM,YACJ,YAAY,QACR;AAAA,UACE;AAAA,UACA;AAAA,UACA,wCAAiB;AAAA,UACjB;AAAA,UACA,CAAC,WAAW,gBAAgB;AAAA,QAC9B,IACA,GAAG,aAAa,GAAG,SAAS,CAAC;AACnC,YAAI,qBAAqB,iBAAiB,CAAC,eAAe;AACxD,kBAAQ,KAAK;AAAA,YACX;AAAA,YACA,KAAK;AAAA,cACH,CAAC,eAAe,GAAG;AAAA,YACrB;AAAA,UACF,CAAC;AAAA,QACH,OAAO;AACL,kBAAQ,KAAK;AAAA,YACX;AAAA,YACA,KAAK;AAAA,cACH,CAAC,aAAa,GAAG;AAAA,gBACf,CAAC,eAAe,GAAG;AAAA,cACrB;AAAA,YACF;AAAA,UACF,CAAC;AAAA,QACH;AACA,cAAM,YAAW,mBAAc,OAAO,eAAe,MAApC,YAAyC,CAAC;AAC3D,cAAM,qBAAoB,cAAS,aAAa,MAAtB,YAA2B,CAAC;AACtD,0BAAkB,aAAa,IAAI;AACnC,YAAI,kBAAkB,kBAAkB;AACtC,4BAAkB,YAAY;AAAA,QAChC;AACA,iBAAS,aAAa,IAAI;AAC1B,sBAAc,OAAO,eAAe,IAAI;AAAA,MAC1C,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;;;ADvEO,IAAM,2BAAN,cAAuC,cAAc;AAAA,EAK1D,YAAY,WAAmB,MAA2B;AACxD,UAAM,CAAC,OAAO,CAAC,CAAC,GAAG,GAAG,IAAI;AAC1B,mBAAe,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,kBAAkB,KAAK,UAAU,QAAQ,OAAO;AAC7F,UAAM,CAAC,EAAE,SAAS,IAAI;AACtB,UAAM,CAAC,EAAE,iBAAiB,IAAI;AAC9B,SAAK,aAAa,KAAK,iBAAiB;AACxC,SAAK,YAAY;AAAA,EACnB;AAAA;AAAA,EAGA,IAAI,aAAqB;AACvB,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AAAA;AAAA,EAGA,IAAI,QAAoB;AACtB,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AAAA,EAEA,wBAA8B;AAC5B,SAAK,SAAS,KAAK,WAAW,YAAY,GAAG,IAAI;AAAA,EACnD;AAAA,EAEA,MAAM,QAA0B;AAC9B,UAAM,EAAE,YAAY,CAAC,EAAE,IAAI,KAAK;AAChC,UAAM,QAAQ,KAAK;AACnB,QAAI,MAAM,SAAS,UAAU,OAAO;AAClC,YAAM,QACJ,MAAM,SAAS,UAAU,WACpB,OAAO,IAAI,MAAM,GAAG,IAAI,EAAmC,SAAS,IACrE,OAAO,IAAI,MAAM,GAAG,IAAI;AAC9B,YAAM,EAAE,SAAS,cAAc,IAAI;AAAA,QACjC;AAAA,QACA,KAAK;AAAA,QACL;AAAA,QACA,KAAK,QAAQ;AAAA,MACf;AACA,WAAK,gBAAgB;AAErB,cAAQ,QAAQ,CAAC,EAAE,WAAW,KAAK,UAAU,MAAM;AAhEzD;AAiEQ,cAAM,eAAe,IAAI,SAA6B;AACtD,cAAM,UAAU,MAAM,WAAW,YAAY;AAE7C,cAAM,QAAe;AAAA,UACnB,CAAC,IAAI,SAAS,EAAE,GAAG;AAAA,YACjB;AAAA,YACA,WAAW,KAAK;AAAA,YAChB,aAAa,KAAK;AAAA,YAClB,QAAO,gBAAK,aAAL,mBAAe,UAAf,YAAwB;AAAA,UACjC;AAAA,QACF;AACA,cAAM,wBAAsC;AAAA,UAC1C;AAAA,YACE,QAAQ,QAAQ;AAAA,YAChB,UAAU;AAAA,cACR,OAAO;AAAA,gBACL,SAAQ,gBAAK,aAAL,mBAAe,MAAM,WAArB,YAA+B;AAAA,gBACvC,OAAM,gBAAK,aAAL,mBAAe,MAAM,SAArB,YAA6B;AAAA,cACrC;AAAA,cACA,KAAK;AAAA,gBACH,SAAQ,gBAAK,aAAL,mBAAe,IAAI,WAAnB,YAA6B;AAAA,gBACrC,OAAM,gBAAK,aAAL,mBAAe,IAAI,SAAnB,YAA2B;AAAA,cACnC;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,aAAK,UAAU,KAAK,CAAC,OAAO,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAAA,MAC7D,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,uBAA6B;AAC3B,QAAI,CAAC,KAAK,eAAe;AACvB,WAAK,sBAAsB;AAC3B;AAAA,IACF;AACA,UAAM,EAAE,YAAY,EAAE,IAAI;AAC1B,UAAM,aAAa,EAAE,eAAe,WAAW,oBAAkC;AACjF,SAAK,SAAS,EAAE,eAAe,YAAY,CAAC,eAAe,KAAK,aAAa,CAAC,CAAC,GAAG,IAAI;AAAA,EACxF;AACF","sourcesContent":["import { Expression } from '@babel/types';\nimport {\n type Params,\n type TailProcessorParams,\n type ValueCache,\n validateParams,\n} from '@wyw-in-js/processor-utils';\nimport { ValueType, type ExpressionValue, type Replacements, type Rules } from '@wyw-in-js/shared';\n\nimport { CSSInterpolation } from '@emotion/css';\nimport BaseProcessor from './base-processor';\nimport type { IOptions } from './styled';\nimport {\n convertAtomicsToCss,\n type Atomics,\n type RuntimeConfig,\n} from '../utils/convertAtomicsToCss';\nimport { css, cache } from '../utils/emotion';\nimport { valueToLiteral } from '../utils/valueToLiteral';\n\nexport class GenerateAtomicsProcessor extends BaseProcessor {\n callParam: ExpressionValue;\n\n runtimeConfig?: RuntimeConfig;\n\n constructor(params: Params, ...args: TailProcessorParams) {\n super([params[0]], ...args);\n validateParams(params, ['callee', ['call']], `Invalid use of ${this.tagSource.imported} tag.`);\n const [, callParam] = params;\n const [, callParamArgument] = callParam;\n this.dependencies.push(callParamArgument);\n this.callParam = callParamArgument;\n }\n\n // eslint-disable-next-line class-methods-use-this\n get asSelector(): string {\n throw new Error('Method not implemented.');\n }\n\n // eslint-disable-next-line class-methods-use-this\n get value(): Expression {\n throw new Error('Method not implemented.');\n }\n\n doEvaltimeReplacement(): void {\n this.replacer(this.astService.nullLiteral(), true);\n }\n\n build(values: ValueCache): void {\n const { themeArgs = {} } = this.options as IOptions;\n const param = this.callParam;\n if (param.kind !== ValueType.CONST) {\n const value =\n param.kind === ValueType.FUNCTION\n ? (values.get(param.ex.name) as (config: unknown) => unknown)(themeArgs)\n : values.get(param.ex.name);\n const { classes, runtimeConfig } = convertAtomicsToCss(\n value as Atomics,\n this.className,\n false,\n this.options.displayName,\n );\n this.runtimeConfig = runtimeConfig;\n\n classes.forEach(({ className, css: cssObject }) => {\n const emotionClass = css(cssObject as CSSInterpolation);\n const cssText = cache.registered[emotionClass];\n\n const rules: Rules = {\n [`.${className}`]: {\n cssText,\n className: this.className,\n displayName: this.displayName,\n start: this.location?.start ?? null,\n },\n };\n const sourceMapReplacements: Replacements = [\n {\n length: cssText.length,\n original: {\n start: {\n column: this.location?.start.column ?? 0,\n line: this.location?.start.line ?? 0,\n },\n end: {\n column: this.location?.end.column ?? 0,\n line: this.location?.end.line ?? 0,\n },\n },\n },\n ];\n this.artifacts.push(['css', [rules, sourceMapReplacements]]);\n });\n }\n }\n\n doRuntimeReplacement(): void {\n if (!this.runtimeConfig) {\n this.doEvaltimeReplacement();\n return;\n }\n const { astService: t } = this;\n const importName = t.addNamedImport('atomics', process.env.PACKAGE_NAME as string);\n this.replacer(t.callExpression(importName, [valueToLiteral(this.runtimeConfig)]), true);\n }\n}\n","import cssesc from 'cssesc';\n\nexport type Atomics = {\n conditions: Record<string, string>;\n defaultCondition: string;\n properties: {\n [key: string]: string[];\n };\n shorthands: Record<string, string[]>;\n};\n\nexport type RuntimeConfig = {\n conditions: string[];\n styles: Record<string, Record<string, Record<string, string>>>;\n shorthands: Atomics['shorthands'];\n};\n\nfunction getClassName(...items: string[]) {\n return cssesc(items.filter(Boolean).join('_'));\n}\n\nexport function convertAtomicsToCss(\n { conditions = {}, defaultCondition, properties, shorthands = {} }: Atomics,\n mainClassName: string,\n isGlobal = false,\n debug = false,\n prefix = 'Mui',\n) {\n const runtimeConfig: RuntimeConfig = {\n styles: {},\n shorthands,\n conditions: Object.keys(conditions),\n };\n let count = 1;\n function getCount() {\n const val = count;\n count += 1;\n return val;\n }\n\n const classes: {\n className: string;\n css: object;\n }[] = [];\n\n Object.entries(conditions).forEach(([conditionName, mediaQueryStr]) => {\n Object.entries(properties).forEach(([cssPropertyName, propertyValues]) => {\n propertyValues.forEach((propertyValue) => {\n const className =\n isGlobal || debug\n ? getClassName(\n prefix,\n cssPropertyName,\n conditionName ?? 'default',\n propertyValue,\n !isGlobal ? mainClassName : '',\n )\n : `${mainClassName}${getCount()}`;\n if (defaultCondition === conditionName || !mediaQueryStr) {\n classes.push({\n className,\n css: {\n [cssPropertyName]: propertyValue,\n },\n });\n } else {\n classes.push({\n className,\n css: {\n [mediaQueryStr]: {\n [cssPropertyName]: propertyValue,\n },\n },\n });\n }\n const classMap = runtimeConfig.styles[cssPropertyName] ?? {};\n const conditionClassMap = classMap[propertyValue] ?? {};\n conditionClassMap[conditionName] = className;\n if (conditionName === defaultCondition) {\n conditionClassMap.$$default = className;\n }\n classMap[propertyValue] = conditionClassMap;\n runtimeConfig.styles[cssPropertyName] = classMap;\n });\n });\n });\n\n return {\n classes,\n runtimeConfig,\n };\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/keyframes.d.mts b/packages/pigment-react/processors/keyframes.d.mts new file mode 100644 index 00000000000000..a53c0861e84942 --- /dev/null +++ b/packages/pigment-react/processors/keyframes.d.mts @@ -0,0 +1,22 @@ +import { Expression } from '@babel/types'; +import { CallParam, TemplateParam, Params, TailProcessorParams, ValueCache } from '@wyw-in-js/processor-utils'; +import { CSSInterpolation } from '@emotion/css'; +import { B as BaseProcessor } from './base-processor-Bkjcr8yc.mjs'; +import '@wyw-in-js/shared'; + +type Primitive = string | number | boolean | null | undefined; +type TemplateCallback = (params: Record<string, unknown> | undefined) => string | number; +declare class KeyframesProcessor extends BaseProcessor { + callParam: CallParam | TemplateParam; + constructor(params: Params, ...args: TailProcessorParams); + build(values: ValueCache): void; + private handleTemplate; + generateArtifacts(styleObjOrTaggged: CSSInterpolation | string[], ...args: Primitive[]): void; + private handleCall; + doEvaltimeReplacement(): void; + doRuntimeReplacement(): void; + get asSelector(): string; + get value(): Expression; +} + +export { KeyframesProcessor, type Primitive, type TemplateCallback }; diff --git a/packages/pigment-react/processors/keyframes.d.ts b/packages/pigment-react/processors/keyframes.d.ts new file mode 100644 index 00000000000000..305a3c3e3bcc32 --- /dev/null +++ b/packages/pigment-react/processors/keyframes.d.ts @@ -0,0 +1,22 @@ +import { Expression } from '@babel/types'; +import { CallParam, TemplateParam, Params, TailProcessorParams, ValueCache } from '@wyw-in-js/processor-utils'; +import { CSSInterpolation } from '@emotion/css'; +import { B as BaseProcessor } from './base-processor-Bkjcr8yc.js'; +import '@wyw-in-js/shared'; + +type Primitive = string | number | boolean | null | undefined; +type TemplateCallback = (params: Record<string, unknown> | undefined) => string | number; +declare class KeyframesProcessor extends BaseProcessor { + callParam: CallParam | TemplateParam; + constructor(params: Params, ...args: TailProcessorParams); + build(values: ValueCache): void; + private handleTemplate; + generateArtifacts(styleObjOrTaggged: CSSInterpolation | string[], ...args: Primitive[]): void; + private handleCall; + doEvaltimeReplacement(): void; + doRuntimeReplacement(): void; + get asSelector(): string; + get value(): Expression; +} + +export { KeyframesProcessor, type Primitive, type TemplateCallback }; diff --git a/packages/pigment-react/processors/keyframes.js b/packages/pigment-react/processors/keyframes.js new file mode 100644 index 00000000000000..75dac2b5b08f03 --- /dev/null +++ b/packages/pigment-react/processors/keyframes.js @@ -0,0 +1,145 @@ +'use strict'; + +var chunk3NOOOXUY_js = require('./chunk-3NOOOXUY.js'); +var chunk7L3GVF7S_js = require('./chunk-7L3GVF7S.js'); +var serialize = require('@emotion/serialize'); +var shared = require('@wyw-in-js/shared'); +var processorUtils = require('@wyw-in-js/processor-utils'); + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var KeyframesProcessor = class extends chunk7L3GVF7S_js.BaseProcessor { + constructor(params, ...args) { + super([params[0]], ...args); + if (params.length < 2) { + throw chunk7L3GVF7S_js.BaseProcessor.SKIP; + } + processorUtils.validateParams( + params, + ["callee", ["call", "template"]], + `Invalid use of ${this.tagSource.imported} tag.` + ); + const [, callParams] = params; + if (callParams[0] === "call") { + this.dependencies.push(callParams[1]); + } else if (callParams[0] === "template") { + callParams[1].forEach((element) => { + if ("kind" in element && element.kind !== shared.ValueType.CONST) { + this.dependencies.push(element); + } + }); + } + this.callParam = callParams; + } + build(values) { + if (this.artifacts.length > 0) { + throw new Error(`MUI: "${this.tagSource.imported}" is already built`); + } + const [callType] = this.callParam; + if (callType === "template") { + this.handleTemplate(this.callParam, values); + } else { + this.handleCall(this.callParam, values); + } + } + handleTemplate([, callArgs], values) { + const templateStrs = []; + templateStrs.raw = []; + const templateExpressions = []; + const { themeArgs } = this.options; + callArgs.forEach((item) => { + if ("kind" in item) { + switch (item.kind) { + case shared.ValueType.FUNCTION: { + const value = values.get(item.ex.name); + templateExpressions.push(value(themeArgs)); + break; + } + case shared.ValueType.CONST: + templateExpressions.push(item.value); + break; + case shared.ValueType.LAZY: { + const evaluatedValue = values.get(item.ex.name); + if (typeof evaluatedValue === "function") { + templateExpressions.push(evaluatedValue(themeArgs)); + } else { + templateExpressions.push(evaluatedValue); + } + break; + } + } + } else if (item.type === "TemplateElement") { + templateStrs.push(item.value.cooked); + templateStrs.raw.push(item.value.raw); + } + }); + this.generateArtifacts(templateStrs, ...templateExpressions); + } + generateArtifacts(styleObjOrTaggged, ...args) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; + const { styles } = serialize.serializeStyles( + [styleObjOrTaggged, args], + chunk3NOOOXUY_js.cache.registered + ); + const cssText = `@keyframes {${styles}}`; + const rules = { + [this.asSelector]: { + className: this.className, + cssText, + displayName: this.displayName, + start: (_b = (_a = this.location) == null ? void 0 : _a.start) != null ? _b : null + } + }; + const sourceMapReplacements = [ + { + length: cssText.length, + original: { + start: { + column: (_d = (_c = this.location) == null ? void 0 : _c.start.column) != null ? _d : 0, + line: (_f = (_e = this.location) == null ? void 0 : _e.start.line) != null ? _f : 0 + }, + end: { + column: (_h = (_g = this.location) == null ? void 0 : _g.end.column) != null ? _h : 0, + line: (_j = (_i = this.location) == null ? void 0 : _i.end.line) != null ? _j : 0 + } + } + } + ]; + this.artifacts.push(["css", [rules, sourceMapReplacements]]); + } + handleCall([, callArg], values) { + let styleObj; + if (callArg.kind === shared.ValueType.LAZY) { + styleObj = values.get(callArg.ex.name); + } else if (callArg.kind === shared.ValueType.FUNCTION) { + const { themeArgs } = this.options; + const value = values.get(callArg.ex.name); + styleObj = value(themeArgs); + } + if (styleObj) { + this.generateArtifacts(styleObj); + } + } + doEvaltimeReplacement() { + this.replacer(this.value, false); + } + doRuntimeReplacement() { + this.doEvaltimeReplacement(); + } + get asSelector() { + return this.className; + } + get value() { + return this.astService.stringLiteral(this.className); + } +}; + +exports.KeyframesProcessor = KeyframesProcessor; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=keyframes.js.map \ No newline at end of file diff --git a/packages/pigment-react/processors/keyframes.js.map b/packages/pigment-react/processors/keyframes.js.map new file mode 100644 index 00000000000000..e6cd3a01d855e9 --- /dev/null +++ b/packages/pigment-react/processors/keyframes.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/processors/keyframes.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Expression } from '@babel/types';\nimport type {\n CallParam,\n TemplateParam,\n Params,\n TailProcessorParams,\n ValueCache,\n} from '@wyw-in-js/processor-utils';\nimport { serializeStyles, Interpolation } from '@emotion/serialize';\nimport { type Replacements, type Rules, ValueType } from '@wyw-in-js/shared';\nimport type { CSSInterpolation } from '@emotion/css';\nimport { validateParams } from '@wyw-in-js/processor-utils';\nimport BaseProcessor from './base-processor';\nimport type { IOptions } from './styled';\nimport { cache } from '../utils/emotion';\n\nexport type Primitive = string | number | boolean | null | undefined;\n\nexport type TemplateCallback = (params: Record<string, unknown> | undefined) => string | number;\n\nexport class KeyframesProcessor extends BaseProcessor {\n callParam: CallParam | TemplateParam;\n\n constructor(params: Params, ...args: TailProcessorParams) {\n super([params[0]], ...args);\n if (params.length < 2) {\n throw BaseProcessor.SKIP;\n }\n validateParams(\n params,\n ['callee', ['call', 'template']],\n `Invalid use of ${this.tagSource.imported} tag.`,\n );\n\n const [, callParams] = params;\n if (callParams[0] === 'call') {\n this.dependencies.push(callParams[1]);\n } else if (callParams[0] === 'template') {\n callParams[1].forEach((element) => {\n if ('kind' in element && element.kind !== ValueType.CONST) {\n this.dependencies.push(element);\n }\n });\n }\n this.callParam = callParams;\n }\n\n build(values: ValueCache) {\n if (this.artifacts.length > 0) {\n throw new Error(`MUI: \"${this.tagSource.imported}\" is already built`);\n }\n\n const [callType] = this.callParam;\n\n if (callType === 'template') {\n this.handleTemplate(this.callParam, values);\n } else {\n this.handleCall(this.callParam, values);\n }\n }\n\n private handleTemplate([, callArgs]: TemplateParam, values: ValueCache) {\n const templateStrs: string[] = [];\n // @ts-ignore @TODO - Fix this. No idea how to initialize a Tagged String array.\n templateStrs.raw = [];\n const templateExpressions: Primitive[] = [];\n const { themeArgs } = this.options as IOptions;\n\n callArgs.forEach((item) => {\n if ('kind' in item) {\n switch (item.kind) {\n case ValueType.FUNCTION: {\n const value = values.get(item.ex.name) as TemplateCallback;\n templateExpressions.push(value(themeArgs));\n break;\n }\n case ValueType.CONST:\n templateExpressions.push(item.value);\n break;\n case ValueType.LAZY: {\n const evaluatedValue = values.get(item.ex.name);\n if (typeof evaluatedValue === 'function') {\n templateExpressions.push(evaluatedValue(themeArgs));\n } else {\n templateExpressions.push(evaluatedValue as Primitive);\n }\n break;\n }\n default:\n break;\n }\n } else if (item.type === 'TemplateElement') {\n templateStrs.push(item.value.cooked as string);\n // @ts-ignore\n templateStrs.raw.push(item.value.raw);\n }\n });\n this.generateArtifacts(templateStrs, ...templateExpressions);\n }\n\n generateArtifacts(styleObjOrTaggged: CSSInterpolation | string[], ...args: Primitive[]) {\n const { styles } = serializeStyles(\n [styleObjOrTaggged as Interpolation<{}>, args],\n cache.registered,\n );\n const cssText = `@keyframes {${styles}}`;\n\n const rules: Rules = {\n [this.asSelector]: {\n className: this.className,\n cssText,\n displayName: this.displayName,\n start: this.location?.start ?? null,\n },\n };\n const sourceMapReplacements: Replacements = [\n {\n length: cssText.length,\n original: {\n start: {\n column: this.location?.start.column ?? 0,\n line: this.location?.start.line ?? 0,\n },\n end: {\n column: this.location?.end.column ?? 0,\n line: this.location?.end.line ?? 0,\n },\n },\n },\n ];\n this.artifacts.push(['css', [rules, sourceMapReplacements]]);\n }\n\n private handleCall([, callArg]: CallParam, values: ValueCache) {\n let styleObj: CSSInterpolation;\n if (callArg.kind === ValueType.LAZY) {\n styleObj = values.get(callArg.ex.name) as CSSInterpolation;\n } else if (callArg.kind === ValueType.FUNCTION) {\n const { themeArgs } = this.options as IOptions;\n const value = values.get(callArg.ex.name) as (\n args: Record<string, unknown> | undefined,\n ) => CSSInterpolation;\n styleObj = value(themeArgs);\n }\n if (styleObj) {\n this.generateArtifacts(styleObj);\n }\n }\n\n doEvaltimeReplacement() {\n this.replacer(this.value, false);\n }\n\n doRuntimeReplacement() {\n this.doEvaltimeReplacement();\n }\n\n get asSelector() {\n return this.className;\n }\n\n get value(): Expression {\n return this.astService.stringLiteral(this.className);\n }\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/keyframes.mjs b/packages/pigment-react/processors/keyframes.mjs new file mode 100644 index 00000000000000..3715ba7a7695b9 --- /dev/null +++ b/packages/pigment-react/processors/keyframes.mjs @@ -0,0 +1,143 @@ +import { cache } from './chunk-IUHN7KUC.mjs'; +import { BaseProcessor } from './chunk-HIMMIWAJ.mjs'; +import { serializeStyles } from '@emotion/serialize'; +import { ValueType } from '@wyw-in-js/shared'; +import { validateParams } from '@wyw-in-js/processor-utils'; + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var KeyframesProcessor = class extends BaseProcessor { + constructor(params, ...args) { + super([params[0]], ...args); + if (params.length < 2) { + throw BaseProcessor.SKIP; + } + validateParams( + params, + ["callee", ["call", "template"]], + `Invalid use of ${this.tagSource.imported} tag.` + ); + const [, callParams] = params; + if (callParams[0] === "call") { + this.dependencies.push(callParams[1]); + } else if (callParams[0] === "template") { + callParams[1].forEach((element) => { + if ("kind" in element && element.kind !== ValueType.CONST) { + this.dependencies.push(element); + } + }); + } + this.callParam = callParams; + } + build(values) { + if (this.artifacts.length > 0) { + throw new Error(`MUI: "${this.tagSource.imported}" is already built`); + } + const [callType] = this.callParam; + if (callType === "template") { + this.handleTemplate(this.callParam, values); + } else { + this.handleCall(this.callParam, values); + } + } + handleTemplate([, callArgs], values) { + const templateStrs = []; + templateStrs.raw = []; + const templateExpressions = []; + const { themeArgs } = this.options; + callArgs.forEach((item) => { + if ("kind" in item) { + switch (item.kind) { + case ValueType.FUNCTION: { + const value = values.get(item.ex.name); + templateExpressions.push(value(themeArgs)); + break; + } + case ValueType.CONST: + templateExpressions.push(item.value); + break; + case ValueType.LAZY: { + const evaluatedValue = values.get(item.ex.name); + if (typeof evaluatedValue === "function") { + templateExpressions.push(evaluatedValue(themeArgs)); + } else { + templateExpressions.push(evaluatedValue); + } + break; + } + } + } else if (item.type === "TemplateElement") { + templateStrs.push(item.value.cooked); + templateStrs.raw.push(item.value.raw); + } + }); + this.generateArtifacts(templateStrs, ...templateExpressions); + } + generateArtifacts(styleObjOrTaggged, ...args) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; + const { styles } = serializeStyles( + [styleObjOrTaggged, args], + cache.registered + ); + const cssText = `@keyframes {${styles}}`; + const rules = { + [this.asSelector]: { + className: this.className, + cssText, + displayName: this.displayName, + start: (_b = (_a = this.location) == null ? void 0 : _a.start) != null ? _b : null + } + }; + const sourceMapReplacements = [ + { + length: cssText.length, + original: { + start: { + column: (_d = (_c = this.location) == null ? void 0 : _c.start.column) != null ? _d : 0, + line: (_f = (_e = this.location) == null ? void 0 : _e.start.line) != null ? _f : 0 + }, + end: { + column: (_h = (_g = this.location) == null ? void 0 : _g.end.column) != null ? _h : 0, + line: (_j = (_i = this.location) == null ? void 0 : _i.end.line) != null ? _j : 0 + } + } + } + ]; + this.artifacts.push(["css", [rules, sourceMapReplacements]]); + } + handleCall([, callArg], values) { + let styleObj; + if (callArg.kind === ValueType.LAZY) { + styleObj = values.get(callArg.ex.name); + } else if (callArg.kind === ValueType.FUNCTION) { + const { themeArgs } = this.options; + const value = values.get(callArg.ex.name); + styleObj = value(themeArgs); + } + if (styleObj) { + this.generateArtifacts(styleObj); + } + } + doEvaltimeReplacement() { + this.replacer(this.value, false); + } + doRuntimeReplacement() { + this.doEvaltimeReplacement(); + } + get asSelector() { + return this.className; + } + get value() { + return this.astService.stringLiteral(this.className); + } +}; + +export { KeyframesProcessor }; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=keyframes.mjs.map \ No newline at end of file diff --git a/packages/pigment-react/processors/keyframes.mjs.map b/packages/pigment-react/processors/keyframes.mjs.map new file mode 100644 index 00000000000000..e6cd3a01d855e9 --- /dev/null +++ b/packages/pigment-react/processors/keyframes.mjs.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/processors/keyframes.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Expression } from '@babel/types';\nimport type {\n CallParam,\n TemplateParam,\n Params,\n TailProcessorParams,\n ValueCache,\n} from '@wyw-in-js/processor-utils';\nimport { serializeStyles, Interpolation } from '@emotion/serialize';\nimport { type Replacements, type Rules, ValueType } from '@wyw-in-js/shared';\nimport type { CSSInterpolation } from '@emotion/css';\nimport { validateParams } from '@wyw-in-js/processor-utils';\nimport BaseProcessor from './base-processor';\nimport type { IOptions } from './styled';\nimport { cache } from '../utils/emotion';\n\nexport type Primitive = string | number | boolean | null | undefined;\n\nexport type TemplateCallback = (params: Record<string, unknown> | undefined) => string | number;\n\nexport class KeyframesProcessor extends BaseProcessor {\n callParam: CallParam | TemplateParam;\n\n constructor(params: Params, ...args: TailProcessorParams) {\n super([params[0]], ...args);\n if (params.length < 2) {\n throw BaseProcessor.SKIP;\n }\n validateParams(\n params,\n ['callee', ['call', 'template']],\n `Invalid use of ${this.tagSource.imported} tag.`,\n );\n\n const [, callParams] = params;\n if (callParams[0] === 'call') {\n this.dependencies.push(callParams[1]);\n } else if (callParams[0] === 'template') {\n callParams[1].forEach((element) => {\n if ('kind' in element && element.kind !== ValueType.CONST) {\n this.dependencies.push(element);\n }\n });\n }\n this.callParam = callParams;\n }\n\n build(values: ValueCache) {\n if (this.artifacts.length > 0) {\n throw new Error(`MUI: \"${this.tagSource.imported}\" is already built`);\n }\n\n const [callType] = this.callParam;\n\n if (callType === 'template') {\n this.handleTemplate(this.callParam, values);\n } else {\n this.handleCall(this.callParam, values);\n }\n }\n\n private handleTemplate([, callArgs]: TemplateParam, values: ValueCache) {\n const templateStrs: string[] = [];\n // @ts-ignore @TODO - Fix this. No idea how to initialize a Tagged String array.\n templateStrs.raw = [];\n const templateExpressions: Primitive[] = [];\n const { themeArgs } = this.options as IOptions;\n\n callArgs.forEach((item) => {\n if ('kind' in item) {\n switch (item.kind) {\n case ValueType.FUNCTION: {\n const value = values.get(item.ex.name) as TemplateCallback;\n templateExpressions.push(value(themeArgs));\n break;\n }\n case ValueType.CONST:\n templateExpressions.push(item.value);\n break;\n case ValueType.LAZY: {\n const evaluatedValue = values.get(item.ex.name);\n if (typeof evaluatedValue === 'function') {\n templateExpressions.push(evaluatedValue(themeArgs));\n } else {\n templateExpressions.push(evaluatedValue as Primitive);\n }\n break;\n }\n default:\n break;\n }\n } else if (item.type === 'TemplateElement') {\n templateStrs.push(item.value.cooked as string);\n // @ts-ignore\n templateStrs.raw.push(item.value.raw);\n }\n });\n this.generateArtifacts(templateStrs, ...templateExpressions);\n }\n\n generateArtifacts(styleObjOrTaggged: CSSInterpolation | string[], ...args: Primitive[]) {\n const { styles } = serializeStyles(\n [styleObjOrTaggged as Interpolation<{}>, args],\n cache.registered,\n );\n const cssText = `@keyframes {${styles}}`;\n\n const rules: Rules = {\n [this.asSelector]: {\n className: this.className,\n cssText,\n displayName: this.displayName,\n start: this.location?.start ?? null,\n },\n };\n const sourceMapReplacements: Replacements = [\n {\n length: cssText.length,\n original: {\n start: {\n column: this.location?.start.column ?? 0,\n line: this.location?.start.line ?? 0,\n },\n end: {\n column: this.location?.end.column ?? 0,\n line: this.location?.end.line ?? 0,\n },\n },\n },\n ];\n this.artifacts.push(['css', [rules, sourceMapReplacements]]);\n }\n\n private handleCall([, callArg]: CallParam, values: ValueCache) {\n let styleObj: CSSInterpolation;\n if (callArg.kind === ValueType.LAZY) {\n styleObj = values.get(callArg.ex.name) as CSSInterpolation;\n } else if (callArg.kind === ValueType.FUNCTION) {\n const { themeArgs } = this.options as IOptions;\n const value = values.get(callArg.ex.name) as (\n args: Record<string, unknown> | undefined,\n ) => CSSInterpolation;\n styleObj = value(themeArgs);\n }\n if (styleObj) {\n this.generateArtifacts(styleObj);\n }\n }\n\n doEvaltimeReplacement() {\n this.replacer(this.value, false);\n }\n\n doRuntimeReplacement() {\n this.doEvaltimeReplacement();\n }\n\n get asSelector() {\n return this.className;\n }\n\n get value(): Expression {\n return this.astService.stringLiteral(this.className);\n }\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/styled.d.mts b/packages/pigment-react/processors/styled.d.mts new file mode 100644 index 00000000000000..d97466d72ae40c --- /dev/null +++ b/packages/pigment-react/processors/styled.d.mts @@ -0,0 +1,245 @@ +import { Identifier, TemplateElement, Expression, SourceLocation } from '@babel/types'; +import { IOptions as IOptions$1, Params, TailProcessorParams, ValueCache } from '@wyw-in-js/processor-utils'; +import { LazyValue, ExpressionValue, ConstValue } from '@wyw-in-js/shared'; +import { SxConfig } from '@mui/system/styleFunctionSx'; +import * as CSS from 'csstype'; +import { B as BaseProcessor } from './base-processor-Bkjcr8yc.mjs'; + +type CSSProperties = CSS.PropertiesFallback<number | string>; + +type CSSPropertiesWithCallback<Props extends object> = { + [K in keyof CSSProperties]: + | CSSProperties[K] + | Array<Extract<CSSProperties[K], string>> + | ((props: Props) => CSSProperties[K]); +}; + +type CSSPseudos<Props extends object> = { + [K in CSS.Pseudos]?: CSSObject<Props>; +}; + +interface CSSOthersObject<Props extends object> { + [selector: string]: CSSObject<Props>; +} + +type CSSObject<Props extends object> = + | CSSPropertiesWithCallback<Props> + | CSSPseudos<Props> + | CSSOthersObject<Props>; + +interface ThemeInput<ColorScheme extends string = string> { + /** + * The prefix to be used for the CSS variables. + */ + cssVarPrefix?: string; + /** + * The color schemes to be used for the theme. + */ + colorSchemes?: Record<ColorScheme, any>; + /** + * The default color scheme to be used for the theme. It must be one of the keys from `theme.colorSchemes`. + * Required when `colorSchemes` is provided. + * @default 'light' + */ + defaultColorScheme?: ColorScheme; + /** + * If provided, it will be used to create a selector for the color scheme. + * This is useful if you want to use class or data-* attributes to apply the color scheme. + * + * The default selector is `:root`. + * + * @example + * // class selector + * (colorScheme) => colorScheme ? `.theme-${colorScheme}` : ":root" + * + * @example + * // data-* attribute selector + * (colorScheme) => colorScheme ? `[data-theme="${colorScheme}"`] : ":root" + */ + getSelector?: (colorScheme: ColorScheme | undefined, css: Record<string, any>) => string | Record<string, any>; + /** + * A function to skip generating a CSS variable for a specific path or value. + * + * Note: properties with function as a value are always skipped. + * + * @example + * // skip the `meta.*` fields from generating CSS variables and `theme.vars` + * (keys, value) => keys[0] === 'meta' + * + */ + shouldSkipGeneratingVar?: (objectPathKeys: Array<string>, value: string | number) => boolean; + components?: Partial<Record<string, { + styleOverrides?: Record<string, any>; + defaultProps: Record<string, any>; + }>>; +} +type ExtendTheme<Options extends { + colorScheme: string; + tokens: Record<string, any>; +} = { + colorScheme: string; + tokens: Record<string, any>; +}> = ThemeInput<Options['colorScheme']> & Options['tokens'] & { + vars: Options['tokens']; + applyStyles: (colorScheme: Options['colorScheme'], styles: CSSObject<any>) => Record<string, CSSObject<any>>; + getColorSchemeSelector: (colorScheme: Options['colorScheme']) => string; + generateCssVars: (colorScheme?: Options['colorScheme']) => { + css: Record<string, string | number>; + selector: string | Record<string, any>; + }; + unstable_sxConfig?: SxConfig; +}; +type Theme = ExtendTheme; + +type PluginCustomOptions = { + /** + * Object to pass as parameter to the styled css callback functions. + */ + themeArgs?: { + theme?: Theme; + }; +}; + +type VariantData = { + props: (componentProps: unknown) => boolean | Record<string, string | number | boolean | null>; + style: object; + originalExpression?: Exclude<ExpressionValue, ConstValue>; +}; +type VariantDataTransformed = { + props: VariantData['props']; + className: string; +}; +type WrappedNode = string | { + node: Identifier; + nonLinaria?: true; + source: string; +}; +type IOptions = IOptions$1 & PluginCustomOptions; +/** + * Linaria tag processor responsible for converting complex `styled()()` calls + * at build-time to simple `styled` calls supported by runtime. + * + * Ex - + * ``` + * const SliderTrack = styled('h4', { + * name: 'MuiSlider', + * slot: 'Track', + * overridesResolver: (props, styles) => styles.track, + * })({ + * fontSize: 13, + * color: (props) => (props.isRed ? 'red' : 'blue'), + * }); + * ``` + * + * gets converted to a simple styled call with no nested calls - + * + * ``` + * const SliderTrack = styled('h4', { + * classes: ['h13ydq1s'], + * vars: { 'b1xyu9xj-0': [(t) => (t.isRed ? 'red' : 'blue'), !1] }, + * variants: [], + * name: 'MuiSlider', + * slot: 'Track', + * overridesResolver: (t, o) => o.track, + * overrideStyles: {} + * }) + * ``` + * + * and this css + * ```css + * .h13ydq1s { + * fontSize: 13px, + * color: var(--b1xyu9xj-0); + * } + * ``` + * + * For Wyw-in-JS tag processors, we need to implement 3 methods of BaseProcessor - + * 1. doEvaltimeReplacement + * 2. build + * 3. doRuntimeReplacement + */ +declare class StyledProcessor extends BaseProcessor { + variableIdx: number; + component?: WrappedNode; + componentMetaArg?: LazyValue; + styleArgs: ExpressionValue[] | (TemplateElement | ExpressionValue)[]; + finalVariants: { + props: Record<string, string | number | boolean | null>; + className: string; + }[]; + overrides: Record<string, string>; + counterMap: Map<string, number>; + baseClasses: string[]; + collectedStyles: [string, string, ExpressionValue | null][]; + collectedVariables: [string, Expression, boolean][]; + collectedVariants: VariantDataTransformed[]; + originalLocation: SourceLocation | null; + isTemplateTag: boolean; + constructor(params: Params, ...args: TailProcessorParams); + getClassName(key?: string): string; + private generateArtifacts; + private buildForTemplateTag; + /** + * There are 2 main phases in Wyw-in-JS's processing, Evaltime and Runtime. During Evaltime, Wyw-in-JS prepares minimal code that gets evaluated to get the actual values of the styled arguments. Here, we mostly want to replace the styled calls with a simple string/object of its classname. This is necessary for class composition. For ex, you could potentially do this - + * ```js + * const Component = styled(...)(...) + * const Component2 = styled()({ + * [`.${Component} &`]: { + * color: 'red' + * } + * }) + * ``` + * to further target `Component` rendered inside `Component2`. + */ + doEvaltimeReplacement(): void; + /** + * This is called by Wyw-in-JS after evaluating the code. Here, we + * get access to the actual values of the `styled` arguments + * which we can use to generate our styles. + * Order of processing styles - + * 1. CSS directly declared in styled call + * 2. CSS declared in theme object's styledOverrides + * 3. Variants declared in styled call + * 3. Variants declared in theme object + */ + build(values: ValueCache): void; + /** + * This is the runtime phase where all of the css have been transformed and we finally want to replace the `styled` call with the code that we want in the final bundle. In this particular case, we replace the `styled` calls with + * ```js + * const Component = styled('div')({ + * displayName: 'Component', + * name: 'MuiSlider', + * slot: 'root', + * classes: ['class', 'class-1', '...'], + * vars: { + * 'var-id': [(props) => props.isRed ? 'red' : 'blue', false], + * // ... + * }, + * variants: [{ + * props: { + * }, + * className: 'class-variant-1', + * }], + * // ... + * }) + * ``` + */ + doRuntimeReplacement(): void; + /** + * Generates css for object directly provided as arguments in the styled call. + */ + processStyle(values: ValueCache, styleArg: ExpressionValue, variantsAccumulator?: VariantData[], themeImportIdentifier?: string): void; + /** + * Generates css for styleOverride objects in the theme object. + */ + processOverrides(values: ValueCache, variantsAccumulator?: VariantData[]): void; + /** + * Generates css for all the variants collected after processing direct css and styleOverride css. + */ + processVariant(variant: VariantData): void; + processCss(styleObjOrFn: ((args: Record<string, unknown>) => void) | object, styleArg: ExpressionValue | null, variantsAccumulator?: VariantData[], themeImportIdentifier?: string): string; + get asSelector(): string; + get value(): Expression; +} + +export { type IOptions, StyledProcessor, type WrappedNode }; diff --git a/packages/pigment-react/processors/styled.d.ts b/packages/pigment-react/processors/styled.d.ts new file mode 100644 index 00000000000000..5915b1e4b031fc --- /dev/null +++ b/packages/pigment-react/processors/styled.d.ts @@ -0,0 +1,245 @@ +import { Identifier, TemplateElement, Expression, SourceLocation } from '@babel/types'; +import { IOptions as IOptions$1, Params, TailProcessorParams, ValueCache } from '@wyw-in-js/processor-utils'; +import { LazyValue, ExpressionValue, ConstValue } from '@wyw-in-js/shared'; +import { SxConfig } from '@mui/system/styleFunctionSx'; +import * as CSS from 'csstype'; +import { B as BaseProcessor } from './base-processor-Bkjcr8yc.js'; + +type CSSProperties = CSS.PropertiesFallback<number | string>; + +type CSSPropertiesWithCallback<Props extends object> = { + [K in keyof CSSProperties]: + | CSSProperties[K] + | Array<Extract<CSSProperties[K], string>> + | ((props: Props) => CSSProperties[K]); +}; + +type CSSPseudos<Props extends object> = { + [K in CSS.Pseudos]?: CSSObject<Props>; +}; + +interface CSSOthersObject<Props extends object> { + [selector: string]: CSSObject<Props>; +} + +type CSSObject<Props extends object> = + | CSSPropertiesWithCallback<Props> + | CSSPseudos<Props> + | CSSOthersObject<Props>; + +interface ThemeInput<ColorScheme extends string = string> { + /** + * The prefix to be used for the CSS variables. + */ + cssVarPrefix?: string; + /** + * The color schemes to be used for the theme. + */ + colorSchemes?: Record<ColorScheme, any>; + /** + * The default color scheme to be used for the theme. It must be one of the keys from `theme.colorSchemes`. + * Required when `colorSchemes` is provided. + * @default 'light' + */ + defaultColorScheme?: ColorScheme; + /** + * If provided, it will be used to create a selector for the color scheme. + * This is useful if you want to use class or data-* attributes to apply the color scheme. + * + * The default selector is `:root`. + * + * @example + * // class selector + * (colorScheme) => colorScheme ? `.theme-${colorScheme}` : ":root" + * + * @example + * // data-* attribute selector + * (colorScheme) => colorScheme ? `[data-theme="${colorScheme}"`] : ":root" + */ + getSelector?: (colorScheme: ColorScheme | undefined, css: Record<string, any>) => string | Record<string, any>; + /** + * A function to skip generating a CSS variable for a specific path or value. + * + * Note: properties with function as a value are always skipped. + * + * @example + * // skip the `meta.*` fields from generating CSS variables and `theme.vars` + * (keys, value) => keys[0] === 'meta' + * + */ + shouldSkipGeneratingVar?: (objectPathKeys: Array<string>, value: string | number) => boolean; + components?: Partial<Record<string, { + styleOverrides?: Record<string, any>; + defaultProps: Record<string, any>; + }>>; +} +type ExtendTheme<Options extends { + colorScheme: string; + tokens: Record<string, any>; +} = { + colorScheme: string; + tokens: Record<string, any>; +}> = ThemeInput<Options['colorScheme']> & Options['tokens'] & { + vars: Options['tokens']; + applyStyles: (colorScheme: Options['colorScheme'], styles: CSSObject<any>) => Record<string, CSSObject<any>>; + getColorSchemeSelector: (colorScheme: Options['colorScheme']) => string; + generateCssVars: (colorScheme?: Options['colorScheme']) => { + css: Record<string, string | number>; + selector: string | Record<string, any>; + }; + unstable_sxConfig?: SxConfig; +}; +type Theme = ExtendTheme; + +type PluginCustomOptions = { + /** + * Object to pass as parameter to the styled css callback functions. + */ + themeArgs?: { + theme?: Theme; + }; +}; + +type VariantData = { + props: (componentProps: unknown) => boolean | Record<string, string | number | boolean | null>; + style: object; + originalExpression?: Exclude<ExpressionValue, ConstValue>; +}; +type VariantDataTransformed = { + props: VariantData['props']; + className: string; +}; +type WrappedNode = string | { + node: Identifier; + nonLinaria?: true; + source: string; +}; +type IOptions = IOptions$1 & PluginCustomOptions; +/** + * Linaria tag processor responsible for converting complex `styled()()` calls + * at build-time to simple `styled` calls supported by runtime. + * + * Ex - + * ``` + * const SliderTrack = styled('h4', { + * name: 'MuiSlider', + * slot: 'Track', + * overridesResolver: (props, styles) => styles.track, + * })({ + * fontSize: 13, + * color: (props) => (props.isRed ? 'red' : 'blue'), + * }); + * ``` + * + * gets converted to a simple styled call with no nested calls - + * + * ``` + * const SliderTrack = styled('h4', { + * classes: ['h13ydq1s'], + * vars: { 'b1xyu9xj-0': [(t) => (t.isRed ? 'red' : 'blue'), !1] }, + * variants: [], + * name: 'MuiSlider', + * slot: 'Track', + * overridesResolver: (t, o) => o.track, + * overrideStyles: {} + * }) + * ``` + * + * and this css + * ```css + * .h13ydq1s { + * fontSize: 13px, + * color: var(--b1xyu9xj-0); + * } + * ``` + * + * For Wyw-in-JS tag processors, we need to implement 3 methods of BaseProcessor - + * 1. doEvaltimeReplacement + * 2. build + * 3. doRuntimeReplacement + */ +declare class StyledProcessor extends BaseProcessor { + variableIdx: number; + component?: WrappedNode; + componentMetaArg?: LazyValue; + styleArgs: ExpressionValue[] | (TemplateElement | ExpressionValue)[]; + finalVariants: { + props: Record<string, string | number | boolean | null>; + className: string; + }[]; + overrides: Record<string, string>; + counterMap: Map<string, number>; + baseClasses: string[]; + collectedStyles: [string, string, ExpressionValue | null][]; + collectedVariables: [string, Expression, boolean][]; + collectedVariants: VariantDataTransformed[]; + originalLocation: SourceLocation | null; + isTemplateTag: boolean; + constructor(params: Params, ...args: TailProcessorParams); + getClassName(key?: string): string; + private generateArtifacts; + private buildForTemplateTag; + /** + * There are 2 main phases in Wyw-in-JS's processing, Evaltime and Runtime. During Evaltime, Wyw-in-JS prepares minimal code that gets evaluated to get the actual values of the styled arguments. Here, we mostly want to replace the styled calls with a simple string/object of its classname. This is necessary for class composition. For ex, you could potentially do this - + * ```js + * const Component = styled(...)(...) + * const Component2 = styled()({ + * [`.${Component} &`]: { + * color: 'red' + * } + * }) + * ``` + * to further target `Component` rendered inside `Component2`. + */ + doEvaltimeReplacement(): void; + /** + * This is called by Wyw-in-JS after evaluating the code. Here, we + * get access to the actual values of the `styled` arguments + * which we can use to generate our styles. + * Order of processing styles - + * 1. CSS directly declared in styled call + * 2. CSS declared in theme object's styledOverrides + * 3. Variants declared in styled call + * 3. Variants declared in theme object + */ + build(values: ValueCache): void; + /** + * This is the runtime phase where all of the css have been transformed and we finally want to replace the `styled` call with the code that we want in the final bundle. In this particular case, we replace the `styled` calls with + * ```js + * const Component = styled('div')({ + * displayName: 'Component', + * name: 'MuiSlider', + * slot: 'root', + * classes: ['class', 'class-1', '...'], + * vars: { + * 'var-id': [(props) => props.isRed ? 'red' : 'blue', false], + * // ... + * }, + * variants: [{ + * props: { + * }, + * className: 'class-variant-1', + * }], + * // ... + * }) + * ``` + */ + doRuntimeReplacement(): void; + /** + * Generates css for object directly provided as arguments in the styled call. + */ + processStyle(values: ValueCache, styleArg: ExpressionValue, variantsAccumulator?: VariantData[], themeImportIdentifier?: string): void; + /** + * Generates css for styleOverride objects in the theme object. + */ + processOverrides(values: ValueCache, variantsAccumulator?: VariantData[]): void; + /** + * Generates css for all the variants collected after processing direct css and styleOverride css. + */ + processVariant(variant: VariantData): void; + processCss(styleObjOrFn: ((args: Record<string, unknown>) => void) | object, styleArg: ExpressionValue | null, variantsAccumulator?: VariantData[], themeImportIdentifier?: string): string; + get asSelector(): string; + get value(): Expression; +} + +export { type IOptions, StyledProcessor, type WrappedNode }; diff --git a/packages/pigment-react/processors/styled.js b/packages/pigment-react/processors/styled.js new file mode 100644 index 00000000000000..87c9cf83f754db --- /dev/null +++ b/packages/pigment-react/processors/styled.js @@ -0,0 +1,399 @@ +'use strict'; + +var chunkSBKEDKMF_js = require('./chunk-SBKEDKMF.js'); +var chunk3NOOOXUY_js = require('./chunk-3NOOOXUY.js'); +var chunkMRLAB7WR_js = require('./chunk-MRLAB7WR.js'); +var chunk7L3GVF7S_js = require('./chunk-7L3GVF7S.js'); +var parser = require('@babel/parser'); +var processorUtils = require('@wyw-in-js/processor-utils'); +var shared = require('@wyw-in-js/shared'); + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var StyledProcessor = class extends chunk7L3GVF7S_js.BaseProcessor { + constructor(params, ...args) { + var _a; + if (params.length <= 2) { + throw chunk7L3GVF7S_js.BaseProcessor.SKIP; + } + super([params[0]], ...args); + this.variableIdx = 0; + this.finalVariants = []; + this.overrides = {}; + this.counterMap = /* @__PURE__ */ new Map(); + this.baseClasses = []; + this.collectedStyles = []; + this.collectedVariables = []; + this.collectedVariants = []; + this.originalLocation = null; + processorUtils.validateParams( + params, + ["callee", ["call", "member"], ["call", "template"]], + `Invalid use of ${this.tagSource.imported} tag.` + ); + const [callee, memberOrCall, styleCallOrTemplate] = params; + const [callType, componentArg, componentMetaArg] = memberOrCall; + const [, ...styleArgs] = styleCallOrTemplate; + this.isTemplateTag = styleCallOrTemplate[0] === "template"; + this.componentMetaArg = componentMetaArg && componentMetaArg.kind === shared.ValueType.LAZY ? componentMetaArg : void 0; + this.styleArgs = styleArgs; + if (callType === "member") { + this.component = componentArg; + } else { + switch (componentArg.kind) { + case shared.ValueType.CONST: + this.component = typeof componentArg.value === "string" ? componentArg.value : void 0; + break; + case shared.ValueType.LAZY: + this.component = { + node: componentArg.ex, + source: componentArg.source + }; + this.dependencies.push(componentArg); + break; + default: + this.component = "FunctionalComponent"; + break; + } + if (componentMetaArg && componentMetaArg.kind !== shared.ValueType.FUNCTION) { + this.dependencies.push(componentMetaArg); + } + } + if (!this.component) { + throw new Error("Invalid usage of `styled` tag"); + } + styleArgs.flat().forEach((item) => { + if ("kind" in item) { + this.dependencies.push(item); + } + }); + if (callee[0] === "callee") { + this.originalLocation = (_a = callee[1].loc) != null ? _a : null; + } + } + getClassName(key = "base") { + if (!this.counterMap.has(key)) { + this.counterMap.set(key, 0); + } + const currentCount = this.counterMap.get(key); + this.counterMap.set(key, currentCount + 1); + return `${this.className}${key === "base" ? "" : `-${key}`}${currentCount > 0 ? `-${currentCount}` : ""}`; + } + generateArtifacts() { + const artifacts = this.collectedStyles.map(([className, cssText]) => { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; + const rules = { + [`.${className}`]: { + className, + cssText, + displayName: this.displayName, + start: (_b = (_a = this.location) == null ? void 0 : _a.start) != null ? _b : null + } + }; + const replacements = [ + { + length: cssText.length, + original: { + start: { + column: (_d = (_c = this.location) == null ? void 0 : _c.start.column) != null ? _d : 0, + line: (_f = (_e = this.location) == null ? void 0 : _e.start.line) != null ? _f : 0 + }, + end: { + column: (_h = (_g = this.location) == null ? void 0 : _g.end.column) != null ? _h : 0, + line: (_j = (_i = this.location) == null ? void 0 : _i.end.line) != null ? _j : 0 + } + } + } + ]; + return [rules, replacements]; + }); + artifacts.forEach((artifact) => { + this.artifacts.push(["css", artifact]); + }); + } + buildForTemplateTag(values) { + const templateStrs = []; + templateStrs.raw = []; + const templateExpressions = []; + const { themeArgs } = this.options; + this.styleArgs.flat().forEach((item) => { + if ("kind" in item) { + switch (item.kind) { + case shared.ValueType.FUNCTION: { + const value = values.get(item.ex.name); + templateExpressions.push(value(themeArgs)); + break; + } + case shared.ValueType.CONST: + templateExpressions.push(item.value); + break; + case shared.ValueType.LAZY: { + const evaluatedValue = values.get(item.ex.name); + if (typeof evaluatedValue === "function") { + templateExpressions.push(evaluatedValue(themeArgs)); + } else { + templateExpressions.push(evaluatedValue); + } + break; + } + } + } else if (item.type === "TemplateElement") { + templateStrs.push(item.value.cooked); + templateStrs.raw.push(item.value.raw); + } + }); + const cssClassName = chunk3NOOOXUY_js.css(templateStrs, ...templateExpressions); + const cssText = chunk3NOOOXUY_js.cache.registered[cssClassName]; + const baseClass = this.getClassName(); + this.baseClasses.push(baseClass); + this.collectedStyles.push([baseClass, cssText, null]); + const variantsAccumulator = []; + this.processOverrides(values, variantsAccumulator); + variantsAccumulator.forEach((variant) => { + this.processVariant(variant); + }); + this.generateArtifacts(); + } + /** + * There are 2 main phases in Wyw-in-JS's processing, Evaltime and Runtime. During Evaltime, Wyw-in-JS prepares minimal code that gets evaluated to get the actual values of the styled arguments. Here, we mostly want to replace the styled calls with a simple string/object of its classname. This is necessary for class composition. For ex, you could potentially do this - + * ```js + * const Component = styled(...)(...) + * const Component2 = styled()({ + * [`.${Component} &`]: { + * color: 'red' + * } + * }) + * ``` + * to further target `Component` rendered inside `Component2`. + */ + doEvaltimeReplacement() { + this.replacer(this.value, false); + } + /** + * This is called by Wyw-in-JS after evaluating the code. Here, we + * get access to the actual values of the `styled` arguments + * which we can use to generate our styles. + * Order of processing styles - + * 1. CSS directly declared in styled call + * 2. CSS declared in theme object's styledOverrides + * 3. Variants declared in styled call + * 3. Variants declared in theme object + */ + build(values) { + if (this.isTemplateTag) { + this.buildForTemplateTag(values); + return; + } + const themeImportIdentifier = this.astService.addDefaultImport( + `${"@pigment-css/react"}/theme`, + "theme" + ); + const variantsAccumulator = []; + this.styleArgs.forEach((styleArg) => { + this.processStyle(values, styleArg, variantsAccumulator, themeImportIdentifier.name); + }); + this.processOverrides(values, variantsAccumulator); + variantsAccumulator.forEach((variant) => { + this.processVariant(variant); + }); + this.generateArtifacts(); + } + /** + * This is the runtime phase where all of the css have been transformed and we finally want to replace the `styled` call with the code that we want in the final bundle. In this particular case, we replace the `styled` calls with + * ```js + * const Component = styled('div')({ + * displayName: 'Component', + * name: 'MuiSlider', + * slot: 'root', + * classes: ['class', 'class-1', '...'], + * vars: { + * 'var-id': [(props) => props.isRed ? 'red' : 'blue', false], + * // ... + * }, + * variants: [{ + * props: { + * }, + * className: 'class-variant-1', + * }], + * // ... + * }) + * ``` + */ + doRuntimeReplacement() { + var _a; + const t = this.astService; + let componentName; + if (typeof this.component === "string") { + if (this.component === "FunctionalComponent") { + componentName = t.arrowFunctionExpression([], t.blockStatement([])); + } else { + componentName = t.stringLiteral(this.component); + } + } else if ((_a = this.component) == null ? void 0 : _a.node) { + componentName = t.callExpression(t.identifier(this.component.node.name), []); + } else { + componentName = t.nullLiteral(); + } + const argProperties = []; + const classNames = Array.from( + /* @__PURE__ */ new Set([this.className, ...this.baseClasses.length ? this.baseClasses : []]) + ); + argProperties.push( + t.objectProperty( + t.identifier("classes"), + t.arrayExpression(classNames.map((cls) => t.stringLiteral(cls))) + ) + ); + const varProperties = this.collectedVariables.map( + ([variableId, expression, isUnitLess]) => t.objectProperty( + t.stringLiteral(variableId), + t.arrayExpression([expression, t.booleanLiteral(isUnitLess)]) + ) + ); + if (varProperties.length) { + argProperties.push(t.objectProperty(t.identifier("vars"), t.objectExpression(varProperties))); + } + if (this.collectedVariants.length) { + argProperties.push( + t.objectProperty(t.identifier("variants"), chunkMRLAB7WR_js.valueToLiteral(this.collectedVariants)) + ); + } + let componentMetaExpression; + if (this.componentMetaArg) { + const parsedMeta = parser.parseExpression(this.componentMetaArg.source); + if (parsedMeta.type === "ObjectExpression") { + componentMetaExpression = parsedMeta; + } + } + const styledImportIdentifier = t.addNamedImport( + this.tagSource.imported, + "@pigment-css/react" + ); + const styledCall = t.callExpression( + styledImportIdentifier, + componentMetaExpression ? [componentName, componentMetaExpression] : [componentName] + ); + const mainCall = t.callExpression(styledCall, [t.objectExpression(argProperties)]); + this.replacer(mainCall, true); + } + /** + * Generates css for object directly provided as arguments in the styled call. + */ + processStyle(values, styleArg, variantsAccumulator, themeImportIdentifier) { + if (styleArg.kind === shared.ValueType.CONST) { + if (typeof styleArg.value === "string") { + this.collectedStyles.push([this.getClassName(), styleArg.value, styleArg]); + } + } else { + const styleObjOrFn = values.get(styleArg.ex.name); + const finalStyle = this.processCss( + styleObjOrFn, + styleArg, + variantsAccumulator, + themeImportIdentifier + ); + const className = this.getClassName(); + this.baseClasses.push(className); + this.collectedStyles.push([className, finalStyle, styleArg]); + } + } + /** + * Generates css for styleOverride objects in the theme object. + */ + processOverrides(values, variantsAccumulator) { + var _a; + if (!this.componentMetaArg) { + return; + } + const value = values.get(this.componentMetaArg.ex.name); + const { themeArgs: { theme } = {} } = this.options; + if (!value.name || !value.slot || !theme) { + return; + } + const componentData = (_a = theme.components) == null ? void 0 : _a[value.name]; + if (!componentData) { + return; + } + if ("styleOverrides" in componentData) { + const overrides = componentData.styleOverrides; + if (!overrides) { + return; + } + const overrideStyle = overrides[value.slot.toLowerCase()] || overrides[value.slot]; + const className = this.getClassName(); + if (typeof overrideStyle === "string") { + this.collectedStyles.push([className, overrideStyle, null]); + return; + } + const finalStyle = this.processCss(overrideStyle, null, variantsAccumulator); + this.baseClasses.push(className); + this.collectedStyles.push([className, finalStyle, null]); + } + if (!variantsAccumulator) { + return; + } + if ("variants" in componentData && componentData.variants && value.slot.toLowerCase() === "root") { + variantsAccumulator.push(...componentData.variants); + } + } + /** + * Generates css for all the variants collected after processing direct css and styleOverride css. + */ + processVariant(variant) { + const { displayName } = this.options; + const className = this.getClassName(displayName ? "variant" : void 0); + const styleObjOrFn = variant.style; + const originalExpression = variant.originalExpression; + const finalStyle = this.processCss(styleObjOrFn, originalExpression != null ? originalExpression : null); + this.collectedStyles.push([className, finalStyle, null]); + this.collectedVariants.push({ + props: variant.props, + className + }); + } + processCss(styleObjOrFn, styleArg, variantsAccumulator, themeImportIdentifier) { + const { themeArgs = {} } = this.options; + const styleObj = typeof styleObjOrFn === "function" ? styleObjOrFn(themeArgs) : styleObjOrFn; + if (!styleObj) { + return ""; + } + if (styleObj.variants) { + variantsAccumulator == null ? void 0 : variantsAccumulator.push( + ...styleObj.variants.map((variant) => chunk7L3GVF7S_js.__spreadProps(chunk7L3GVF7S_js.__spreadValues({}, variant), { + originalExpression: styleArg + })) + ); + } + delete styleObj.variants; + const res = chunkSBKEDKMF_js.cssFnValueToVariable({ + styleObj, + expressionValue: styleArg, + getVariableName: (cssKey, source, hasUnit) => this.getCustomVariableId(cssKey, source, hasUnit), + filename: this.context.filename, + options: this.options, + includeThemeArg: typeof styleObjOrFn === "function", + themeImportIdentifier + }); + if (res.length) { + this.collectedVariables.push(...res); + } + return chunkSBKEDKMF_js.processCssObject(styleObj, themeArgs); + } + get asSelector() { + return `.${this.className}`; + } + get value() { + const t = this.astService; + return t.stringLiteral(this.className); + } +}; + +exports.StyledProcessor = StyledProcessor; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=styled.js.map \ No newline at end of file diff --git a/packages/pigment-react/processors/styled.js.map b/packages/pigment-react/processors/styled.js.map new file mode 100644 index 00000000000000..34ec1c21e9c92a --- /dev/null +++ b/packages/pigment-react/processors/styled.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/processors/styled.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseExpression } from '@babel/parser';\nimport type {\n ObjectExpression,\n SourceLocation,\n Identifier,\n Expression,\n TemplateElement,\n} from '@babel/types';\nimport {\n Params,\n TailProcessorParams,\n ValueCache,\n validateParams,\n IOptions as IBaseOptions,\n} from '@wyw-in-js/processor-utils';\nimport {\n ValueType,\n type ConstValue,\n type ExpressionValue,\n type LazyValue,\n type Replacements,\n type Rules,\n} from '@wyw-in-js/shared';\nimport { CSSObject } from '@emotion/serialize';\nimport type { PluginCustomOptions } from '../utils/cssFnValueToVariable';\nimport { cssFnValueToVariable } from '../utils/cssFnValueToVariable';\nimport { processCssObject } from '../utils/processCssObject';\nimport { valueToLiteral } from '../utils/valueToLiteral';\nimport BaseProcessor from './base-processor';\nimport { Primitive, TemplateCallback } from './keyframes';\nimport { cache, css } from '../utils/emotion';\n\ntype Theme = { [key: 'unstable_sxConfig' | string]: string | number | Theme };\n\ntype VariantData = {\n props: (componentProps: unknown) => boolean | Record<string, string | number | boolean | null>;\n style: object;\n originalExpression?: Exclude<ExpressionValue, ConstValue>;\n};\n\ntype VariantDataTransformed = {\n props: VariantData['props'];\n className: string;\n};\n\nexport type WrappedNode =\n | string\n | {\n node: Identifier;\n nonLinaria?: true;\n source: string;\n };\n\nexport type IOptions = IBaseOptions & PluginCustomOptions;\ntype ComponentNames = keyof Exclude<Theme['components'], undefined>;\n\ntype ComponentMeta = {\n name?: ComponentNames;\n slot?: string;\n skipVariantsResolver?: boolean;\n skipSx?: boolean;\n};\n\n/**\n * Linaria tag processor responsible for converting complex `styled()()` calls\n * at build-time to simple `styled` calls supported by runtime.\n *\n * Ex -\n * ```\n * const SliderTrack = styled('h4', {\n * name: 'MuiSlider',\n * slot: 'Track',\n * overridesResolver: (props, styles) => styles.track,\n * })({\n * fontSize: 13,\n * color: (props) => (props.isRed ? 'red' : 'blue'),\n * });\n * ```\n *\n * gets converted to a simple styled call with no nested calls -\n *\n * ```\n * const SliderTrack = styled('h4', {\n * classes: ['h13ydq1s'],\n * vars: { 'b1xyu9xj-0': [(t) => (t.isRed ? 'red' : 'blue'), !1] },\n * variants: [],\n * name: 'MuiSlider',\n * slot: 'Track',\n * overridesResolver: (t, o) => o.track,\n * overrideStyles: {}\n * })\n * ```\n *\n * and this css\n * ```css\n * .h13ydq1s {\n * fontSize: 13px,\n * color: var(--b1xyu9xj-0);\n * }\n * ```\n *\n * For Wyw-in-JS tag processors, we need to implement 3 methods of BaseProcessor -\n * 1. doEvaltimeReplacement\n * 2. build\n * 3. doRuntimeReplacement\n */\nexport class StyledProcessor extends BaseProcessor {\n variableIdx = 0;\n\n component?: WrappedNode;\n\n componentMetaArg?: LazyValue;\n\n styleArgs: ExpressionValue[] | (TemplateElement | ExpressionValue)[];\n\n finalVariants: {\n props: Record<string, string | number | boolean | null>;\n className: string;\n }[] = [];\n\n overrides: Record<string, string> = {};\n\n counterMap: Map<string, number> = new Map();\n\n baseClasses: string[] = [];\n\n collectedStyles: [string, string, ExpressionValue | null][] = [];\n\n collectedVariables: [string, Expression, boolean][] = [];\n\n collectedVariants: VariantDataTransformed[] = [];\n\n originalLocation: SourceLocation | null = null;\n\n isTemplateTag: boolean;\n\n constructor(params: Params, ...args: TailProcessorParams) {\n if (params.length <= 2) {\n // no need to do any processing if it is an already transformed call or just a reference.\n throw BaseProcessor.SKIP;\n }\n super([params[0]], ...args);\n validateParams(\n params,\n ['callee', ['call', 'member'], ['call', 'template']],\n `Invalid use of ${this.tagSource.imported} tag.`,\n );\n const [callee, memberOrCall, styleCallOrTemplate] = params;\n const [callType, componentArg, componentMetaArg] = memberOrCall;\n const [, ...styleArgs] = styleCallOrTemplate;\n this.isTemplateTag = styleCallOrTemplate[0] === 'template';\n this.componentMetaArg =\n componentMetaArg && componentMetaArg.kind === ValueType.LAZY ? componentMetaArg : undefined;\n this.styleArgs = styleArgs as ExpressionValue[];\n\n if (callType === 'member') {\n this.component = componentArg;\n } else {\n switch (componentArg.kind) {\n case ValueType.CONST:\n this.component = typeof componentArg.value === 'string' ? componentArg.value : undefined;\n break;\n case ValueType.LAZY:\n this.component = {\n node: componentArg.ex,\n source: componentArg.source,\n };\n this.dependencies.push(componentArg);\n break;\n default:\n this.component = 'FunctionalComponent';\n break;\n }\n if (componentMetaArg && componentMetaArg.kind !== ValueType.FUNCTION) {\n this.dependencies.push(componentMetaArg);\n }\n }\n if (!this.component) {\n throw new Error('Invalid usage of `styled` tag');\n }\n\n styleArgs.flat().forEach((item) => {\n if ('kind' in item) {\n // push item in dependencies so that they get evaluated and we receive its value in build call.\n this.dependencies.push(item);\n }\n });\n if (callee[0] === 'callee') {\n this.originalLocation = callee[1].loc ?? null;\n }\n }\n\n getClassName(key = 'base') {\n if (!this.counterMap.has(key)) {\n this.counterMap.set(key, 0);\n }\n const currentCount = this.counterMap.get(key) as number;\n this.counterMap.set(key, currentCount + 1);\n\n return `${this.className}${key === 'base' ? '' : `-${key}`}${\n currentCount > 0 ? `-${currentCount}` : ''\n }`;\n }\n\n private generateArtifacts() {\n const artifacts: [Rules, Replacements][] = this.collectedStyles.map(([className, cssText]) => {\n const rules: Rules = {\n [`.${className}`]: {\n className,\n cssText,\n displayName: this.displayName,\n start: this.location?.start ?? null,\n },\n };\n // @TODO - Refactor for finer location tracking in original code.\n const replacements: Replacements = [\n {\n length: cssText.length,\n original: {\n start: {\n column: this.location?.start.column ?? 0,\n line: this.location?.start.line ?? 0,\n },\n end: {\n column: this.location?.end.column ?? 0,\n line: this.location?.end.line ?? 0,\n },\n },\n },\n ];\n return [rules, replacements];\n });\n artifacts.forEach((artifact) => {\n // Wyw-in-JS accesses artifacts array to get the final\n // css definitions which are then exposed to the bundler.\n this.artifacts.push(['css', artifact]);\n });\n }\n\n private buildForTemplateTag(values: ValueCache): void {\n const templateStrs: string[] = [];\n // @ts-ignore @TODO - Fix this. No idea how to initialize a Tagged String array.\n templateStrs.raw = [];\n const templateExpressions: Primitive[] = [];\n const { themeArgs } = this.options as IOptions;\n\n this.styleArgs.flat().forEach((item) => {\n if ('kind' in item) {\n switch (item.kind) {\n case ValueType.FUNCTION: {\n const value = values.get(item.ex.name) as TemplateCallback;\n templateExpressions.push(value(themeArgs));\n break;\n }\n case ValueType.CONST:\n templateExpressions.push(item.value);\n break;\n case ValueType.LAZY: {\n const evaluatedValue = values.get(item.ex.name);\n if (typeof evaluatedValue === 'function') {\n templateExpressions.push(evaluatedValue(themeArgs));\n } else {\n templateExpressions.push(evaluatedValue as Primitive);\n }\n break;\n }\n default:\n break;\n }\n } else if (item.type === 'TemplateElement') {\n templateStrs.push(item.value.cooked as string);\n // @ts-ignore\n templateStrs.raw.push(item.value.raw);\n }\n });\n const cssClassName = css(templateStrs, ...templateExpressions);\n const cssText = cache.registered[cssClassName] as string;\n\n const baseClass = this.getClassName();\n this.baseClasses.push(baseClass);\n this.collectedStyles.push([baseClass, cssText, null]);\n const variantsAccumulator: VariantData[] = [];\n this.processOverrides(values, variantsAccumulator);\n variantsAccumulator.forEach((variant) => {\n this.processVariant(variant);\n });\n this.generateArtifacts();\n }\n\n /**\n * There are 2 main phases in Wyw-in-JS's processing, Evaltime and Runtime. During Evaltime, Wyw-in-JS prepares minimal code that gets evaluated to get the actual values of the styled arguments. Here, we mostly want to replace the styled calls with a simple string/object of its classname. This is necessary for class composition. For ex, you could potentially do this -\n * ```js\n * const Component = styled(...)(...)\n * const Component2 = styled()({\n * [`.${Component} &`]: {\n * color: 'red'\n * }\n * })\n * ```\n * to further target `Component` rendered inside `Component2`.\n */\n doEvaltimeReplacement() {\n this.replacer(this.value, false);\n }\n\n /**\n * This is called by Wyw-in-JS after evaluating the code. Here, we\n * get access to the actual values of the `styled` arguments\n * which we can use to generate our styles.\n * Order of processing styles -\n * 1. CSS directly declared in styled call\n * 2. CSS declared in theme object's styledOverrides\n * 3. Variants declared in styled call\n * 3. Variants declared in theme object\n */\n build(values: ValueCache): void {\n if (this.isTemplateTag) {\n this.buildForTemplateTag(values);\n return;\n }\n const themeImportIdentifier = this.astService.addDefaultImport(\n `${process.env.PACKAGE_NAME}/theme`,\n 'theme',\n );\n // all the variant definitions are collected here so that we can\n // apply variant styles after base styles for more specific targetting.\n const variantsAccumulator: VariantData[] = [];\n (this.styleArgs as ExpressionValue[]).forEach((styleArg) => {\n this.processStyle(values, styleArg, variantsAccumulator, themeImportIdentifier.name);\n });\n this.processOverrides(values, variantsAccumulator);\n variantsAccumulator.forEach((variant) => {\n this.processVariant(variant);\n });\n this.generateArtifacts();\n }\n\n /**\n * This is the runtime phase where all of the css have been transformed and we finally want to replace the `styled` call with the code that we want in the final bundle. In this particular case, we replace the `styled` calls with\n * ```js\n * const Component = styled('div')({\n * displayName: 'Component',\n * name: 'MuiSlider',\n * slot: 'root',\n * classes: ['class', 'class-1', '...'],\n * vars: {\n * 'var-id': [(props) => props.isRed ? 'red' : 'blue', false],\n * // ...\n * },\n * variants: [{\n * props: {\n * },\n * className: 'class-variant-1',\n * }],\n * // ...\n * })\n * ```\n */\n doRuntimeReplacement(): void {\n const t = this.astService;\n let componentName: Expression;\n if (typeof this.component === 'string') {\n if (this.component === 'FunctionalComponent') {\n componentName = t.arrowFunctionExpression([], t.blockStatement([]));\n } else {\n componentName = t.stringLiteral(this.component);\n }\n } else if (this.component?.node) {\n componentName = t.callExpression(t.identifier(this.component.node.name), []);\n } else {\n componentName = t.nullLiteral();\n }\n const argProperties: ReturnType<\n typeof t.objectProperty | typeof t.spreadElement | typeof t.objectMethod\n >[] = [];\n\n const classNames = Array.from(\n new Set([this.className, ...(this.baseClasses.length ? this.baseClasses : [])]),\n );\n argProperties.push(\n t.objectProperty(\n t.identifier('classes'),\n t.arrayExpression(classNames.map((cls) => t.stringLiteral(cls))),\n ),\n );\n\n const varProperties: ReturnType<typeof t.objectProperty>[] = this.collectedVariables.map(\n ([variableId, expression, isUnitLess]) =>\n t.objectProperty(\n t.stringLiteral(variableId),\n t.arrayExpression([expression, t.booleanLiteral(isUnitLess)]),\n ),\n );\n if (varProperties.length) {\n argProperties.push(t.objectProperty(t.identifier('vars'), t.objectExpression(varProperties)));\n }\n if (this.collectedVariants.length) {\n argProperties.push(\n t.objectProperty(t.identifier('variants'), valueToLiteral(this.collectedVariants)),\n );\n }\n\n let componentMetaExpression: ObjectExpression | undefined;\n\n if (this.componentMetaArg) {\n const parsedMeta = parseExpression(this.componentMetaArg.source);\n if (parsedMeta.type === 'ObjectExpression') {\n componentMetaExpression = parsedMeta as ObjectExpression;\n }\n }\n\n const styledImportIdentifier = t.addNamedImport(\n this.tagSource.imported,\n process.env.PACKAGE_NAME as string,\n );\n const styledCall = t.callExpression(\n styledImportIdentifier,\n componentMetaExpression ? [componentName, componentMetaExpression] : [componentName],\n );\n const mainCall = t.callExpression(styledCall, [t.objectExpression(argProperties)]);\n this.replacer(mainCall, true);\n }\n\n /**\n * Generates css for object directly provided as arguments in the styled call.\n */\n processStyle(\n values: ValueCache,\n styleArg: ExpressionValue,\n variantsAccumulator?: VariantData[],\n themeImportIdentifier?: string,\n ) {\n if (styleArg.kind === ValueType.CONST) {\n if (typeof styleArg.value === 'string') {\n this.collectedStyles.push([this.getClassName(), styleArg.value, styleArg]);\n }\n } else {\n const styleObjOrFn = values.get(styleArg.ex.name);\n const finalStyle = this.processCss(\n styleObjOrFn as object | (() => void),\n styleArg,\n variantsAccumulator,\n themeImportIdentifier,\n );\n const className = this.getClassName();\n this.baseClasses.push(className);\n this.collectedStyles.push([className, finalStyle, styleArg]);\n }\n }\n\n /**\n * Generates css for styleOverride objects in the theme object.\n */\n processOverrides(values: ValueCache, variantsAccumulator?: VariantData[]) {\n if (!this.componentMetaArg) {\n return;\n }\n const value = values.get(this.componentMetaArg.ex.name) as ComponentMeta;\n const { themeArgs: { theme } = {} } = this.options as IOptions;\n if (!value.name || !value.slot || !theme) {\n return;\n }\n const componentData = theme.components?.[value.name];\n if (!componentData) {\n return;\n }\n\n if ('styleOverrides' in componentData) {\n const overrides = componentData.styleOverrides as Record<string, CSSObject>;\n if (!overrides) {\n return;\n }\n const overrideStyle = (overrides[value.slot.toLowerCase()] || overrides[value.slot]) as\n | string\n | CSSObject;\n const className = this.getClassName();\n if (typeof overrideStyle === 'string') {\n this.collectedStyles.push([className, overrideStyle, null]);\n return;\n }\n const finalStyle = this.processCss(overrideStyle, null, variantsAccumulator);\n this.baseClasses.push(className);\n this.collectedStyles.push([className, finalStyle, null]);\n }\n\n if (!variantsAccumulator) {\n return;\n }\n\n if (\n 'variants' in componentData &&\n componentData.variants &&\n value.slot.toLowerCase() === 'root'\n ) {\n variantsAccumulator.push(...(componentData.variants as unknown as VariantData[]));\n }\n }\n\n /**\n * Generates css for all the variants collected after processing direct css and styleOverride css.\n */\n processVariant(variant: VariantData) {\n const { displayName } = this.options;\n const className = this.getClassName(displayName ? 'variant' : undefined);\n const styleObjOrFn = variant.style;\n const originalExpression = variant.originalExpression;\n const finalStyle = this.processCss(styleObjOrFn, originalExpression ?? null);\n this.collectedStyles.push([className, finalStyle, null]);\n this.collectedVariants.push({\n props: variant.props,\n className,\n });\n }\n\n processCss(\n styleObjOrFn: ((args: Record<string, unknown>) => void) | object,\n styleArg: ExpressionValue | null,\n variantsAccumulator?: VariantData[],\n themeImportIdentifier?: string,\n ) {\n const { themeArgs = {} } = this.options as IOptions;\n const styleObj = typeof styleObjOrFn === 'function' ? styleObjOrFn(themeArgs) : styleObjOrFn;\n if (!styleObj) {\n return '';\n }\n if (styleObj.variants) {\n variantsAccumulator?.push(\n ...styleObj.variants.map((variant: Omit<VariantData, 'originalExpression'>) => ({\n ...variant,\n originalExpression: styleArg,\n })),\n );\n }\n delete styleObj.variants;\n const res = cssFnValueToVariable({\n styleObj,\n expressionValue: styleArg,\n getVariableName: (cssKey: string, source: string, hasUnit: boolean) =>\n this.getCustomVariableId(cssKey, source, hasUnit),\n filename: this.context.filename,\n options: this.options as IOptions,\n includeThemeArg: typeof styleObjOrFn === 'function',\n themeImportIdentifier,\n });\n if (res.length) {\n this.collectedVariables.push(...res);\n }\n return processCssObject(styleObj, themeArgs);\n }\n\n public override get asSelector(): string {\n return `.${this.className}`;\n }\n\n get value(): Expression {\n const t = this.astService;\n return t.stringLiteral(this.className);\n }\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/styled.mjs b/packages/pigment-react/processors/styled.mjs new file mode 100644 index 00000000000000..e6c3e2d56dd9ce --- /dev/null +++ b/packages/pigment-react/processors/styled.mjs @@ -0,0 +1,397 @@ +import { cssFnValueToVariable, processCssObject } from './chunk-QVEBVMS6.mjs'; +import { css, cache } from './chunk-IUHN7KUC.mjs'; +import { valueToLiteral } from './chunk-DTNT7PFI.mjs'; +import { BaseProcessor, __spreadProps, __spreadValues } from './chunk-HIMMIWAJ.mjs'; +import { parseExpression } from '@babel/parser'; +import { validateParams } from '@wyw-in-js/processor-utils'; +import { ValueType } from '@wyw-in-js/shared'; + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var StyledProcessor = class extends BaseProcessor { + constructor(params, ...args) { + var _a; + if (params.length <= 2) { + throw BaseProcessor.SKIP; + } + super([params[0]], ...args); + this.variableIdx = 0; + this.finalVariants = []; + this.overrides = {}; + this.counterMap = /* @__PURE__ */ new Map(); + this.baseClasses = []; + this.collectedStyles = []; + this.collectedVariables = []; + this.collectedVariants = []; + this.originalLocation = null; + validateParams( + params, + ["callee", ["call", "member"], ["call", "template"]], + `Invalid use of ${this.tagSource.imported} tag.` + ); + const [callee, memberOrCall, styleCallOrTemplate] = params; + const [callType, componentArg, componentMetaArg] = memberOrCall; + const [, ...styleArgs] = styleCallOrTemplate; + this.isTemplateTag = styleCallOrTemplate[0] === "template"; + this.componentMetaArg = componentMetaArg && componentMetaArg.kind === ValueType.LAZY ? componentMetaArg : void 0; + this.styleArgs = styleArgs; + if (callType === "member") { + this.component = componentArg; + } else { + switch (componentArg.kind) { + case ValueType.CONST: + this.component = typeof componentArg.value === "string" ? componentArg.value : void 0; + break; + case ValueType.LAZY: + this.component = { + node: componentArg.ex, + source: componentArg.source + }; + this.dependencies.push(componentArg); + break; + default: + this.component = "FunctionalComponent"; + break; + } + if (componentMetaArg && componentMetaArg.kind !== ValueType.FUNCTION) { + this.dependencies.push(componentMetaArg); + } + } + if (!this.component) { + throw new Error("Invalid usage of `styled` tag"); + } + styleArgs.flat().forEach((item) => { + if ("kind" in item) { + this.dependencies.push(item); + } + }); + if (callee[0] === "callee") { + this.originalLocation = (_a = callee[1].loc) != null ? _a : null; + } + } + getClassName(key = "base") { + if (!this.counterMap.has(key)) { + this.counterMap.set(key, 0); + } + const currentCount = this.counterMap.get(key); + this.counterMap.set(key, currentCount + 1); + return `${this.className}${key === "base" ? "" : `-${key}`}${currentCount > 0 ? `-${currentCount}` : ""}`; + } + generateArtifacts() { + const artifacts = this.collectedStyles.map(([className, cssText]) => { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; + const rules = { + [`.${className}`]: { + className, + cssText, + displayName: this.displayName, + start: (_b = (_a = this.location) == null ? void 0 : _a.start) != null ? _b : null + } + }; + const replacements = [ + { + length: cssText.length, + original: { + start: { + column: (_d = (_c = this.location) == null ? void 0 : _c.start.column) != null ? _d : 0, + line: (_f = (_e = this.location) == null ? void 0 : _e.start.line) != null ? _f : 0 + }, + end: { + column: (_h = (_g = this.location) == null ? void 0 : _g.end.column) != null ? _h : 0, + line: (_j = (_i = this.location) == null ? void 0 : _i.end.line) != null ? _j : 0 + } + } + } + ]; + return [rules, replacements]; + }); + artifacts.forEach((artifact) => { + this.artifacts.push(["css", artifact]); + }); + } + buildForTemplateTag(values) { + const templateStrs = []; + templateStrs.raw = []; + const templateExpressions = []; + const { themeArgs } = this.options; + this.styleArgs.flat().forEach((item) => { + if ("kind" in item) { + switch (item.kind) { + case ValueType.FUNCTION: { + const value = values.get(item.ex.name); + templateExpressions.push(value(themeArgs)); + break; + } + case ValueType.CONST: + templateExpressions.push(item.value); + break; + case ValueType.LAZY: { + const evaluatedValue = values.get(item.ex.name); + if (typeof evaluatedValue === "function") { + templateExpressions.push(evaluatedValue(themeArgs)); + } else { + templateExpressions.push(evaluatedValue); + } + break; + } + } + } else if (item.type === "TemplateElement") { + templateStrs.push(item.value.cooked); + templateStrs.raw.push(item.value.raw); + } + }); + const cssClassName = css(templateStrs, ...templateExpressions); + const cssText = cache.registered[cssClassName]; + const baseClass = this.getClassName(); + this.baseClasses.push(baseClass); + this.collectedStyles.push([baseClass, cssText, null]); + const variantsAccumulator = []; + this.processOverrides(values, variantsAccumulator); + variantsAccumulator.forEach((variant) => { + this.processVariant(variant); + }); + this.generateArtifacts(); + } + /** + * There are 2 main phases in Wyw-in-JS's processing, Evaltime and Runtime. During Evaltime, Wyw-in-JS prepares minimal code that gets evaluated to get the actual values of the styled arguments. Here, we mostly want to replace the styled calls with a simple string/object of its classname. This is necessary for class composition. For ex, you could potentially do this - + * ```js + * const Component = styled(...)(...) + * const Component2 = styled()({ + * [`.${Component} &`]: { + * color: 'red' + * } + * }) + * ``` + * to further target `Component` rendered inside `Component2`. + */ + doEvaltimeReplacement() { + this.replacer(this.value, false); + } + /** + * This is called by Wyw-in-JS after evaluating the code. Here, we + * get access to the actual values of the `styled` arguments + * which we can use to generate our styles. + * Order of processing styles - + * 1. CSS directly declared in styled call + * 2. CSS declared in theme object's styledOverrides + * 3. Variants declared in styled call + * 3. Variants declared in theme object + */ + build(values) { + if (this.isTemplateTag) { + this.buildForTemplateTag(values); + return; + } + const themeImportIdentifier = this.astService.addDefaultImport( + `${"@pigment-css/react"}/theme`, + "theme" + ); + const variantsAccumulator = []; + this.styleArgs.forEach((styleArg) => { + this.processStyle(values, styleArg, variantsAccumulator, themeImportIdentifier.name); + }); + this.processOverrides(values, variantsAccumulator); + variantsAccumulator.forEach((variant) => { + this.processVariant(variant); + }); + this.generateArtifacts(); + } + /** + * This is the runtime phase where all of the css have been transformed and we finally want to replace the `styled` call with the code that we want in the final bundle. In this particular case, we replace the `styled` calls with + * ```js + * const Component = styled('div')({ + * displayName: 'Component', + * name: 'MuiSlider', + * slot: 'root', + * classes: ['class', 'class-1', '...'], + * vars: { + * 'var-id': [(props) => props.isRed ? 'red' : 'blue', false], + * // ... + * }, + * variants: [{ + * props: { + * }, + * className: 'class-variant-1', + * }], + * // ... + * }) + * ``` + */ + doRuntimeReplacement() { + var _a; + const t = this.astService; + let componentName; + if (typeof this.component === "string") { + if (this.component === "FunctionalComponent") { + componentName = t.arrowFunctionExpression([], t.blockStatement([])); + } else { + componentName = t.stringLiteral(this.component); + } + } else if ((_a = this.component) == null ? void 0 : _a.node) { + componentName = t.callExpression(t.identifier(this.component.node.name), []); + } else { + componentName = t.nullLiteral(); + } + const argProperties = []; + const classNames = Array.from( + /* @__PURE__ */ new Set([this.className, ...this.baseClasses.length ? this.baseClasses : []]) + ); + argProperties.push( + t.objectProperty( + t.identifier("classes"), + t.arrayExpression(classNames.map((cls) => t.stringLiteral(cls))) + ) + ); + const varProperties = this.collectedVariables.map( + ([variableId, expression, isUnitLess]) => t.objectProperty( + t.stringLiteral(variableId), + t.arrayExpression([expression, t.booleanLiteral(isUnitLess)]) + ) + ); + if (varProperties.length) { + argProperties.push(t.objectProperty(t.identifier("vars"), t.objectExpression(varProperties))); + } + if (this.collectedVariants.length) { + argProperties.push( + t.objectProperty(t.identifier("variants"), valueToLiteral(this.collectedVariants)) + ); + } + let componentMetaExpression; + if (this.componentMetaArg) { + const parsedMeta = parseExpression(this.componentMetaArg.source); + if (parsedMeta.type === "ObjectExpression") { + componentMetaExpression = parsedMeta; + } + } + const styledImportIdentifier = t.addNamedImport( + this.tagSource.imported, + "@pigment-css/react" + ); + const styledCall = t.callExpression( + styledImportIdentifier, + componentMetaExpression ? [componentName, componentMetaExpression] : [componentName] + ); + const mainCall = t.callExpression(styledCall, [t.objectExpression(argProperties)]); + this.replacer(mainCall, true); + } + /** + * Generates css for object directly provided as arguments in the styled call. + */ + processStyle(values, styleArg, variantsAccumulator, themeImportIdentifier) { + if (styleArg.kind === ValueType.CONST) { + if (typeof styleArg.value === "string") { + this.collectedStyles.push([this.getClassName(), styleArg.value, styleArg]); + } + } else { + const styleObjOrFn = values.get(styleArg.ex.name); + const finalStyle = this.processCss( + styleObjOrFn, + styleArg, + variantsAccumulator, + themeImportIdentifier + ); + const className = this.getClassName(); + this.baseClasses.push(className); + this.collectedStyles.push([className, finalStyle, styleArg]); + } + } + /** + * Generates css for styleOverride objects in the theme object. + */ + processOverrides(values, variantsAccumulator) { + var _a; + if (!this.componentMetaArg) { + return; + } + const value = values.get(this.componentMetaArg.ex.name); + const { themeArgs: { theme } = {} } = this.options; + if (!value.name || !value.slot || !theme) { + return; + } + const componentData = (_a = theme.components) == null ? void 0 : _a[value.name]; + if (!componentData) { + return; + } + if ("styleOverrides" in componentData) { + const overrides = componentData.styleOverrides; + if (!overrides) { + return; + } + const overrideStyle = overrides[value.slot.toLowerCase()] || overrides[value.slot]; + const className = this.getClassName(); + if (typeof overrideStyle === "string") { + this.collectedStyles.push([className, overrideStyle, null]); + return; + } + const finalStyle = this.processCss(overrideStyle, null, variantsAccumulator); + this.baseClasses.push(className); + this.collectedStyles.push([className, finalStyle, null]); + } + if (!variantsAccumulator) { + return; + } + if ("variants" in componentData && componentData.variants && value.slot.toLowerCase() === "root") { + variantsAccumulator.push(...componentData.variants); + } + } + /** + * Generates css for all the variants collected after processing direct css and styleOverride css. + */ + processVariant(variant) { + const { displayName } = this.options; + const className = this.getClassName(displayName ? "variant" : void 0); + const styleObjOrFn = variant.style; + const originalExpression = variant.originalExpression; + const finalStyle = this.processCss(styleObjOrFn, originalExpression != null ? originalExpression : null); + this.collectedStyles.push([className, finalStyle, null]); + this.collectedVariants.push({ + props: variant.props, + className + }); + } + processCss(styleObjOrFn, styleArg, variantsAccumulator, themeImportIdentifier) { + const { themeArgs = {} } = this.options; + const styleObj = typeof styleObjOrFn === "function" ? styleObjOrFn(themeArgs) : styleObjOrFn; + if (!styleObj) { + return ""; + } + if (styleObj.variants) { + variantsAccumulator == null ? void 0 : variantsAccumulator.push( + ...styleObj.variants.map((variant) => __spreadProps(__spreadValues({}, variant), { + originalExpression: styleArg + })) + ); + } + delete styleObj.variants; + const res = cssFnValueToVariable({ + styleObj, + expressionValue: styleArg, + getVariableName: (cssKey, source, hasUnit) => this.getCustomVariableId(cssKey, source, hasUnit), + filename: this.context.filename, + options: this.options, + includeThemeArg: typeof styleObjOrFn === "function", + themeImportIdentifier + }); + if (res.length) { + this.collectedVariables.push(...res); + } + return processCssObject(styleObj, themeArgs); + } + get asSelector() { + return `.${this.className}`; + } + get value() { + const t = this.astService; + return t.stringLiteral(this.className); + } +}; + +export { StyledProcessor }; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=styled.mjs.map \ No newline at end of file diff --git a/packages/pigment-react/processors/styled.mjs.map b/packages/pigment-react/processors/styled.mjs.map new file mode 100644 index 00000000000000..34ec1c21e9c92a --- /dev/null +++ b/packages/pigment-react/processors/styled.mjs.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/processors/styled.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseExpression } from '@babel/parser';\nimport type {\n ObjectExpression,\n SourceLocation,\n Identifier,\n Expression,\n TemplateElement,\n} from '@babel/types';\nimport {\n Params,\n TailProcessorParams,\n ValueCache,\n validateParams,\n IOptions as IBaseOptions,\n} from '@wyw-in-js/processor-utils';\nimport {\n ValueType,\n type ConstValue,\n type ExpressionValue,\n type LazyValue,\n type Replacements,\n type Rules,\n} from '@wyw-in-js/shared';\nimport { CSSObject } from '@emotion/serialize';\nimport type { PluginCustomOptions } from '../utils/cssFnValueToVariable';\nimport { cssFnValueToVariable } from '../utils/cssFnValueToVariable';\nimport { processCssObject } from '../utils/processCssObject';\nimport { valueToLiteral } from '../utils/valueToLiteral';\nimport BaseProcessor from './base-processor';\nimport { Primitive, TemplateCallback } from './keyframes';\nimport { cache, css } from '../utils/emotion';\n\ntype Theme = { [key: 'unstable_sxConfig' | string]: string | number | Theme };\n\ntype VariantData = {\n props: (componentProps: unknown) => boolean | Record<string, string | number | boolean | null>;\n style: object;\n originalExpression?: Exclude<ExpressionValue, ConstValue>;\n};\n\ntype VariantDataTransformed = {\n props: VariantData['props'];\n className: string;\n};\n\nexport type WrappedNode =\n | string\n | {\n node: Identifier;\n nonLinaria?: true;\n source: string;\n };\n\nexport type IOptions = IBaseOptions & PluginCustomOptions;\ntype ComponentNames = keyof Exclude<Theme['components'], undefined>;\n\ntype ComponentMeta = {\n name?: ComponentNames;\n slot?: string;\n skipVariantsResolver?: boolean;\n skipSx?: boolean;\n};\n\n/**\n * Linaria tag processor responsible for converting complex `styled()()` calls\n * at build-time to simple `styled` calls supported by runtime.\n *\n * Ex -\n * ```\n * const SliderTrack = styled('h4', {\n * name: 'MuiSlider',\n * slot: 'Track',\n * overridesResolver: (props, styles) => styles.track,\n * })({\n * fontSize: 13,\n * color: (props) => (props.isRed ? 'red' : 'blue'),\n * });\n * ```\n *\n * gets converted to a simple styled call with no nested calls -\n *\n * ```\n * const SliderTrack = styled('h4', {\n * classes: ['h13ydq1s'],\n * vars: { 'b1xyu9xj-0': [(t) => (t.isRed ? 'red' : 'blue'), !1] },\n * variants: [],\n * name: 'MuiSlider',\n * slot: 'Track',\n * overridesResolver: (t, o) => o.track,\n * overrideStyles: {}\n * })\n * ```\n *\n * and this css\n * ```css\n * .h13ydq1s {\n * fontSize: 13px,\n * color: var(--b1xyu9xj-0);\n * }\n * ```\n *\n * For Wyw-in-JS tag processors, we need to implement 3 methods of BaseProcessor -\n * 1. doEvaltimeReplacement\n * 2. build\n * 3. doRuntimeReplacement\n */\nexport class StyledProcessor extends BaseProcessor {\n variableIdx = 0;\n\n component?: WrappedNode;\n\n componentMetaArg?: LazyValue;\n\n styleArgs: ExpressionValue[] | (TemplateElement | ExpressionValue)[];\n\n finalVariants: {\n props: Record<string, string | number | boolean | null>;\n className: string;\n }[] = [];\n\n overrides: Record<string, string> = {};\n\n counterMap: Map<string, number> = new Map();\n\n baseClasses: string[] = [];\n\n collectedStyles: [string, string, ExpressionValue | null][] = [];\n\n collectedVariables: [string, Expression, boolean][] = [];\n\n collectedVariants: VariantDataTransformed[] = [];\n\n originalLocation: SourceLocation | null = null;\n\n isTemplateTag: boolean;\n\n constructor(params: Params, ...args: TailProcessorParams) {\n if (params.length <= 2) {\n // no need to do any processing if it is an already transformed call or just a reference.\n throw BaseProcessor.SKIP;\n }\n super([params[0]], ...args);\n validateParams(\n params,\n ['callee', ['call', 'member'], ['call', 'template']],\n `Invalid use of ${this.tagSource.imported} tag.`,\n );\n const [callee, memberOrCall, styleCallOrTemplate] = params;\n const [callType, componentArg, componentMetaArg] = memberOrCall;\n const [, ...styleArgs] = styleCallOrTemplate;\n this.isTemplateTag = styleCallOrTemplate[0] === 'template';\n this.componentMetaArg =\n componentMetaArg && componentMetaArg.kind === ValueType.LAZY ? componentMetaArg : undefined;\n this.styleArgs = styleArgs as ExpressionValue[];\n\n if (callType === 'member') {\n this.component = componentArg;\n } else {\n switch (componentArg.kind) {\n case ValueType.CONST:\n this.component = typeof componentArg.value === 'string' ? componentArg.value : undefined;\n break;\n case ValueType.LAZY:\n this.component = {\n node: componentArg.ex,\n source: componentArg.source,\n };\n this.dependencies.push(componentArg);\n break;\n default:\n this.component = 'FunctionalComponent';\n break;\n }\n if (componentMetaArg && componentMetaArg.kind !== ValueType.FUNCTION) {\n this.dependencies.push(componentMetaArg);\n }\n }\n if (!this.component) {\n throw new Error('Invalid usage of `styled` tag');\n }\n\n styleArgs.flat().forEach((item) => {\n if ('kind' in item) {\n // push item in dependencies so that they get evaluated and we receive its value in build call.\n this.dependencies.push(item);\n }\n });\n if (callee[0] === 'callee') {\n this.originalLocation = callee[1].loc ?? null;\n }\n }\n\n getClassName(key = 'base') {\n if (!this.counterMap.has(key)) {\n this.counterMap.set(key, 0);\n }\n const currentCount = this.counterMap.get(key) as number;\n this.counterMap.set(key, currentCount + 1);\n\n return `${this.className}${key === 'base' ? '' : `-${key}`}${\n currentCount > 0 ? `-${currentCount}` : ''\n }`;\n }\n\n private generateArtifacts() {\n const artifacts: [Rules, Replacements][] = this.collectedStyles.map(([className, cssText]) => {\n const rules: Rules = {\n [`.${className}`]: {\n className,\n cssText,\n displayName: this.displayName,\n start: this.location?.start ?? null,\n },\n };\n // @TODO - Refactor for finer location tracking in original code.\n const replacements: Replacements = [\n {\n length: cssText.length,\n original: {\n start: {\n column: this.location?.start.column ?? 0,\n line: this.location?.start.line ?? 0,\n },\n end: {\n column: this.location?.end.column ?? 0,\n line: this.location?.end.line ?? 0,\n },\n },\n },\n ];\n return [rules, replacements];\n });\n artifacts.forEach((artifact) => {\n // Wyw-in-JS accesses artifacts array to get the final\n // css definitions which are then exposed to the bundler.\n this.artifacts.push(['css', artifact]);\n });\n }\n\n private buildForTemplateTag(values: ValueCache): void {\n const templateStrs: string[] = [];\n // @ts-ignore @TODO - Fix this. No idea how to initialize a Tagged String array.\n templateStrs.raw = [];\n const templateExpressions: Primitive[] = [];\n const { themeArgs } = this.options as IOptions;\n\n this.styleArgs.flat().forEach((item) => {\n if ('kind' in item) {\n switch (item.kind) {\n case ValueType.FUNCTION: {\n const value = values.get(item.ex.name) as TemplateCallback;\n templateExpressions.push(value(themeArgs));\n break;\n }\n case ValueType.CONST:\n templateExpressions.push(item.value);\n break;\n case ValueType.LAZY: {\n const evaluatedValue = values.get(item.ex.name);\n if (typeof evaluatedValue === 'function') {\n templateExpressions.push(evaluatedValue(themeArgs));\n } else {\n templateExpressions.push(evaluatedValue as Primitive);\n }\n break;\n }\n default:\n break;\n }\n } else if (item.type === 'TemplateElement') {\n templateStrs.push(item.value.cooked as string);\n // @ts-ignore\n templateStrs.raw.push(item.value.raw);\n }\n });\n const cssClassName = css(templateStrs, ...templateExpressions);\n const cssText = cache.registered[cssClassName] as string;\n\n const baseClass = this.getClassName();\n this.baseClasses.push(baseClass);\n this.collectedStyles.push([baseClass, cssText, null]);\n const variantsAccumulator: VariantData[] = [];\n this.processOverrides(values, variantsAccumulator);\n variantsAccumulator.forEach((variant) => {\n this.processVariant(variant);\n });\n this.generateArtifacts();\n }\n\n /**\n * There are 2 main phases in Wyw-in-JS's processing, Evaltime and Runtime. During Evaltime, Wyw-in-JS prepares minimal code that gets evaluated to get the actual values of the styled arguments. Here, we mostly want to replace the styled calls with a simple string/object of its classname. This is necessary for class composition. For ex, you could potentially do this -\n * ```js\n * const Component = styled(...)(...)\n * const Component2 = styled()({\n * [`.${Component} &`]: {\n * color: 'red'\n * }\n * })\n * ```\n * to further target `Component` rendered inside `Component2`.\n */\n doEvaltimeReplacement() {\n this.replacer(this.value, false);\n }\n\n /**\n * This is called by Wyw-in-JS after evaluating the code. Here, we\n * get access to the actual values of the `styled` arguments\n * which we can use to generate our styles.\n * Order of processing styles -\n * 1. CSS directly declared in styled call\n * 2. CSS declared in theme object's styledOverrides\n * 3. Variants declared in styled call\n * 3. Variants declared in theme object\n */\n build(values: ValueCache): void {\n if (this.isTemplateTag) {\n this.buildForTemplateTag(values);\n return;\n }\n const themeImportIdentifier = this.astService.addDefaultImport(\n `${process.env.PACKAGE_NAME}/theme`,\n 'theme',\n );\n // all the variant definitions are collected here so that we can\n // apply variant styles after base styles for more specific targetting.\n const variantsAccumulator: VariantData[] = [];\n (this.styleArgs as ExpressionValue[]).forEach((styleArg) => {\n this.processStyle(values, styleArg, variantsAccumulator, themeImportIdentifier.name);\n });\n this.processOverrides(values, variantsAccumulator);\n variantsAccumulator.forEach((variant) => {\n this.processVariant(variant);\n });\n this.generateArtifacts();\n }\n\n /**\n * This is the runtime phase where all of the css have been transformed and we finally want to replace the `styled` call with the code that we want in the final bundle. In this particular case, we replace the `styled` calls with\n * ```js\n * const Component = styled('div')({\n * displayName: 'Component',\n * name: 'MuiSlider',\n * slot: 'root',\n * classes: ['class', 'class-1', '...'],\n * vars: {\n * 'var-id': [(props) => props.isRed ? 'red' : 'blue', false],\n * // ...\n * },\n * variants: [{\n * props: {\n * },\n * className: 'class-variant-1',\n * }],\n * // ...\n * })\n * ```\n */\n doRuntimeReplacement(): void {\n const t = this.astService;\n let componentName: Expression;\n if (typeof this.component === 'string') {\n if (this.component === 'FunctionalComponent') {\n componentName = t.arrowFunctionExpression([], t.blockStatement([]));\n } else {\n componentName = t.stringLiteral(this.component);\n }\n } else if (this.component?.node) {\n componentName = t.callExpression(t.identifier(this.component.node.name), []);\n } else {\n componentName = t.nullLiteral();\n }\n const argProperties: ReturnType<\n typeof t.objectProperty | typeof t.spreadElement | typeof t.objectMethod\n >[] = [];\n\n const classNames = Array.from(\n new Set([this.className, ...(this.baseClasses.length ? this.baseClasses : [])]),\n );\n argProperties.push(\n t.objectProperty(\n t.identifier('classes'),\n t.arrayExpression(classNames.map((cls) => t.stringLiteral(cls))),\n ),\n );\n\n const varProperties: ReturnType<typeof t.objectProperty>[] = this.collectedVariables.map(\n ([variableId, expression, isUnitLess]) =>\n t.objectProperty(\n t.stringLiteral(variableId),\n t.arrayExpression([expression, t.booleanLiteral(isUnitLess)]),\n ),\n );\n if (varProperties.length) {\n argProperties.push(t.objectProperty(t.identifier('vars'), t.objectExpression(varProperties)));\n }\n if (this.collectedVariants.length) {\n argProperties.push(\n t.objectProperty(t.identifier('variants'), valueToLiteral(this.collectedVariants)),\n );\n }\n\n let componentMetaExpression: ObjectExpression | undefined;\n\n if (this.componentMetaArg) {\n const parsedMeta = parseExpression(this.componentMetaArg.source);\n if (parsedMeta.type === 'ObjectExpression') {\n componentMetaExpression = parsedMeta as ObjectExpression;\n }\n }\n\n const styledImportIdentifier = t.addNamedImport(\n this.tagSource.imported,\n process.env.PACKAGE_NAME as string,\n );\n const styledCall = t.callExpression(\n styledImportIdentifier,\n componentMetaExpression ? [componentName, componentMetaExpression] : [componentName],\n );\n const mainCall = t.callExpression(styledCall, [t.objectExpression(argProperties)]);\n this.replacer(mainCall, true);\n }\n\n /**\n * Generates css for object directly provided as arguments in the styled call.\n */\n processStyle(\n values: ValueCache,\n styleArg: ExpressionValue,\n variantsAccumulator?: VariantData[],\n themeImportIdentifier?: string,\n ) {\n if (styleArg.kind === ValueType.CONST) {\n if (typeof styleArg.value === 'string') {\n this.collectedStyles.push([this.getClassName(), styleArg.value, styleArg]);\n }\n } else {\n const styleObjOrFn = values.get(styleArg.ex.name);\n const finalStyle = this.processCss(\n styleObjOrFn as object | (() => void),\n styleArg,\n variantsAccumulator,\n themeImportIdentifier,\n );\n const className = this.getClassName();\n this.baseClasses.push(className);\n this.collectedStyles.push([className, finalStyle, styleArg]);\n }\n }\n\n /**\n * Generates css for styleOverride objects in the theme object.\n */\n processOverrides(values: ValueCache, variantsAccumulator?: VariantData[]) {\n if (!this.componentMetaArg) {\n return;\n }\n const value = values.get(this.componentMetaArg.ex.name) as ComponentMeta;\n const { themeArgs: { theme } = {} } = this.options as IOptions;\n if (!value.name || !value.slot || !theme) {\n return;\n }\n const componentData = theme.components?.[value.name];\n if (!componentData) {\n return;\n }\n\n if ('styleOverrides' in componentData) {\n const overrides = componentData.styleOverrides as Record<string, CSSObject>;\n if (!overrides) {\n return;\n }\n const overrideStyle = (overrides[value.slot.toLowerCase()] || overrides[value.slot]) as\n | string\n | CSSObject;\n const className = this.getClassName();\n if (typeof overrideStyle === 'string') {\n this.collectedStyles.push([className, overrideStyle, null]);\n return;\n }\n const finalStyle = this.processCss(overrideStyle, null, variantsAccumulator);\n this.baseClasses.push(className);\n this.collectedStyles.push([className, finalStyle, null]);\n }\n\n if (!variantsAccumulator) {\n return;\n }\n\n if (\n 'variants' in componentData &&\n componentData.variants &&\n value.slot.toLowerCase() === 'root'\n ) {\n variantsAccumulator.push(...(componentData.variants as unknown as VariantData[]));\n }\n }\n\n /**\n * Generates css for all the variants collected after processing direct css and styleOverride css.\n */\n processVariant(variant: VariantData) {\n const { displayName } = this.options;\n const className = this.getClassName(displayName ? 'variant' : undefined);\n const styleObjOrFn = variant.style;\n const originalExpression = variant.originalExpression;\n const finalStyle = this.processCss(styleObjOrFn, originalExpression ?? null);\n this.collectedStyles.push([className, finalStyle, null]);\n this.collectedVariants.push({\n props: variant.props,\n className,\n });\n }\n\n processCss(\n styleObjOrFn: ((args: Record<string, unknown>) => void) | object,\n styleArg: ExpressionValue | null,\n variantsAccumulator?: VariantData[],\n themeImportIdentifier?: string,\n ) {\n const { themeArgs = {} } = this.options as IOptions;\n const styleObj = typeof styleObjOrFn === 'function' ? styleObjOrFn(themeArgs) : styleObjOrFn;\n if (!styleObj) {\n return '';\n }\n if (styleObj.variants) {\n variantsAccumulator?.push(\n ...styleObj.variants.map((variant: Omit<VariantData, 'originalExpression'>) => ({\n ...variant,\n originalExpression: styleArg,\n })),\n );\n }\n delete styleObj.variants;\n const res = cssFnValueToVariable({\n styleObj,\n expressionValue: styleArg,\n getVariableName: (cssKey: string, source: string, hasUnit: boolean) =>\n this.getCustomVariableId(cssKey, source, hasUnit),\n filename: this.context.filename,\n options: this.options as IOptions,\n includeThemeArg: typeof styleObjOrFn === 'function',\n themeImportIdentifier,\n });\n if (res.length) {\n this.collectedVariables.push(...res);\n }\n return processCssObject(styleObj, themeArgs);\n }\n\n public override get asSelector(): string {\n return `.${this.className}`;\n }\n\n get value(): Expression {\n const t = this.astService;\n return t.stringLiteral(this.className);\n }\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/sx.d.mts b/packages/pigment-react/processors/sx.d.mts new file mode 100644 index 00000000000000..3157f2dc5125dc --- /dev/null +++ b/packages/pigment-react/processors/sx.d.mts @@ -0,0 +1,20 @@ +import { Expression } from '@babel/types'; +import { Params, TailProcessorParams, ValueCache } from '@wyw-in-js/processor-utils'; +import { ExpressionValue } from '@wyw-in-js/shared'; +import { B as BaseProcessor } from './base-processor-Bkjcr8yc.mjs'; + +declare class SxProcessor extends BaseProcessor { + sxArguments: ExpressionValue[]; + variableIdx: number; + collectedVariables: [string, Expression, boolean][]; + elementClassName: string; + constructor(params: Params, ...args: TailProcessorParams); + build(values: ValueCache): void; + doEvaltimeReplacement(): void; + doRuntimeReplacement(): void; + get asSelector(): string; + get value(): Expression; + private processCss; +} + +export { SxProcessor }; diff --git a/packages/pigment-react/processors/sx.d.ts b/packages/pigment-react/processors/sx.d.ts new file mode 100644 index 00000000000000..03d7f44980cb9e --- /dev/null +++ b/packages/pigment-react/processors/sx.d.ts @@ -0,0 +1,20 @@ +import { Expression } from '@babel/types'; +import { Params, TailProcessorParams, ValueCache } from '@wyw-in-js/processor-utils'; +import { ExpressionValue } from '@wyw-in-js/shared'; +import { B as BaseProcessor } from './base-processor-Bkjcr8yc.js'; + +declare class SxProcessor extends BaseProcessor { + sxArguments: ExpressionValue[]; + variableIdx: number; + collectedVariables: [string, Expression, boolean][]; + elementClassName: string; + constructor(params: Params, ...args: TailProcessorParams); + build(values: ValueCache): void; + doEvaltimeReplacement(): void; + doRuntimeReplacement(): void; + get asSelector(): string; + get value(): Expression; + private processCss; +} + +export { SxProcessor }; diff --git a/packages/pigment-react/processors/sx.js b/packages/pigment-react/processors/sx.js new file mode 100644 index 00000000000000..a115b0b6d17efd --- /dev/null +++ b/packages/pigment-react/processors/sx.js @@ -0,0 +1,153 @@ +'use strict'; + +var chunkSBKEDKMF_js = require('./chunk-SBKEDKMF.js'); +require('./chunk-3NOOOXUY.js'); +var chunk7L3GVF7S_js = require('./chunk-7L3GVF7S.js'); +var processorUtils = require('@wyw-in-js/processor-utils'); +var shared = require('@wyw-in-js/shared'); + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var SxProcessor = class extends chunk7L3GVF7S_js.BaseProcessor { + constructor(params, ...args) { + super([params[0]], ...args); + this.sxArguments = []; + this.variableIdx = 0; + this.collectedVariables = []; + this.elementClassName = ""; + processorUtils.validateParams(params, ["callee", "call"], "Invalid usage of sx call."); + const [, [, ...sxCallArguments]] = params; + sxCallArguments.forEach((arg) => { + if ("kind" in arg) { + this.dependencies.push(arg); + } + }); + this.sxArguments = sxCallArguments; + } + build(values) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; + const [sxStyle, elementClassExpression] = this.sxArguments; + if (elementClassExpression.kind === shared.ValueType.LAZY) { + const elementClassValue = values.get(elementClassExpression.ex.name); + if (typeof elementClassValue === "string") { + this.elementClassName = elementClassValue; + } + } + if (!this.elementClassName) { + return; + } + let cssText = ""; + if (sxStyle.kind === shared.ValueType.CONST) { + if (sxStyle.ex.type === "StringLiteral") { + cssText = sxStyle.ex.value; + } + } else { + const styleObjOrFn = values.get(sxStyle.ex.name); + cssText = this.processCss(styleObjOrFn, sxStyle); + } + const selector = this.elementClassName ? `.${this.elementClassName}${this.asSelector}` : this.asSelector; + if (!cssText) { + return; + } + const rules = { + [selector]: { + className: this.className, + cssText, + displayName: this.displayName, + start: (_b = (_a = this.location) == null ? void 0 : _a.start) != null ? _b : null + } + }; + const replacements = [ + { + length: cssText.length, + original: { + start: { + column: (_d = (_c = this.location) == null ? void 0 : _c.start.column) != null ? _d : 0, + line: (_f = (_e = this.location) == null ? void 0 : _e.start.line) != null ? _f : 0 + }, + end: { + column: (_h = (_g = this.location) == null ? void 0 : _g.end.column) != null ? _h : 0, + line: (_j = (_i = this.location) == null ? void 0 : _i.end.line) != null ? _j : 0 + } + } + } + ]; + this.artifacts.push(["css", [rules, replacements]]); + } + doEvaltimeReplacement() { + this.replacer(this.value, false); + } + doRuntimeReplacement() { + const t = this.astService; + if (!this.elementClassName) { + return; + } + if (this.collectedVariables.length) { + const varProperties = this.collectedVariables.map( + ([variableId, expression, isUnitLess]) => { + switch (expression.type) { + case "ArrowFunctionExpression": { + return t.objectProperty( + t.stringLiteral(variableId), + t.arrayExpression([expression.body, t.booleanLiteral(isUnitLess)]) + ); + } + case "FunctionExpression": { + const returnStatement = expression.body.body[0]; + if (returnStatement.type === "ReturnStatement" && returnStatement.argument) { + return t.objectProperty( + t.stringLiteral(variableId), + t.arrayExpression([returnStatement.argument, t.booleanLiteral(isUnitLess)]) + ); + } + throw this.sxArguments[0].buildCodeFrameError( + "Invalid transformation encountered. The callbacks in sx properties should directly return an Expression." + ); + } + default: { + return t.objectProperty(t.stringLiteral(variableId), t.nullLiteral()); + } + } + } + ); + const obj = t.objectExpression([ + t.objectProperty(t.identifier("className"), t.stringLiteral(this.className)), + t.objectProperty(t.identifier("vars"), t.objectExpression(varProperties)) + ]); + this.replacer(obj, false); + } else { + this.replacer(this.value, false); + } + } + get asSelector() { + return `.${this.className}`; + } + get value() { + return this.astService.stringLiteral(this.className); + } + processCss(styleObjOrFn, expressionValue) { + const { themeArgs } = this.options; + const styleObj = typeof styleObjOrFn === "function" ? styleObjOrFn(themeArgs) : styleObjOrFn; + const res = chunkSBKEDKMF_js.cssFnValueToVariable({ + styleObj, + expressionValue, + getVariableName: (cssKey, source, hasUnit) => this.getCustomVariableId(cssKey, source, hasUnit), + filename: this.context.filename, + options: this.options + }); + if (res.length) { + this.collectedVariables.push(...res); + } + return chunkSBKEDKMF_js.processCssObject(styleObj, themeArgs, false); + } +}; + +exports.SxProcessor = SxProcessor; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=sx.js.map \ No newline at end of file diff --git a/packages/pigment-react/processors/sx.js.map b/packages/pigment-react/processors/sx.js.map new file mode 100644 index 00000000000000..f0e74dd8aefd4b --- /dev/null +++ b/packages/pigment-react/processors/sx.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/processors/sx.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Expression } from '@babel/types';\nimport {\n validateParams,\n type Params,\n type TailProcessorParams,\n type ValueCache,\n} from '@wyw-in-js/processor-utils';\nimport { ValueType, type ExpressionValue, type Replacements, type Rules } from '@wyw-in-js/shared';\nimport type { IOptions } from './styled';\nimport { processCssObject } from '../utils/processCssObject';\nimport { cssFnValueToVariable } from '../utils/cssFnValueToVariable';\nimport BaseProcessor from './base-processor';\n\nexport class SxProcessor extends BaseProcessor {\n sxArguments: ExpressionValue[] = [];\n\n variableIdx: number = 0;\n\n collectedVariables: [string, Expression, boolean][] = [];\n\n elementClassName = '';\n\n constructor(params: Params, ...args: TailProcessorParams) {\n super([params[0]], ...args);\n validateParams(params, ['callee', 'call'], 'Invalid usage of sx call.');\n const [, [, ...sxCallArguments]] = params;\n sxCallArguments.forEach((arg) => {\n if ('kind' in arg) {\n this.dependencies.push(arg);\n }\n });\n this.sxArguments = sxCallArguments;\n }\n\n build(values: ValueCache) {\n const [sxStyle, elementClassExpression] = this.sxArguments;\n if (elementClassExpression.kind === ValueType.LAZY) {\n const elementClassValue = values.get(elementClassExpression.ex.name);\n if (typeof elementClassValue === 'string') {\n this.elementClassName = elementClassValue;\n }\n }\n\n if (!this.elementClassName) {\n return;\n }\n\n let cssText: string = '';\n if (sxStyle.kind === ValueType.CONST) {\n if (sxStyle.ex.type === 'StringLiteral') {\n cssText = sxStyle.ex.value;\n }\n } else {\n const styleObjOrFn = values.get(sxStyle.ex.name);\n cssText = this.processCss(styleObjOrFn, sxStyle);\n }\n const selector = this.elementClassName\n ? `.${this.elementClassName}${this.asSelector}`\n : this.asSelector;\n\n if (!cssText) {\n return;\n }\n\n const rules: Rules = {\n [selector]: {\n className: this.className,\n cssText,\n displayName: this.displayName,\n start: this.location?.start ?? null,\n },\n };\n const replacements: Replacements = [\n {\n length: cssText.length,\n original: {\n start: {\n column: this.location?.start.column ?? 0,\n line: this.location?.start.line ?? 0,\n },\n end: {\n column: this.location?.end.column ?? 0,\n line: this.location?.end.line ?? 0,\n },\n },\n },\n ];\n this.artifacts.push(['css', [rules, replacements]]);\n }\n\n doEvaltimeReplacement() {\n this.replacer(this.value, false);\n }\n\n doRuntimeReplacement() {\n const t = this.astService;\n // do not replace if sx prop is not on zero-runtime styled component\n if (!this.elementClassName) {\n return;\n }\n if (this.collectedVariables.length) {\n const varProperties: ReturnType<typeof t.objectProperty>[] = this.collectedVariables.map(\n ([variableId, expression, isUnitLess]) => {\n switch (expression.type) {\n case 'ArrowFunctionExpression': {\n return t.objectProperty(\n t.stringLiteral(variableId),\n t.arrayExpression([expression.body as Expression, t.booleanLiteral(isUnitLess)]),\n );\n }\n case 'FunctionExpression': {\n const returnStatement = expression.body.body[0];\n if (returnStatement.type === 'ReturnStatement' && returnStatement.argument) {\n return t.objectProperty(\n t.stringLiteral(variableId),\n t.arrayExpression([returnStatement.argument, t.booleanLiteral(isUnitLess)]),\n );\n }\n throw this.sxArguments[0].buildCodeFrameError(\n 'Invalid transformation encountered. The callbacks in sx properties should directly return an Expression.',\n );\n }\n default: {\n return t.objectProperty(t.stringLiteral(variableId), t.nullLiteral());\n }\n }\n },\n );\n\n const obj = t.objectExpression([\n t.objectProperty(t.identifier('className'), t.stringLiteral(this.className)),\n t.objectProperty(t.identifier('vars'), t.objectExpression(varProperties)),\n ]);\n this.replacer(obj, false);\n } else {\n this.replacer(this.value, false);\n }\n }\n\n get asSelector(): string {\n return `.${this.className}`;\n }\n\n get value(): Expression {\n return this.astService.stringLiteral(this.className);\n }\n\n private processCss(styleObjOrFn: unknown, expressionValue: ExpressionValue) {\n const { themeArgs } = this.options as IOptions;\n const styleObj = typeof styleObjOrFn === 'function' ? styleObjOrFn(themeArgs) : styleObjOrFn;\n\n const res = cssFnValueToVariable({\n styleObj,\n expressionValue,\n getVariableName: (cssKey: string, source: string, hasUnit: boolean) =>\n this.getCustomVariableId(cssKey, source, hasUnit),\n filename: this.context.filename,\n options: this.options as IOptions,\n });\n if (res.length) {\n this.collectedVariables.push(...res);\n }\n\n return processCssObject(styleObj, themeArgs, false);\n }\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/processors/sx.mjs b/packages/pigment-react/processors/sx.mjs new file mode 100644 index 00000000000000..341730baebb9e6 --- /dev/null +++ b/packages/pigment-react/processors/sx.mjs @@ -0,0 +1,151 @@ +import { cssFnValueToVariable, processCssObject } from './chunk-QVEBVMS6.mjs'; +import './chunk-IUHN7KUC.mjs'; +import { BaseProcessor } from './chunk-HIMMIWAJ.mjs'; +import { validateParams } from '@wyw-in-js/processor-utils'; +import { ValueType } from '@wyw-in-js/shared'; + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var SxProcessor = class extends BaseProcessor { + constructor(params, ...args) { + super([params[0]], ...args); + this.sxArguments = []; + this.variableIdx = 0; + this.collectedVariables = []; + this.elementClassName = ""; + validateParams(params, ["callee", "call"], "Invalid usage of sx call."); + const [, [, ...sxCallArguments]] = params; + sxCallArguments.forEach((arg) => { + if ("kind" in arg) { + this.dependencies.push(arg); + } + }); + this.sxArguments = sxCallArguments; + } + build(values) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; + const [sxStyle, elementClassExpression] = this.sxArguments; + if (elementClassExpression.kind === ValueType.LAZY) { + const elementClassValue = values.get(elementClassExpression.ex.name); + if (typeof elementClassValue === "string") { + this.elementClassName = elementClassValue; + } + } + if (!this.elementClassName) { + return; + } + let cssText = ""; + if (sxStyle.kind === ValueType.CONST) { + if (sxStyle.ex.type === "StringLiteral") { + cssText = sxStyle.ex.value; + } + } else { + const styleObjOrFn = values.get(sxStyle.ex.name); + cssText = this.processCss(styleObjOrFn, sxStyle); + } + const selector = this.elementClassName ? `.${this.elementClassName}${this.asSelector}` : this.asSelector; + if (!cssText) { + return; + } + const rules = { + [selector]: { + className: this.className, + cssText, + displayName: this.displayName, + start: (_b = (_a = this.location) == null ? void 0 : _a.start) != null ? _b : null + } + }; + const replacements = [ + { + length: cssText.length, + original: { + start: { + column: (_d = (_c = this.location) == null ? void 0 : _c.start.column) != null ? _d : 0, + line: (_f = (_e = this.location) == null ? void 0 : _e.start.line) != null ? _f : 0 + }, + end: { + column: (_h = (_g = this.location) == null ? void 0 : _g.end.column) != null ? _h : 0, + line: (_j = (_i = this.location) == null ? void 0 : _i.end.line) != null ? _j : 0 + } + } + } + ]; + this.artifacts.push(["css", [rules, replacements]]); + } + doEvaltimeReplacement() { + this.replacer(this.value, false); + } + doRuntimeReplacement() { + const t = this.astService; + if (!this.elementClassName) { + return; + } + if (this.collectedVariables.length) { + const varProperties = this.collectedVariables.map( + ([variableId, expression, isUnitLess]) => { + switch (expression.type) { + case "ArrowFunctionExpression": { + return t.objectProperty( + t.stringLiteral(variableId), + t.arrayExpression([expression.body, t.booleanLiteral(isUnitLess)]) + ); + } + case "FunctionExpression": { + const returnStatement = expression.body.body[0]; + if (returnStatement.type === "ReturnStatement" && returnStatement.argument) { + return t.objectProperty( + t.stringLiteral(variableId), + t.arrayExpression([returnStatement.argument, t.booleanLiteral(isUnitLess)]) + ); + } + throw this.sxArguments[0].buildCodeFrameError( + "Invalid transformation encountered. The callbacks in sx properties should directly return an Expression." + ); + } + default: { + return t.objectProperty(t.stringLiteral(variableId), t.nullLiteral()); + } + } + } + ); + const obj = t.objectExpression([ + t.objectProperty(t.identifier("className"), t.stringLiteral(this.className)), + t.objectProperty(t.identifier("vars"), t.objectExpression(varProperties)) + ]); + this.replacer(obj, false); + } else { + this.replacer(this.value, false); + } + } + get asSelector() { + return `.${this.className}`; + } + get value() { + return this.astService.stringLiteral(this.className); + } + processCss(styleObjOrFn, expressionValue) { + const { themeArgs } = this.options; + const styleObj = typeof styleObjOrFn === "function" ? styleObjOrFn(themeArgs) : styleObjOrFn; + const res = cssFnValueToVariable({ + styleObj, + expressionValue, + getVariableName: (cssKey, source, hasUnit) => this.getCustomVariableId(cssKey, source, hasUnit), + filename: this.context.filename, + options: this.options + }); + if (res.length) { + this.collectedVariables.push(...res); + } + return processCssObject(styleObj, themeArgs, false); + } +}; + +export { SxProcessor }; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=sx.mjs.map \ No newline at end of file diff --git a/packages/pigment-react/processors/sx.mjs.map b/packages/pigment-react/processors/sx.mjs.map new file mode 100644 index 00000000000000..f0e74dd8aefd4b --- /dev/null +++ b/packages/pigment-react/processors/sx.mjs.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/processors/sx.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Expression } from '@babel/types';\nimport {\n validateParams,\n type Params,\n type TailProcessorParams,\n type ValueCache,\n} from '@wyw-in-js/processor-utils';\nimport { ValueType, type ExpressionValue, type Replacements, type Rules } from '@wyw-in-js/shared';\nimport type { IOptions } from './styled';\nimport { processCssObject } from '../utils/processCssObject';\nimport { cssFnValueToVariable } from '../utils/cssFnValueToVariable';\nimport BaseProcessor from './base-processor';\n\nexport class SxProcessor extends BaseProcessor {\n sxArguments: ExpressionValue[] = [];\n\n variableIdx: number = 0;\n\n collectedVariables: [string, Expression, boolean][] = [];\n\n elementClassName = '';\n\n constructor(params: Params, ...args: TailProcessorParams) {\n super([params[0]], ...args);\n validateParams(params, ['callee', 'call'], 'Invalid usage of sx call.');\n const [, [, ...sxCallArguments]] = params;\n sxCallArguments.forEach((arg) => {\n if ('kind' in arg) {\n this.dependencies.push(arg);\n }\n });\n this.sxArguments = sxCallArguments;\n }\n\n build(values: ValueCache) {\n const [sxStyle, elementClassExpression] = this.sxArguments;\n if (elementClassExpression.kind === ValueType.LAZY) {\n const elementClassValue = values.get(elementClassExpression.ex.name);\n if (typeof elementClassValue === 'string') {\n this.elementClassName = elementClassValue;\n }\n }\n\n if (!this.elementClassName) {\n return;\n }\n\n let cssText: string = '';\n if (sxStyle.kind === ValueType.CONST) {\n if (sxStyle.ex.type === 'StringLiteral') {\n cssText = sxStyle.ex.value;\n }\n } else {\n const styleObjOrFn = values.get(sxStyle.ex.name);\n cssText = this.processCss(styleObjOrFn, sxStyle);\n }\n const selector = this.elementClassName\n ? `.${this.elementClassName}${this.asSelector}`\n : this.asSelector;\n\n if (!cssText) {\n return;\n }\n\n const rules: Rules = {\n [selector]: {\n className: this.className,\n cssText,\n displayName: this.displayName,\n start: this.location?.start ?? null,\n },\n };\n const replacements: Replacements = [\n {\n length: cssText.length,\n original: {\n start: {\n column: this.location?.start.column ?? 0,\n line: this.location?.start.line ?? 0,\n },\n end: {\n column: this.location?.end.column ?? 0,\n line: this.location?.end.line ?? 0,\n },\n },\n },\n ];\n this.artifacts.push(['css', [rules, replacements]]);\n }\n\n doEvaltimeReplacement() {\n this.replacer(this.value, false);\n }\n\n doRuntimeReplacement() {\n const t = this.astService;\n // do not replace if sx prop is not on zero-runtime styled component\n if (!this.elementClassName) {\n return;\n }\n if (this.collectedVariables.length) {\n const varProperties: ReturnType<typeof t.objectProperty>[] = this.collectedVariables.map(\n ([variableId, expression, isUnitLess]) => {\n switch (expression.type) {\n case 'ArrowFunctionExpression': {\n return t.objectProperty(\n t.stringLiteral(variableId),\n t.arrayExpression([expression.body as Expression, t.booleanLiteral(isUnitLess)]),\n );\n }\n case 'FunctionExpression': {\n const returnStatement = expression.body.body[0];\n if (returnStatement.type === 'ReturnStatement' && returnStatement.argument) {\n return t.objectProperty(\n t.stringLiteral(variableId),\n t.arrayExpression([returnStatement.argument, t.booleanLiteral(isUnitLess)]),\n );\n }\n throw this.sxArguments[0].buildCodeFrameError(\n 'Invalid transformation encountered. The callbacks in sx properties should directly return an Expression.',\n );\n }\n default: {\n return t.objectProperty(t.stringLiteral(variableId), t.nullLiteral());\n }\n }\n },\n );\n\n const obj = t.objectExpression([\n t.objectProperty(t.identifier('className'), t.stringLiteral(this.className)),\n t.objectProperty(t.identifier('vars'), t.objectExpression(varProperties)),\n ]);\n this.replacer(obj, false);\n } else {\n this.replacer(this.value, false);\n }\n }\n\n get asSelector(): string {\n return `.${this.className}`;\n }\n\n get value(): Expression {\n return this.astService.stringLiteral(this.className);\n }\n\n private processCss(styleObjOrFn: unknown, expressionValue: ExpressionValue) {\n const { themeArgs } = this.options as IOptions;\n const styleObj = typeof styleObjOrFn === 'function' ? styleObjOrFn(themeArgs) : styleObjOrFn;\n\n const res = cssFnValueToVariable({\n styleObj,\n expressionValue,\n getVariableName: (cssKey: string, source: string, hasUnit: boolean) =>\n this.getCustomVariableId(cssKey, source, hasUnit),\n filename: this.context.filename,\n options: this.options as IOptions,\n });\n if (res.length) {\n this.collectedVariables.push(...res);\n }\n\n return processCssObject(styleObj, themeArgs, false);\n }\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/tests/css/fixtures/css.output.js b/packages/pigment-react/tests/css/fixtures/css.output.js deleted file mode 100644 index afbe5dcc772db6..00000000000000 --- a/packages/pigment-react/tests/css/fixtures/css.output.js +++ /dev/null @@ -1 +0,0 @@ -const cls1 = 'c1wr0t7p'; diff --git a/packages/pigment-react/tests/keyframes/fixtures/keyframes.output.css b/packages/pigment-react/tests/keyframes/fixtures/keyframes.output.css deleted file mode 100644 index 43ac2b3341e9fb..00000000000000 --- a/packages/pigment-react/tests/keyframes/fixtures/keyframes.output.css +++ /dev/null @@ -1,8 +0,0 @@ -@keyframes r14c1bqo { - from { - transform: rotate(360deg); - } - to { - transform: rotate(0deg); - } -} diff --git a/packages/pigment-react/tests/keyframes/fixtures/keyframes.output.js b/packages/pigment-react/tests/keyframes/fixtures/keyframes.output.js deleted file mode 100644 index 2df1df78892cc8..00000000000000 --- a/packages/pigment-react/tests/keyframes/fixtures/keyframes.output.js +++ /dev/null @@ -1 +0,0 @@ -const rotateKeyframe = 'r14c1bqo'; diff --git a/packages/pigment-react/tests/keyframes/keyframes.test.ts b/packages/pigment-react/tests/keyframes/keyframes.test.ts deleted file mode 100644 index 337dedbaf76fdb..00000000000000 --- a/packages/pigment-react/tests/keyframes/keyframes.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import path from 'node:path'; -import { runTransformation, expect } from '../testUtils'; - -describe('Pigment CSS - keyframes', () => { - it('basics', async () => { - const { output, fixture } = await runTransformation( - path.join(__dirname, 'fixtures/keyframes.input.js'), - ); - - expect(output.js).to.equal(fixture.js); - expect(output.css).to.equal(fixture.css); - }); -}); diff --git a/packages/pigment-react/tests/styled/styled.test.ts b/packages/pigment-react/tests/styled/styled.test.ts deleted file mode 100644 index 4827244fa29ca9..00000000000000 --- a/packages/pigment-react/tests/styled/styled.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import path from 'node:path'; -import { runTransformation, expect } from '../testUtils'; - -const theme = { - palette: { - primary: { - main: 'red', - }, - }, - size: { - font: { - h1: '3rem', - }, - }, - components: { - MuiSlider: { - styleOverrides: { - rail: { - fontSize: '3rem', - }, - }, - }, - }, -}; - -describe('Pigment CSS - styled', () => { - it('basics', async () => { - const { output, fixture } = await runTransformation( - path.join(__dirname, 'fixtures/styled.input.js'), - { - themeArgs: { - theme, - }, - }, - ); - - expect(output.js).to.equal(fixture.js); - expect(output.css).to.equal(fixture.css); - }); -}); diff --git a/packages/pigment-react/utils/chunk-RY3PR5BX.js b/packages/pigment-react/utils/chunk-RY3PR5BX.js new file mode 100644 index 00000000000000..e75ed100532348 --- /dev/null +++ b/packages/pigment-react/utils/chunk-RY3PR5BX.js @@ -0,0 +1,49 @@ +'use strict'; + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var __defProp = Object.defineProperty; +var __defProps = Object.defineProperties; +var __getOwnPropDescs = Object.getOwnPropertyDescriptors; +var __getOwnPropSymbols = Object.getOwnPropertySymbols; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __propIsEnum = Object.prototype.propertyIsEnumerable; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __spreadValues = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + if (__getOwnPropSymbols) + for (var prop of __getOwnPropSymbols(b)) { + if (__propIsEnum.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + } + return a; +}; +var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); +var __restKey = (key) => typeof key === "symbol" ? key : key + ""; +var __objRest = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols) + for (var prop of __getOwnPropSymbols(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) + target[prop] = source[prop]; + } + return target; +}; + +exports.__objRest = __objRest; +exports.__restKey = __restKey; +exports.__spreadProps = __spreadProps; +exports.__spreadValues = __spreadValues; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=chunk-RY3PR5BX.js.map \ No newline at end of file diff --git a/packages/pigment-react/utils/chunk-RY3PR5BX.js.map b/packages/pigment-react/utils/chunk-RY3PR5BX.js.map new file mode 100644 index 00000000000000..9ffd4b22fced46 --- /dev/null +++ b/packages/pigment-react/utils/chunk-RY3PR5BX.js.map @@ -0,0 +1 @@ +{"version":3,"sources":[],"names":[],"mappings":"","sourcesContent":[]} \ No newline at end of file diff --git a/packages/pigment-react/utils/chunk-XZYQOGJP.mjs b/packages/pigment-react/utils/chunk-XZYQOGJP.mjs new file mode 100644 index 00000000000000..8529fd4c1edca7 --- /dev/null +++ b/packages/pigment-react/utils/chunk-XZYQOGJP.mjs @@ -0,0 +1,44 @@ +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var __defProp = Object.defineProperty; +var __defProps = Object.defineProperties; +var __getOwnPropDescs = Object.getOwnPropertyDescriptors; +var __getOwnPropSymbols = Object.getOwnPropertySymbols; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __propIsEnum = Object.prototype.propertyIsEnumerable; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __spreadValues = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + if (__getOwnPropSymbols) + for (var prop of __getOwnPropSymbols(b)) { + if (__propIsEnum.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + } + return a; +}; +var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); +var __restKey = (key) => typeof key === "symbol" ? key : key + ""; +var __objRest = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols) + for (var prop of __getOwnPropSymbols(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) + target[prop] = source[prop]; + } + return target; +}; + +export { __objRest, __restKey, __spreadProps, __spreadValues }; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=chunk-XZYQOGJP.mjs.map \ No newline at end of file diff --git a/packages/pigment-react/utils/chunk-XZYQOGJP.mjs.map b/packages/pigment-react/utils/chunk-XZYQOGJP.mjs.map new file mode 100644 index 00000000000000..9ffd4b22fced46 --- /dev/null +++ b/packages/pigment-react/utils/chunk-XZYQOGJP.mjs.map @@ -0,0 +1 @@ +{"version":3,"sources":[],"names":[],"mappings":"","sourcesContent":[]} \ No newline at end of file diff --git a/packages/pigment-react/utils/index.d.mts b/packages/pigment-react/utils/index.d.mts new file mode 100644 index 00000000000000..f70ee885cd18a5 --- /dev/null +++ b/packages/pigment-react/utils/index.d.mts @@ -0,0 +1,121 @@ +import { SxConfig } from '@mui/system/styleFunctionSx'; +import * as CSS from 'csstype'; + +type CSSProperties = CSS.PropertiesFallback<number | string>; + +type CSSPropertiesWithCallback<Props extends object> = { + [K in keyof CSSProperties]: + | CSSProperties[K] + | Array<Extract<CSSProperties[K], string>> + | ((props: Props) => CSSProperties[K]); +}; + +type CSSPseudos<Props extends object> = { + [K in CSS.Pseudos]?: CSSObject<Props>; +}; + +interface CSSOthersObject<Props extends object> { + [selector: string]: CSSObject<Props>; +} + +type CSSObject<Props extends object> = + | CSSPropertiesWithCallback<Props> + | CSSPseudos<Props> + | CSSOthersObject<Props>; + +interface ThemeInput<ColorScheme extends string = string> { + /** + * The prefix to be used for the CSS variables. + */ + cssVarPrefix?: string; + /** + * The color schemes to be used for the theme. + */ + colorSchemes?: Record<ColorScheme, any>; + /** + * The default color scheme to be used for the theme. It must be one of the keys from `theme.colorSchemes`. + * Required when `colorSchemes` is provided. + * @default 'light' + */ + defaultColorScheme?: ColorScheme; + /** + * If provided, it will be used to create a selector for the color scheme. + * This is useful if you want to use class or data-* attributes to apply the color scheme. + * + * The default selector is `:root`. + * + * @example + * // class selector + * (colorScheme) => colorScheme ? `.theme-${colorScheme}` : ":root" + * + * @example + * // data-* attribute selector + * (colorScheme) => colorScheme ? `[data-theme="${colorScheme}"`] : ":root" + */ + getSelector?: (colorScheme: ColorScheme | undefined, css: Record<string, any>) => string | Record<string, any>; + /** + * A function to skip generating a CSS variable for a specific path or value. + * + * Note: properties with function as a value are always skipped. + * + * @example + * // skip the `meta.*` fields from generating CSS variables and `theme.vars` + * (keys, value) => keys[0] === 'meta' + * + */ + shouldSkipGeneratingVar?: (objectPathKeys: Array<string>, value: string | number) => boolean; + components?: Partial<Record<string, { + styleOverrides?: Record<string, any>; + defaultProps: Record<string, any>; + }>>; +} +type ExtendTheme<Options extends { + colorScheme: string; + tokens: Record<string, any>; +} = { + colorScheme: string; + tokens: Record<string, any>; +}> = ThemeInput<Options['colorScheme']> & Options['tokens'] & { + vars: Options['tokens']; + applyStyles: (colorScheme: Options['colorScheme'], styles: CSSObject<any>) => Record<string, CSSObject<any>>; + getColorSchemeSelector: (colorScheme: Options['colorScheme']) => string; + generateCssVars: (colorScheme?: Options['colorScheme']) => { + css: Record<string, string | number>; + selector: string | Record<string, any>; + }; + unstable_sxConfig?: SxConfig; +}; +type Theme = ExtendTheme; +/** + * A utility to tell zero-runtime to generate CSS variables for the theme. + */ +declare function extendTheme<Options extends { + colorScheme: string; + tokens: Record<string, any>; +} = { + colorScheme: string; + tokens: Record<string, any>; +}>(theme: ThemeInput): ExtendTheme<{ + colorScheme: Options['colorScheme']; + tokens: Options['tokens']; +}>; + +type PluginCustomOptions = { + /** + * Object to pass as parameter to the styled css callback functions. + */ + themeArgs?: { + theme?: Theme; + }; +}; + +declare function preprocessor(selector: string, cssText: string): string; + +declare function generateTokenCss(theme: Theme): string; +declare function generateThemeTokens(theme: Theme): { + vars?: undefined; +} | { + vars: Record<string, any>; +}; + +export { type ExtendTheme, type PluginCustomOptions, type Theme, type ThemeInput, extendTheme, generateThemeTokens, generateTokenCss, preprocessor }; diff --git a/packages/pigment-react/utils/index.d.ts b/packages/pigment-react/utils/index.d.ts new file mode 100644 index 00000000000000..f70ee885cd18a5 --- /dev/null +++ b/packages/pigment-react/utils/index.d.ts @@ -0,0 +1,121 @@ +import { SxConfig } from '@mui/system/styleFunctionSx'; +import * as CSS from 'csstype'; + +type CSSProperties = CSS.PropertiesFallback<number | string>; + +type CSSPropertiesWithCallback<Props extends object> = { + [K in keyof CSSProperties]: + | CSSProperties[K] + | Array<Extract<CSSProperties[K], string>> + | ((props: Props) => CSSProperties[K]); +}; + +type CSSPseudos<Props extends object> = { + [K in CSS.Pseudos]?: CSSObject<Props>; +}; + +interface CSSOthersObject<Props extends object> { + [selector: string]: CSSObject<Props>; +} + +type CSSObject<Props extends object> = + | CSSPropertiesWithCallback<Props> + | CSSPseudos<Props> + | CSSOthersObject<Props>; + +interface ThemeInput<ColorScheme extends string = string> { + /** + * The prefix to be used for the CSS variables. + */ + cssVarPrefix?: string; + /** + * The color schemes to be used for the theme. + */ + colorSchemes?: Record<ColorScheme, any>; + /** + * The default color scheme to be used for the theme. It must be one of the keys from `theme.colorSchemes`. + * Required when `colorSchemes` is provided. + * @default 'light' + */ + defaultColorScheme?: ColorScheme; + /** + * If provided, it will be used to create a selector for the color scheme. + * This is useful if you want to use class or data-* attributes to apply the color scheme. + * + * The default selector is `:root`. + * + * @example + * // class selector + * (colorScheme) => colorScheme ? `.theme-${colorScheme}` : ":root" + * + * @example + * // data-* attribute selector + * (colorScheme) => colorScheme ? `[data-theme="${colorScheme}"`] : ":root" + */ + getSelector?: (colorScheme: ColorScheme | undefined, css: Record<string, any>) => string | Record<string, any>; + /** + * A function to skip generating a CSS variable for a specific path or value. + * + * Note: properties with function as a value are always skipped. + * + * @example + * // skip the `meta.*` fields from generating CSS variables and `theme.vars` + * (keys, value) => keys[0] === 'meta' + * + */ + shouldSkipGeneratingVar?: (objectPathKeys: Array<string>, value: string | number) => boolean; + components?: Partial<Record<string, { + styleOverrides?: Record<string, any>; + defaultProps: Record<string, any>; + }>>; +} +type ExtendTheme<Options extends { + colorScheme: string; + tokens: Record<string, any>; +} = { + colorScheme: string; + tokens: Record<string, any>; +}> = ThemeInput<Options['colorScheme']> & Options['tokens'] & { + vars: Options['tokens']; + applyStyles: (colorScheme: Options['colorScheme'], styles: CSSObject<any>) => Record<string, CSSObject<any>>; + getColorSchemeSelector: (colorScheme: Options['colorScheme']) => string; + generateCssVars: (colorScheme?: Options['colorScheme']) => { + css: Record<string, string | number>; + selector: string | Record<string, any>; + }; + unstable_sxConfig?: SxConfig; +}; +type Theme = ExtendTheme; +/** + * A utility to tell zero-runtime to generate CSS variables for the theme. + */ +declare function extendTheme<Options extends { + colorScheme: string; + tokens: Record<string, any>; +} = { + colorScheme: string; + tokens: Record<string, any>; +}>(theme: ThemeInput): ExtendTheme<{ + colorScheme: Options['colorScheme']; + tokens: Options['tokens']; +}>; + +type PluginCustomOptions = { + /** + * Object to pass as parameter to the styled css callback functions. + */ + themeArgs?: { + theme?: Theme; + }; +}; + +declare function preprocessor(selector: string, cssText: string): string; + +declare function generateTokenCss(theme: Theme): string; +declare function generateThemeTokens(theme: Theme): { + vars?: undefined; +} | { + vars: Record<string, any>; +}; + +export { type ExtendTheme, type PluginCustomOptions, type Theme, type ThemeInput, extendTheme, generateThemeTokens, generateTokenCss, preprocessor }; diff --git a/packages/pigment-react/utils/index.js b/packages/pigment-react/utils/index.js new file mode 100644 index 00000000000000..a13d9aa507b542 --- /dev/null +++ b/packages/pigment-react/utils/index.js @@ -0,0 +1,148 @@ +'use strict'; + +var chunkRY3PR5BX_js = require('./chunk-RY3PR5BX.js'); +var stylis$1 = require('stylis'); +var serialize = require('@emotion/serialize'); +var deepMerge = require('lodash/merge'); +var cssVars = require('@mui/system/cssVars'); + +function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } + +var deepMerge__default = /*#__PURE__*/_interopDefault(deepMerge); + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +function globalSelector(element) { + switch (element.type) { + case "rule": + element.props = element.props.map((value) => { + if (value.match(/(:where|:is)\(/)) { + value = value.replace(/\.[^:]+(:where|:is)/, "$1"); + return value; + } + return value; + }); + break; + } +} +var serializer = stylis$1.middleware([globalSelector, stylis$1.stringify]); +var stylis = (css) => stylis$1.serialize(stylis$1.compile(css), serializer); +function preprocessor(selector, cssText) { + if (cssText.startsWith("@keyframes")) { + return stylis(cssText.replace("@keyframes", `@keyframes ${selector}`)); + } + return stylis(`${selector}{${cssText}}`); +} +function generateTokenCss(theme) { + var _a; + const { css: rootCss, selector: rootSelector } = theme.generateCssVars(); + const stylesheets = []; + if (Object.keys(rootCss).length) { + stylesheets.push(typeof rootSelector === "string" ? { [rootSelector]: rootCss } : rootSelector); + } + if (theme.colorSchemes) { + const _b = theme.colorSchemes, { [_a = theme.defaultColorScheme]: defaultScheme } = _b, otherColorSchemes = chunkRY3PR5BX_js.__objRest(_b, [chunkRY3PR5BX_js.__restKey(_a)]); + if (defaultScheme) { + const { css, selector } = theme.generateCssVars(theme.defaultColorScheme); + if (Object.keys(css).length) { + stylesheets.push(typeof selector === "string" ? { [selector]: css } : selector); + } + } + Object.entries(otherColorSchemes).forEach(([key]) => { + const { css, selector } = theme.generateCssVars(key); + if (Object.keys(css).length) { + stylesheets.push(typeof selector === "string" ? { [selector]: css } : selector); + } + }); + } + const { styles } = serialize.serializeStyles(stylesheets); + return styles; +} +function generateThemeTokens(theme) { + if (!theme || typeof theme !== "object") { + return {}; + } + if ("vars" in theme && theme.vars) { + return { + vars: theme.vars + }; + } + return {}; +} +function extendTheme(theme) { + const _a = theme, { + cssVarPrefix, + shouldSkipGeneratingVar, + getSelector = defaultGetSelector, + defaultColorScheme = "light" + } = _a, otherTheme = chunkRY3PR5BX_js.__objRest(_a, [ + "cssVarPrefix", + "shouldSkipGeneratingVar", + "getSelector", + "defaultColorScheme" + ]); + function defaultGetSelector(colorScheme, css) { + if (colorScheme === "light" && defaultColorScheme !== "light") { + return { + "@media (prefers-color-scheme: light)": { + ":root": css + } + }; + } + if (colorScheme === "dark" && defaultColorScheme !== "dark") { + return { + "@media (prefers-color-scheme: dark)": { + ":root": css + } + }; + } + return ":root"; + } + if (theme.colorSchemes && (!defaultColorScheme || !Object.keys(theme.colorSchemes).includes(defaultColorScheme))) { + throw new Error( + `Zero: \`defaultColorScheme\` must be one of ${JSON.stringify( + theme.colorSchemes + )}, but got "\`${theme.defaultColorScheme}\`".` + ); + } + const parserConfig = { + prefix: cssVarPrefix, + shouldSkipGeneratingVar, + getSelector + }; + const { generateCssVars } = cssVars.prepareCssVars(otherTheme, parserConfig); + let { vars } = generateCssVars(); + Object.entries(theme.colorSchemes || {}).forEach(([key]) => { + vars = deepMerge__default.default(vars, generateCssVars(key).vars); + }); + const finalTheme = chunkRY3PR5BX_js.__spreadProps(chunkRY3PR5BX_js.__spreadValues({}, theme), { + defaultColorScheme, + vars, + generateCssVars + }); + finalTheme.getColorSchemeSelector = (colorScheme) => { + if (!theme.getSelector) { + return `@media (prefers-color-scheme: ${colorScheme})`; + } + return `:where(${theme.getSelector(colorScheme, {})}) &`; + }; + finalTheme.applyStyles = function applyStyles(colorScheme, styles) { + return { + [this.getColorSchemeSelector(colorScheme)]: styles + }; + }; + return finalTheme; +} + +exports.extendTheme = extendTheme; +exports.generateThemeTokens = generateThemeTokens; +exports.generateTokenCss = generateTokenCss; +exports.preprocessor = preprocessor; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/pigment-react/utils/index.js.map b/packages/pigment-react/utils/index.js.map new file mode 100644 index 00000000000000..6f3efe7a2ec587 --- /dev/null +++ b/packages/pigment-react/utils/index.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/utils/preprocessor.ts","../src/utils/generateCss.ts","../src/utils/extendTheme.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Element } from 'stylis';\nimport { serialize, compile, stringify, middleware } from 'stylis';\n\nfunction globalSelector(element: Element) {\n switch (element.type) {\n case 'rule':\n element.props = (element.props as string[]).map((value) => {\n if (value.match(/(:where|:is)\\(/)) {\n value = value.replace(/\\.[^:]+(:where|:is)/, '$1');\n return value;\n }\n return value;\n });\n break;\n default:\n break;\n }\n}\n\nconst serializer = middleware([globalSelector, stringify]);\n\nconst stylis = (css: string) => serialize(compile(css), serializer);\n\nexport function preprocessor(selector: string, cssText: string) {\n if (cssText.startsWith('@keyframes')) {\n return stylis(cssText.replace('@keyframes', `@keyframes ${selector}`));\n }\n return stylis(`${selector}{${cssText}}`);\n}\n","import { serializeStyles } from '@emotion/serialize';\nimport { Theme } from './extendTheme';\n\nexport function generateTokenCss(theme: Theme) {\n // create stylesheet as object\n const { css: rootCss, selector: rootSelector } = theme.generateCssVars();\n const stylesheets: Array<Record<string, any>> = [];\n if (Object.keys(rootCss).length) {\n stylesheets.push(typeof rootSelector === 'string' ? { [rootSelector]: rootCss } : rootSelector);\n }\n if (theme.colorSchemes) {\n const { [theme.defaultColorScheme!]: defaultScheme, ...otherColorSchemes } = theme.colorSchemes;\n\n if (defaultScheme) {\n // need to generate default color scheme first for the prefers-color-scheme media query to work\n // because media-queries does not increase specificity\n const { css, selector } = theme.generateCssVars(theme.defaultColorScheme);\n if (Object.keys(css).length) {\n stylesheets.push(typeof selector === 'string' ? { [selector]: css } : selector);\n }\n }\n\n Object.entries(otherColorSchemes).forEach(([key]) => {\n const { css, selector } = theme.generateCssVars(key);\n if (Object.keys(css).length) {\n stylesheets.push(typeof selector === 'string' ? { [selector]: css } : selector);\n }\n });\n }\n\n // use emotion to serialize the object to css string\n const { styles } = serializeStyles(stylesheets);\n return styles;\n}\n\nexport function generateThemeTokens(theme: Theme) {\n if (!theme || typeof theme !== 'object') {\n return {};\n }\n // is created using extendTheme\n if ('vars' in theme && theme.vars) {\n return {\n vars: theme.vars,\n };\n }\n return {};\n}\n","import deepMerge from 'lodash/merge';\nimport { prepareCssVars } from '@mui/system/cssVars';\nimport type { SxConfig } from '@mui/system/styleFunctionSx';\nimport type { CSSObject } from '../base';\n\nexport interface ThemeInput<ColorScheme extends string = string> {\n /**\n * The prefix to be used for the CSS variables.\n */\n cssVarPrefix?: string;\n /**\n * The color schemes to be used for the theme.\n */\n colorSchemes?: Record<ColorScheme, any>;\n /**\n * The default color scheme to be used for the theme. It must be one of the keys from `theme.colorSchemes`.\n * Required when `colorSchemes` is provided.\n * @default 'light'\n */\n defaultColorScheme?: ColorScheme;\n /**\n * If provided, it will be used to create a selector for the color scheme.\n * This is useful if you want to use class or data-* attributes to apply the color scheme.\n *\n * The default selector is `:root`.\n *\n * @example\n * // class selector\n * (colorScheme) => colorScheme ? `.theme-${colorScheme}` : \":root\"\n *\n * @example\n * // data-* attribute selector\n * (colorScheme) => colorScheme ? `[data-theme=\"${colorScheme}\"`] : \":root\"\n */\n getSelector?: (\n colorScheme: ColorScheme | undefined,\n css: Record<string, any>,\n ) => string | Record<string, any>;\n /**\n * A function to skip generating a CSS variable for a specific path or value.\n *\n * Note: properties with function as a value are always skipped.\n *\n * @example\n * // skip the `meta.*` fields from generating CSS variables and `theme.vars`\n * (keys, value) => keys[0] === 'meta'\n *\n */\n shouldSkipGeneratingVar?: (objectPathKeys: Array<string>, value: string | number) => boolean;\n components?: Partial<\n Record<\n string,\n {\n styleOverrides?: Record<string, any>;\n defaultProps: Record<string, any>;\n }\n >\n >;\n}\n\nexport type ExtendTheme<\n Options extends {\n colorScheme: string;\n tokens: Record<string, any>;\n } = {\n colorScheme: string;\n tokens: Record<string, any>;\n },\n> = ThemeInput<Options['colorScheme']> &\n Options['tokens'] & {\n vars: Options['tokens'];\n applyStyles: (\n colorScheme: Options['colorScheme'],\n styles: CSSObject<any>,\n ) => Record<string, CSSObject<any>>;\n getColorSchemeSelector: (colorScheme: Options['colorScheme']) => string;\n generateCssVars: (colorScheme?: Options['colorScheme']) => {\n css: Record<string, string | number>;\n selector: string | Record<string, any>;\n };\n unstable_sxConfig?: SxConfig;\n };\n\nexport type Theme = ExtendTheme;\n\n/**\n * A utility to tell zero-runtime to generate CSS variables for the theme.\n */\nexport function extendTheme<\n Options extends {\n colorScheme: string;\n tokens: Record<string, any>;\n } = {\n colorScheme: string;\n tokens: Record<string, any>;\n },\n>(theme: ThemeInput) {\n const {\n cssVarPrefix,\n shouldSkipGeneratingVar,\n getSelector = defaultGetSelector,\n defaultColorScheme = 'light',\n ...otherTheme\n } = theme;\n\n function defaultGetSelector(\n colorScheme: string | undefined,\n css: Record<string, any>,\n ): string | Record<string, any> {\n if (colorScheme === 'light' && defaultColorScheme !== 'light') {\n return {\n '@media (prefers-color-scheme: light)': {\n ':root': css,\n },\n };\n }\n if (colorScheme === 'dark' && defaultColorScheme !== 'dark') {\n return {\n '@media (prefers-color-scheme: dark)': {\n ':root': css,\n },\n };\n }\n return ':root';\n }\n\n if (\n theme.colorSchemes &&\n (!defaultColorScheme || !Object.keys(theme.colorSchemes).includes(defaultColorScheme))\n ) {\n throw new Error(\n `Zero: \\`defaultColorScheme\\` must be one of ${JSON.stringify(\n theme.colorSchemes,\n )}, but got \"\\`${theme.defaultColorScheme}\\`\".`,\n );\n }\n\n const parserConfig = {\n prefix: cssVarPrefix,\n shouldSkipGeneratingVar,\n getSelector,\n };\n const { generateCssVars } = prepareCssVars(otherTheme, parserConfig);\n\n let { vars } = generateCssVars();\n Object.entries(theme.colorSchemes || {}).forEach(([key]) => {\n vars = deepMerge(vars, generateCssVars(key).vars);\n });\n\n const finalTheme = {\n ...theme,\n defaultColorScheme,\n vars,\n generateCssVars,\n } as unknown as ExtendTheme<{ colorScheme: Options['colorScheme']; tokens: Options['tokens'] }>;\n\n finalTheme.getColorSchemeSelector = (colorScheme: string) => {\n if (!theme.getSelector) {\n return `@media (prefers-color-scheme: ${colorScheme})`;\n }\n return `:where(${theme.getSelector(colorScheme, {})}) &`;\n };\n\n finalTheme.applyStyles = function applyStyles(colorScheme, styles) {\n return {\n [this.getColorSchemeSelector(colorScheme)]: styles,\n };\n };\n\n return finalTheme;\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/utils/index.mjs b/packages/pigment-react/utils/index.mjs new file mode 100644 index 00000000000000..60fa92a4a1f5fa --- /dev/null +++ b/packages/pigment-react/utils/index.mjs @@ -0,0 +1,139 @@ +import { __objRest, __restKey, __spreadProps, __spreadValues } from './chunk-XZYQOGJP.mjs'; +import { middleware, stringify, serialize, compile } from 'stylis'; +import { serializeStyles } from '@emotion/serialize'; +import deepMerge from 'lodash/merge'; +import { prepareCssVars } from '@mui/system/cssVars'; + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +function globalSelector(element) { + switch (element.type) { + case "rule": + element.props = element.props.map((value) => { + if (value.match(/(:where|:is)\(/)) { + value = value.replace(/\.[^:]+(:where|:is)/, "$1"); + return value; + } + return value; + }); + break; + } +} +var serializer = middleware([globalSelector, stringify]); +var stylis = (css) => serialize(compile(css), serializer); +function preprocessor(selector, cssText) { + if (cssText.startsWith("@keyframes")) { + return stylis(cssText.replace("@keyframes", `@keyframes ${selector}`)); + } + return stylis(`${selector}{${cssText}}`); +} +function generateTokenCss(theme) { + var _a; + const { css: rootCss, selector: rootSelector } = theme.generateCssVars(); + const stylesheets = []; + if (Object.keys(rootCss).length) { + stylesheets.push(typeof rootSelector === "string" ? { [rootSelector]: rootCss } : rootSelector); + } + if (theme.colorSchemes) { + const _b = theme.colorSchemes, { [_a = theme.defaultColorScheme]: defaultScheme } = _b, otherColorSchemes = __objRest(_b, [__restKey(_a)]); + if (defaultScheme) { + const { css, selector } = theme.generateCssVars(theme.defaultColorScheme); + if (Object.keys(css).length) { + stylesheets.push(typeof selector === "string" ? { [selector]: css } : selector); + } + } + Object.entries(otherColorSchemes).forEach(([key]) => { + const { css, selector } = theme.generateCssVars(key); + if (Object.keys(css).length) { + stylesheets.push(typeof selector === "string" ? { [selector]: css } : selector); + } + }); + } + const { styles } = serializeStyles(stylesheets); + return styles; +} +function generateThemeTokens(theme) { + if (!theme || typeof theme !== "object") { + return {}; + } + if ("vars" in theme && theme.vars) { + return { + vars: theme.vars + }; + } + return {}; +} +function extendTheme(theme) { + const _a = theme, { + cssVarPrefix, + shouldSkipGeneratingVar, + getSelector = defaultGetSelector, + defaultColorScheme = "light" + } = _a, otherTheme = __objRest(_a, [ + "cssVarPrefix", + "shouldSkipGeneratingVar", + "getSelector", + "defaultColorScheme" + ]); + function defaultGetSelector(colorScheme, css) { + if (colorScheme === "light" && defaultColorScheme !== "light") { + return { + "@media (prefers-color-scheme: light)": { + ":root": css + } + }; + } + if (colorScheme === "dark" && defaultColorScheme !== "dark") { + return { + "@media (prefers-color-scheme: dark)": { + ":root": css + } + }; + } + return ":root"; + } + if (theme.colorSchemes && (!defaultColorScheme || !Object.keys(theme.colorSchemes).includes(defaultColorScheme))) { + throw new Error( + `Zero: \`defaultColorScheme\` must be one of ${JSON.stringify( + theme.colorSchemes + )}, but got "\`${theme.defaultColorScheme}\`".` + ); + } + const parserConfig = { + prefix: cssVarPrefix, + shouldSkipGeneratingVar, + getSelector + }; + const { generateCssVars } = prepareCssVars(otherTheme, parserConfig); + let { vars } = generateCssVars(); + Object.entries(theme.colorSchemes || {}).forEach(([key]) => { + vars = deepMerge(vars, generateCssVars(key).vars); + }); + const finalTheme = __spreadProps(__spreadValues({}, theme), { + defaultColorScheme, + vars, + generateCssVars + }); + finalTheme.getColorSchemeSelector = (colorScheme) => { + if (!theme.getSelector) { + return `@media (prefers-color-scheme: ${colorScheme})`; + } + return `:where(${theme.getSelector(colorScheme, {})}) &`; + }; + finalTheme.applyStyles = function applyStyles(colorScheme, styles) { + return { + [this.getColorSchemeSelector(colorScheme)]: styles + }; + }; + return finalTheme; +} + +export { extendTheme, generateThemeTokens, generateTokenCss, preprocessor }; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=index.mjs.map \ No newline at end of file diff --git a/packages/pigment-react/utils/index.mjs.map b/packages/pigment-react/utils/index.mjs.map new file mode 100644 index 00000000000000..6f3efe7a2ec587 --- /dev/null +++ b/packages/pigment-react/utils/index.mjs.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/utils/preprocessor.ts","../src/utils/generateCss.ts","../src/utils/extendTheme.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Element } from 'stylis';\nimport { serialize, compile, stringify, middleware } from 'stylis';\n\nfunction globalSelector(element: Element) {\n switch (element.type) {\n case 'rule':\n element.props = (element.props as string[]).map((value) => {\n if (value.match(/(:where|:is)\\(/)) {\n value = value.replace(/\\.[^:]+(:where|:is)/, '$1');\n return value;\n }\n return value;\n });\n break;\n default:\n break;\n }\n}\n\nconst serializer = middleware([globalSelector, stringify]);\n\nconst stylis = (css: string) => serialize(compile(css), serializer);\n\nexport function preprocessor(selector: string, cssText: string) {\n if (cssText.startsWith('@keyframes')) {\n return stylis(cssText.replace('@keyframes', `@keyframes ${selector}`));\n }\n return stylis(`${selector}{${cssText}}`);\n}\n","import { serializeStyles } from '@emotion/serialize';\nimport { Theme } from './extendTheme';\n\nexport function generateTokenCss(theme: Theme) {\n // create stylesheet as object\n const { css: rootCss, selector: rootSelector } = theme.generateCssVars();\n const stylesheets: Array<Record<string, any>> = [];\n if (Object.keys(rootCss).length) {\n stylesheets.push(typeof rootSelector === 'string' ? { [rootSelector]: rootCss } : rootSelector);\n }\n if (theme.colorSchemes) {\n const { [theme.defaultColorScheme!]: defaultScheme, ...otherColorSchemes } = theme.colorSchemes;\n\n if (defaultScheme) {\n // need to generate default color scheme first for the prefers-color-scheme media query to work\n // because media-queries does not increase specificity\n const { css, selector } = theme.generateCssVars(theme.defaultColorScheme);\n if (Object.keys(css).length) {\n stylesheets.push(typeof selector === 'string' ? { [selector]: css } : selector);\n }\n }\n\n Object.entries(otherColorSchemes).forEach(([key]) => {\n const { css, selector } = theme.generateCssVars(key);\n if (Object.keys(css).length) {\n stylesheets.push(typeof selector === 'string' ? { [selector]: css } : selector);\n }\n });\n }\n\n // use emotion to serialize the object to css string\n const { styles } = serializeStyles(stylesheets);\n return styles;\n}\n\nexport function generateThemeTokens(theme: Theme) {\n if (!theme || typeof theme !== 'object') {\n return {};\n }\n // is created using extendTheme\n if ('vars' in theme && theme.vars) {\n return {\n vars: theme.vars,\n };\n }\n return {};\n}\n","import deepMerge from 'lodash/merge';\nimport { prepareCssVars } from '@mui/system/cssVars';\nimport type { SxConfig } from '@mui/system/styleFunctionSx';\nimport type { CSSObject } from '../base';\n\nexport interface ThemeInput<ColorScheme extends string = string> {\n /**\n * The prefix to be used for the CSS variables.\n */\n cssVarPrefix?: string;\n /**\n * The color schemes to be used for the theme.\n */\n colorSchemes?: Record<ColorScheme, any>;\n /**\n * The default color scheme to be used for the theme. It must be one of the keys from `theme.colorSchemes`.\n * Required when `colorSchemes` is provided.\n * @default 'light'\n */\n defaultColorScheme?: ColorScheme;\n /**\n * If provided, it will be used to create a selector for the color scheme.\n * This is useful if you want to use class or data-* attributes to apply the color scheme.\n *\n * The default selector is `:root`.\n *\n * @example\n * // class selector\n * (colorScheme) => colorScheme ? `.theme-${colorScheme}` : \":root\"\n *\n * @example\n * // data-* attribute selector\n * (colorScheme) => colorScheme ? `[data-theme=\"${colorScheme}\"`] : \":root\"\n */\n getSelector?: (\n colorScheme: ColorScheme | undefined,\n css: Record<string, any>,\n ) => string | Record<string, any>;\n /**\n * A function to skip generating a CSS variable for a specific path or value.\n *\n * Note: properties with function as a value are always skipped.\n *\n * @example\n * // skip the `meta.*` fields from generating CSS variables and `theme.vars`\n * (keys, value) => keys[0] === 'meta'\n *\n */\n shouldSkipGeneratingVar?: (objectPathKeys: Array<string>, value: string | number) => boolean;\n components?: Partial<\n Record<\n string,\n {\n styleOverrides?: Record<string, any>;\n defaultProps: Record<string, any>;\n }\n >\n >;\n}\n\nexport type ExtendTheme<\n Options extends {\n colorScheme: string;\n tokens: Record<string, any>;\n } = {\n colorScheme: string;\n tokens: Record<string, any>;\n },\n> = ThemeInput<Options['colorScheme']> &\n Options['tokens'] & {\n vars: Options['tokens'];\n applyStyles: (\n colorScheme: Options['colorScheme'],\n styles: CSSObject<any>,\n ) => Record<string, CSSObject<any>>;\n getColorSchemeSelector: (colorScheme: Options['colorScheme']) => string;\n generateCssVars: (colorScheme?: Options['colorScheme']) => {\n css: Record<string, string | number>;\n selector: string | Record<string, any>;\n };\n unstable_sxConfig?: SxConfig;\n };\n\nexport type Theme = ExtendTheme;\n\n/**\n * A utility to tell zero-runtime to generate CSS variables for the theme.\n */\nexport function extendTheme<\n Options extends {\n colorScheme: string;\n tokens: Record<string, any>;\n } = {\n colorScheme: string;\n tokens: Record<string, any>;\n },\n>(theme: ThemeInput) {\n const {\n cssVarPrefix,\n shouldSkipGeneratingVar,\n getSelector = defaultGetSelector,\n defaultColorScheme = 'light',\n ...otherTheme\n } = theme;\n\n function defaultGetSelector(\n colorScheme: string | undefined,\n css: Record<string, any>,\n ): string | Record<string, any> {\n if (colorScheme === 'light' && defaultColorScheme !== 'light') {\n return {\n '@media (prefers-color-scheme: light)': {\n ':root': css,\n },\n };\n }\n if (colorScheme === 'dark' && defaultColorScheme !== 'dark') {\n return {\n '@media (prefers-color-scheme: dark)': {\n ':root': css,\n },\n };\n }\n return ':root';\n }\n\n if (\n theme.colorSchemes &&\n (!defaultColorScheme || !Object.keys(theme.colorSchemes).includes(defaultColorScheme))\n ) {\n throw new Error(\n `Zero: \\`defaultColorScheme\\` must be one of ${JSON.stringify(\n theme.colorSchemes,\n )}, but got \"\\`${theme.defaultColorScheme}\\`\".`,\n );\n }\n\n const parserConfig = {\n prefix: cssVarPrefix,\n shouldSkipGeneratingVar,\n getSelector,\n };\n const { generateCssVars } = prepareCssVars(otherTheme, parserConfig);\n\n let { vars } = generateCssVars();\n Object.entries(theme.colorSchemes || {}).forEach(([key]) => {\n vars = deepMerge(vars, generateCssVars(key).vars);\n });\n\n const finalTheme = {\n ...theme,\n defaultColorScheme,\n vars,\n generateCssVars,\n } as unknown as ExtendTheme<{ colorScheme: Options['colorScheme']; tokens: Options['tokens'] }>;\n\n finalTheme.getColorSchemeSelector = (colorScheme: string) => {\n if (!theme.getSelector) {\n return `@media (prefers-color-scheme: ${colorScheme})`;\n }\n return `:where(${theme.getSelector(colorScheme, {})}) &`;\n };\n\n finalTheme.applyStyles = function applyStyles(colorScheme, styles) {\n return {\n [this.getColorSchemeSelector(colorScheme)]: styles,\n };\n };\n\n return finalTheme;\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/utils/pre-linaria-plugin.d.mts b/packages/pigment-react/utils/pre-linaria-plugin.d.mts new file mode 100644 index 00000000000000..3aef2f5c117359 --- /dev/null +++ b/packages/pigment-react/utils/pre-linaria-plugin.d.mts @@ -0,0 +1,8 @@ +import * as _babel_core from '@babel/core'; + +declare const babelPlugin: (api: object, options: { + propName?: string | undefined; + importName?: string | undefined; +} | null | undefined, dirname: string) => _babel_core.PluginObj<_babel_core.PluginPass>; + +export { babelPlugin }; diff --git a/packages/pigment-react/utils/pre-linaria-plugin.d.ts b/packages/pigment-react/utils/pre-linaria-plugin.d.ts new file mode 100644 index 00000000000000..3aef2f5c117359 --- /dev/null +++ b/packages/pigment-react/utils/pre-linaria-plugin.d.ts @@ -0,0 +1,8 @@ +import * as _babel_core from '@babel/core'; + +declare const babelPlugin: (api: object, options: { + propName?: string | undefined; + importName?: string | undefined; +} | null | undefined, dirname: string) => _babel_core.PluginObj<_babel_core.PluginPass>; + +export { babelPlugin }; diff --git a/packages/pigment-react/utils/pre-linaria-plugin.js b/packages/pigment-react/utils/pre-linaria-plugin.js new file mode 100644 index 00000000000000..05a2788a656abf --- /dev/null +++ b/packages/pigment-react/utils/pre-linaria-plugin.js @@ -0,0 +1,260 @@ +'use strict'; + +require('./chunk-RY3PR5BX.js'); +var helperModuleImports = require('@babel/helper-module-imports'); +var helperPluginUtils = require('@babel/helper-plugin-utils'); +var types = require('@babel/types'); +var transform = require('@wyw-in-js/transform'); + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + + +// src/utils/checkStaticObjectOrArray.ts +function isStaticObjectExpression(nodePath) { + const properties = nodePath.get("properties"); + return properties.every((property) => { + if (!property.isObjectProperty()) { + return false; + } + const key = property.get("key"); + const value = property.get("value"); + return key.isIdentifier() && value.isLiteral() || value.isObjectExpression() && isStaticObjectExpression(value); + }); +} +function isStaticObjectOrArrayExpression(nodePath) { + if (nodePath.isArrayExpression()) { + const elements = nodePath.get("elements"); + return elements.every((item) => { + if (item.isLiteral()) { + return true; + } + if (item.isObjectExpression()) { + return isStaticObjectExpression(item); + } + if (item.isArrayExpression()) { + return isStaticObjectOrArrayExpression(nodePath); + } + return false; + }); + } + if (nodePath.isObjectExpression()) { + return isStaticObjectExpression(nodePath); + } + return false; +} + +// src/utils/sxObjectExtractor.ts +function validateObjectKey(keyPath, parentCall) { + const rootScope = keyPath.scope.getProgramParent(); + if (keyPath.isIdentifier()) { + return; + } + const identifiers = transform.findIdentifiers([keyPath]); + if (!identifiers.length) { + return; + } + if (!parentCall) { + throw keyPath.buildCodeFrameError( + `${"@pigment-css/react"}: Expressions in css object keys are not supported.` + ); + } + if (!identifiers.every((item) => { + const binding = item.scope.getBinding(item.node.name); + if (!binding) { + return false; + } + if (binding.path.findParent((parent) => parent === parentCall) || binding.path.scope === rootScope) { + return true; + } + return false; + })) { + throw keyPath.buildCodeFrameError( + `${"@pigment-css/react"}: Variables in css object keys should only use the passed theme(s) object or variables that are defined in the root scope.` + ); + } +} +function traverseObjectExpression(nodePath, parentCall) { + const rootScope = nodePath.scope.getProgramParent(); + const properties = nodePath.get("properties"); + properties.forEach((property) => { + if (property.isObjectProperty()) { + validateObjectKey(property.get("key"), parentCall); + const value = property.get("value"); + if (!value.isExpression()) { + throw value.buildCodeFrameError( + `${"@pigment-css/react"}: This value is not supported. It can only be static values or local variables.` + ); + } + if (value.isObjectExpression()) { + traverseObjectExpression(value, parentCall); + } else if (value.isArrowFunctionExpression()) { + throw value.buildCodeFrameError( + `${"@pigment-css/react"}: Arrow functions are not supported as values of sx object.` + ); + } else if (!value.isLiteral() && !isStaticObjectOrArrayExpression(value)) { + const identifiers = transform.findIdentifiers([value], "reference"); + const localIdentifiers = []; + identifiers.forEach((id) => { + if (!id.isIdentifier()) { + return; + } + const binding = id.scope.getBinding(id.node.name); + if (!binding) { + return; + } + if (binding.path.findParent((parent) => parent === parentCall)) ; else if (binding.scope !== rootScope) { + localIdentifiers.push(id); + } else { + throw id.buildCodeFrameError( + `${"@pigment-css/react"}: Consider moving this variable to the root scope if it has all static values.` + ); + } + }); + if (localIdentifiers.length) { + const arrowFn = types.arrowFunctionExpression( + localIdentifiers.map((i) => i.node), + types.cloneNode(value.node) + ); + value.replaceWith(arrowFn); + } + } + } else if (property.isSpreadElement()) { + const identifiers = transform.findIdentifiers([property.get("argument")]); + if (!identifiers.every((id) => { + const binding = property.scope.getBinding(id.node.name); + return binding && (binding.scope === rootScope || binding.scope === property.scope); + })) { + throw property.buildCodeFrameError( + `${"@pigment-css/react"}: You can only use variables in the spread that are defined in the root scope of the file.` + ); + } + } else if (property.isObjectMethod()) { + throw property.buildCodeFrameError( + `${"@pigment-css/react"}: sx prop object does not support ObjectMethods.` + ); + } else { + throw property.buildCodeFrameError( + `${"@pigment-css/react"}: Unknown property in object.` + ); + } + }); +} +function sxObjectExtractor(nodePath) { + if (nodePath.isObjectExpression()) { + traverseObjectExpression(nodePath); + } else if (nodePath.isArrowFunctionExpression()) { + const body = nodePath.get("body"); + if (!body.isObjectExpression()) { + throw body.buildCodeFrameError( + `${"@pigment-css/react"}: sx prop only supports arrow functions directly returning an object, e.g. () => ({color: 'red'}). You can accept theme object in the params if required.` + ); + } + traverseObjectExpression(body, nodePath); + } +} + +// src/utils/sxPropConverter.ts +function isAllowedExpression(node) { + return node.isObjectExpression() || node.isArrowFunctionExpression(); +} +function sxPropConverter(node, wrapWithSxCall) { + if (node.isConditionalExpression()) { + const consequent = node.get("consequent"); + const alternate = node.get("alternate"); + if (isAllowedExpression(consequent)) { + sxObjectExtractor(consequent); + wrapWithSxCall(consequent); + } + if (isAllowedExpression(alternate)) { + sxObjectExtractor(alternate); + wrapWithSxCall(alternate); + } + } else if (node.isLogicalExpression()) { + const right = node.get("right"); + if (isAllowedExpression(right)) { + sxObjectExtractor(right); + wrapWithSxCall(right); + } + } else if (isAllowedExpression(node)) { + sxObjectExtractor(node); + wrapWithSxCall(node); + } else if (node.isIdentifier()) { + const rootScope = node.scope.getProgramParent(); + const binding = node.scope.getBinding(node.node.name); + if ((binding == null ? void 0 : binding.scope) === rootScope) { + wrapWithSxCall(node); + } + } +} + +// src/utils/pre-linaria-plugin.ts +function replaceNodePath(expressionPath, namePath, importName, t, tagName) { + const sxIdentifier = helperModuleImports.addNamed(namePath, importName, "@pigment-css/react"); + const wrapWithSxCall = (expPath) => { + expPath.replaceWith( + t.callExpression(sxIdentifier, [expPath.node, t.identifier(tagName.node.name)]) + ); + }; + sxPropConverter(expressionPath, wrapWithSxCall); +} +var babelPlugin = helperPluginUtils.declare( + (api, { propName = "sx", importName = "sx" }) => { + api.assertVersion(7); + const { types: t } = api; + return { + name: "@pigmentcss/zero-babel-plugin", + visitor: { + JSXAttribute(path) { + const namePath = path.get("name"); + const openingElement = path.findParent((p) => p.isJSXOpeningElement()); + if (!openingElement || !openingElement.isJSXOpeningElement() || !namePath.isJSXIdentifier() || namePath.node.name !== propName) { + return; + } + const tagName = openingElement.get("name"); + if (!tagName.isJSXIdentifier()) { + return; + } + const valuePath = path.get("value"); + if (!valuePath.isJSXExpressionContainer()) { + return; + } + const expressionPath = valuePath.get("expression"); + if (!expressionPath.isExpression()) { + return; + } + replaceNodePath(expressionPath, namePath, importName, t, tagName); + }, + ObjectProperty(path) { + const keyPath = path.get("key"); + if (!keyPath.isIdentifier() || keyPath.node.name !== propName) { + return; + } + const valuePath = path.get("value"); + if (!valuePath.isObjectExpression() && !valuePath.isArrowFunctionExpression()) { + return; + } + const parentJsxCall = path.findParent((p) => p.isCallExpression()); + if (!parentJsxCall || !parentJsxCall.isCallExpression()) { + return; + } + const callee = parentJsxCall.get("callee"); + if (!callee.isIdentifier() || !callee.node.name.includes("jsx")) { + return; + } + const jsxElement = parentJsxCall.get("arguments")[0]; + replaceNodePath(valuePath, keyPath, importName, t, jsxElement); + } + } + }; + } +); + +exports.babelPlugin = babelPlugin; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=pre-linaria-plugin.js.map \ No newline at end of file diff --git a/packages/pigment-react/utils/pre-linaria-plugin.js.map b/packages/pigment-react/utils/pre-linaria-plugin.js.map new file mode 100644 index 00000000000000..18cd057d0006bc --- /dev/null +++ b/packages/pigment-react/utils/pre-linaria-plugin.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/utils/pre-linaria-plugin.ts","../src/utils/sxObjectExtractor.ts","../src/utils/checkStaticObjectOrArray.ts","../src/utils/sxPropConverter.ts"],"names":[],"mappings":"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","sourcesContent":["import { addNamed } from '@babel/helper-module-imports';\nimport { declare } from '@babel/helper-plugin-utils';\nimport { NodePath } from '@babel/core';\nimport * as Types from '@babel/types';\nimport { sxPropConverter } from './sxPropConverter';\n\nfunction replaceNodePath(\n expressionPath: NodePath<Types.Expression>,\n namePath: NodePath<Types.JSXIdentifier | Types.Identifier>,\n importName: string,\n t: typeof Types,\n tagName: NodePath<Types.JSXIdentifier | Types.Identifier>,\n) {\n const sxIdentifier = addNamed(namePath, importName, process.env.PACKAGE_NAME as string);\n\n const wrapWithSxCall = (expPath: NodePath<Types.Expression>) => {\n expPath.replaceWith(\n t.callExpression(sxIdentifier, [expPath.node, t.identifier(tagName.node.name)]),\n );\n };\n\n sxPropConverter(expressionPath, wrapWithSxCall);\n}\n\nexport const babelPlugin = declare<{ propName?: string; importName?: string }>(\n (api, { propName = 'sx', importName = 'sx' }) => {\n api.assertVersion(7);\n const { types: t } = api;\n return {\n name: '@pigmentcss/zero-babel-plugin',\n visitor: {\n JSXAttribute(path) {\n const namePath = path.get('name');\n const openingElement = path.findParent((p) => p.isJSXOpeningElement());\n if (\n !openingElement ||\n !openingElement.isJSXOpeningElement() ||\n !namePath.isJSXIdentifier() ||\n namePath.node.name !== propName\n ) {\n return;\n }\n const tagName = openingElement.get('name');\n if (!tagName.isJSXIdentifier()) {\n return;\n }\n const valuePath = path.get('value');\n if (!valuePath.isJSXExpressionContainer()) {\n return;\n }\n const expressionPath = valuePath.get('expression');\n if (!expressionPath.isExpression()) {\n return;\n }\n replaceNodePath(expressionPath, namePath, importName, t, tagName);\n },\n ObjectProperty(path) {\n // @TODO - Maybe add support for React.createElement calls as well.\n // Right now, it only checks for jsx(),jsxs(),jsxDEV() and jsxsDEV() calls.\n const keyPath = path.get('key');\n if (!keyPath.isIdentifier() || keyPath.node.name !== propName) {\n return;\n }\n const valuePath = path.get('value');\n if (!valuePath.isObjectExpression() && !valuePath.isArrowFunctionExpression()) {\n return;\n }\n const parentJsxCall = path.findParent((p) => p.isCallExpression());\n if (!parentJsxCall || !parentJsxCall.isCallExpression()) {\n return;\n }\n const callee = parentJsxCall.get('callee');\n if (!callee.isIdentifier() || !callee.node.name.includes('jsx')) {\n return;\n }\n const jsxElement = parentJsxCall.get('arguments')[0] as NodePath<Types.Identifier>;\n replaceNodePath(valuePath, keyPath, importName, t, jsxElement);\n },\n },\n };\n },\n);\n","import type { NodePath } from '@babel/core';\nimport { arrowFunctionExpression, cloneNode } from '@babel/types';\nimport type {\n ArrowFunctionExpression,\n Expression,\n Identifier,\n ObjectExpression,\n PrivateName,\n} from '@babel/types';\nimport { findIdentifiers } from '@wyw-in-js/transform';\nimport { isStaticObjectOrArrayExpression } from './checkStaticObjectOrArray';\n\nfunction validateObjectKey(\n keyPath: NodePath<PrivateName | Expression>,\n parentCall?: NodePath<ArrowFunctionExpression>,\n) {\n const rootScope = keyPath.scope.getProgramParent();\n if (keyPath.isIdentifier()) {\n return;\n }\n const identifiers = findIdentifiers([keyPath]);\n if (!identifiers.length) {\n return;\n }\n if (!parentCall) {\n throw keyPath.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: Expressions in css object keys are not supported.`,\n );\n }\n if (\n !identifiers.every((item) => {\n const binding = item.scope.getBinding(item.node.name);\n if (!binding) {\n return false;\n }\n if (\n binding.path.findParent((parent) => parent === parentCall) ||\n binding.path.scope === rootScope\n ) {\n return true;\n }\n return false;\n })\n ) {\n throw keyPath.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: Variables in css object keys should only use the passed theme(s) object or variables that are defined in the root scope.`,\n );\n }\n}\n\nfunction traverseObjectExpression(\n nodePath: NodePath<ObjectExpression>,\n parentCall?: NodePath<ArrowFunctionExpression>,\n) {\n const rootScope = nodePath.scope.getProgramParent();\n const properties = nodePath.get('properties');\n properties.forEach((property) => {\n if (property.isObjectProperty()) {\n validateObjectKey(property.get('key'), parentCall);\n\n const value = property.get('value');\n if (!value.isExpression()) {\n throw value.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: This value is not supported. It can only be static values or local variables.`,\n );\n }\n if (value.isObjectExpression()) {\n traverseObjectExpression(value, parentCall);\n } else if (value.isArrowFunctionExpression()) {\n throw value.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: Arrow functions are not supported as values of sx object.`,\n );\n } else if (!value.isLiteral() && !isStaticObjectOrArrayExpression(value)) {\n const identifiers = findIdentifiers([value], 'reference');\n const themeIdentifiers: NodePath<Identifier>[] = [];\n const localIdentifiers: NodePath<Identifier>[] = [];\n identifiers.forEach((id) => {\n if (!id.isIdentifier()) {\n return;\n }\n const binding = id.scope.getBinding(id.node.name);\n if (!binding) {\n return;\n }\n if (binding.path.findParent((parent) => parent === parentCall)) {\n themeIdentifiers.push(id);\n } else if (binding.scope !== rootScope) {\n localIdentifiers.push(id);\n } else {\n throw id.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: Consider moving this variable to the root scope if it has all static values.`,\n );\n }\n });\n if (localIdentifiers.length) {\n const arrowFn = arrowFunctionExpression(\n localIdentifiers.map((i) => i.node),\n cloneNode(value.node),\n );\n value.replaceWith(arrowFn);\n }\n }\n } else if (property.isSpreadElement()) {\n const identifiers = findIdentifiers([property.get('argument')]);\n if (\n !identifiers.every((id) => {\n const binding = property.scope.getBinding(id.node.name);\n // the indentifier definition should either be in the root scope or in the same scope\n // as the object property, ie, ({theme}) => ({...theme.applyStyles()})\n return binding && (binding.scope === rootScope || binding.scope === property.scope);\n })\n ) {\n throw property.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: You can only use variables in the spread that are defined in the root scope of the file.`,\n );\n }\n } else if (property.isObjectMethod()) {\n throw property.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: sx prop object does not support ObjectMethods.`,\n );\n } else {\n throw property.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: Unknown property in object.`,\n );\n }\n });\n}\n\nexport function sxObjectExtractor(nodePath: NodePath<ObjectExpression | ArrowFunctionExpression>) {\n if (nodePath.isObjectExpression()) {\n traverseObjectExpression(nodePath);\n } else if (nodePath.isArrowFunctionExpression()) {\n const body = nodePath.get('body');\n if (!body.isObjectExpression()) {\n throw body.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: sx prop only supports arrow functions directly returning an object, e.g. () => ({color: 'red'}). You can accept theme object in the params if required.`,\n );\n }\n traverseObjectExpression(body, nodePath);\n }\n}\n","import type { NodePath } from '@babel/core';\nimport type * as t from '@babel/types';\n\nexport function isStaticObjectExpression(\n nodePath: NodePath<t.ObjectExpression>,\n): nodePath is NodePath<t.ObjectExpression> {\n const properties = nodePath.get('properties');\n return properties.every((property): boolean => {\n if (!property.isObjectProperty()) {\n return false;\n }\n const key = property.get('key');\n const value = property.get('value');\n return (\n (key.isIdentifier() && value.isLiteral()) ||\n (value.isObjectExpression() && isStaticObjectExpression(value))\n );\n });\n}\n\n/**\n * Recursively check if all items in an array or all keys and values in\n * an object are static.\n */\nexport function isStaticObjectOrArrayExpression(\n nodePath: NodePath<t.Expression>,\n): nodePath is NodePath<t.ArrayExpression> | NodePath<t.ObjectExpression> {\n if (nodePath.isArrayExpression()) {\n const elements = nodePath.get('elements');\n return elements.every((item) => {\n if (item.isLiteral()) {\n return true;\n }\n if (item.isObjectExpression()) {\n return isStaticObjectExpression(item);\n }\n if (item.isArrayExpression()) {\n return isStaticObjectOrArrayExpression(nodePath);\n }\n return false;\n });\n }\n if (nodePath.isObjectExpression()) {\n return isStaticObjectExpression(nodePath);\n }\n return false;\n}\n","import { NodePath } from '@babel/core';\nimport { ArrowFunctionExpression, Expression, ObjectExpression } from '@babel/types';\nimport { sxObjectExtractor } from './sxObjectExtractor';\n\nfunction isAllowedExpression(\n node: NodePath<Expression>,\n): node is NodePath<ObjectExpression> | NodePath<ArrowFunctionExpression> {\n return node.isObjectExpression() || node.isArrowFunctionExpression();\n}\n\nexport function sxPropConverter(\n node: NodePath<Expression>,\n wrapWithSxCall: (expPath: NodePath<Expression>) => void,\n) {\n if (node.isConditionalExpression()) {\n const consequent = node.get('consequent');\n const alternate = node.get('alternate');\n\n if (isAllowedExpression(consequent)) {\n sxObjectExtractor(consequent);\n wrapWithSxCall(consequent);\n }\n if (isAllowedExpression(alternate)) {\n sxObjectExtractor(alternate);\n wrapWithSxCall(alternate);\n }\n } else if (node.isLogicalExpression()) {\n const right = node.get('right');\n if (isAllowedExpression(right)) {\n sxObjectExtractor(right);\n wrapWithSxCall(right);\n }\n } else if (isAllowedExpression(node)) {\n sxObjectExtractor(node);\n wrapWithSxCall(node);\n } else if (node.isIdentifier()) {\n const rootScope = node.scope.getProgramParent();\n const binding = node.scope.getBinding(node.node.name);\n // Simplest case, ie, const styles = {static object}\n // and is used as <Component sx={styles} />\n if (binding?.scope === rootScope) {\n wrapWithSxCall(node);\n }\n }\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-react/utils/pre-linaria-plugin.mjs b/packages/pigment-react/utils/pre-linaria-plugin.mjs new file mode 100644 index 00000000000000..2b62bab06d214f --- /dev/null +++ b/packages/pigment-react/utils/pre-linaria-plugin.mjs @@ -0,0 +1,258 @@ +import './chunk-XZYQOGJP.mjs'; +import { addNamed } from '@babel/helper-module-imports'; +import { declare } from '@babel/helper-plugin-utils'; +import { arrowFunctionExpression, cloneNode } from '@babel/types'; +import { findIdentifiers } from '@wyw-in-js/transform'; + +/** + * @pigment-css/react v0.0.1 + * + * @license MIT + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + + +// src/utils/checkStaticObjectOrArray.ts +function isStaticObjectExpression(nodePath) { + const properties = nodePath.get("properties"); + return properties.every((property) => { + if (!property.isObjectProperty()) { + return false; + } + const key = property.get("key"); + const value = property.get("value"); + return key.isIdentifier() && value.isLiteral() || value.isObjectExpression() && isStaticObjectExpression(value); + }); +} +function isStaticObjectOrArrayExpression(nodePath) { + if (nodePath.isArrayExpression()) { + const elements = nodePath.get("elements"); + return elements.every((item) => { + if (item.isLiteral()) { + return true; + } + if (item.isObjectExpression()) { + return isStaticObjectExpression(item); + } + if (item.isArrayExpression()) { + return isStaticObjectOrArrayExpression(nodePath); + } + return false; + }); + } + if (nodePath.isObjectExpression()) { + return isStaticObjectExpression(nodePath); + } + return false; +} + +// src/utils/sxObjectExtractor.ts +function validateObjectKey(keyPath, parentCall) { + const rootScope = keyPath.scope.getProgramParent(); + if (keyPath.isIdentifier()) { + return; + } + const identifiers = findIdentifiers([keyPath]); + if (!identifiers.length) { + return; + } + if (!parentCall) { + throw keyPath.buildCodeFrameError( + `${"@pigment-css/react"}: Expressions in css object keys are not supported.` + ); + } + if (!identifiers.every((item) => { + const binding = item.scope.getBinding(item.node.name); + if (!binding) { + return false; + } + if (binding.path.findParent((parent) => parent === parentCall) || binding.path.scope === rootScope) { + return true; + } + return false; + })) { + throw keyPath.buildCodeFrameError( + `${"@pigment-css/react"}: Variables in css object keys should only use the passed theme(s) object or variables that are defined in the root scope.` + ); + } +} +function traverseObjectExpression(nodePath, parentCall) { + const rootScope = nodePath.scope.getProgramParent(); + const properties = nodePath.get("properties"); + properties.forEach((property) => { + if (property.isObjectProperty()) { + validateObjectKey(property.get("key"), parentCall); + const value = property.get("value"); + if (!value.isExpression()) { + throw value.buildCodeFrameError( + `${"@pigment-css/react"}: This value is not supported. It can only be static values or local variables.` + ); + } + if (value.isObjectExpression()) { + traverseObjectExpression(value, parentCall); + } else if (value.isArrowFunctionExpression()) { + throw value.buildCodeFrameError( + `${"@pigment-css/react"}: Arrow functions are not supported as values of sx object.` + ); + } else if (!value.isLiteral() && !isStaticObjectOrArrayExpression(value)) { + const identifiers = findIdentifiers([value], "reference"); + const localIdentifiers = []; + identifiers.forEach((id) => { + if (!id.isIdentifier()) { + return; + } + const binding = id.scope.getBinding(id.node.name); + if (!binding) { + return; + } + if (binding.path.findParent((parent) => parent === parentCall)) ; else if (binding.scope !== rootScope) { + localIdentifiers.push(id); + } else { + throw id.buildCodeFrameError( + `${"@pigment-css/react"}: Consider moving this variable to the root scope if it has all static values.` + ); + } + }); + if (localIdentifiers.length) { + const arrowFn = arrowFunctionExpression( + localIdentifiers.map((i) => i.node), + cloneNode(value.node) + ); + value.replaceWith(arrowFn); + } + } + } else if (property.isSpreadElement()) { + const identifiers = findIdentifiers([property.get("argument")]); + if (!identifiers.every((id) => { + const binding = property.scope.getBinding(id.node.name); + return binding && (binding.scope === rootScope || binding.scope === property.scope); + })) { + throw property.buildCodeFrameError( + `${"@pigment-css/react"}: You can only use variables in the spread that are defined in the root scope of the file.` + ); + } + } else if (property.isObjectMethod()) { + throw property.buildCodeFrameError( + `${"@pigment-css/react"}: sx prop object does not support ObjectMethods.` + ); + } else { + throw property.buildCodeFrameError( + `${"@pigment-css/react"}: Unknown property in object.` + ); + } + }); +} +function sxObjectExtractor(nodePath) { + if (nodePath.isObjectExpression()) { + traverseObjectExpression(nodePath); + } else if (nodePath.isArrowFunctionExpression()) { + const body = nodePath.get("body"); + if (!body.isObjectExpression()) { + throw body.buildCodeFrameError( + `${"@pigment-css/react"}: sx prop only supports arrow functions directly returning an object, e.g. () => ({color: 'red'}). You can accept theme object in the params if required.` + ); + } + traverseObjectExpression(body, nodePath); + } +} + +// src/utils/sxPropConverter.ts +function isAllowedExpression(node) { + return node.isObjectExpression() || node.isArrowFunctionExpression(); +} +function sxPropConverter(node, wrapWithSxCall) { + if (node.isConditionalExpression()) { + const consequent = node.get("consequent"); + const alternate = node.get("alternate"); + if (isAllowedExpression(consequent)) { + sxObjectExtractor(consequent); + wrapWithSxCall(consequent); + } + if (isAllowedExpression(alternate)) { + sxObjectExtractor(alternate); + wrapWithSxCall(alternate); + } + } else if (node.isLogicalExpression()) { + const right = node.get("right"); + if (isAllowedExpression(right)) { + sxObjectExtractor(right); + wrapWithSxCall(right); + } + } else if (isAllowedExpression(node)) { + sxObjectExtractor(node); + wrapWithSxCall(node); + } else if (node.isIdentifier()) { + const rootScope = node.scope.getProgramParent(); + const binding = node.scope.getBinding(node.node.name); + if ((binding == null ? void 0 : binding.scope) === rootScope) { + wrapWithSxCall(node); + } + } +} + +// src/utils/pre-linaria-plugin.ts +function replaceNodePath(expressionPath, namePath, importName, t, tagName) { + const sxIdentifier = addNamed(namePath, importName, "@pigment-css/react"); + const wrapWithSxCall = (expPath) => { + expPath.replaceWith( + t.callExpression(sxIdentifier, [expPath.node, t.identifier(tagName.node.name)]) + ); + }; + sxPropConverter(expressionPath, wrapWithSxCall); +} +var babelPlugin = declare( + (api, { propName = "sx", importName = "sx" }) => { + api.assertVersion(7); + const { types: t } = api; + return { + name: "@pigmentcss/zero-babel-plugin", + visitor: { + JSXAttribute(path) { + const namePath = path.get("name"); + const openingElement = path.findParent((p) => p.isJSXOpeningElement()); + if (!openingElement || !openingElement.isJSXOpeningElement() || !namePath.isJSXIdentifier() || namePath.node.name !== propName) { + return; + } + const tagName = openingElement.get("name"); + if (!tagName.isJSXIdentifier()) { + return; + } + const valuePath = path.get("value"); + if (!valuePath.isJSXExpressionContainer()) { + return; + } + const expressionPath = valuePath.get("expression"); + if (!expressionPath.isExpression()) { + return; + } + replaceNodePath(expressionPath, namePath, importName, t, tagName); + }, + ObjectProperty(path) { + const keyPath = path.get("key"); + if (!keyPath.isIdentifier() || keyPath.node.name !== propName) { + return; + } + const valuePath = path.get("value"); + if (!valuePath.isObjectExpression() && !valuePath.isArrowFunctionExpression()) { + return; + } + const parentJsxCall = path.findParent((p) => p.isCallExpression()); + if (!parentJsxCall || !parentJsxCall.isCallExpression()) { + return; + } + const callee = parentJsxCall.get("callee"); + if (!callee.isIdentifier() || !callee.node.name.includes("jsx")) { + return; + } + const jsxElement = parentJsxCall.get("arguments")[0]; + replaceNodePath(valuePath, keyPath, importName, t, jsxElement); + } + } + }; + } +); + +export { babelPlugin }; +//# sourceMappingURL=out.js.map +//# sourceMappingURL=pre-linaria-plugin.mjs.map \ No newline at end of file diff --git a/packages/pigment-react/utils/pre-linaria-plugin.mjs.map b/packages/pigment-react/utils/pre-linaria-plugin.mjs.map new file mode 100644 index 00000000000000..18cd057d0006bc --- /dev/null +++ b/packages/pigment-react/utils/pre-linaria-plugin.mjs.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/utils/pre-linaria-plugin.ts","../src/utils/sxObjectExtractor.ts","../src/utils/checkStaticObjectOrArray.ts","../src/utils/sxPropConverter.ts"],"names":[],"mappings":"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","sourcesContent":["import { addNamed } from '@babel/helper-module-imports';\nimport { declare } from '@babel/helper-plugin-utils';\nimport { NodePath } from '@babel/core';\nimport * as Types from '@babel/types';\nimport { sxPropConverter } from './sxPropConverter';\n\nfunction replaceNodePath(\n expressionPath: NodePath<Types.Expression>,\n namePath: NodePath<Types.JSXIdentifier | Types.Identifier>,\n importName: string,\n t: typeof Types,\n tagName: NodePath<Types.JSXIdentifier | Types.Identifier>,\n) {\n const sxIdentifier = addNamed(namePath, importName, process.env.PACKAGE_NAME as string);\n\n const wrapWithSxCall = (expPath: NodePath<Types.Expression>) => {\n expPath.replaceWith(\n t.callExpression(sxIdentifier, [expPath.node, t.identifier(tagName.node.name)]),\n );\n };\n\n sxPropConverter(expressionPath, wrapWithSxCall);\n}\n\nexport const babelPlugin = declare<{ propName?: string; importName?: string }>(\n (api, { propName = 'sx', importName = 'sx' }) => {\n api.assertVersion(7);\n const { types: t } = api;\n return {\n name: '@pigmentcss/zero-babel-plugin',\n visitor: {\n JSXAttribute(path) {\n const namePath = path.get('name');\n const openingElement = path.findParent((p) => p.isJSXOpeningElement());\n if (\n !openingElement ||\n !openingElement.isJSXOpeningElement() ||\n !namePath.isJSXIdentifier() ||\n namePath.node.name !== propName\n ) {\n return;\n }\n const tagName = openingElement.get('name');\n if (!tagName.isJSXIdentifier()) {\n return;\n }\n const valuePath = path.get('value');\n if (!valuePath.isJSXExpressionContainer()) {\n return;\n }\n const expressionPath = valuePath.get('expression');\n if (!expressionPath.isExpression()) {\n return;\n }\n replaceNodePath(expressionPath, namePath, importName, t, tagName);\n },\n ObjectProperty(path) {\n // @TODO - Maybe add support for React.createElement calls as well.\n // Right now, it only checks for jsx(),jsxs(),jsxDEV() and jsxsDEV() calls.\n const keyPath = path.get('key');\n if (!keyPath.isIdentifier() || keyPath.node.name !== propName) {\n return;\n }\n const valuePath = path.get('value');\n if (!valuePath.isObjectExpression() && !valuePath.isArrowFunctionExpression()) {\n return;\n }\n const parentJsxCall = path.findParent((p) => p.isCallExpression());\n if (!parentJsxCall || !parentJsxCall.isCallExpression()) {\n return;\n }\n const callee = parentJsxCall.get('callee');\n if (!callee.isIdentifier() || !callee.node.name.includes('jsx')) {\n return;\n }\n const jsxElement = parentJsxCall.get('arguments')[0] as NodePath<Types.Identifier>;\n replaceNodePath(valuePath, keyPath, importName, t, jsxElement);\n },\n },\n };\n },\n);\n","import type { NodePath } from '@babel/core';\nimport { arrowFunctionExpression, cloneNode } from '@babel/types';\nimport type {\n ArrowFunctionExpression,\n Expression,\n Identifier,\n ObjectExpression,\n PrivateName,\n} from '@babel/types';\nimport { findIdentifiers } from '@wyw-in-js/transform';\nimport { isStaticObjectOrArrayExpression } from './checkStaticObjectOrArray';\n\nfunction validateObjectKey(\n keyPath: NodePath<PrivateName | Expression>,\n parentCall?: NodePath<ArrowFunctionExpression>,\n) {\n const rootScope = keyPath.scope.getProgramParent();\n if (keyPath.isIdentifier()) {\n return;\n }\n const identifiers = findIdentifiers([keyPath]);\n if (!identifiers.length) {\n return;\n }\n if (!parentCall) {\n throw keyPath.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: Expressions in css object keys are not supported.`,\n );\n }\n if (\n !identifiers.every((item) => {\n const binding = item.scope.getBinding(item.node.name);\n if (!binding) {\n return false;\n }\n if (\n binding.path.findParent((parent) => parent === parentCall) ||\n binding.path.scope === rootScope\n ) {\n return true;\n }\n return false;\n })\n ) {\n throw keyPath.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: Variables in css object keys should only use the passed theme(s) object or variables that are defined in the root scope.`,\n );\n }\n}\n\nfunction traverseObjectExpression(\n nodePath: NodePath<ObjectExpression>,\n parentCall?: NodePath<ArrowFunctionExpression>,\n) {\n const rootScope = nodePath.scope.getProgramParent();\n const properties = nodePath.get('properties');\n properties.forEach((property) => {\n if (property.isObjectProperty()) {\n validateObjectKey(property.get('key'), parentCall);\n\n const value = property.get('value');\n if (!value.isExpression()) {\n throw value.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: This value is not supported. It can only be static values or local variables.`,\n );\n }\n if (value.isObjectExpression()) {\n traverseObjectExpression(value, parentCall);\n } else if (value.isArrowFunctionExpression()) {\n throw value.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: Arrow functions are not supported as values of sx object.`,\n );\n } else if (!value.isLiteral() && !isStaticObjectOrArrayExpression(value)) {\n const identifiers = findIdentifiers([value], 'reference');\n const themeIdentifiers: NodePath<Identifier>[] = [];\n const localIdentifiers: NodePath<Identifier>[] = [];\n identifiers.forEach((id) => {\n if (!id.isIdentifier()) {\n return;\n }\n const binding = id.scope.getBinding(id.node.name);\n if (!binding) {\n return;\n }\n if (binding.path.findParent((parent) => parent === parentCall)) {\n themeIdentifiers.push(id);\n } else if (binding.scope !== rootScope) {\n localIdentifiers.push(id);\n } else {\n throw id.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: Consider moving this variable to the root scope if it has all static values.`,\n );\n }\n });\n if (localIdentifiers.length) {\n const arrowFn = arrowFunctionExpression(\n localIdentifiers.map((i) => i.node),\n cloneNode(value.node),\n );\n value.replaceWith(arrowFn);\n }\n }\n } else if (property.isSpreadElement()) {\n const identifiers = findIdentifiers([property.get('argument')]);\n if (\n !identifiers.every((id) => {\n const binding = property.scope.getBinding(id.node.name);\n // the indentifier definition should either be in the root scope or in the same scope\n // as the object property, ie, ({theme}) => ({...theme.applyStyles()})\n return binding && (binding.scope === rootScope || binding.scope === property.scope);\n })\n ) {\n throw property.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: You can only use variables in the spread that are defined in the root scope of the file.`,\n );\n }\n } else if (property.isObjectMethod()) {\n throw property.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: sx prop object does not support ObjectMethods.`,\n );\n } else {\n throw property.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: Unknown property in object.`,\n );\n }\n });\n}\n\nexport function sxObjectExtractor(nodePath: NodePath<ObjectExpression | ArrowFunctionExpression>) {\n if (nodePath.isObjectExpression()) {\n traverseObjectExpression(nodePath);\n } else if (nodePath.isArrowFunctionExpression()) {\n const body = nodePath.get('body');\n if (!body.isObjectExpression()) {\n throw body.buildCodeFrameError(\n `${process.env.PACKAGE_NAME}: sx prop only supports arrow functions directly returning an object, e.g. () => ({color: 'red'}). You can accept theme object in the params if required.`,\n );\n }\n traverseObjectExpression(body, nodePath);\n }\n}\n","import type { NodePath } from '@babel/core';\nimport type * as t from '@babel/types';\n\nexport function isStaticObjectExpression(\n nodePath: NodePath<t.ObjectExpression>,\n): nodePath is NodePath<t.ObjectExpression> {\n const properties = nodePath.get('properties');\n return properties.every((property): boolean => {\n if (!property.isObjectProperty()) {\n return false;\n }\n const key = property.get('key');\n const value = property.get('value');\n return (\n (key.isIdentifier() && value.isLiteral()) ||\n (value.isObjectExpression() && isStaticObjectExpression(value))\n );\n });\n}\n\n/**\n * Recursively check if all items in an array or all keys and values in\n * an object are static.\n */\nexport function isStaticObjectOrArrayExpression(\n nodePath: NodePath<t.Expression>,\n): nodePath is NodePath<t.ArrayExpression> | NodePath<t.ObjectExpression> {\n if (nodePath.isArrayExpression()) {\n const elements = nodePath.get('elements');\n return elements.every((item) => {\n if (item.isLiteral()) {\n return true;\n }\n if (item.isObjectExpression()) {\n return isStaticObjectExpression(item);\n }\n if (item.isArrayExpression()) {\n return isStaticObjectOrArrayExpression(nodePath);\n }\n return false;\n });\n }\n if (nodePath.isObjectExpression()) {\n return isStaticObjectExpression(nodePath);\n }\n return false;\n}\n","import { NodePath } from '@babel/core';\nimport { ArrowFunctionExpression, Expression, ObjectExpression } from '@babel/types';\nimport { sxObjectExtractor } from './sxObjectExtractor';\n\nfunction isAllowedExpression(\n node: NodePath<Expression>,\n): node is NodePath<ObjectExpression> | NodePath<ArrowFunctionExpression> {\n return node.isObjectExpression() || node.isArrowFunctionExpression();\n}\n\nexport function sxPropConverter(\n node: NodePath<Expression>,\n wrapWithSxCall: (expPath: NodePath<Expression>) => void,\n) {\n if (node.isConditionalExpression()) {\n const consequent = node.get('consequent');\n const alternate = node.get('alternate');\n\n if (isAllowedExpression(consequent)) {\n sxObjectExtractor(consequent);\n wrapWithSxCall(consequent);\n }\n if (isAllowedExpression(alternate)) {\n sxObjectExtractor(alternate);\n wrapWithSxCall(alternate);\n }\n } else if (node.isLogicalExpression()) {\n const right = node.get('right');\n if (isAllowedExpression(right)) {\n sxObjectExtractor(right);\n wrapWithSxCall(right);\n }\n } else if (isAllowedExpression(node)) {\n sxObjectExtractor(node);\n wrapWithSxCall(node);\n } else if (node.isIdentifier()) {\n const rootScope = node.scope.getProgramParent();\n const binding = node.scope.getBinding(node.node.name);\n // Simplest case, ie, const styles = {static object}\n // and is used as <Component sx={styles} />\n if (binding?.scope === rootScope) {\n wrapWithSxCall(node);\n }\n }\n}\n"]} \ No newline at end of file diff --git a/packages/pigment-unplugin/LICENSE b/packages/pigment-unplugin/LICENSE new file mode 100644 index 00000000000000..af1beaff7f5645 --- /dev/null +++ b/packages/pigment-unplugin/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Call-Em-All + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/pigment-vite-plugin/LICENSE b/packages/pigment-vite-plugin/LICENSE new file mode 100644 index 00000000000000..af1beaff7f5645 --- /dev/null +++ b/packages/pigment-vite-plugin/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Call-Em-All + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/rsc-builder/buildRsc.ts b/packages/rsc-builder/buildRsc.ts index 960ad717f53df0..a31674176307c1 100644 --- a/packages/rsc-builder/buildRsc.ts +++ b/packages/rsc-builder/buildRsc.ts @@ -22,10 +22,6 @@ const PROJECTS: Project[] = [ name: 'material', rootPath: path.join(process.cwd(), 'packages/mui-material'), }, - { - name: 'material-next', - rootPath: path.join(process.cwd(), 'packages/mui-material-next'), - }, { name: 'joy', rootPath: path.join(process.cwd(), 'packages/mui-joy'), diff --git a/packages/rsc-builder/package.json b/packages/rsc-builder/package.json index 0a9e852f449bb6..8e9a60d6c178e2 100644 --- a/packages/rsc-builder/package.json +++ b/packages/rsc-builder/package.json @@ -9,6 +9,6 @@ }, "devDependencies": { "@types/mocha": "^10.0.6", - "@types/node": "^18.19.21" + "@types/node": "^18.19.25" } } diff --git a/packages/test-utils/src/createMount.tsx b/packages/test-utils/src/createMount.tsx index 406e8e65b793c5..03f0d273d08bff 100644 --- a/packages/test-utils/src/createMount.tsx +++ b/packages/test-utils/src/createMount.tsx @@ -109,7 +109,7 @@ export default function createMount(options: CreateMountOptions = {}) { }); // some tests require that no other components are in the tree - // e.g. when doing .instance(), .state() etc. + // for example when doing .instance(), .state() etc. const wrapper = mount( strict == null ? node : <Mode __element={node} __strict={Boolean(strict)} />, { diff --git a/packages/test-utils/src/createRenderer.tsx b/packages/test-utils/src/createRenderer.tsx index 51f31c6b463789..33d7a6017dfa3b 100644 --- a/packages/test-utils/src/createRenderer.tsx +++ b/packages/test-utils/src/createRenderer.tsx @@ -533,7 +533,7 @@ export function createRenderer(globalOptions: CreateRendererOptions = {}): Rende "Can't cleanup before fake timers are restored.\n" + 'Be sure to:\n' + ' 1. Only use `clock` from `createRenderer`.\n' + - ' 2. Call `createRenderer` in a suite and not any test hook (e.g. `beforeEach`) or test itself (e.g. `it`).', + ' 2. Call `createRenderer` in a suite and not any test hook (for example `beforeEach`) or test itself (for example `it`).', ); // Use saved stack otherwise the stack trace will not include the test location. error.stack = createClientRenderStack; diff --git a/packages/test-utils/src/initMatchers.ts b/packages/test-utils/src/initMatchers.ts index 33465a93f40470..e0f23f9070426b 100644 --- a/packages/test-utils/src/initMatchers.ts +++ b/packages/test-utils/src/initMatchers.ts @@ -167,7 +167,7 @@ chai.use((chaiAPI, utils) => { // `CSSStyleDeclaration` is not constructable // https://stackoverflow.com/a/52732909/3406963 // this is not equivalent to the declaration from `getComputedStyle` - // e.g. `getComputedStyle` would return a readonly declaration + // for example `getComputedStyle` would return a readonly declaration // let's hope this doesn't get passed around until it's no longer clear where it comes from const declaration = document.createElement('span').style; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b13e0946fa8755..6dc6963882c3fd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,10 +19,10 @@ overrides: '@babel/preset-typescript': ^7.23.3 '@babel/runtime': ^7.23.9 '@babel/types': ^7.23.9 - '@definitelytyped/header-parser': ^0.2.6 - '@definitelytyped/typescript-versions': ^0.1.0 + '@definitelytyped/header-parser': ^0.2.8 + '@definitelytyped/typescript-versions': ^0.1.1 '@definitelytyped/utils': ^0.1.5 - '@types/node': ^18.19.21 + '@types/node': ^18.19.25 '@types/react': ^18.2.55 '@types/react-dom': 18.2.19 cross-fetch: ^4.0.0 @@ -34,6 +34,9 @@ importers: '@googleapis/sheets': specifier: ^5.0.5 version: 5.0.5 + '@netlify/functions': + specifier: ^2.6.0 + version: 2.6.0 '@slack/bolt': specifier: ^3.17.1 version: 3.17.1 @@ -41,12 +44,12 @@ importers: specifier: ^8.0.1 version: 8.0.1 google-auth-library: - specifier: ^9.6.3 - version: 9.6.3 + specifier: ^9.7.0 + version: 9.7.0 devDependencies: '@argos-ci/core': - specifier: ^1.5.4 - version: 1.5.4 + specifier: ^1.5.5 + version: 1.5.5 '@babel/cli': specifier: ^7.23.9 version: 7.23.9(@babel/core@7.23.9) @@ -98,12 +101,12 @@ importers: '@mui-internal/api-docs-builder-core': specifier: workspace:^ version: link:packages/api-docs-builder-core - '@mui-internal/docs-utils': - specifier: workspace:^ - version: link:packages/docs-utils '@mui-internal/test-utils': specifier: workspace:^ version: link:packages/test-utils + '@mui/internal-docs-utils': + specifier: workspace:^ + version: link:packages-internal/docs-utils '@mui/internal-scripts': specifier: workspace:^ version: link:packages-internal/scripts @@ -117,14 +120,14 @@ importers: specifier: workspace:^ version: link:packages/mui-utils/build '@next/eslint-plugin-next': - specifier: ^14.1.1 - version: 14.1.1 + specifier: ^14.1.3 + version: 14.1.3 '@octokit/rest': specifier: ^20.0.2 version: 20.0.2 '@pigment-css/react': specifier: workspace:^ - version: link:packages/pigment-react + version: link:packages/pigment-css-react '@playwright/test': specifier: 1.42.1 version: 1.42.1 @@ -135,14 +138,14 @@ importers: specifier: ^11.0.4 version: 11.0.4 '@types/lodash': - specifier: ^4.14.202 - version: 4.14.202 + specifier: ^4.17.0 + version: 4.17.0 '@types/mocha': specifier: ^10.0.6 version: 10.0.6 '@types/node': - specifier: ^18.19.21 - version: 18.19.21 + specifier: ^18.19.25 + version: 18.19.25 '@types/prettier': specifier: ^2.7.3 version: 2.7.3 @@ -183,8 +186,8 @@ importers: specifier: ^5.3.0 version: 5.3.0 compression-webpack-plugin: - specifier: ^11.0.0 - version: 11.0.0(webpack@5.90.3) + specifier: ^11.1.0 + version: 11.1.0(webpack@5.90.3) concurrently: specifier: ^8.2.2 version: 8.2.2 @@ -205,7 +208,7 @@ importers: version: 8.56.0 eslint-config-airbnb: specifier: ^19.0.4 - version: 19.0.4(eslint-plugin-import@2.29.1)(eslint-plugin-jsx-a11y@6.7.1)(eslint-plugin-react-hooks@4.6.0)(eslint-plugin-react@7.33.2)(eslint@8.56.0) + version: 19.0.4(eslint-plugin-import@2.29.1)(eslint-plugin-jsx-a11y@6.7.1)(eslint-plugin-react-hooks@4.6.0)(eslint-plugin-react@7.34.1)(eslint@8.56.0) eslint-config-airbnb-base: specifier: ^15.0.0 version: 15.0.0(eslint-plugin-import@2.29.1)(eslint@8.56.0) @@ -234,11 +237,11 @@ importers: specifier: workspace:^ version: link:packages/eslint-plugin-material-ui eslint-plugin-mocha: - specifier: ^10.3.0 - version: 10.3.0(eslint@8.56.0) + specifier: ^10.4.1 + version: 10.4.1(eslint@8.56.0) eslint-plugin-react: - specifier: ^7.33.2 - version: 7.33.2(eslint@8.56.0) + specifier: ^7.34.1 + version: 7.34.1(eslint@8.56.0) eslint-plugin-react-hooks: specifier: ^4.6.0 version: 4.6.0(eslint@8.56.0) @@ -264,8 +267,8 @@ importers: specifier: ^3.0.3 version: 3.0.3 karma-firefox-launcher: - specifier: ^2.1.2 - version: 2.1.2 + specifier: ^2.1.3 + version: 2.1.3 karma-mocha: specifier: ^2.0.1 version: 2.0.1 @@ -288,8 +291,8 @@ importers: specifier: ^10.3.0 version: 10.3.0 nx: - specifier: ^17.2.8 - version: 17.2.8 + specifier: ^17.3.2 + version: 17.3.2 nyc: specifier: ^15.1.0 version: 15.1.0 @@ -447,8 +450,8 @@ importers: specifier: ^7.23.9 version: 7.23.9 '@docsearch/react': - specifier: ^3.5.2 - version: 3.5.2(@algolia/client-search@4.22.1)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0) + specifier: ^3.6.0 + version: 3.6.0(@algolia/client-search@4.22.1)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0) '@emotion/cache': specifier: ^11.11.0 version: 11.11.0 @@ -491,9 +494,6 @@ importers: '@mui/material': specifier: workspace:^ version: link:../packages/mui-material/build - '@mui/material-next': - specifier: workspace:* - version: link:../packages/mui-material-next/build '@mui/styled-engine': specifier: workspace:^ version: link:../packages/mui-styled-engine/build @@ -516,23 +516,23 @@ importers: specifier: 6.19.5 version: 6.19.5(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) '@mui/x-data-grid': - specifier: 6.19.6 - version: 6.19.6(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) + specifier: 7.0.0-beta.7 + version: 7.0.0-beta.7(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) '@mui/x-data-grid-generator': - specifier: 6.19.6 - version: 6.19.6(@mui/icons-material@packages+mui-icons-material+build)(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) + specifier: 7.0.0-beta.7 + version: 7.0.0-beta.7(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/icons-material@packages+mui-icons-material+build)(@mui/material@packages+mui-material+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) '@mui/x-data-grid-premium': - specifier: 6.19.6 - version: 6.19.6(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) + specifier: 7.0.0-beta.7 + version: 7.0.0-beta.7(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) '@mui/x-data-grid-pro': - specifier: 6.19.6 - version: 6.19.6(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) + specifier: 7.0.0-beta.7 + version: 7.0.0-beta.7(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) '@mui/x-date-pickers': - specifier: 6.19.6 - version: 6.19.6(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(date-fns-jalali@2.21.3-1)(date-fns@2.30.0)(react-dom@18.2.0)(react@18.2.0) + specifier: 6.19.7 + version: 6.19.7(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(date-fns-jalali@2.21.3-1)(date-fns@2.30.0)(react-dom@18.2.0)(react@18.2.0) '@mui/x-date-pickers-pro': - specifier: 6.19.6 - version: 6.19.6(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(date-fns-jalali@2.21.3-1)(date-fns@2.30.0)(react-dom@18.2.0)(react@18.2.0) + specifier: 6.19.7 + version: 6.19.7(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(date-fns-jalali@2.21.3-1)(date-fns@2.30.0)(react-dom@18.2.0)(react@18.2.0) '@mui/x-license-pro': specifier: 6.10.2 version: 6.10.2(@types/react@18.2.55)(react@18.2.0) @@ -621,8 +621,8 @@ importers: specifier: ^1.5.0 version: 1.5.0 markdown-to-jsx: - specifier: ^7.4.1 - version: 7.4.1(react@18.2.0) + specifier: ^7.4.3 + version: 7.4.3(react@18.2.0) material-ui-popup-state: specifier: ^5.0.10 version: 5.0.10(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) @@ -657,11 +657,11 @@ importers: specifier: ^6.5.9 version: 6.5.9(final-form@4.20.10)(react@18.2.0) react-imask: - specifier: ^7.3.0 - version: 7.3.0(react@18.2.0) + specifier: ^7.5.0 + version: 7.5.0(react@18.2.0) react-intersection-observer: - specifier: ^9.5.3 - version: 9.5.3(react@18.2.0) + specifier: ^9.8.1 + version: 9.8.1(react-dom@18.2.0)(react@18.2.0) react-is: specifier: ^18.2.0 version: 18.2.0 @@ -690,8 +690,8 @@ importers: specifier: ^4.4.5 version: 4.4.5(react-dom@18.2.0)(react@18.2.0) react-virtuoso: - specifier: ^4.6.3 - version: 4.6.3(react-dom@18.2.0)(react@18.2.0) + specifier: ^4.7.2 + version: 4.7.2(react-dom@18.2.0)(react@18.2.0) react-window: specifier: ^1.8.10 version: 1.8.10(react-dom@18.2.0)(react@18.2.0) @@ -720,12 +720,12 @@ importers: '@babel/preset-typescript': specifier: ^7.23.3 version: 7.23.3(@babel/core@7.23.9) - '@mui-internal/docs-utils': - specifier: workspace:^ - version: link:../packages/docs-utils '@mui-internal/test-utils': specifier: workspace:^ version: link:../packages/test-utils + '@mui/internal-docs-utils': + specifier: workspace:^ + version: link:../packages-internal/docs-utils '@mui/internal-scripts': specifier: workspace:^ version: link:../packages-internal/scripts @@ -742,8 +742,8 @@ importers: specifier: ^0.2.2 version: 0.2.2 '@types/node': - specifier: ^18.19.21 - version: 18.19.21 + specifier: ^18.19.25 + version: 18.19.25 '@types/prop-types': specifier: ^15.7.11 version: 15.7.11 @@ -793,6 +793,15 @@ importers: specifier: ^17.7.2 version: 17.7.2 + packages-internal/docs-utils: + dependencies: + rimraf: + specifier: ^5.0.5 + version: 5.0.5 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + packages-internal/scripts: dependencies: '@babel/core': @@ -810,9 +819,9 @@ importers: '@babel/types': specifier: ^7.23.9 version: 7.23.9 - '@mui-internal/docs-utils': + '@mui/internal-docs-utils': specifier: workspace:^ - version: link:../../packages/docs-utils + version: link:../docs-utils doctrine: specifier: ^3.0.0 version: 3.0.0 @@ -839,11 +848,11 @@ importers: specifier: ^0.0.9 version: 0.0.9 '@types/lodash': - specifier: ^4.14.202 - version: 4.14.202 + specifier: ^4.17.0 + version: 4.17.0 '@types/node': - specifier: ^18.19.21 - version: 18.19.21 + specifier: ^18.19.25 + version: 18.19.25 '@types/prettier': specifier: ^2.7.3 version: 2.7.3 @@ -877,9 +886,9 @@ importers: '@babel/traverse': specifier: ^7.23.9 version: 7.23.9 - '@mui-internal/docs-utils': + '@mui/internal-docs-utils': specifier: workspace:^ - version: link:../docs-utils + version: link:../../packages-internal/docs-utils '@mui/internal-markdown': specifier: workspace:^ version: link:../markdown @@ -905,8 +914,8 @@ importers: specifier: ^5.4.3 version: 5.4.3 recast: - specifier: ^0.23.5 - version: 0.23.5 + specifier: ^0.23.6 + version: 0.23.6 remark: specifier: ^13.0.0 version: 13.0.0 @@ -936,8 +945,8 @@ importers: specifier: ^10.0.6 version: 10.0.6 '@types/node': - specifier: ^18.19.21 - version: 18.19.21 + specifier: ^18.19.25 + version: 18.19.25 '@types/react-docgen': specifier: workspace:* version: link:../react-docgen-types @@ -973,8 +982,8 @@ importers: specifier: ^10.0.6 version: 10.0.6 '@types/node': - specifier: ^18.19.21 - version: 18.19.21 + specifier: ^18.19.25 + version: 18.19.25 '@types/sinon': specifier: ^10.0.20 version: 10.0.20 @@ -988,15 +997,6 @@ importers: specifier: ^5.3.3 version: 5.3.3 - packages/docs-utils: - dependencies: - rimraf: - specifier: ^5.0.5 - version: 5.0.5 - typescript: - specifier: ^5.3.3 - version: 5.3.3 - packages/eslint-plugin-material-ui: dependencies: emoji-regex: @@ -1023,8 +1023,8 @@ importers: version: 9.0.1 optionalDependencies: aws-sdk: - specifier: ^2.1569.0 - version: 2.1569.0 + specifier: ^2.1579.0 + version: 2.1579.0 devDependencies: claudia: specifier: ^5.14.1 @@ -1080,8 +1080,8 @@ importers: specifier: ^10.0.6 version: 10.0.6 '@types/node': - specifier: ^18.19.21 - version: 18.19.21 + specifier: ^18.19.25 + version: 18.19.25 babel-plugin-tester: specifier: ^11.0.4 version: 11.0.4(@babel/core@7.23.9) @@ -1226,8 +1226,8 @@ importers: version: 15.8.1 devDependencies: '@types/node': - specifier: ^18.19.21 - version: 18.19.21 + specifier: ^18.19.25 + version: 18.19.25 '@types/prop-types': specifier: ^15.7.11 version: 15.7.11 @@ -1618,95 +1618,6 @@ importers: version: 15.2.0 publishDirectory: build - packages/mui-material-next: - dependencies: - '@babel/runtime': - specifier: ^7.23.9 - version: 7.23.9 - '@emotion/styled': - specifier: ^11.3.0 - version: 11.11.0(@emotion/react@11.11.4)(@types/react@18.2.55)(react@18.2.0) - '@mui/base': - specifier: workspace:* - version: link:../mui-base/build - '@mui/material': - specifier: workspace:^ - version: link:../mui-material/build - '@mui/system': - specifier: workspace:^ - version: link:../mui-system/build - '@mui/types': - specifier: workspace:^ - version: link:../mui-types/build - '@mui/utils': - specifier: workspace:^ - version: link:../mui-utils/build - '@types/react-transition-group': - specifier: ^4.4.10 - version: 4.4.10 - clsx: - specifier: ^2.1.0 - version: 2.1.0 - prop-types: - specifier: ^15.8.1 - version: 15.8.1 - react-is: - specifier: ^18.2.0 - version: 18.2.0 - react-transition-group: - specifier: ^4.4.5 - version: 4.4.5(react-dom@18.2.0)(react@18.2.0) - devDependencies: - '@emotion/react': - specifier: ^11.11.4 - version: 11.11.4(@types/react@18.2.55)(react@18.2.0) - '@mui-internal/test-utils': - specifier: workspace:^ - version: link:../test-utils - '@mui/internal-babel-macros': - specifier: workspace:^ - version: link:../mui-babel-macros - '@testing-library/user-event': - specifier: ^14.5.2 - version: 14.5.2(@testing-library/dom@9.3.4) - '@types/chai': - specifier: ^4.3.12 - version: 4.3.12 - '@types/prop-types': - specifier: ^15.7.11 - version: 15.7.11 - '@types/react': - specifier: ^18.2.55 - version: 18.2.55 - '@types/react-dom': - specifier: 18.2.19 - version: 18.2.19 - '@types/react-is': - specifier: ^18.2.4 - version: 18.2.4 - '@types/sinon': - specifier: ^10.0.20 - version: 10.0.20 - chai: - specifier: ^4.4.1 - version: 4.4.1 - lodash: - specifier: ^4.17.21 - version: 4.17.21 - react: - specifier: ^18.2.0 - version: 18.2.0 - react-dom: - specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) - react-router-dom: - specifier: ^6.21.3 - version: 6.22.1(react-dom@18.2.0)(react@18.2.0) - sinon: - specifier: ^15.2.0 - version: 15.2.0 - publishDirectory: build - packages/mui-material-nextjs: dependencies: '@babel/runtime': @@ -2051,8 +1962,8 @@ importers: specifier: ^10.0.6 version: 10.0.6 '@types/node': - specifier: ^18.19.21 - version: 18.19.21 + specifier: ^18.19.25 + version: 18.19.25 '@types/react': specifier: ^18.2.55 version: 18.2.55 @@ -2085,17 +1996,17 @@ importers: specifier: ^11.2.0 version: 11.2.0 - packages/pigment-nextjs-plugin: + packages/pigment-css-nextjs-plugin: dependencies: '@pigment-css/unplugin': specifier: workspace:^ - version: link:../pigment-unplugin + version: link:../pigment-css-unplugin devDependencies: next: specifier: ^13.5.1 version: 13.5.1(@babel/core@7.23.9)(babel-plugin-macros@3.1.0)(react-dom@18.2.0)(react@18.2.0) - packages/pigment-react: + packages/pigment-css-react: dependencies: '@babel/core': specifier: ^7.23.9 @@ -2171,14 +2082,14 @@ importers: specifier: ^3.0.2 version: 3.0.2 '@types/lodash': - specifier: ^4.14.202 - version: 4.14.202 + specifier: ^4.17.0 + version: 4.17.0 '@types/mocha': specifier: ^10.0.6 version: 10.0.6 '@types/node': - specifier: ^18.19.21 - version: 18.19.21 + specifier: ^18.19.25 + version: 18.19.25 '@types/react': specifier: ^18.2.55 version: 18.2.55 @@ -2195,14 +2106,14 @@ importers: specifier: ^18.2.0 version: 18.2.0 - packages/pigment-unplugin: + packages/pigment-css-unplugin: dependencies: '@babel/core': specifier: ^7.23.9 version: 7.23.9 '@pigment-css/react': specifier: workspace:^ - version: link:../pigment-react + version: link:../pigment-css-react '@wyw-in-js/shared': specifier: ^0.5.0 version: 0.5.0 @@ -2220,7 +2131,7 @@ importers: specifier: ^7.20.5 version: 7.20.5 - packages/pigment-vite-plugin: + packages/pigment-css-vite-plugin: dependencies: '@babel/core': specifier: ^7.23.9 @@ -2230,7 +2141,7 @@ importers: version: 7.23.3(@babel/core@7.23.9) '@pigment-css/react': specifier: workspace:^ - version: link:../pigment-react + version: link:../pigment-css-react '@wyw-in-js/shared': specifier: ^0.5.0 version: 0.5.0 @@ -2246,7 +2157,7 @@ importers: version: 7.20.5 vite: specifier: ^5.0.12 - version: 5.0.12(@types/node@18.19.21) + version: 5.0.12(@types/node@18.19.25) packages/react-docgen-types: devDependencies: @@ -2267,8 +2178,8 @@ importers: specifier: ^10.0.6 version: 10.0.6 '@types/node': - specifier: ^18.19.21 - version: 18.19.21 + specifier: ^18.19.25 + version: 18.19.25 packages/test-utils: dependencies: @@ -2407,9 +2318,6 @@ importers: '@mui/material': specifier: workspace:^ version: link:../packages/mui-material/build - '@mui/material-next': - specifier: workspace:* - version: link:../packages/mui-material-next/build '@mui/system': specifier: workspace:^ version: link:../packages/mui-system/build @@ -2683,8 +2591,8 @@ packages: '@jridgewell/gen-mapping': 0.1.1 '@jridgewell/trace-mapping': 0.3.20 - /@argos-ci/core@1.5.4: - resolution: {integrity: sha512-XgRJSJYfauCbqOGNLFFiVri70TAp0TORfKrK/6yXyt7k9NgZD6vD10hUhSMeJP7REwYrqD3wxY7l85K+wwoo3Q==} + /@argos-ci/core@1.5.5: + resolution: {integrity: sha512-m271rCtp5fnpUU89eSlS/If5WnDIUGduEHfW9YrwVHnMYWPCNBDxp2q87Wgjl3O5RTm/x+sDED3vFD9i1q+yTQ==} engines: {node: '>=16.0.0'} dependencies: '@argos-ci/util': 1.2.1 @@ -4094,8 +4002,8 @@ packages: regenerator-runtime: 0.14.0 dev: false - /@babel/runtime-corejs3@7.23.7: - resolution: {integrity: sha512-ER55qzLREVA5YxeyQ3Qu48tgsF2ZrFjFjUS6V6wF0cikSw+goBJgB9PBRM1T6+Ah4iiM+sxmfS/Sy/jdzFfhiQ==} + /@babel/runtime-corejs3@7.24.0: + resolution: {integrity: sha512-HxiRMOncx3ly6f3fcZ1GVKf+/EROcI9qwPgmij8Czqy6Okm/0T37T4y2ZIlLUuEUFjtM7NRsfdCO8Y3tAiJZew==} engines: {node: '>=6.9.0'} dependencies: core-js-pure: 3.32.1 @@ -4293,12 +4201,12 @@ packages: resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} engines: {node: '>=10.0.0'} - /@docsearch/css@3.5.2: - resolution: {integrity: sha512-SPiDHaWKQZpwR2siD0KQUwlStvIAnEyK6tAE2h2Wuoq8ue9skzhlyVQ1ddzOxX6khULnAALDiR/isSF3bnuciA==} + /@docsearch/css@3.6.0: + resolution: {integrity: sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==} dev: false - /@docsearch/react@3.5.2(@algolia/client-search@4.22.1)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0): - resolution: {integrity: sha512-9Ahcrs5z2jq/DcAvYtvlqEBHImbm4YJI8M9y0x6Tqg598P40HTEkX7hsMcIuThI+hTFxRGZ9hll0Wygm2yEjng==} + /@docsearch/react@3.6.0(@algolia/client-search@4.22.1)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0): + resolution: {integrity: sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==} peerDependencies: '@types/react': ^18.2.55 react: '>= 16.8.0 < 19.0.0' @@ -4316,7 +4224,7 @@ packages: dependencies: '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.22.1)(algoliasearch@4.19.1)(search-insights@2.13.0) '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.22.1)(algoliasearch@4.19.1) - '@docsearch/css': 3.5.2 + '@docsearch/css': 3.6.0 '@types/react': 18.2.55 algoliasearch: 4.19.1 react: 18.2.0 @@ -4685,7 +4593,7 @@ packages: ajv: 6.12.6 debug: 4.3.4(supports-color@8.1.1) espree: 9.6.1 - globals: 13.19.0 + globals: 13.24.0 ignore: 5.3.1 import-fresh: 3.3.0 js-yaml: 4.1.0 @@ -4703,7 +4611,7 @@ packages: /@fast-csv/format@4.3.5: resolution: {integrity: sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 lodash.escaperegexp: 4.1.2 lodash.isboolean: 3.0.3 lodash.isequal: 4.5.0 @@ -4714,7 +4622,7 @@ packages: /@fast-csv/parse@4.3.6: resolution: {integrity: sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 lodash.escaperegexp: 4.1.2 lodash.groupby: 4.6.0 lodash.isfunction: 3.0.9 @@ -4906,7 +4814,7 @@ packages: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.21 + '@types/node': 18.19.25 jest-mock: 29.7.0 dev: false @@ -4916,7 +4824,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 18.19.21 + '@types/node': 18.19.25 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -4934,7 +4842,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 18.19.21 + '@types/node': 18.19.25 '@types/yargs': 15.0.19 chalk: 4.1.2 dev: false @@ -4946,7 +4854,7 @@ packages: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 18.19.21 + '@types/node': 18.19.25 '@types/yargs': 17.0.32 chalk: 4.1.2 dev: false @@ -4994,7 +4902,7 @@ packages: engines: {node: '>=18.0.0'} dependencies: '@npmcli/run-script': 7.0.2 - '@nx/devkit': 17.2.8(nx@17.2.8) + '@nx/devkit': 17.2.8(nx@17.3.2) '@octokit/plugin-enterprise-rest': 6.0.1 '@octokit/rest': 19.0.11 byte-size: 8.1.1 @@ -5031,7 +4939,7 @@ packages: npm-packlist: 5.1.1 npm-registry-fetch: 14.0.5 npmlog: 6.0.2 - nx: 17.2.8 + nx: 17.3.2 p-map: 4.0.0 p-map-series: 2.1.0 p-queue: 6.6.2 @@ -5091,7 +4999,7 @@ packages: enzyme-adapter-utils: 1.14.0(react@18.2.0) enzyme-shallow-equal: 1.0.4 has: 1.0.3 - object.assign: 4.1.4 + object.assign: 4.1.5 object.values: 1.1.7 prop-types: 15.8.1 react: 18.2.0 @@ -5115,7 +5023,7 @@ packages: '@babel/runtime': 7.23.9 '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0) '@mui/types': 7.2.13(@types/react@18.2.55) - '@mui/utils': 5.15.8(@types/react@18.2.55)(react@18.2.0) + '@mui/utils': 5.15.12(@types/react@18.2.55)(react@18.2.0) '@popperjs/core': 2.11.8 '@types/react': 18.2.55 clsx: 2.1.0 @@ -5138,7 +5046,30 @@ packages: '@babel/runtime': 7.23.9 '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0) '@mui/types': 7.2.13(@types/react@18.2.55) - '@mui/utils': 5.15.8(@types/react@18.2.55)(react@18.2.0) + '@mui/utils': 5.15.12(@types/react@18.2.55)(react@18.2.0) + '@popperjs/core': 2.11.8 + '@types/react': 18.2.55 + clsx: 2.1.0 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@mui/base@5.0.0-beta.37(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-/o3anbb+DeCng8jNsd3704XtmmLDZju1Fo8R2o7ugrVtPQ/QpcqddwKNzKPZwa0J5T8YNW3ZVuHyQgbTnQLisQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/react': ^18.2.55 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.9 + '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0) + '@mui/types': 7.2.13(@types/react@18.2.55) + '@mui/utils': 5.15.12(@types/react@18.2.55)(react@18.2.0) '@popperjs/core': 2.11.8 '@types/react': 18.2.55 clsx: 2.1.0 @@ -5173,9 +5104,9 @@ packages: '@emotion/styled': 11.11.0(@emotion/react@11.11.4)(@types/react@18.2.55)(react@18.2.0) '@mui/base': 5.0.0-beta.31(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) '@mui/core-downloads-tracker': 5.15.8 - '@mui/system': 5.15.8(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.55)(react@18.2.0) + '@mui/system': 5.15.12(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.55)(react@18.2.0) '@mui/types': 7.2.13(@types/react@18.2.55) - '@mui/utils': 5.15.8(@types/react@18.2.55)(react@18.2.0) + '@mui/utils': 5.15.12(@types/react@18.2.55)(react@18.2.0) '@types/react': 18.2.55 clsx: 2.1.0 prop-types: 15.8.1 @@ -5205,9 +5136,9 @@ packages: '@emotion/styled': 11.11.0(@emotion/react@11.11.4)(@types/react@18.2.55)(react@18.2.0) '@mui/base': 5.0.0-beta.31(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) '@mui/core-downloads-tracker': 5.15.8 - '@mui/system': 5.15.8(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.55)(react@18.2.0) + '@mui/system': 5.15.12(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.55)(react@18.2.0) '@mui/types': 7.2.13(@types/react@18.2.55) - '@mui/utils': 5.15.8(@types/react@18.2.55)(react@18.2.0) + '@mui/utils': 5.15.12(@types/react@18.2.55)(react@18.2.0) '@types/react': 18.2.55 '@types/react-transition-group': 4.4.10 clsx: 2.1.0 @@ -5219,8 +5150,8 @@ packages: react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) dev: false - /@mui/private-theming@5.15.8(@types/react@18.2.55)(react@18.2.0): - resolution: {integrity: sha512-HMDPO416iMZPqs8nGUL3GJMDNpJtE1Uefw/Aw+zTKJHX5JnT+Bms41e2065BUT/zR5dYcKjFP4gQMwW5QX7nvA==} + /@mui/private-theming@5.15.12(@types/react@18.2.55)(react@18.2.0): + resolution: {integrity: sha512-cqoSo9sgA5HE+8vZClbLrq9EkyOnYysooepi5eKaKvJ41lReT2c5wOZAeDDM1+xknrMDos+0mT2zr3sZmUiRRA==} engines: {node: '>=12.0.0'} peerDependencies: '@types/react': ^18.2.55 @@ -5230,14 +5161,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.9 - '@mui/utils': 5.15.8(@types/react@18.2.55)(react@18.2.0) + '@mui/utils': 5.15.12(@types/react@18.2.55)(react@18.2.0) '@types/react': 18.2.55 prop-types: 15.8.1 react: 18.2.0 dev: false - /@mui/styled-engine@5.15.8(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0): - resolution: {integrity: sha512-31ZKPGsS0OiSwuzi8RWoTiWRdUWXPRiOQkyG9bRYX/zvoYeBXEdbsLEgbryug5mVRsPpvwbH5q/i/t6MkjQ71g==} + /@mui/styled-engine@5.15.11(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0): + resolution: {integrity: sha512-So21AhAngqo07ces4S/JpX5UaMU2RHXpEA6hNzI6IQjd/1usMPxpgK8wkGgTe3JKmC2KDmH8cvoycq5H3Ii7/w==} engines: {node: '>=12.0.0'} peerDependencies: '@emotion/react': ^11.4.1 @@ -5258,8 +5189,8 @@ packages: react: 18.2.0 dev: false - /@mui/system@5.15.8(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.55)(react@18.2.0): - resolution: {integrity: sha512-BUMJvlz1UqIqDPyrvc+MwjOUkWKskUPAOUuRh2KMAworiXuuUmtIivxSfdGll2ex6RHSylu4yc5dJZByOI8EcQ==} + /@mui/system@5.15.12(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.55)(react@18.2.0): + resolution: {integrity: sha512-/pq+GO6yN3X7r3hAwFTrzkAh7K1bTF5r8IzS79B9eyKJg7v6B/t4/zZYMR6OT9qEPtwf6rYN2Utg1e6Z7F1OgQ==} engines: {node: '>=12.0.0'} peerDependencies: '@emotion/react': ^11.5.0 @@ -5277,10 +5208,10 @@ packages: '@babel/runtime': 7.23.9 '@emotion/react': 11.11.4(@types/react@18.2.55)(react@18.2.0) '@emotion/styled': 11.11.0(@emotion/react@11.11.4)(@types/react@18.2.55)(react@18.2.0) - '@mui/private-theming': 5.15.8(@types/react@18.2.55)(react@18.2.0) - '@mui/styled-engine': 5.15.8(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) + '@mui/private-theming': 5.15.12(@types/react@18.2.55)(react@18.2.0) + '@mui/styled-engine': 5.15.11(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) '@mui/types': 7.2.13(@types/react@18.2.55) - '@mui/utils': 5.15.8(@types/react@18.2.55)(react@18.2.0) + '@mui/utils': 5.15.12(@types/react@18.2.55)(react@18.2.0) '@types/react': 18.2.55 clsx: 2.1.0 csstype: 3.1.3 @@ -5299,8 +5230,8 @@ packages: '@types/react': 18.2.55 dev: false - /@mui/utils@5.15.8(@types/react@18.2.55)(react@18.2.0): - resolution: {integrity: sha512-Q6Z/xSxi1Z6xQ5Qj9p4ZTHudwfrrwFALtU6H1O222pXudg9Qm0zHdiwJQiHT9L6jMIN78ZujEfGHserMoHUrQw==} + /@mui/utils@5.15.12(@types/react@18.2.55)(react@18.2.0): + resolution: {integrity: sha512-8SDGCnO2DY9Yy+5bGzu00NZowSDtuyHP4H8gunhHGQoIlhlY2Z3w64wBzAOLpYw/ZhJNzksDTnS/i8qdJvxuow==} engines: {node: '>=12.0.0'} peerDependencies: '@types/react': ^18.2.55 @@ -5352,71 +5283,72 @@ packages: - '@types/react' dev: false - /@mui/x-data-grid-generator@6.19.6(@mui/icons-material@packages+mui-icons-material+build)(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-NULYBJgJQpTcQlp6uPPGIzWfYEagi+rdsxKCpTDQLuWQ8sCCgTA/f9NLC0cmev8DQSb0utvIBTdWq3sTT1rzAQ==} + /@mui/x-data-grid-generator@7.0.0-beta.7(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/icons-material@packages+mui-icons-material+build)(@mui/material@packages+mui-material+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-CV6AaC11RvNRKeN3Pd+ynxUnGE2IoK5NacXSxkelrfoeC+W9UH1GZhoZeh744b7KJxcVnKabxbMRa9qZj0jY5Q==} engines: {node: '>=14.0.0'} peerDependencies: '@mui/icons-material': ^5.4.1 - '@mui/material': ^5.4.1 + '@mui/material': ^5.15.0 react: ^17.0.0 || ^18.0.0 dependencies: '@babel/runtime': 7.23.9 - '@mui/base': 5.0.0-beta.30(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) + '@mui/base': 5.0.0-beta.37(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) '@mui/icons-material': link:packages/mui-icons-material/build '@mui/material': link:packages/mui-material/build - '@mui/x-data-grid-premium': 6.19.6(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) + '@mui/x-data-grid-premium': 7.0.0-beta.7(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) chance: 1.1.11 clsx: 2.1.0 lru-cache: 7.18.3 react: 18.2.0 transitivePeerDependencies: - - '@mui/system' + - '@emotion/react' + - '@emotion/styled' - '@types/react' - react-dom dev: false - /@mui/x-data-grid-premium@6.19.6(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-G0wQeagVcq2Sl8yDWVXolJk6JJwVIzeW9wUU55niF9a+i1VN0r8Wipp858liIA3A+sS++H/Ai6CDyx7KwqylhQ==} + /@mui/x-data-grid-premium@7.0.0-beta.7(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-8qtdDSvIDVCFnWX6s6hMqSgayU8igdVHK7waGjjPxwrCj3u9JTzMGdxJF2mu6gaCDcQ4Bn3PFT7IA8qt6yHhaw==} engines: {node: '>=14.0.0'} peerDependencies: - '@mui/material': ^5.4.1 - '@mui/system': ^5.4.1 + '@mui/material': ^5.15.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 dependencies: '@babel/runtime': 7.23.9 '@mui/material': link:packages/mui-material/build - '@mui/system': link:packages/mui-system/build - '@mui/utils': 5.15.8(@types/react@18.2.55)(react@18.2.0) - '@mui/x-data-grid': 6.19.6(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) - '@mui/x-data-grid-pro': 6.19.6(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) - '@mui/x-license-pro': 6.10.2(@types/react@18.2.55)(react@18.2.0) + '@mui/system': 5.15.12(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.55)(react@18.2.0) + '@mui/utils': 5.15.12(@types/react@18.2.55)(react@18.2.0) + '@mui/x-data-grid': 7.0.0-beta.7(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) + '@mui/x-data-grid-pro': 7.0.0-beta.7(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) + '@mui/x-license': 7.0.0-beta.6(@types/react@18.2.55)(react@18.2.0) '@types/format-util': 1.0.4 clsx: 2.1.0 - exceljs: 4.3.0 + exceljs: 4.4.0 prop-types: 15.8.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) reselect: 4.1.8 transitivePeerDependencies: + - '@emotion/react' + - '@emotion/styled' - '@types/react' dev: false - /@mui/x-data-grid-pro@6.19.6(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-Bh2qvRHoHxgrUHKMeu46VRzl2lOSvxjtPAhJncRUJuYE6hVu2VZxvqrdmdxdIWkom2ygpFwq6YP6xMJsDFX+QQ==} + /@mui/x-data-grid-pro@7.0.0-beta.7(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-sVw/r5eOjIqxMpfLtsJzntk1tgdNcWpxndwqo3VT3fmU6i78SbJgihJ7yntWai0nn2rjDjmRZPpZifAc0gNeIg==} engines: {node: '>=14.0.0'} peerDependencies: - '@mui/material': ^5.4.1 - '@mui/system': ^5.4.1 + '@mui/material': ^5.15.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 dependencies: '@babel/runtime': 7.23.9 '@mui/material': link:packages/mui-material/build - '@mui/system': link:packages/mui-system/build - '@mui/utils': 5.15.8(@types/react@18.2.55)(react@18.2.0) - '@mui/x-data-grid': 6.19.6(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) - '@mui/x-license-pro': 6.10.2(@types/react@18.2.55)(react@18.2.0) + '@mui/system': 5.15.12(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.55)(react@18.2.0) + '@mui/utils': 5.15.12(@types/react@18.2.55)(react@18.2.0) + '@mui/x-data-grid': 7.0.0-beta.7(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) + '@mui/x-license': 7.0.0-beta.6(@types/react@18.2.55)(react@18.2.0) '@types/format-util': 1.0.4 clsx: 2.1.0 prop-types: 15.8.1 @@ -5424,33 +5356,36 @@ packages: react-dom: 18.2.0(react@18.2.0) reselect: 4.1.8 transitivePeerDependencies: + - '@emotion/react' + - '@emotion/styled' - '@types/react' dev: false - /@mui/x-data-grid@6.19.6(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-jpZkX1Gnlo87gKcD10mKMY8YoAzUD8Cv3/IvedH3FINDKO3hnraMeOciKDeUk0tYSj8RUDB02kpTHCM8ojLVBA==} + /@mui/x-data-grid@7.0.0-beta.7(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-rZ7lvibUDbGqEABBGlNvfQ5SdIa+8ve3/d8YjdZ2DUOeJwR+K7X4MnVItIrZuKPRYoSy1kWDRsjr8u0sEIIEoA==} engines: {node: '>=14.0.0'} peerDependencies: - '@mui/material': ^5.4.1 - '@mui/system': ^5.4.1 + '@mui/material': ^5.15.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 dependencies: '@babel/runtime': 7.23.9 '@mui/material': link:packages/mui-material/build - '@mui/system': link:packages/mui-system/build - '@mui/utils': 5.15.8(@types/react@18.2.55)(react@18.2.0) + '@mui/system': 5.15.12(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.55)(react@18.2.0) + '@mui/utils': 5.15.12(@types/react@18.2.55)(react@18.2.0) clsx: 2.1.0 prop-types: 15.8.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) reselect: 4.1.8 transitivePeerDependencies: + - '@emotion/react' + - '@emotion/styled' - '@types/react' dev: false - /@mui/x-date-pickers-pro@6.19.6(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(date-fns-jalali@2.21.3-1)(date-fns@2.30.0)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-6TwlGgAGE0TMIrfYaOrGhh79YyHDKt9ze22irPBOwz7IQeJ6vnGLzE1KJ+ddklQjaGtN9X7O++55r8mfs3mLSA==} + /@mui/x-date-pickers-pro@6.19.7(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(date-fns-jalali@2.21.3-1)(date-fns@2.30.0)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-KpYNZIx3zp1yXKkeGKkIBj4PFWutFKBBwQLJxMp/PDyl8uZSeB3P8Rd9tlqvFZ+k+vksP7q+S3GvAlIa3GwKNQ==} engines: {node: '>=14.0.0'} peerDependencies: '@emotion/react': ^11.9.0 @@ -5492,8 +5427,8 @@ packages: '@mui/base': 5.0.0-beta.30(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) '@mui/material': link:packages/mui-material/build '@mui/system': link:packages/mui-system/build - '@mui/utils': 5.15.8(@types/react@18.2.55)(react@18.2.0) - '@mui/x-date-pickers': 6.19.6(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(date-fns-jalali@2.21.3-1)(date-fns@2.30.0)(react-dom@18.2.0)(react@18.2.0) + '@mui/utils': 5.15.12(@types/react@18.2.55)(react@18.2.0) + '@mui/x-date-pickers': 6.19.7(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(date-fns-jalali@2.21.3-1)(date-fns@2.30.0)(react-dom@18.2.0)(react@18.2.0) '@mui/x-license-pro': 6.10.2(@types/react@18.2.55)(react@18.2.0) clsx: 2.1.0 date-fns: 2.30.0 @@ -5506,8 +5441,8 @@ packages: - '@types/react' dev: false - /@mui/x-date-pickers@6.19.6(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(date-fns-jalali@2.21.3-1)(date-fns@2.30.0)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-QW9AFcPi0vLpkUhmquhhyhLaBvB0AZJuu3NTrE173qNKx3Z3n51aCLY9bc7c6i4ltZMMsVRHlvzQjsve04TC8A==} + /@mui/x-date-pickers@6.19.7(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@mui/material@packages+mui-material+build)(@mui/system@packages+mui-system+build)(@types/react@18.2.55)(date-fns-jalali@2.21.3-1)(date-fns@2.30.0)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-BCTOQjAuyU29Ymd2FJrHHdRh0U6Qve7MsthdrD2jjaMaR8ou455JuxsNTQUGSpiMkGHWOMVq+B8N1EBcSYH63g==} engines: {node: '>=14.0.0'} peerDependencies: '@emotion/react': ^11.9.0 @@ -5549,7 +5484,7 @@ packages: '@mui/base': 5.0.0-beta.30(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) '@mui/material': link:packages/mui-material/build '@mui/system': link:packages/mui-system/build - '@mui/utils': 5.15.8(@types/react@18.2.55)(react@18.2.0) + '@mui/utils': 5.15.12(@types/react@18.2.55)(react@18.2.0) '@types/react-transition-group': 4.4.10 clsx: 2.1.0 date-fns: 2.30.0 @@ -5569,7 +5504,20 @@ packages: react: ^17.0.0 || ^18.0.0 dependencies: '@babel/runtime': 7.23.9 - '@mui/utils': 5.15.8(@types/react@18.2.55)(react@18.2.0) + '@mui/utils': 5.15.12(@types/react@18.2.55)(react@18.2.0) + react: 18.2.0 + transitivePeerDependencies: + - '@types/react' + dev: false + + /@mui/x-license@7.0.0-beta.6(@types/react@18.2.55)(react@18.2.0): + resolution: {integrity: sha512-S1HYQFf9DqDfjNN1nxPvrHyp+lhkcGUeSTpCEpzX9FX9ZfRuZEP9n9B3Vh3QuuXlETRK4aLg4jWKws5kzAoWgg==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.23.9 + '@mui/utils': 5.15.12(@types/react@18.2.55)(react@18.2.0) react: 18.2.0 transitivePeerDependencies: - '@types/react' @@ -5592,7 +5540,7 @@ packages: '@mui/base': 5.0.0-beta.30(@types/react@18.2.55)(react-dom@18.2.0)(react@18.2.0) '@mui/material': link:packages/mui-material/build '@mui/system': link:packages/mui-system/build - '@mui/utils': 5.15.8(@types/react@18.2.55)(react@18.2.0) + '@mui/utils': 5.15.12(@types/react@18.2.55)(react@18.2.0) '@types/react-transition-group': 4.4.10 clsx: 2.1.0 prop-types: 15.8.1 @@ -5603,11 +5551,31 @@ packages: - '@types/react' dev: false + /@netlify/functions@2.6.0: + resolution: {integrity: sha512-vU20tij0fb4nRGACqb+5SQvKd50JYyTyEhQetCMHdakcJFzjLDivvRR16u1G2Oy4A7xNAtGJF1uz8reeOtTVcQ==} + engines: {node: '>=14.0.0'} + dependencies: + '@netlify/serverless-functions-api': 1.14.0 + dev: false + + /@netlify/node-cookies@0.1.0: + resolution: {integrity: sha512-OAs1xG+FfLX0LoRASpqzVntVV/RpYkgpI0VrUnw2u0Q1qiZUzcPffxRK8HF3gc4GjuhG5ahOEMJ9bswBiZPq0g==} + engines: {node: ^14.16.0 || >=16.0.0} + dev: false + + /@netlify/serverless-functions-api@1.14.0: + resolution: {integrity: sha512-HUNETLNvNiC2J+SB/YuRwJA9+agPrc0azSoWVk8H85GC+YE114hcS5JW+dstpKwVerp2xILE3vNWN7IMXP5Q5Q==} + engines: {node: ^14.18.0 || >=16.0.0} + dependencies: + '@netlify/node-cookies': 0.1.0 + urlpattern-polyfill: 8.0.2 + dev: false + /@next/env@13.5.1: resolution: {integrity: sha512-CIMWiOTyflFn/GFx33iYXkgLSQsMQZV4jB91qaj/TfxGaGOXxn8C1j72TaUSPIyN7ziS/AYG46kGmnvuk1oOpg==} - /@next/eslint-plugin-next@14.1.1: - resolution: {integrity: sha512-NP1WoGFnFLpqqCWgGFjnn/sTwUExdPyjeFKRdQP1X/bL/tjAQ/TXDmYqw6vzGaP5NaZ2u6xzg+N/0nd7fOPOGQ==} + /@next/eslint-plugin-next@14.1.3: + resolution: {integrity: sha512-VCnZI2cy77Yaj3L7Uhs3+44ikMM1VD/fBMwvTBb3hIaTIuqa+DmG4dhUDq+MASu3yx97KhgsVJbsas0XuiKyww==} dependencies: glob: 10.3.10 dev: true @@ -5778,19 +5746,19 @@ packages: - supports-color dev: true - /@nrwl/devkit@17.2.8(nx@17.2.8): + /@nrwl/devkit@17.2.8(nx@17.3.2): resolution: {integrity: sha512-l2dFy5LkWqSA45s6pee6CoqJeluH+sjRdVnAAQfjLHRNSx6mFAKblyzq5h1f4P0EUCVVVqLs+kVqmNx5zxYqvw==} dependencies: - '@nx/devkit': 17.2.8(nx@17.2.8) + '@nx/devkit': 17.2.8(nx@17.3.2) transitivePeerDependencies: - nx dev: true - /@nrwl/tao@17.2.8: - resolution: {integrity: sha512-Qpk5YKeJ+LppPL/wtoDyNGbJs2MsTi6qyX/RdRrEc8lc4bk6Cw3Oul1qTXCI6jT0KzTz+dZtd0zYD/G7okkzvg==} + /@nrwl/tao@17.3.2: + resolution: {integrity: sha512-5uvpSmij0J9tteFV/0M/024K+H/o3XAlqtSdU8j03Auj1IleclSLF2yCTuIo7pYXhG3cgx1+nR+3nMs1QVAdUA==} hasBin: true dependencies: - nx: 17.2.8 + nx: 17.3.2 tslib: 2.6.2 transitivePeerDependencies: - '@swc-node/register' @@ -5798,23 +5766,23 @@ packages: - debug dev: true - /@nx/devkit@17.2.8(nx@17.2.8): + /@nx/devkit@17.2.8(nx@17.3.2): resolution: {integrity: sha512-6LtiQihtZwqz4hSrtT5cCG5XMCWppG6/B8c1kNksg97JuomELlWyUyVF+sxmeERkcLYFaKPTZytP0L3dmCFXaw==} peerDependencies: nx: '>= 16 <= 18' dependencies: - '@nrwl/devkit': 17.2.8(nx@17.2.8) + '@nrwl/devkit': 17.2.8(nx@17.3.2) ejs: 3.1.8 enquirer: 2.3.6 ignore: 5.3.1 - nx: 17.2.8 + nx: 17.3.2 semver: 7.5.3 tmp: 0.2.1 tslib: 2.6.2 dev: true - /@nx/nx-darwin-arm64@17.2.8: - resolution: {integrity: sha512-dMb0uxug4hM7tusISAU1TfkDK3ixYmzc1zhHSZwpR7yKJIyKLtUpBTbryt8nyso37AS1yH+dmfh2Fj2WxfBHTg==} + /@nx/nx-darwin-arm64@17.3.2: + resolution: {integrity: sha512-hn12o/tt26Pf4wG+8rIBgNIEZq5BFlHLv3scNrgKbd5SancHlTbY4RveRGct737UQ/78GCMCgMDRgNdagbCr6w==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -5822,8 +5790,8 @@ packages: dev: true optional: true - /@nx/nx-darwin-x64@17.2.8: - resolution: {integrity: sha512-0cXzp1tGr7/6lJel102QiLA4NkaLCkQJj6VzwbwuvmuCDxPbpmbz7HC1tUteijKBtOcdXit1/MEoEU007To8Bw==} + /@nx/nx-darwin-x64@17.3.2: + resolution: {integrity: sha512-5F28wrfE7yU60MzEXGjndy1sPJmNMIaV2W/g82kTXzxAbGHgSjwrGFmrJsrexzLp9oDlWkbc6YmInKV8gmmIaQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -5831,8 +5799,8 @@ packages: dev: true optional: true - /@nx/nx-freebsd-x64@17.2.8: - resolution: {integrity: sha512-YFMgx5Qpp2btCgvaniDGdu7Ctj56bfFvbbaHQWmOeBPK1krNDp2mqp8HK6ZKOfEuDJGOYAp7HDtCLvdZKvJxzA==} + /@nx/nx-freebsd-x64@17.3.2: + resolution: {integrity: sha512-07MMTfsJooONqL1Vrm5L6qk/gzmSrYLazjkiTmJz+9mrAM61RdfSYfO3mSyAoyfgWuQ5yEvfI56P036mK8aoPg==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] @@ -5840,8 +5808,8 @@ packages: dev: true optional: true - /@nx/nx-linux-arm-gnueabihf@17.2.8: - resolution: {integrity: sha512-iN2my6MrhLRkVDtdivQHugK8YmR7URo1wU9UDuHQ55z3tEcny7LV3W9NSsY9UYPK/FrxdDfevj0r2hgSSdhnzA==} + /@nx/nx-linux-arm-gnueabihf@17.3.2: + resolution: {integrity: sha512-gQxMF6U/h18Rz+FZu50DZCtfOdk27hHghNh3d3YTeVsrJTd1SmUQbYublmwU/ia1HhFS8RVI8GvkaKt5ph0HoA==} engines: {node: '>= 10'} cpu: [arm] os: [linux] @@ -5849,8 +5817,8 @@ packages: dev: true optional: true - /@nx/nx-linux-arm64-gnu@17.2.8: - resolution: {integrity: sha512-Iy8BjoW6mOKrSMiTGujUcNdv+xSM1DALTH6y3iLvNDkGbjGK1Re6QNnJAzqcXyDpv32Q4Fc57PmuexyysZxIGg==} + /@nx/nx-linux-arm64-gnu@17.3.2: + resolution: {integrity: sha512-X20wiXtXmKlC01bpVEREsRls1uVOM22xDTpqILvVty6+P+ytEYFR3Vs5EjDtzBKF51wjrwf03rEoToZbmgM8MA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -5858,8 +5826,8 @@ packages: dev: true optional: true - /@nx/nx-linux-arm64-musl@17.2.8: - resolution: {integrity: sha512-9wkAxWzknjpzdofL1xjtU6qPFF1PHlvKCZI3hgEYJDo4mQiatGI+7Ttko+lx/ZMP6v4+Umjtgq7+qWrApeKamQ==} + /@nx/nx-linux-arm64-musl@17.3.2: + resolution: {integrity: sha512-yko3Xsezkn4tjeudZYLjxFl07X/YB84K+DLK7EFyh9elRWV/8VjFcQmBAKUS2r9LfaEMNXq8/vhWMOWYyWBrIA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -5867,8 +5835,8 @@ packages: dev: true optional: true - /@nx/nx-linux-x64-gnu@17.2.8: - resolution: {integrity: sha512-sjG1bwGsjLxToasZ3lShildFsF0eyeGu+pOQZIp9+gjFbeIkd19cTlCnHrOV9hoF364GuKSXQyUlwtFYFR4VTQ==} + /@nx/nx-linux-x64-gnu@17.3.2: + resolution: {integrity: sha512-RiPvvQMmlZmDu9HdT6n6sV0+fEkyAqR5VocrD5ZAzEzFIlh4dyVLripFR3+MD+QhIhXyPt/hpri1kq9sgs4wnw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -5876,8 +5844,8 @@ packages: dev: true optional: true - /@nx/nx-linux-x64-musl@17.2.8: - resolution: {integrity: sha512-QiakXZ1xBCIptmkGEouLHQbcM4klQkcr+kEaz2PlNwy/sW3gH1b/1c0Ed5J1AN9xgQxWspriAONpScYBRgxdhA==} + /@nx/nx-linux-x64-musl@17.3.2: + resolution: {integrity: sha512-PWfVGmFsFJi+N1Nljg/jTKLHdufpGuHlxyfHqhDso/o4Qc0exZKSeZ1C63WkD7eTcT5kInifTQ/PffLiIDE3MA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -5885,8 +5853,8 @@ packages: dev: true optional: true - /@nx/nx-win32-arm64-msvc@17.2.8: - resolution: {integrity: sha512-XBWUY/F/GU3vKN9CAxeI15gM4kr3GOBqnzFZzoZC4qJt2hKSSUEWsMgeZtsMgeqEClbi4ZyCCkY7YJgU32WUGA==} + /@nx/nx-win32-arm64-msvc@17.3.2: + resolution: {integrity: sha512-O+4FFPbQz1mqaIj+SVE02ppe7T9ELj7Z5soQct5TbRRhwjGaw5n5xaPPBW7jUuQe2L5htid1E82LJyq3JpVc8A==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] @@ -5894,8 +5862,8 @@ packages: dev: true optional: true - /@nx/nx-win32-x64-msvc@17.2.8: - resolution: {integrity: sha512-HTqDv+JThlLzbcEm/3f+LbS5/wYQWzb5YDXbP1wi7nlCTihNZOLNqGOkEmwlrR5tAdNHPRpHSmkYg4305W0CtA==} + /@nx/nx-win32-x64-msvc@17.3.2: + resolution: {integrity: sha512-4hQm+7coy+hBqGY9J709hz/tUPijhf/WS7eML2r2xBmqBew3PMHfeZuaAAYWN690nIsu0WX3wyDsNjulR8HGPQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -7056,14 +7024,14 @@ packages: resolution: {integrity: sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA==} engines: {node: '>= 12.13.0', npm: '>= 6.12.0'} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 dev: false /@slack/logger@4.0.0: resolution: {integrity: sha512-Wz7QYfPAlG/DR+DfABddUZeNgoeY7d1J39OCR2jR+v7VBsB8ezulDK5szTnDDPDwLH5IWhLvXIHlCFZV7MSKgA==} engines: {node: '>= 18', npm: '>= 8.6.0'} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 dev: false /@slack/oauth@2.6.2: @@ -7073,7 +7041,7 @@ packages: '@slack/logger': 3.0.0 '@slack/web-api': 6.12.0 '@types/jsonwebtoken': 8.5.9 - '@types/node': 18.19.21 + '@types/node': 18.19.25 jsonwebtoken: 9.0.0 lodash.isstring: 4.0.1 transitivePeerDependencies: @@ -7086,7 +7054,7 @@ packages: dependencies: '@slack/logger': 3.0.0 '@slack/web-api': 6.12.0 - '@types/node': 18.19.21 + '@types/node': 18.19.25 '@types/p-queue': 2.3.2 '@types/ws': 7.4.7 eventemitter3: 3.1.2 @@ -7112,7 +7080,7 @@ packages: '@slack/logger': 3.0.0 '@slack/types': 2.11.0 '@types/is-stream': 1.1.0 - '@types/node': 18.19.21 + '@types/node': 18.19.25 axios: 1.6.5(debug@4.3.4) eventemitter3: 3.1.2 form-data: 2.5.1 @@ -7432,7 +7400,7 @@ packages: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: '@types/connect': 3.4.35 - '@types/node': 18.19.21 + '@types/node': 18.19.25 dev: false /@types/cacheable-request@6.0.2: @@ -7440,7 +7408,7 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 18.19.21 + '@types/node': 18.19.25 '@types/responselike': 1.0.0 dev: true @@ -7457,13 +7425,13 @@ packages: /@types/cheerio@0.22.31: resolution: {integrity: sha512-Kt7Cdjjdi2XWSfrZ53v4Of0wG3ZcmaegFXjMmz9tfNrZSkzzo36G0AL1YqSdcIA78Etjt6E609pt5h1xnQkPUw==} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 dev: true /@types/connect@3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 dev: false /@types/cookie@0.4.1: @@ -7511,7 +7479,7 @@ packages: /@types/express-serve-static-core@4.17.35: resolution: {integrity: sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 '@types/send': 0.17.1 @@ -7533,7 +7501,7 @@ packages: resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} dependencies: '@types/jsonfile': 6.1.1 - '@types/node': 18.19.21 + '@types/node': 18.19.25 dev: true /@types/hoist-non-react-statics@3.3.5: @@ -7557,7 +7525,7 @@ packages: /@types/is-stream@1.1.0: resolution: {integrity: sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg==} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 dev: false /@types/istanbul-lib-coverage@2.0.6: @@ -7597,29 +7565,29 @@ packages: /@types/jsonfile@6.1.1: resolution: {integrity: sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 dev: true /@types/jsonwebtoken@8.5.9: resolution: {integrity: sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 dev: false /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 dev: true /@types/lodash.mergewith@4.6.7: resolution: {integrity: sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==} dependencies: - '@types/lodash': 4.14.202 + '@types/lodash': 4.17.0 dev: false - /@types/lodash@4.14.202: - resolution: {integrity: sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==} + /@types/lodash@4.17.0: + resolution: {integrity: sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==} /@types/mdast@3.0.10: resolution: {integrity: sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==} @@ -7653,8 +7621,8 @@ packages: resolution: {integrity: sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==} dev: true - /@types/node@18.19.21: - resolution: {integrity: sha512-2Q2NeB6BmiTFQi4DHBzncSoq/cJMLDdhPaAoJFnFCyD9a8VPZRf7a1GAwp1Edb7ROaZc5Jz/tnZyL6EsWMRaqw==} + /@types/node@18.19.25: + resolution: {integrity: sha512-NrNXHJCexZtcbR9K1hsv1fSbwAwnhv7ql7l331aKvW0sej5H0NY1o64BHe0AA2ZoQuTm7NE6fyNW079MOWXe4Q==} dependencies: undici-types: 5.26.5 @@ -7752,13 +7720,13 @@ packages: /@types/resolve@0.0.8: resolution: {integrity: sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 dev: true /@types/responselike@1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 dev: true /@types/retry@0.12.0: @@ -7776,7 +7744,7 @@ packages: resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==} dependencies: '@types/mime': 1.3.2 - '@types/node': 18.19.21 + '@types/node': 18.19.25 dev: false /@types/serve-static@1.15.2: @@ -7784,7 +7752,7 @@ packages: dependencies: '@types/http-errors': 2.0.1 '@types/mime': 3.0.1 - '@types/node': 18.19.21 + '@types/node': 18.19.25 dev: false /@types/sinon@10.0.20: @@ -7840,7 +7808,7 @@ packages: /@types/ws@7.4.7: resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 dev: false /@types/yargs-parser@21.0.3: @@ -8309,11 +8277,11 @@ packages: react: ^0.14 || ^15.0.0 || ^16.0.0-alpha dependencies: array.prototype.find: 2.2.2 - function.prototype.name: 1.1.5 + function.prototype.name: 1.1.6 is-regex: 1.1.4 object-is: 1.1.5 - object.assign: 4.1.4 - object.entries: 1.1.6 + object.assign: 4.1.5 + object.entries: 1.1.7 prop-types: 15.8.1 prop-types-exact: 1.2.0 react: 18.2.0 @@ -8569,11 +8537,12 @@ packages: engines: {node: '>=0.10.0'} dev: false - /array-buffer-byte-length@1.0.0: - resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} + /array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 - is-array-buffer: 3.0.2 + is-array-buffer: 3.0.4 /array-differ@3.0.0: resolution: {integrity: sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==} @@ -8593,7 +8562,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 + es-abstract: 1.22.5 get-intrinsic: 1.2.4 is-string: 1.0.7 dev: true @@ -8621,7 +8590,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 + es-abstract: 1.22.5 es-array-method-boxes-properly: 1.0.0 is-string: 1.0.7 @@ -8630,8 +8599,19 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 - es-shim-unscopables: 1.0.0 + es-abstract: 1.22.5 + es-shim-unscopables: 1.0.2 + + /array.prototype.findlast@1.2.4: + resolution: {integrity: sha512-BMtLxpV+8BD+6ZPFIWmnUBpQoy+A+ujcg4rhp2iwCRJYA7PEh2MS4NL3lz8EiDlLrJPp2hg9qWihr5pd//jcGw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-errors: 1.3.0 + es-shim-unscopables: 1.0.2 + dev: true /array.prototype.findlastindex@1.2.3: resolution: {integrity: sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==} @@ -8639,8 +8619,8 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 - es-shim-unscopables: 1.0.0 + es-abstract: 1.22.5 + es-shim-unscopables: 1.0.2 get-intrinsic: 1.2.4 dev: true @@ -8650,8 +8630,8 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 - es-shim-unscopables: 1.0.0 + es-abstract: 1.22.5 + es-shim-unscopables: 1.0.2 /array.prototype.flatmap@1.3.2: resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} @@ -8659,8 +8639,8 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 - es-shim-unscopables: 1.0.0 + es-abstract: 1.22.5 + es-shim-unscopables: 1.0.2 dev: true /array.prototype.map@1.0.5: @@ -8669,7 +8649,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 + es-abstract: 1.22.5 es-array-method-boxes-properly: 1.0.0 is-string: 1.0.7 dev: false @@ -8680,31 +8660,42 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 + es-abstract: 1.22.5 es-array-method-boxes-properly: 1.0.0 is-string: 1.0.7 dev: true - /array.prototype.tosorted@1.1.1: - resolution: {integrity: sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==} + /array.prototype.toreversed@1.1.2: + resolution: {integrity: sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 - es-shim-unscopables: 1.0.0 - get-intrinsic: 1.2.4 + es-abstract: 1.22.5 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.tosorted@1.1.3: + resolution: {integrity: sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-errors: 1.3.0 + es-shim-unscopables: 1.0.2 dev: true - /arraybuffer.prototype.slice@1.0.1: - resolution: {integrity: sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==} + /arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} engines: {node: '>= 0.4'} dependencies: - array-buffer-byte-length: 1.0.0 + array-buffer-byte-length: 1.0.1 call-bind: 1.0.7 define-properties: 1.2.1 + es-abstract: 1.22.5 + es-errors: 1.3.0 get-intrinsic: 1.2.4 - is-array-buffer: 3.0.2 - is-shared-array-buffer: 1.0.2 + is-array-buffer: 3.0.4 + is-shared-array-buffer: 1.0.3 /arrify@1.0.1: resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} @@ -8834,9 +8825,10 @@ packages: dependencies: possible-typed-array-names: 1.0.0 - /aws-sdk@2.1569.0: - resolution: {integrity: sha512-9puKjesHKOjAYPqFurW/9nv3qhQ+STu3bVa5PN158SCeZPE6NsxZIWnHLglJvKU7N8UXJo1aJHmKDUGrsS7rXw==} + /aws-sdk@2.1579.0: + resolution: {integrity: sha512-TYv/bK2kVT/dfrpjAPL6sEhku4uH9EB18gRa6p9gi0fPtmhe49HrYoN2xmPNSqLPj4/CtSXmZhg91Ny1DhG8/Q==} engines: {node: '>= 10.0.0'} + requiresBuild: true dependencies: buffer: 4.9.2 events: 1.1.1 @@ -9638,7 +9630,7 @@ packages: engines: {node: '>=12.13.0'} hasBin: true dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -9653,7 +9645,7 @@ packages: /chromium-edge-launcher@1.0.0: resolution: {integrity: sha512-pgtgjNKZ7i5U++1g1PWv75umkHvhVTDOQIZ+sjeUX9483S7Y6MUvO0lrd7ShGlQlFHMN4SwKTCq/X8hWrbv2KA==} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -9695,7 +9687,7 @@ packages: hasBin: true dependencies: archiver: 3.1.1 - aws-sdk: 2.1569.0 + aws-sdk: 2.1579.0 fs-extra: 6.0.1 glob: 7.2.3 gunzip-maybe: 1.4.2 @@ -9977,8 +9969,8 @@ packages: dependencies: mime-db: 1.52.0 - /compression-webpack-plugin@11.0.0(webpack@5.90.3): - resolution: {integrity: sha512-Nz9dMiu0sag+mgJ5QTkRx0+vwrDZPU/gps7IdrkFE+oRSkgyoX4wbMol7QnXjI5/TEWx8yEwew9MiMjZgdLtjg==} + /compression-webpack-plugin@11.1.0(webpack@5.90.3): + resolution: {integrity: sha512-zDOQYp10+upzLxW+VRSjEpRRwBXJdsb5lBMlRxx1g8hckIFBpe3DTI0en2w7h+beuq89576RVzfiXrkdPGrHhA==} engines: {node: '>= 18.12.0'} peerDependencies: webpack: ^5.1.0 @@ -10691,19 +10683,19 @@ packages: resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} engines: {node: '>= 0.4'} dependencies: - array-buffer-byte-length: 1.0.0 + array-buffer-byte-length: 1.0.1 call-bind: 1.0.7 es-get-iterator: 1.1.3 get-intrinsic: 1.2.4 is-arguments: 1.1.1 - is-array-buffer: 3.0.2 + is-array-buffer: 3.0.4 is-date-object: 1.0.5 is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 + is-shared-array-buffer: 1.0.3 isarray: 2.0.5 object-is: 1.1.5 object-keys: 1.1.1 - object.assign: 4.1.4 + object.assign: 4.1.5 regexp.prototype.flags: 1.5.2 side-channel: 1.0.4 which-boxed-primitive: 1.0.2 @@ -11080,7 +11072,7 @@ packages: dependencies: '@types/cookie': 0.4.1 '@types/cors': 2.8.12 - '@types/node': 18.19.21 + '@types/node': 18.19.25 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.4.2 @@ -11151,9 +11143,9 @@ packages: react: 0.13.x || 0.14.x || ^15.0.0-0 || ^16.0.0-0 dependencies: airbnb-prop-types: 2.16.0(react@18.2.0) - function.prototype.name: 1.1.5 + function.prototype.name: 1.1.6 has: 1.0.3 - object.assign: 4.1.4 + object.assign: 4.1.5 object.fromentries: 2.0.7 prop-types: 15.8.1 react: 18.2.0 @@ -11171,7 +11163,7 @@ packages: array.prototype.flat: 1.3.2 cheerio: 1.0.0-rc.12 enzyme-shallow-equal: 1.0.4 - function.prototype.name: 1.1.5 + function.prototype.name: 1.1.6 has: 1.0.3 html-element-map: 1.3.1 is-boolean-object: 1.1.2 @@ -11182,14 +11174,14 @@ packages: is-subset: 0.1.1 lodash.escape: 4.0.1 lodash.isequal: 4.5.0 - object-inspect: 1.12.3 + object-inspect: 1.13.1 object-is: 1.1.5 - object.assign: 4.1.4 - object.entries: 1.1.6 + object.assign: 4.1.5 + object.entries: 1.1.7 object.values: 1.1.7 raf: 3.4.1 rst-selector-parser: 2.2.3 - string.prototype.trim: 1.2.7 + string.prototype.trim: 1.2.8 /err-code@2.0.3: resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} @@ -11214,47 +11206,49 @@ packages: escape-html: 1.0.3 dev: false - /es-abstract@1.22.1: - resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==} + /es-abstract@1.22.5: + resolution: {integrity: sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==} engines: {node: '>= 0.4'} dependencies: - array-buffer-byte-length: 1.0.0 - arraybuffer.prototype.slice: 1.0.1 + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 available-typed-arrays: 1.0.7 call-bind: 1.0.7 - es-set-tostringtag: 2.0.1 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-set-tostringtag: 2.0.3 es-to-primitive: 1.2.1 - function.prototype.name: 1.1.5 + function.prototype.name: 1.1.6 get-intrinsic: 1.2.4 - get-symbol-description: 1.0.0 + get-symbol-description: 1.0.2 globalthis: 1.0.3 gopd: 1.0.1 - has: 1.0.3 has-property-descriptors: 1.0.2 - has-proto: 1.0.1 + has-proto: 1.0.3 has-symbols: 1.0.3 - internal-slot: 1.0.5 - is-array-buffer: 3.0.2 + hasown: 2.0.2 + internal-slot: 1.0.7 + is-array-buffer: 3.0.4 is-callable: 1.2.7 - is-negative-zero: 2.0.2 + is-negative-zero: 2.0.3 is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 + is-shared-array-buffer: 1.0.3 is-string: 1.0.7 - is-typed-array: 1.1.12 + is-typed-array: 1.1.13 is-weakref: 1.0.2 - object-inspect: 1.12.3 + object-inspect: 1.13.1 object-keys: 1.1.1 - object.assign: 4.1.4 + object.assign: 4.1.5 regexp.prototype.flags: 1.5.2 - safe-array-concat: 1.0.1 - safe-regex-test: 1.0.0 - string.prototype.trim: 1.2.7 - string.prototype.trimend: 1.0.6 - string.prototype.trimstart: 1.0.6 - typed-array-buffer: 1.0.0 - typed-array-byte-length: 1.0.0 - typed-array-byte-offset: 1.0.0 - typed-array-length: 1.0.4 + safe-array-concat: 1.1.2 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.8 + string.prototype.trimend: 1.0.7 + string.prototype.trimstart: 1.0.7 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.5 unbox-primitive: 1.0.2 which-typed-array: 1.1.14 @@ -11284,40 +11278,42 @@ packages: isarray: 2.0.5 stop-iteration-iterator: 1.0.0 - /es-iterator-helpers@1.0.15: - resolution: {integrity: sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==} + /es-iterator-helpers@1.0.17: + resolution: {integrity: sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==} + engines: {node: '>= 0.4'} dependencies: asynciterator.prototype: 1.0.0 call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 - es-set-tostringtag: 2.0.1 + es-abstract: 1.22.5 + es-errors: 1.3.0 + es-set-tostringtag: 2.0.3 function-bind: 1.1.2 get-intrinsic: 1.2.4 globalthis: 1.0.3 has-property-descriptors: 1.0.2 - has-proto: 1.0.1 + has-proto: 1.0.3 has-symbols: 1.0.3 - internal-slot: 1.0.5 + internal-slot: 1.0.7 iterator.prototype: 1.1.2 - safe-array-concat: 1.0.1 + safe-array-concat: 1.1.2 dev: true /es-module-lexer@1.3.0: resolution: {integrity: sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==} - /es-set-tostringtag@2.0.1: - resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} + /es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} engines: {node: '>= 0.4'} dependencies: get-intrinsic: 1.2.4 - has: 1.0.3 has-tostringtag: 1.0.2 + hasown: 2.0.2 - /es-shim-unscopables@1.0.0: - resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} + /es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} dependencies: - has: 1.0.3 + hasown: 2.0.2 /es-to-primitive@1.2.1: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} @@ -11406,8 +11402,8 @@ packages: confusing-browser-globals: 1.0.11 eslint: 8.56.0 eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-webpack@0.13.8)(eslint@8.56.0) - object.assign: 4.1.4 - object.entries: 1.1.6 + object.assign: 4.1.5 + object.entries: 1.1.7 semver: 6.3.1 dev: true @@ -11426,7 +11422,7 @@ packages: eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-webpack@0.13.8)(eslint@8.56.0) dev: true - /eslint-config-airbnb@19.0.4(eslint-plugin-import@2.29.1)(eslint-plugin-jsx-a11y@6.7.1)(eslint-plugin-react-hooks@4.6.0)(eslint-plugin-react@7.33.2)(eslint@8.56.0): + /eslint-config-airbnb@19.0.4(eslint-plugin-import@2.29.1)(eslint-plugin-jsx-a11y@6.7.1)(eslint-plugin-react-hooks@4.6.0)(eslint-plugin-react@7.34.1)(eslint@8.56.0): resolution: {integrity: sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==} engines: {node: ^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -11440,10 +11436,10 @@ packages: eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.29.1)(eslint@8.56.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-webpack@0.13.8)(eslint@8.56.0) eslint-plugin-jsx-a11y: 6.7.1(eslint@8.56.0) - eslint-plugin-react: 7.33.2(eslint@8.56.0) + eslint-plugin-react: 7.34.1(eslint@8.56.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.56.0) - object.assign: 4.1.4 - object.entries: 1.1.6 + object.assign: 4.1.5 + object.entries: 1.1.7 dev: true /eslint-config-prettier@9.1.0(eslint@8.56.0): @@ -11477,7 +11473,7 @@ packages: enhanced-resolve: 0.9.1 eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-webpack@0.13.8)(eslint@8.56.0) find-root: 1.1.0 - hasown: 2.0.0 + hasown: 2.0.2 interpret: 1.4.0 is-core-module: 2.13.1 is-regex: 1.1.4 @@ -11561,7 +11557,7 @@ packages: eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-webpack@0.13.8)(eslint@8.56.0) - hasown: 2.0.0 + hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 minimatch: 3.1.2 @@ -11596,19 +11592,20 @@ packages: jsx-ast-utils: 3.3.3 language-tags: 1.0.5 minimatch: 3.1.2 - object.entries: 1.1.6 + object.entries: 1.1.7 object.fromentries: 2.0.7 semver: 6.3.1 dev: true - /eslint-plugin-mocha@10.3.0(eslint@8.56.0): - resolution: {integrity: sha512-IWzbg2K6B1Q7h37Ih4zMyW+nhmw1JvUlHlbCUUUu6PfOOAUGCB0gxmvv7/U+TQQ6e8yHUv+q7KMdIIum4bx+PA==} + /eslint-plugin-mocha@10.4.1(eslint@8.56.0): + resolution: {integrity: sha512-G85ALUgKaLzuEuHhoW3HVRgPTmia6njQC3qCG6CEvA8/Ja9PDZnRZOuzekMki+HaViEQXINuYsmhp5WR5/4MfA==} engines: {node: '>=14.0.0'} peerDependencies: eslint: '>=7.0.0' dependencies: eslint: 8.56.0 eslint-utils: 3.0.0(eslint@8.56.0) + globals: 13.24.0 rambda: 7.5.0 dev: true @@ -11621,29 +11618,31 @@ packages: eslint: 8.56.0 dev: true - /eslint-plugin-react@7.33.2(eslint@8.56.0): - resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} + /eslint-plugin-react@7.34.1(eslint@8.56.0): + resolution: {integrity: sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==} engines: {node: '>=4'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 dependencies: array-includes: 3.1.7 + array.prototype.findlast: 1.2.4 array.prototype.flatmap: 1.3.2 - array.prototype.tosorted: 1.1.1 + array.prototype.toreversed: 1.1.2 + array.prototype.tosorted: 1.1.3 doctrine: 2.1.0 - es-iterator-helpers: 1.0.15 + es-iterator-helpers: 1.0.17 eslint: 8.56.0 estraverse: 5.3.0 jsx-ast-utils: 3.3.3 minimatch: 3.1.2 - object.entries: 1.1.6 + object.entries: 1.1.7 object.fromentries: 2.0.7 - object.hasown: 1.1.2 + object.hasown: 1.1.3 object.values: 1.1.7 prop-types: 15.8.1 resolve: 2.0.0-next.5 semver: 6.3.1 - string.prototype.matchall: 4.0.8 + string.prototype.matchall: 4.0.10 dev: true /eslint-rule-composer@0.3.0: @@ -11714,7 +11713,7 @@ packages: file-entry-cache: 6.0.1 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.19.0 + globals: 13.24.0 graphemer: 1.4.0 ignore: 5.3.1 imurmurhash: 0.1.4 @@ -11832,8 +11831,8 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - /exceljs@4.3.0: - resolution: {integrity: sha512-hTAeo5b5TPvf8Z02I2sKIT4kSfCnOO2bCxYX8ABqODCdAjppI3gI9VYiGCQQYVcBaBSKlFDMKlAQRqC+kV9O8w==} + /exceljs@4.4.0: + resolution: {integrity: sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==} engines: {node: '>=8.3.0'} dependencies: archiver: 5.3.1 @@ -12442,13 +12441,13 @@ packages: /function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - /function.prototype.name@1.1.5: - resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + /function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 + es-abstract: 1.22.5 functions-have-names: 1.2.3 /functions-have-names@1.2.3: @@ -12509,9 +12508,9 @@ packages: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 - has-proto: 1.0.1 + has-proto: 1.0.3 has-symbols: 1.0.3 - hasown: 2.0.0 + hasown: 2.0.2 /get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} @@ -12565,11 +12564,12 @@ packages: engines: {node: '>=16'} dev: false - /get-symbol-description@1.0.0: - resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + /get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 + es-errors: 1.3.0 get-intrinsic: 1.2.4 /get-tsconfig@4.7.2: @@ -12669,17 +12669,6 @@ packages: minipass: 7.0.4 path-scurry: 1.10.1 - /glob@7.1.4: - resolution: {integrity: sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - dev: true - /glob@7.1.6: resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} dependencies: @@ -12740,8 +12729,8 @@ packages: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} - /globals@13.19.0: - resolution: {integrity: sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==} + /globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} dependencies: type-fest: 0.20.2 @@ -12823,8 +12812,8 @@ packages: csstype: 3.1.3 dev: false - /google-auth-library@9.6.3: - resolution: {integrity: sha512-4CacM29MLC2eT9Cey5GDVK4Q8t+MMp8+OEdOaqD9MG6b0dOyLORaaeJMPQ7EESVgm/+z5EKYyFLxgzBJlJgyHQ==} + /google-auth-library@9.7.0: + resolution: {integrity: sha512-I/AvzBiUXDzLOy4iIZ2W+Zq33W4lcukQv1nl7C8WUA6SQwyQwUwu3waNmWNAvzds//FG8SZ+DnKnW/2k6mQS8A==} engines: {node: '>=14'} dependencies: base64-js: 1.5.1 @@ -12844,7 +12833,7 @@ packages: dependencies: extend: 3.0.2 gaxios: 6.1.1 - google-auth-library: 9.6.3 + google-auth-library: 9.7.0 qs: 6.11.0 url-template: 2.0.8 uuid: 9.0.1 @@ -12961,8 +12950,8 @@ packages: dependencies: es-define-property: 1.0.0 - /has-proto@1.0.1: - resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + /has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} engines: {node: '>= 0.4'} /has-symbols@1.0.3: @@ -13024,8 +13013,8 @@ packages: type-fest: 0.8.1 dev: true - /hasown@2.0.0: - resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} + /hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} dependencies: function-bind: 1.1.2 @@ -13339,11 +13328,11 @@ packages: queue: 6.0.2 dev: false - /imask@7.3.0: - resolution: {integrity: sha512-TG+/rfb62JaQDM2KVrzEHMb4lv2srbsby7vHndXhqgQFB1MgPIXl60VQUfly/Xv5iWfA9ytB+rfQ+skUgINw7A==} + /imask@7.5.0: + resolution: {integrity: sha512-eoTEnw67KAamB1zsiYtU35s0Fj1XYZ8mN2q3ZDGO4ot4FtPmBpw9S6kOTj0kaOILdsEA6ZhNtH2TAMXe/NChmQ==} engines: {npm: '>=4.0.0'} dependencies: - '@babel/runtime-corejs3': 7.23.7 + '@babel/runtime-corejs3': 7.24.0 dev: false /immediate@3.0.6: @@ -13439,12 +13428,12 @@ packages: wrap-ansi: 7.0.0 dev: true - /internal-slot@1.0.5: - resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} + /internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} dependencies: - get-intrinsic: 1.2.4 - has: 1.0.3 + es-errors: 1.3.0 + hasown: 2.0.2 side-channel: 1.0.4 /internmap@1.0.1: @@ -13512,12 +13501,12 @@ packages: call-bind: 1.0.7 has-tostringtag: 1.0.2 - /is-array-buffer@3.0.2: - resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + /is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 - is-typed-array: 1.1.12 /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} @@ -13575,7 +13564,7 @@ packages: /is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: - hasown: 2.0.0 + hasown: 2.0.2 /is-data-descriptor@0.1.4: resolution: {integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==} @@ -13710,8 +13699,8 @@ packages: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} dev: true - /is-negative-zero@2.0.2: - resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + /is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} /is-number-object@1.0.7: @@ -13789,8 +13778,9 @@ packages: /is-set@2.0.2: resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} - /is-shared-array-buffer@1.0.2: - resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + /is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 @@ -13841,8 +13831,8 @@ packages: text-extensions: 1.9.0 dev: true - /is-typed-array@1.1.12: - resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} + /is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} dependencies: which-typed-array: 1.1.14 @@ -14075,7 +14065,7 @@ packages: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.21 + '@types/node': 18.19.25 jest-mock: 29.7.0 jest-util: 29.7.0 dev: false @@ -14104,7 +14094,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 18.19.21 + '@types/node': 18.19.25 jest-util: 29.7.0 dev: false @@ -14113,7 +14103,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 18.19.21 + '@types/node': 18.19.25 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -14136,7 +14126,7 @@ packages: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 merge-stream: 2.0.0 supports-color: 7.2.0 dev: true @@ -14145,7 +14135,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -14153,7 +14143,7 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -14556,7 +14546,7 @@ packages: engines: {node: '>=4.0'} dependencies: array-includes: 3.1.7 - object.assign: 4.1.4 + object.assign: 4.1.5 dev: true /jszip@3.10.1: @@ -14635,11 +14625,11 @@ packages: - supports-color dev: true - /karma-firefox-launcher@2.1.2: - resolution: {integrity: sha512-VV9xDQU1QIboTrjtGVD4NCfzIH7n01ZXqy/qpBhnOeGVOkG5JYPEm8kuSd7psHE6WouZaQ9Ool92g8LFweSNMA==} + /karma-firefox-launcher@2.1.3: + resolution: {integrity: sha512-LMM2bseebLbYjODBOVt7TCPP9OI2vZIXCavIXhkO9m+10Uj5l7u/SKoeRmYx8FYHTVGZSpk6peX+3BMHC1WwNw==} dependencies: is-wsl: 2.2.0 - which: 2.0.2 + which: 3.0.1 dev: true /karma-mocha@2.0.1: @@ -14771,7 +14761,7 @@ packages: dependencies: '@lerna/create': 8.1.2 '@npmcli/run-script': 7.0.2 - '@nx/devkit': 17.2.8(nx@17.2.8) + '@nx/devkit': 17.2.8(nx@17.3.2) '@octokit/plugin-enterprise-rest': 6.0.1 '@octokit/rest': 19.0.11 byte-size: 8.1.1 @@ -14814,7 +14804,7 @@ packages: npm-packlist: 5.1.1 npm-registry-fetch: 14.0.5 npmlog: 6.0.2 - nx: 17.2.8 + nx: 17.3.2 p-map: 4.0.0 p-map-series: 2.1.0 p-pipe: 3.1.0 @@ -15346,8 +15336,8 @@ packages: uc.micro: 2.0.0 dev: true - /markdown-to-jsx@7.4.1(react@18.2.0): - resolution: {integrity: sha512-GbrbkTnHp9u6+HqbPRFJbObi369AgJNXi/sGqq5HRsoZW063xR1XDCaConqq+whfEIAlzB1YPnOgsPc7B7bc/A==} + /markdown-to-jsx@7.4.3(react@18.2.0): + resolution: {integrity: sha512-qwu2XftKs/SP+f6oCe0ruAFKX6jZaKxrBfDBV4CthqbVbRQwHhNM28QGDQuTldCaOn+hocaqbmGvCuXO5m3smA==} engines: {node: '>= 10'} peerDependencies: react: '>= 0.14.0' @@ -16551,8 +16541,8 @@ packages: resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==} dev: false - /nx@17.2.8: - resolution: {integrity: sha512-rM5zXbuXLEuqQqcjVjClyvHwRJwt+NVImR2A6KFNG40Z60HP6X12wAxxeLHF5kXXTDRU0PFhf/yACibrpbPrAw==} + /nx@17.3.2: + resolution: {integrity: sha512-QjF1gnwKebQISvATrSbW7dsmIcLbA0fcyDyxLo5wVHx/MIlcaIb/lLYaPTld73ZZ6svHEZ6n2gOkhMitmkIPQA==} hasBin: true requiresBuild: true peerDependencies: @@ -16564,7 +16554,7 @@ packages: '@swc/core': optional: true dependencies: - '@nrwl/tao': 17.2.8 + '@nrwl/tao': 17.3.2 '@yarnpkg/lockfile': 1.1.0 '@yarnpkg/parsers': 3.0.0-rc.46 '@zkochan/js-yaml': 0.0.6 @@ -16579,17 +16569,17 @@ packages: figures: 3.2.0 flat: 5.0.2 fs-extra: 11.2.0 - glob: 7.1.4 ignore: 5.3.1 jest-diff: 29.7.0 js-yaml: 4.1.0 jsonc-parser: 3.2.0 lines-and-columns: 2.0.3 - minimatch: 3.0.5 + minimatch: 9.0.3 node-machine-id: 1.1.12 npm-run-path: 4.0.1 open: 8.4.0 - semver: 7.5.3 + ora: 5.3.0 + semver: 7.6.0 string-width: 4.2.3 strong-log-transformer: 2.1.0 tar-stream: 2.2.0 @@ -16599,16 +16589,16 @@ packages: yargs: 17.7.2 yargs-parser: 21.1.1 optionalDependencies: - '@nx/nx-darwin-arm64': 17.2.8 - '@nx/nx-darwin-x64': 17.2.8 - '@nx/nx-freebsd-x64': 17.2.8 - '@nx/nx-linux-arm-gnueabihf': 17.2.8 - '@nx/nx-linux-arm64-gnu': 17.2.8 - '@nx/nx-linux-arm64-musl': 17.2.8 - '@nx/nx-linux-x64-gnu': 17.2.8 - '@nx/nx-linux-x64-musl': 17.2.8 - '@nx/nx-win32-arm64-msvc': 17.2.8 - '@nx/nx-win32-x64-msvc': 17.2.8 + '@nx/nx-darwin-arm64': 17.3.2 + '@nx/nx-darwin-x64': 17.3.2 + '@nx/nx-freebsd-x64': 17.3.2 + '@nx/nx-linux-arm-gnueabihf': 17.3.2 + '@nx/nx-linux-arm64-gnu': 17.3.2 + '@nx/nx-linux-arm64-musl': 17.3.2 + '@nx/nx-linux-x64-gnu': 17.3.2 + '@nx/nx-linux-x64-musl': 17.3.2 + '@nx/nx-win32-arm64-msvc': 17.3.2 + '@nx/nx-win32-x64-msvc': 17.3.2 transitivePeerDependencies: - debug dev: true @@ -16676,8 +16666,8 @@ packages: engines: {node: '>= 6'} dev: true - /object-inspect@1.12.3: - resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} /object-is@1.1.5: resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} @@ -16700,8 +16690,8 @@ packages: isobject: 3.0.1 dev: false - /object.assign@4.1.4: - resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + /object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 @@ -16709,13 +16699,13 @@ packages: has-symbols: 1.0.3 object-keys: 1.1.1 - /object.entries@1.1.6: - resolution: {integrity: sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==} + /object.entries@1.1.7: + resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 + es-abstract: 1.22.5 /object.fromentries@2.0.7: resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} @@ -16723,7 +16713,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 + es-abstract: 1.22.5 /object.getownpropertydescriptors@2.1.4: resolution: {integrity: sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==} @@ -16732,7 +16722,7 @@ packages: array.prototype.reduce: 1.0.4 call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 + es-abstract: 1.22.5 dev: true /object.groupby@1.0.1: @@ -16740,15 +16730,15 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 + es-abstract: 1.22.5 get-intrinsic: 1.2.4 dev: true - /object.hasown@1.1.2: - resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} + /object.hasown@1.1.3: + resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==} dependencies: define-properties: 1.2.1 - es-abstract: 1.22.1 + es-abstract: 1.22.5 dev: true /object.omit@3.0.0: @@ -16771,7 +16761,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 + es-abstract: 1.22.5 /oh-no-i-insist@1.1.1: resolution: {integrity: sha512-Jfc1rBoS9dMIz+OcWjUibUXQJ21ju1+4Mr0ZNAdGN8VfIU3nqt+unyWlcSjINo0VGwJBMQwkzddiytUdTkg9+w==} @@ -16851,6 +16841,20 @@ packages: type-check: 0.4.0 dev: true + /ora@5.3.0: + resolution: {integrity: sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==} + engines: {node: '>=10'} + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.7.0 + is-interactive: 1.0.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + dev: true + /ora@5.4.1: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} @@ -17398,6 +17402,7 @@ packages: /possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} + requiresBuild: true /postcss-cli@8.3.1(postcss@8.4.35): resolution: {integrity: sha512-leHXsQRq89S3JC9zw/tKyiVV2jAhnfQe0J8VI4eQQbUjwIe0XxVqLrR+7UsahF1s9wi4GlqP6SJ8ydf44cgF2Q==} @@ -17717,7 +17722,7 @@ packages: array.prototype.map: 1.0.5 call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 + es-abstract: 1.22.5 get-intrinsic: 1.2.4 iterate-value: 1.0.2 dev: false @@ -17747,7 +17752,7 @@ packages: resolution: {integrity: sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA==} dependencies: has: 1.0.3 - object.assign: 4.1.4 + object.assign: 4.1.5 reflect.ownkeys: 0.2.0 /prop-types@15.8.1: @@ -18026,23 +18031,28 @@ packages: react: 18.2.0 dev: false - /react-imask@7.3.0(react@18.2.0): - resolution: {integrity: sha512-AHoQUeXil6PfqDzJHN08hO2liWxNDRJosNUa2XSqliFY2tXGL/3Elm0msupDNAyNPItAnyF9G5FGFoCfiCn+AQ==} + /react-imask@7.5.0(react@18.2.0): + resolution: {integrity: sha512-yWExhHphDmNaHvmOsYR+5QcludeBdYk6bXyo8kouIJFAub5sF+O0kLPVupg2yhd7EfTqjLswFZ/tqY1AhKnd9Q==} engines: {npm: '>=4.0.0'} peerDependencies: react: '>=0.14.0' dependencies: - imask: 7.3.0 + imask: 7.5.0 prop-types: 15.8.1 react: 18.2.0 dev: false - /react-intersection-observer@9.5.3(react@18.2.0): - resolution: {integrity: sha512-NJzagSdUPS5rPhaLsHXYeJbsvdpbJwL6yCHtMk91hc0ufQ2BnXis+0QQ9NBh6n9n+Q3OyjR6OQLShYbaNBkThQ==} + /react-intersection-observer@9.8.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-QzOFdROX8D8MH3wE3OVKH0f3mLjKTtEN1VX/rkNuECCff+aKky0pIjulDhr3Ewqj5el/L+MhBkM3ef0Tbt+qUQ==} peerDependencies: react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react-dom: + optional: true dependencies: react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) dev: false /react-is@16.13.1: @@ -18360,8 +18370,8 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false - /react-virtuoso@4.6.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-NcoSsf4B0OCx7U8i2s+VWe8b9e+FWzcN/5ly4hKjErynBzGONbWORZ1C5amUlWrPi6+HbUQ2PjnT4OpyQIpP9A==} + /react-virtuoso@4.7.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-xF4es1ajK36Hs9MciaiGA9l16tV2bpkpgxM9JVKh0rJSn6uhdAUrxSQ6FttQOOPAGt4GRG2A4gdjlo18JWhyRw==} engines: {node: '>=10'} peerDependencies: react: '>=16 || >=17 || >= 18' @@ -18566,8 +18576,8 @@ packages: tslib: 2.6.2 dev: false - /recast@0.23.5: - resolution: {integrity: sha512-M67zIddJiwXdfPQRYKJ0qZO1SLdH1I0hYeb0wzxA+pNOvAZiQHulWzuk+fYsEWRQ8VfZrgjyucqsCOtCyM01/A==} + /recast@0.23.6: + resolution: {integrity: sha512-9FHoNjX1yjuesMwuthAmPKabxYQdOgihFYmT5ebXfYGBcnqXZf3WOVz+5foEZ8Y83P4ZY6yQD5GMmtV+pgCCAQ==} engines: {node: '>= 4'} dependencies: ast-types: 0.16.1 @@ -18618,7 +18628,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 + es-abstract: 1.22.5 get-intrinsic: 1.2.4 globalthis: 1.0.3 which-builtin-type: 1.1.3 @@ -19034,8 +19044,8 @@ packages: tslib: 2.6.2 dev: true - /safe-array-concat@1.0.1: - resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} + /safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} engines: {node: '>=0.4'} dependencies: call-bind: 1.0.7 @@ -19049,11 +19059,12 @@ packages: /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - /safe-regex-test@1.0.0: - resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + /safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 - get-intrinsic: 1.2.4 + es-errors: 1.3.0 is-regex: 1.1.4 /safe-regex@1.1.0: @@ -19246,6 +19257,7 @@ packages: /set-function-length@1.2.1: resolution: {integrity: sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==} engines: {node: '>= 0.4'} + requiresBuild: true dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 @@ -19344,7 +19356,7 @@ packages: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 - object-inspect: 1.12.3 + object-inspect: 1.13.1 /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -19742,7 +19754,7 @@ packages: resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} engines: {node: '>= 0.4'} dependencies: - internal-slot: 1.0.5 + internal-slot: 1.0.7 /stream-combiner@0.0.4: resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} @@ -19801,40 +19813,41 @@ packages: emoji-regex: 9.2.2 strip-ansi: 7.0.1 - /string.prototype.matchall@4.0.8: - resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} + /string.prototype.matchall@4.0.10: + resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 + es-abstract: 1.22.5 get-intrinsic: 1.2.4 has-symbols: 1.0.3 - internal-slot: 1.0.5 + internal-slot: 1.0.7 regexp.prototype.flags: 1.5.2 + set-function-name: 2.0.1 side-channel: 1.0.4 dev: true - /string.prototype.trim@1.2.7: - resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} + /string.prototype.trim@1.2.8: + resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 + es-abstract: 1.22.5 - /string.prototype.trimend@1.0.6: - resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} + /string.prototype.trimend@1.0.7: + resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 + es-abstract: 1.22.5 - /string.prototype.trimstart@1.0.6: - resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} + /string.prototype.trimstart@1.0.7: + resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.1 + es-abstract: 1.22.5 /string_decoder@0.10.31: resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} @@ -20726,39 +20739,45 @@ packages: media-typer: 0.3.0 mime-types: 2.1.35 - /typed-array-buffer@1.0.0: - resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} + /typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 - get-intrinsic: 1.2.4 - is-typed-array: 1.1.12 + es-errors: 1.3.0 + is-typed-array: 1.1.13 - /typed-array-byte-length@1.0.0: - resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} + /typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 for-each: 0.3.3 - has-proto: 1.0.1 - is-typed-array: 1.1.12 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 - /typed-array-byte-offset@1.0.0: - resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} + /typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} engines: {node: '>= 0.4'} dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.7 for-each: 0.3.3 - has-proto: 1.0.1 - is-typed-array: 1.1.12 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 - /typed-array-length@1.0.4: - resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + /typed-array-length@1.0.5: + resolution: {integrity: sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==} + engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 for-each: 0.3.3 - is-typed-array: 1.1.12 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + possible-typed-array-names: 1.0.0 /typedarray-to-buffer@3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} @@ -20990,6 +21009,10 @@ packages: punycode: 1.3.2 querystring: 0.2.0 + /urlpattern-polyfill@8.0.2: + resolution: {integrity: sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==} + dev: false + /use-count-up@3.0.1(react@18.2.0): resolution: {integrity: sha512-jlVsXJYje6jh+xwQaCEYrwHoB+nRyillNEmr21bhe9kw7tpRzyrSq9jQs9UOlo+8hCFkuOmjUihL3IjEK/piVg==} peerDependencies: @@ -21030,7 +21053,7 @@ packages: inherits: 2.0.4 is-arguments: 1.1.1 is-generator-function: 1.0.10 - is-typed-array: 1.1.12 + is-typed-array: 1.1.13 which-typed-array: 1.1.14 /utila@0.4.0: @@ -21110,12 +21133,12 @@ packages: vfile-message: 2.0.4 dev: false - /vite@5.0.12(@types/node@18.19.21): + /vite@5.0.12(@types/node@18.19.25): resolution: {integrity: sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: - '@types/node': ^18.19.21 + '@types/node': ^18.19.25 less: '*' lightningcss: ^1.21.0 sass: '*' @@ -21138,7 +21161,7 @@ packages: terser: optional: true dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.25 esbuild: 0.19.11 postcss: 8.4.35 rollup: 4.9.2 @@ -21385,7 +21408,7 @@ packages: resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} engines: {node: '>= 0.4'} dependencies: - function.prototype.name: 1.1.5 + function.prototype.name: 1.1.6 has-tostringtag: 1.0.2 is-async-function: 2.0.0 is-date-object: 1.0.5 @@ -21434,6 +21457,14 @@ packages: dependencies: isexe: 2.0.0 + /which@3.0.1: + resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + /which@4.0.0: resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} engines: {node: ^16.13.0 || >=18.0.0} diff --git a/scripts/README.md b/scripts/README.md index 36cdbda0519c27..713c7947b497ad 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -28,7 +28,7 @@ The following steps must be proposed as a pull request. 2. Clean the generated changelog: 1. Match the format of https://github.com/mui/material-ui/releases. - 2. Change the packages names casing to be lowercase if applicable, e.g. change `Material` to `material` + 2. Change the packages names casing to be lowercase if applicable 3. Update the root `/package.json`'s version 4. Run `pnpm release:version`. Keep the package versions of stable public packages the same as the root `package.json` version. 5. Open PR with changes and wait for review and green CI @@ -54,7 +54,7 @@ Follow the instructions in https://mui-org.notion.site/Releases-7490ef9581b4447e Sometimes it is necessary to deploy the selected commit(s) without deploying all the changes that have been merged into the main branch -since the previous release (e.g. publishing a blog post or releasing +since the previous release (for example publishing a blog post or releasing urgent docs updates). To do so, follow these steps: diff --git a/scripts/copyFiles.mjs b/scripts/copyFiles.mjs index 27eeaa53ac89e1..6a22cb96604213 100644 --- a/scripts/copyFiles.mjs +++ b/scripts/copyFiles.mjs @@ -52,9 +52,10 @@ async function run() { const packageData = await createPackageFile(); await Promise.all( - ['./README.md', '../../CHANGELOG.md', '../../LICENSE', ...extraFiles].map((file) => - includeFileInBuild(file), - ), + ['./README.md', '../../CHANGELOG.md', '../../LICENSE', ...extraFiles].map(async (file) => { + const [sourcePath, targetPath] = file.split(':'); + await includeFileInBuild(sourcePath, targetPath); + }), ); await addLicense(packageData); diff --git a/scripts/copyFilesUtils.mjs b/scripts/copyFilesUtils.mjs index f9a55addab0ca7..40b05542614f9e 100644 --- a/scripts/copyFilesUtils.mjs +++ b/scripts/copyFilesUtils.mjs @@ -6,9 +6,17 @@ import glob from 'fast-glob'; const packagePath = process.cwd(); const buildPath = path.join(packagePath, './build'); -export async function includeFileInBuild(file) { +/** + * Copies a file into the build directory. By default it copies it under the same + * base name in the root, but you can provide a second argument to specify a + * different subpath. + * @param {string} file source file path + * @param {string=} target target file path + * @returns {Promise<void>} + */ +export async function includeFileInBuild(file, target = path.basename(file)) { const sourcePath = path.resolve(packagePath, file); - const targetPath = path.resolve(buildPath, path.basename(file)); + const targetPath = path.resolve(buildPath, target); await fse.copy(sourcePath, targetPath); console.log(`Copied ${sourcePath} to ${targetPath}`); } diff --git a/scripts/coreTypeScriptProjects.js b/scripts/coreTypeScriptProjects.js index 9d512434015770..acdb8bc42c5666 100644 --- a/scripts/coreTypeScriptProjects.js +++ b/scripts/coreTypeScriptProjects.js @@ -21,10 +21,6 @@ export default { rootPath: path.join(process.cwd(), 'packages/mui-system'), entryPointPath: 'src/index.d.ts', }, - 'material-next': { - rootPath: path.join(process.cwd(), 'packages/mui-material-next'), - entryPointPath: 'src/index.ts', - }, docs: { rootPath: path.join(process.cwd(), 'docs'), tsConfigPath: 'tsconfig.json', diff --git a/scripts/generateProptypes.ts b/scripts/generateProptypes.ts index f029876db67f78..c75ec6f11e0cdd 100644 --- a/scripts/generateProptypes.ts +++ b/scripts/generateProptypes.ts @@ -10,7 +10,7 @@ import { fixBabelGeneratorIssues, fixLineEndings, getUnstyledFilename, -} from '@mui-internal/docs-utils'; +} from '@mui/internal-docs-utils'; import { getPropTypesFromFile, injectPropTypesInFile, @@ -188,7 +188,7 @@ async function generateProptypes( return; } - // exclude internal slot components, e.g. ButtonRoot + // exclude internal slot components, for example ButtonRoot const cleanComponents = components.filter((component) => { if (component.propsFilename?.endsWith('.tsx')) { // only check for .tsx @@ -334,7 +334,6 @@ async function run(argv: HandlerArgv) { path.resolve(__dirname, '../packages/mui-base/src'), path.resolve(__dirname, '../packages/mui-material/src'), path.resolve(__dirname, '../packages/mui-lab/src'), - path.resolve(__dirname, '../packages/mui-material-next/src'), path.resolve(__dirname, '../packages/mui-joy/src'), ].map((folderPath) => glob('+([A-Z])*/+([A-Z])*.*@(d.ts|ts|tsx)', { diff --git a/scripts/pigmentcss-render-mui-demos.mjs b/scripts/pigmentcss-render-mui-demos.mjs index 0dbb457ef40ea4..c099bbcc52dfd9 100644 --- a/scripts/pigmentcss-render-mui-demos.mjs +++ b/scripts/pigmentcss-render-mui-demos.mjs @@ -73,13 +73,13 @@ ${renders.join('\n')} // Create the page in pigment apps const nextFilepath = path.join( process.cwd(), - `apps/pigment-next-app/src/app/material-ui/${args[0]}/page.tsx`, + `apps/pigment-css-next-app/src/app/material-ui/${args[0]}/page.tsx`, ); const prettiedNextFileContent = await prettier.format(nextFileContent, { ...prettierConfig, filepath: nextFilepath, }); - await fse.mkdirp(`apps/pigment-next-app/src/app/material-ui/${args[0]}`); + await fse.mkdirp(`apps/pigment-css-next-app/src/app/material-ui/${args[0]}`); await fse.writeFile(nextFilepath, prettiedNextFileContent); /** @@ -105,13 +105,13 @@ ${renders.join('\n')} // Create the page in pigment apps const viteFilepath = path.join( process.cwd(), - `apps/pigment-vite-app/src/pages/material-ui/${args[0]}.tsx`, + `apps/pigment-css-vite-app/src/pages/material-ui/${args[0]}.tsx`, ); const prettiedViteFileContent = await prettier.format(viteFileContent, { ...prettierConfig, filepath: viteFilepath, }); - await fse.mkdirp(`apps/pigment-vite-app/src/pages/material-ui`); + await fse.mkdirp(`apps/pigment-css-vite-app/src/pages/material-ui`); await fse.writeFile(viteFilepath, prettiedViteFileContent); } diff --git a/scripts/releaseChangelog.mjs b/scripts/releaseChangelog.mjs index 6f8d5c7808c1bb..2c6b15abcf84ee 100644 --- a/scripts/releaseChangelog.mjs +++ b/scripts/releaseChangelog.mjs @@ -195,7 +195,7 @@ yargs(process.argv.slice(2)) }) .option('release', { // #default-branch-switch - default: 'master', + default: 'next', describe: 'Ref which we want to release', type: 'string', }) diff --git a/scripts/sizeSnapshot/webpack.config.js b/scripts/sizeSnapshot/webpack.config.js index c32d969700b063..0d40434955fc49 100644 --- a/scripts/sizeSnapshot/webpack.config.js +++ b/scripts/sizeSnapshot/webpack.config.js @@ -55,18 +55,6 @@ async function getWebpackEntries() { }, ); - const materialNextPackagePath = path.join(workspaceRoot, 'packages/mui-material-next/build'); - const materialNextComponents = ( - await glob(path.join(materialNextPackagePath, '([A-Z])*/index.js')) - ).map((componentPath) => { - const componentName = path.basename(path.dirname(componentPath)); - - return { - id: `@mui/material-next/${componentName}`, - path: path.relative(workspaceRoot, path.dirname(componentPath)), - }; - }); - const joyPackagePath = path.join(workspaceRoot, 'packages/mui-joy/build'); const joyComponents = (await glob(path.join(joyPackagePath, '([A-Z])*/index.js'))).map( (componentPath) => { @@ -152,11 +140,6 @@ async function getWebpackEntries() { id: '@material-ui/core.legacy', path: path.join(path.relative(workspaceRoot, materialPackagePath), 'legacy/index.js'), }, - { - id: '@mui/material-next', - path: path.join(path.relative(workspaceRoot, materialNextPackagePath), 'index.js'), - }, - ...materialNextComponents, { id: '@mui/joy', path: path.join(path.relative(workspaceRoot, joyPackagePath), 'index.js'), @@ -218,7 +201,6 @@ function createWebpackConfig(entry, environment) { '@mui/private-theming': path.join(workspaceRoot, 'packages/mui-private-theming/build'), '@mui/utils': path.join(workspaceRoot, 'packages/mui-utils/build'), '@mui/base': path.join(workspaceRoot, 'packages/mui-base/build'), - '@mui/material-next': path.join(workspaceRoot, 'packages/mui-material-next/build'), '@mui/material-nextjs': path.join(workspaceRoot, 'packages/mui-material-nextjs/build'), '@mui/joy': path.join(workspaceRoot, 'packages/mui-joy/build'), }, diff --git a/test/README.md b/test/README.md index 4b2c21bb2e98dd..e25729d4971ee1 100644 --- a/test/README.md +++ b/test/README.md @@ -43,11 +43,11 @@ In addition to the core matchers from `chai` we also use matchers from [`chai-do Deciding where to put a test is (like naming things) a hard problem: -- When in doubt, put the new test case directly in the unit test file for that component e.g. `packages/mui-material/src/Button/Button.test.js`. +- When in doubt, put the new test case directly in the unit test file for that component, for example `packages/mui-material/src/Button/Button.test.js`. - If your test requires multiple components from the library create a new integration test. - If you find yourself using a lot of `data-testid` attributes or you're accessing a lot of styles consider adding a component (that doesn't require any interaction) - to `test/regressions/tests/` e.g. `test/regressions/tests/List/ListWithSomeStyleProp` + to `test/regressions/tests/`, for example `test/regressions/tests/List/ListWithSomeStyleProp` - If you have to dispatch and compose many different DOM events prefer end-to-end tests (Checkout the [end-to-end testing readme](./e2e/README.md) for more information.) ### Unexpected calls to `console.error` or `console.warn` @@ -115,7 +115,7 @@ trade-off, mainly completeness vs. speed. #### Debugging tests -If you want to debug tests with the e.g. Chrome inspector (chrome://inspect) you can run `pnpm t <testFilePattern> --debug`. +If you want to debug tests with the, for example Chrome inspector (chrome://inspect) you can run `pnpm t <testFilePattern> --debug`. Note that the test will not get executed until you start code execution in the inspector. We have a dedicated task to use VSCode's integrated debugger to debug the currently opened test file. @@ -212,7 +212,7 @@ the accessibility (a11y) tree and which are excluded. This check is fairly expensive which is why it is disabled when tests are run locally by default. The rationale being that in almost all cases including or excluding elements from a query-set depending on their a11y-tree membership makes no difference. -The queries where this does make a difference explicitly include checking for a11y tree inclusion e.g. `getByRole('button', { hidden: false })` (see [byRole documentation](https://testing-library.com/docs/dom-testing-library/api-queries#byrole) for more information). +The queries where this does make a difference explicitly include checking for a11y tree inclusion, for example `getByRole('button', { hidden: false })` (see [byRole documentation](https://testing-library.com/docs/dom-testing-library/api-queries#byrole) for more information). To see if your test (`test:karma` or `test:unit`) behaves the same between CI and local environment, set the environment variable `CI` to `'true'`. Not considering a11y tree exclusion is a common cause of "Unable to find an accessible element with the role" or "Found multiple elements with the role". @@ -245,13 +245,13 @@ For example, in https://app.circleci.com/pipelines/github/mui/material-ui/32796/ ### Testing multiple versions of React -You can check integration of different versions of React (e.g. different [release channels](https://react.dev/community/versioning-policy) or PRs to React) by running `node scripts/useReactVersion.mjs <version>`. +You can check integration of different versions of React (for example different [release channels](https://react.dev/community/versioning-policy) or PRs to React) by running `node scripts/useReactVersion.mjs <version>`. Possible values for `version`: - default: `stable` (minimum supported React version) -- a tag on npm e.g. `next`, `experimental` or `latest` -- an older version e.g. `^17.0.0` +- a tag on npm, for example `next`, `experimental` or `latest` +- an older version, for example `^17.0.0` #### CI diff --git a/test/bundling/README.md b/test/bundling/README.md index 733b241b4a172e..497cbb60db0f57 100644 --- a/test/bundling/README.md +++ b/test/bundling/README.md @@ -8,7 +8,7 @@ The created file might need some manual adjustment since not every edge case is ## Run a fixture 1. Navigate into the fixture you want to test (where the `package.json` is located) -1. Use the node version you want to use (e.g. `nvm use 14.0.0`) +1. Use the node version you want to use (for example `nvm use 14.0.0`) 1. Prepare the package.json - to test a Pull Request 1. checkout branch @@ -17,7 +17,7 @@ The created file might need some manual adjustment since not every edge case is 1. `cd` to fixture 1. `pnpm install` 1. `node ../../scripts/useBuildFromSource.js .` - - to test a published npm dist tag (e.g. `latest` or `next`) on npm + - to test a published npm dist tag (for example `latest` or `next`) on npm 1. `cd` to fixture 1. adjust the dependencies in the package.json accordingly 1. `pnpm install` diff --git a/test/e2e/README.md b/test/e2e/README.md index 9c2903e8c37778..ce3a9c9978b501 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -15,7 +15,7 @@ If you're adding a new test prefer a new component instead of editing existing f We're using [`playwright`](https://playwright.dev) to replay user actions. Each test tests only a single fixture. -A fixture can be loaded with `await renderFixture(fixturePath)` e.g. `renderFixture('FocusTrap/OpenFocusTrap')`. +A fixture can be loaded with `await renderFixture(fixturePath)`, for example `renderFixture('FocusTrap/OpenFocusTrap')`. ## Commands diff --git a/test/integration/mui-system/theme-scoping.test.tsx b/test/integration/mui-system/theme-scoping.test.tsx index 1d1f97c9ad7304..c75f9005c15e97 100644 --- a/test/integration/mui-system/theme-scoping.test.tsx +++ b/test/integration/mui-system/theme-scoping.test.tsx @@ -5,7 +5,6 @@ import { createRenderer } from '@mui-internal/test-utils'; import { ThemeContext } from '@mui/styled-engine'; import * as material from '@mui/material'; import * as joy from '@mui/joy'; -import * as md3 from '@mui/material-next'; // simulate 3rd-party library like Theme-UI, Chakra-UI, or Mantine interface LibTheme { @@ -63,21 +62,6 @@ const CustomMaterial = material.styled('div')(({ theme }) => ({ borderRadius: theme.shape.borderRadius, })); -const md3Theme = md3.extendTheme({ - components: { - MuiButton: { - defaultProps: { - variant: 'outlined', - }, - styleOverrides: { - root: ({ theme }) => ({ - color: theme.vars.palette.error.dark, - }), - }, - }, - }, -}); - describe('Multiple nested theme providers', () => { const { render } = createRenderer(); let originalMatchmedia: any; @@ -140,33 +124,6 @@ describe('Multiple nested theme providers', () => { expect(getByText('Material')).toHaveComputedStyle({ mixBlendMode: 'darken' }); }); - it('MD3 + Joy UI', () => { - const { getByText } = render( - <md3.CssVarsProvider theme={md3Theme}> - <joy.CssVarsProvider theme={{ [joy.THEME_ID]: joyTheme }}> - <joy.Button - sx={(theme) => ({ - // test `sx` - bgcolor: theme.vars.palette.neutral[100], - })} - > - Joy - </joy.Button> - <md3.Button - sx={(theme) => ({ - bgcolor: theme.palette.secondary.light, - })} - > - MD3 - </md3.Button> - </joy.CssVarsProvider> - </md3.CssVarsProvider>, - ); - // these test if `useThemeProps` works with theme scoping - expect(getByText('Joy')).to.have.class(joy.buttonClasses.variantOutlined); - expect(getByText('MD3')).to.have.class(material.buttonClasses.outlined); - }); - it('Material UI works with 3rd-party lib', () => { const { getByText } = render( <LibThemeProvider> diff --git a/test/karma.conf.js b/test/karma.conf.js index 48ced7625cdada..fa08656dd215eb 100644 --- a/test/karma.conf.js +++ b/test/karma.conf.js @@ -160,7 +160,6 @@ module.exports = function setKarmaConfig(config) { '@mui/private-theming': './packages/mui-private-theming/src', '@mui/utils': './packages/mui-utils/src', '@mui/base': './packages/mui-base/src', - '@mui/material-next': './packages/mui-material-next/src', '@mui/material-nextjs': './packages/mui-material-nextjs/src', '@mui/joy': './packages/mui-joy/src', }, diff --git a/test/karma.tests.js b/test/karma.tests.js index 8925bc09cc3bd4..9fdd41a634bc63 100644 --- a/test/karma.tests.js +++ b/test/karma.tests.js @@ -41,12 +41,5 @@ baseUnitContext.keys().forEach(baseUnitContext); const utilsContext = require.context('../packages/mui-utils/src/', true, /\.test\.(js|ts|tsx)$/); utilsContext.keys().forEach(utilsContext); -const materialNextContext = require.context( - '../packages/mui-material-next/src/', - true, - /\.test\.(js|ts|tsx)$/, -); -materialNextContext.keys().forEach(materialNextContext); - const joyContext = require.context('../packages/mui-joy/src', true, /\.test\.(js|ts|tsx)$/); joyContext.keys().forEach(joyContext); diff --git a/test/package.json b/test/package.json index 8e57a12ac6aa65..95c44493b32902 100644 --- a/test/package.json +++ b/test/package.json @@ -15,7 +15,6 @@ "@mui/joy": "workspace:*", "@mui/lab": "workspace:*", "@mui/material": "workspace:^", - "@mui/material-next": "workspace:*", "@mui/system": "workspace:^", "@mui/utils": "workspace:^", "@playwright/test": "1.42.1", diff --git a/test/regressions/README.md b/test/regressions/README.md index 47dfd0eb9df03b..1c579da77afdfd 100644 --- a/test/regressions/README.md +++ b/test/regressions/README.md @@ -29,7 +29,7 @@ If you're adding a new test prefer a new component instead of editing existing f `pnpm test:regressions:dev` will build all fixtures and render an overview page that lists all fixtures. This can be used to debug individual fixtures. -By default, a devtools-like view is shown that can be disabled by appending `#no-dev` to the URL e.g. `http://localhost:5001/docs-customization-typography/CustomResponsiveFontSizes#no-dev` or forced by appending `#dev` to the URL e.g. `http://localhost:5001/docs-customization-typography/CustomResponsiveFontSizes#dev`. +By default, a devtools-like view is shown that can be disabled by appending `#no-dev` to the URL, for example `http://localhost:5001/docs-customization-typography/CustomResponsiveFontSizes#no-dev` or forced by appending `#dev` to the URL, for example `http://localhost:5001/docs-customization-typography/CustomResponsiveFontSizes#dev`. ### Automatic @@ -41,7 +41,7 @@ It allows catching regressions like this one: Screenshots are saved in `./screenshots/$BROWSER_NAME/`. Each test tests only a single fixture. -A fixture can be loaded with `await renderFixture(fixturePath)` e.g. `renderFixture('FocusTrap/OpenFocusTrap')`. +A fixture can be loaded with `await renderFixture(fixturePath)`, for example `renderFixture('FocusTrap/OpenFocusTrap')`. ## Commands diff --git a/test/regressions/fixtures/ButtonNext/IconLabelButtonsNext.js b/test/regressions/fixtures/ButtonNext/IconLabelButtonsNext.js deleted file mode 100644 index 5bf13d5f356586..00000000000000 --- a/test/regressions/fixtures/ButtonNext/IconLabelButtonsNext.js +++ /dev/null @@ -1,26 +0,0 @@ -import * as React from 'react'; -import Button from '@mui/material-next/Button'; -import Icon from '@mui/material/Icon'; -import SendIcon from '@mui/icons-material/Send'; - -export default function IconLabelButtonsNext() { - return ( - <div> - <Button variant="filled" color="secondary" startIcon={<SendIcon />}> - Send - </Button> - <Button variant="filled" endIcon={<Icon>send</Icon>}> - Send - </Button> - <Button variant="filled" disabled color="secondary" startIcon={<SendIcon />}> - Send - </Button> - <Button variant="filled" size="small" startIcon={<SendIcon />}> - Send - </Button> - <Button variant="filled" size="large" startIcon={<SendIcon />}> - Send - </Button> - </div> - ); -} diff --git a/test/regressions/fixtures/ButtonNext/MultilineButtonNext.js b/test/regressions/fixtures/ButtonNext/MultilineButtonNext.js deleted file mode 100644 index 546607ecc4db32..00000000000000 --- a/test/regressions/fixtures/ButtonNext/MultilineButtonNext.js +++ /dev/null @@ -1,14 +0,0 @@ -import * as React from 'react'; -import Button from '@mui/material-next/Button'; - -export default function MultilineButtonNext() { - return ( - <Button variant="filled" style={{ width: 400 }}> - {[ - 'Contained buttons are rectangular-shaped buttons.', - 'They may be used inline.', - 'They lift and display ink reactions on press.', - ].join(' ')} - </Button> - ); -} diff --git a/tsconfig.json b/tsconfig.json index 94f8dff28f53c3..7fa1f4d5fb5964 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -37,20 +37,18 @@ "@mui/utils/*": ["./packages/mui-utils/src/*"], "@mui/docs": ["./packages/mui-docs/src"], "@mui/docs/*": ["./packages/mui-docs/src/*"], - "@mui/material-next": ["./packages/mui-material-next/src"], - "@mui/material-next/*": ["./packages/mui-material-next/src/*"], "@mui/material-nextjs": ["./packages/mui-material-nextjs/src"], "@mui/material-nextjs/*": ["./packages/mui-material-nextjs/src/*"], "@mui/joy": ["./packages/mui-joy/src"], "@mui/joy/*": ["./packages/mui-joy/src/*"], "@mui/icons-material/*": ["./packages/mui-icons-material/src/icon.d.ts"], - "@pigment-css/nextjs-plugin": ["./packages/pigment-nextjs-plugin/src"], - "@pigment-css/nextjs-plugin/*": ["./packages/pigment-nextjs-plugin/src/*"], - "@pigment-css/react": ["./packages/pigment-react/src"], - "@pigment-css/react/*": ["./packages/pigment-react/src/*"], - "@pigment-css/vite-plugin": ["./packages/pigment-vite-plugin/src"], - "@pigment-css/vite-plugin/*": ["./packages/pigment-vite-plugin/src/*"], - "@mui-internal/docs-utils": ["./packages/docs-utils/src"], + "@pigment-css/nextjs-plugin": ["./packages/pigment-css-nextjs-plugin/src"], + "@pigment-css/nextjs-plugin/*": ["./packages/pigment-css-nextjs-plugin/src/*"], + "@pigment-css/react": ["./packages/pigment-css-react/src"], + "@pigment-css/react/*": ["./packages/pigment-css-react/src/*"], + "@pigment-css/vite-plugin": ["./packages/pigment-css-vite-plugin/src"], + "@pigment-css/vite-plugin/*": ["./packages/pigment-css-vite-plugin/src/*"], + "@mui/internal-docs-utils": ["./packages-internal/docs-utils/src"], "@mui/internal-scripts/typescript-to-proptypes": [ "./packages-internal/scripts/typescript-to-proptypes/src" ] diff --git a/tsup.config.ts b/tsup.config.ts index 10795948887c53..159ef80835ef07 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -27,4 +27,7 @@ export default defineConfig({ banner: { js: licenseText, }, + env: { + PACKAGE_NAME: pkgJson.name, + }, }); diff --git a/webpackBaseConfig.js b/webpackBaseConfig.js index 66f7f8121b5e6f..f5d6df1e58de0e 100644 --- a/webpackBaseConfig.js +++ b/webpackBaseConfig.js @@ -21,11 +21,10 @@ module.exports = { '@mui/private-theming': path.resolve(__dirname, './packages/mui-private-theming/src'), '@mui/base': path.resolve(__dirname, './packages/mui-base/src'), '@mui/utils': path.resolve(__dirname, './packages/mui-utils/src'), - '@mui/material-next': path.resolve(__dirname, './packages/mui-material-next/src'), '@mui/material-nextjs': path.resolve(__dirname, './packages/mui-material-nextjs/src'), '@mui/joy': path.resolve(__dirname, './packages/mui-joy/src'), - '@pigment-css/react': path.resolve(__dirname, './packages/pigment-react/src'), - '@mui-internal/docs-utils': path.resolve(__dirname, './packages/docs-utils/src'), + '@pigment-css/react': path.resolve(__dirname, './packages/pigment-css-react/src'), + '@mui/internal-docs-utils': path.resolve(__dirname, './packages-internal/docs-utils/src'), '@mui/internal-scripts/typescript-to-proptypes': path.resolve( __dirname, './packages-internal/scripts/typescript-to-proptypes/src',