import 'docs/src/modules/components/bootstrap'; // --- Post bootstrap ----- import * as React from 'react'; import { loadCSS } from 'fg-loadcss'; import NextHead from 'next/head'; import PropTypes from 'prop-types'; import { useRouter } from 'next/router'; import { LicenseInfo } from '@mui/x-license'; import materialPkgJson from '@mui/material/package.json'; import joyPkgJson from '@mui/joy/package.json'; import systemPkgJson from '@mui/system/package.json'; import generalDocsPages from 'docs/data/docs/pages'; import docsInfraPages from 'docs/data/docs-infra/pages'; import materialPages from 'docs/data/material/pages'; import joyPages from 'docs/data/joy/pages'; import systemPages from 'docs/data/system/pages'; import PageContext from 'docs/src/modules/components/PageContext'; import GoogleAnalytics from 'docs/src/modules/components/GoogleAnalytics'; import { CodeCopyProvider } from '@mui/docs/CodeCopy'; import { ThemeProvider } from 'docs/src/modules/components/ThemeContext'; import { CodeVariantProvider } from 'docs/src/modules/utils/codeVariant'; import DocsStyledEngineProvider from 'docs/src/modules/utils/StyledEngineProvider'; import createEmotionCache from 'docs/src/createEmotionCache'; import findActivePage from 'docs/src/modules/utils/findActivePage'; import getProductInfoFromUrl from 'docs/src/modules/utils/getProductInfoFromUrl'; import { DocsProvider } from '@mui/docs/DocsProvider'; import { mapTranslations } from '@mui/docs/i18n'; import SvgMuiLogomark, { muiSvgLogoString, muiSvgWordmarkString, } from 'docs/src/icons/SvgMuiLogomark'; import './global.css'; import '../public/static/components-gallery/base-theme.css'; import { Inter, Roboto } from 'next/font/google'; import localFont from 'next/font/local'; import * as config from '../config'; const inter = Inter({ weight: ['300', '400', '500', '600', '700'], subsets: ['latin'], }); const roboto = Roboto({ weight: ['300', '400', '500', '700'], style: ['normal', 'italic'], subsets: ['latin'], }); const generalSans = localFont({ declarations: [{ prop: 'font-family', value: 'General Sans' }], src: [ { path: '../public/static/fonts/GeneralSans-Regular.woff2', weight: '400', style: 'normal' }, { path: '../public/static/fonts/GeneralSans-Medium.woff2', weight: '500', style: 'normal' }, { path: '../public/static/fonts/GeneralSans-Semibold.woff2', weight: '600', style: 'normal' }, { path: '../public/static/fonts/GeneralSans-Bold.woff2', weight: '700', style: 'normal' }, ], }); const ibmPlexSans = localFont({ declarations: [{ prop: 'font-family', value: 'IBM Plex Sans' }], src: [ { path: '../public/static/fonts/IBMPlexSans-Regular.woff2', weight: '400', style: 'normal' }, { path: '../public/static/fonts/IBMPlexSans-Medium.woff2', weight: '500', style: 'normal' }, { path: '../public/static/fonts/IBMPlexSans-SemiBold.woff2', weight: '600', style: 'normal' }, { path: '../public/static/fonts/IBMPlexSans-Bold.woff2', weight: '700', style: 'normal' }, ], }); export const fontClasses = `${inter.className} ${roboto.className} ${generalSans.className} ${ibmPlexSans.className}`; // Remove the license warning from demonstration purposes LicenseInfo.setLicenseKey(process.env.NEXT_PUBLIC_MUI_LICENSE); // Client-side cache, shared for the whole session of the user in the browser. const clientSideEmotionCache = createEmotionCache(); let reloadInterval; // Avoid infinite loop when "Upload on reload" is set in the Chrome sw dev tools. function lazyReload() { clearInterval(reloadInterval); reloadInterval = setInterval(() => { if (document.hasFocus()) { window.location.reload(); } }, 100); } // Inspired by // https://developers.google.com/web/tools/workbox/guides/advanced-recipes#offer_a_page_reload_for_users function forcePageReload(registration) { // console.log('already controlled?', Boolean(navigator.serviceWorker.controller)); if (!navigator.serviceWorker.controller) { // The window client isn't currently controlled so it's a new service // worker that will activate immediately. return; } // console.log('registration waiting?', Boolean(registration.waiting)); if (registration.waiting) { // SW is waiting to activate. Can occur if multiple clients open and // one of the clients is refreshed. registration.waiting.postMessage('skipWaiting'); return; } function listenInstalledStateChange() { registration.installing.addEventListener('statechange', (event) => { // console.log('statechange', event.target.state); if (event.target.state === 'installed' && registration.waiting) { // A new service worker is available, inform the user registration.waiting.postMessage('skipWaiting'); } else if (event.target.state === 'activated') { // Force the control of the page by the activated service worker. lazyReload(); } }); } if (registration.installing) { listenInstalledStateChange(); return; } // We are currently controlled so a new SW may be found... // Add a listener in case a new SW is found, registration.addEventListener('updatefound', listenInstalledStateChange); } async function registerServiceWorker() { if ( 'serviceWorker' in navigator && process.env.NODE_ENV === 'production' && window.location.host.includes('mui.com') ) { // register() automatically attempts to refresh the sw.js. const registration = await navigator.serviceWorker.register('/sw.js'); // Force the page reload for users. forcePageReload(registration); } } let dependenciesLoaded = false; function loadDependencies() { if (dependenciesLoaded) { return; } dependenciesLoaded = true; loadCSS( 'https://fonts.googleapis.com/icon?family=Material+Icons|Material+Icons+Two+Tone', document.querySelector('#material-icon-font'), ); } if (typeof window !== 'undefined' && process.env.NODE_ENV === 'production') { // eslint-disable-next-line no-console console.log( `%c ███╗ ███╗ ██╗ ██╗ ██████╗ ████╗ ████║ ██║ ██║ ██╔═╝ ██╔████╔██║ ██║ ██║ ██║ ██║╚██╔╝██║ ██║ ██║ ██║ ██║ ╚═╝ ██║ ╚██████╔╝ ██████╗ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ Tip: you can access the documentation \`theme\` object directly in the console. `, 'font-family:monospace;color:#1976d2;font-size:12px;', ); } function AppWrapper(props) { const { children, emotionCache, pageProps } = props; const router = useRouter(); // TODO move productId & productCategoryId resolution to page layout. // We should use the productId field from the markdown and fallback to getProductInfoFromUrl() // if not present const { productId, productCategoryId } = getProductInfoFromUrl(router.asPath); React.useEffect(() => { loadDependencies(); registerServiceWorker(); // Remove the server-side injected CSS. const jssStyles = document.querySelector('#jss-server-side'); if (jssStyles) { jssStyles.parentElement.removeChild(jssStyles); } }, []); const productIdentifier = React.useMemo(() => { const languagePrefix = pageProps.userLanguage === 'en' ? '' : `/${pageProps.userLanguage}`; if (productId === 'material-ui') { return { metadata: '', name: 'Material UI', logo: SvgMuiLogomark, logoSvg: muiSvgLogoString, wordmarkSvg: muiSvgWordmarkString, versions: [ { text: `v${materialPkgJson.version}`, current: true }, { text: 'v6', href: `https://v6.mui.com${languagePrefix}/material-ui/getting-started/`, }, { text: 'v5', href: `https://v5.mui.com${languagePrefix}/getting-started/installation/`, }, { text: 'v4', href: `https://v4.mui.com${languagePrefix}/getting-started/installation/`, }, { text: 'View all versions', href: `https://mui.com${languagePrefix}/versions/`, }, ], }; } if (productId === 'joy-ui') { return { metadata: '', name: 'Joy UI', logo: SvgMuiLogomark, logoSvg: muiSvgLogoString, wordmarkSvg: muiSvgWordmarkString, versions: [{ text: `v${joyPkgJson.version}`, current: true }], }; } if (productId === 'system') { return { metadata: '', name: 'MUI System', logo: SvgMuiLogomark, logoSvg: muiSvgLogoString, wordmarkSvg: muiSvgWordmarkString, versions: [ { text: `v${systemPkgJson.version}`, current: true }, { text: 'v6', href: `https://v6.mui.com${languagePrefix}/system/getting-started/` }, { text: 'v5', href: `https://v5.mui.com${languagePrefix}/system/getting-started/` }, { text: 'v4', href: `https://v4.mui.com${languagePrefix}/system/basics/` }, { text: 'View all versions', href: `https://mui.com${languagePrefix}/versions/`, }, ], }; } if (productId === 'core') { return { metadata: '', name: 'MUI Core', logo: SvgMuiLogomark, logoSvg: muiSvgLogoString, wordmarkSvg: muiSvgWordmarkString, versions: [ { text: `v${materialPkgJson.version}`, current: true }, { text: 'View all versions', href: `https://mui.com${languagePrefix}/versions/`, }, ], }; } if (productId === 'docs-infra') { return { metadata: '', name: 'Docs-infra', logo: SvgMuiLogomark, logoSvg: muiSvgLogoString, wordmarkSvg: muiSvgWordmarkString, versions: [ { text: 'v0.0.0', href: `https://mui.com${languagePrefix}/versions/`, }, ], }; } if (productId === 'docs') { return { metadata: '', name: 'Home docs', logo: SvgMuiLogomark, logoSvg: muiSvgLogoString, wordmarkSvg: muiSvgWordmarkString, versions: [ { text: 'v0.0.0', href: `https://mui.com${languagePrefix}/versions/`, }, ], }; } return null; }, [pageProps.userLanguage, productId]); const pageContextValue = React.useMemo(() => { let pages = generalDocsPages; if (productId === 'material-ui') { pages = materialPages; } else if (productId === 'joy-ui') { pages = joyPages; } else if (productId === 'system') { pages = systemPages; } else if (productId === 'docs-infra') { pages = docsInfraPages; } const { activePage, activePageParents } = findActivePage(pages, router.pathname); return { activePage, activePageParents, pages, productIdentifier, productId, productCategoryId, }; }, [productId, productCategoryId, productIdentifier, router.pathname]); return ( {children} ); } AppWrapper.propTypes = { children: PropTypes.node.isRequired, emotionCache: PropTypes.object.isRequired, pageProps: PropTypes.object.isRequired, }; export default function MyApp(props) { const { Component, emotionCache = clientSideEmotionCache, pageProps } = props; const getLayout = Component.getLayout ?? ((page) => page); return ( {getLayout()} ); } MyApp.propTypes = { Component: PropTypes.elementType.isRequired, emotionCache: PropTypes.object, pageProps: PropTypes.object.isRequired, }; MyApp.getInitialProps = async ({ ctx, Component }) => { let pageProps = {}; const req = require.context('docs/translations', false, /\.\/translations.*\.json$/); const translations = mapTranslations(req); if (Component.getInitialProps) { pageProps = await Component.getInitialProps(ctx); } return { pageProps: { userLanguage: ctx.query.userLanguage || 'en', translations, ...pageProps, }, }; }; // Track fraction of actual events to prevent exceeding event quota. // Filter sessions instead of individual events so that we can track multiple metrics per device. // See https://github.com/GoogleChromeLabs/web-vitals-report to use this data const disableWebVitalsReporting = Math.random() > 0.0001; export function reportWebVitals({ id, name, label, delta, value }) { if (disableWebVitalsReporting) { return; } window.gtag('event', name, { value: delta, metric_label: label === 'web-vital' ? 'Web Vitals' : 'Next.js custom metric', metric_value: value, metric_delta: delta, metric_id: id, // id unique to current page load }); }