diff --git a/.eslintrc.js b/.eslintrc.js index a67246aa85790a..051f32dd561433 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -52,6 +52,17 @@ module.exports = { { patterns: [ '@material-ui/*/*/*', + // Begin block: Packages with files instead of packages in the top level + // Importing from the top level pulls in CommonJS instead of ES modules + // Allowing /icons as to reduce cold-start of dev builds significantly. + // There's nothing to tree-shake when importing from /icons this way: + // '@material-ui/icons/*/', + '@material-ui/system/*', + '@material-ui/utils/*', + // End block + // Macros are fine since their import path is transpiled away + '!@material-ui/utils/macros', + '@material-ui/utils/macros/*', '!@material-ui/utils/macros/*.macro', // public API: https://next.material-ui-pickers.dev/getting-started/installation#peer-library '!@material-ui/pickers/adapter/*', diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7f698a2035b3b6..320661c22fe352 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -98,6 +98,7 @@ steps: displayName: Cache nextjs build - script: | + set -o pipefail mkdir -p scripts/sizeSnapshot/build yarn docs:build | tee scripts/sizeSnapshot/build/docs.next displayName: 'build docs for size snapshot' diff --git a/docs/pages/_app.js b/docs/pages/_app.js index 7d6d0f0b195dda..fb08420ec44641 100644 --- a/docs/pages/_app.js +++ b/docs/pages/_app.js @@ -24,6 +24,7 @@ import RtlContext from 'docs/src/modules/utils/RtlContext'; import { ThemeProvider } from 'docs/src/modules/components/ThemeContext'; import { pathnameToLanguage, getCookie } from 'docs/src/modules/utils/helpers'; import { ACTION_TYPES, CODE_VARIANTS } from 'docs/src/modules/constants'; +import { useUserLanguage } from 'docs/src/modules/utils/i18n'; // Configure JSS const jss = create({ @@ -45,7 +46,7 @@ acceptLanguage.languages(['en', 'zh', 'pt', 'ru']); function LanguageNegotiation() { const dispatch = useDispatch(); const router = useRouter(); - const userLanguage = useSelector((state) => state.options.userLanguage); + const userLanguage = useUserLanguage(); React.useEffect(() => { const { userLanguage: userLanguageUrl, canonical } = pathnameToLanguage(router.asPath); diff --git a/docs/pages/index.js b/docs/pages/index.js index 5cd9da36c8c216..1d058862f6ade3 100644 --- a/docs/pages/index.js +++ b/docs/pages/index.js @@ -1,6 +1,5 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; import { makeStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; import Button from '@material-ui/core/Button'; @@ -19,6 +18,7 @@ import AppFrame from 'docs/src/modules/components/AppFrame'; import Link from 'docs/src/modules/components/Link'; import Head from 'docs/src/modules/components/Head'; import loadScript from 'docs/src/modules/utils/loadScript'; +import { useTranslate } from 'docs/src/modules/utils/i18n'; let dependenciesLoaded = false; @@ -119,7 +119,7 @@ export default function LandingPage(props) { React.useEffect(() => { loadDependencies(); }, []); - const t = useSelector((state) => state.options.t); + const t = useTranslate(); const classes = useStyles(); return ( diff --git a/docs/src/modules/components/Ad.js b/docs/src/modules/components/Ad.js index 746e161e2948a3..0a6035f1236772 100644 --- a/docs/src/modules/components/Ad.js +++ b/docs/src/modules/components/Ad.js @@ -1,7 +1,6 @@ import * as React from 'react'; import clsx from 'clsx'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; import { withStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; import Paper from '@material-ui/core/Paper'; @@ -9,6 +8,7 @@ import AdCarbon from 'docs/src/modules/components/AdCarbon'; import AdReadthedocs from 'docs/src/modules/components/AdReadthedocs'; import AdInHouse from 'docs/src/modules/components/AdInHouse'; import { AdContext, adShape } from 'docs/src/modules/components/AdManager'; +import { useTranslate } from 'docs/src/modules/utils/i18n'; const styles = (theme) => ({ root: { @@ -39,7 +39,7 @@ const styles = (theme) => ({ }); function PleaseDisableAdblock(props) { - const t = useSelector((state) => state.options.t); + const t = useTranslate(); return ( diff --git a/docs/src/modules/components/AppDrawer.js b/docs/src/modules/components/AppDrawer.js index f8097ee0e675da..bddc591192ad9b 100644 --- a/docs/src/modules/components/AppDrawer.js +++ b/docs/src/modules/components/AppDrawer.js @@ -1,7 +1,6 @@ import * as React from 'react'; import clsx from 'clsx'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; import { withStyles } from '@material-ui/core/styles'; import List from '@material-ui/core/List'; import Drawer from '@material-ui/core/Drawer'; @@ -14,6 +13,7 @@ import AppDrawerNavItem from 'docs/src/modules/components/AppDrawerNavItem'; import Link from 'docs/src/modules/components/Link'; import { pageToTitleI18n } from 'docs/src/modules/utils/helpers'; import PageContext from 'docs/src/modules/components/PageContext'; +import { useUserLanguage, useTranslate } from 'docs/src/modules/utils/i18n'; let savedScrollTop = null; function PersistScroll(props) { @@ -140,9 +140,9 @@ const iOS = process.browser && /iPad|iPhone|iPod/.test(navigator.userAgent); function AppDrawer(props) { const { classes, className, disablePermanent, mobileOpen, onClose, onOpen } = props; const { activePage, pages } = React.useContext(PageContext); - const userLanguage = useSelector((state) => state.options.userLanguage); + const userLanguage = useUserLanguage(); const languagePrefix = userLanguage === 'en' ? '' : `/${userLanguage}`; - const t = useSelector((state) => state.options.t); + const t = useTranslate(); const navItems = React.useMemo( () => renderNavItems({ onClose, pages, activePage, depth: 0, t }), diff --git a/docs/src/modules/components/AppFooter.js b/docs/src/modules/components/AppFooter.js index 497a7332c4fc57..0954a3e131ad45 100644 --- a/docs/src/modules/components/AppFooter.js +++ b/docs/src/modules/components/AppFooter.js @@ -1,7 +1,6 @@ /* eslint-disable material-ui/no-hardcoded-labels */ import * as React from 'react'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; import Interpolate from '@trendmicro/react-interpolate'; import { withStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; @@ -9,6 +8,7 @@ import Grid from '@material-ui/core/Grid'; import Container from '@material-ui/core/Container'; import Divider from '@material-ui/core/Divider'; import Link from 'docs/src/modules/components/Link'; +import { useUserLanguage, useTranslate } from 'docs/src/modules/utils/i18n'; const styles = (theme) => ({ root: { @@ -52,9 +52,9 @@ const styles = (theme) => ({ function AppFooter(props) { const { classes } = props; - const userLanguage = useSelector((state) => state.options.userLanguage); + const userLanguage = useUserLanguage(); const languagePrefix = userLanguage === 'en' ? '' : `/${userLanguage}`; - const t = useSelector((state) => state.options.t); + const t = useTranslate(); return (
diff --git a/docs/src/modules/components/AppFrame.js b/docs/src/modules/components/AppFrame.js index e9572cadb4d207..85ff498d13a73d 100644 --- a/docs/src/modules/components/AppFrame.js +++ b/docs/src/modules/components/AppFrame.js @@ -1,6 +1,5 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; import Router, { useRouter } from 'next/router'; import { withStyles, useTheme } from '@material-ui/core/styles'; import NProgress from 'nprogress'; @@ -35,6 +34,7 @@ import { pathnameToLanguage } from 'docs/src/modules/utils/helpers'; import RtlContext from 'docs/src/modules/utils/RtlContext'; import { useChangeTheme } from 'docs/src/modules/components/ThemeContext'; import PageContext from 'docs/src/modules/components/PageContext'; +import { useUserLanguage, useTranslate } from 'docs/src/modules/utils/i18n'; const LOCALES = { zh: 'zh-CN', pt: 'pt-BR', es: 'es-ES' }; const CROWDIN_ROOT_URL = 'https://translate.material-ui.com/project/material-ui-docs/'; @@ -146,8 +146,8 @@ const styles = (theme) => ({ function AppFrame(props) { const { children, classes, disableDrawer = false } = props; const theme = useTheme(); - const t = useSelector((state) => state.options.t); - const userLanguage = useSelector((state) => state.options.userLanguage); + const t = useTranslate(); + const userLanguage = useUserLanguage(); const { rtl, setRtl } = React.useContext(RtlContext); const crowdInLocale = LOCALES[userLanguage] || userLanguage; diff --git a/docs/src/modules/components/AppSearch.js b/docs/src/modules/components/AppSearch.js index 91f268712b8cc8..643af6e0e32698 100644 --- a/docs/src/modules/components/AppSearch.js +++ b/docs/src/modules/components/AppSearch.js @@ -1,6 +1,5 @@ import * as React from 'react'; import url from 'url'; -import { useSelector } from 'react-redux'; import useLazyCSS from 'docs/src/modules/utils/useLazyCSS'; import useMediaQuery from '@material-ui/core/useMediaQuery'; import { alpha, useTheme, makeStyles } from '@material-ui/core/styles'; @@ -9,6 +8,7 @@ import SearchIcon from '@material-ui/icons/Search'; import { handleEvent } from 'docs/src/modules/components/MarkdownLinks'; import docsearch from 'docsearch.js'; import { LANGUAGES_SSR } from 'docs/src/modules/constants'; +import { useUserLanguage, useTranslate } from 'docs/src/modules/utils/i18n'; const useStyles = makeStyles( (theme) => ({ @@ -117,8 +117,8 @@ export default function AppSearch() { const classes = useStyles(); const inputRef = React.useRef(null); const theme = useTheme(); - const userLanguage = useSelector((state) => state.options.userLanguage); - const t = useSelector((state) => state.options.t); + const userLanguage = useUserLanguage(); + const t = useTranslate(); useLazyCSS('https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css', '#app-search'); diff --git a/docs/src/modules/components/AppTableOfContents.js b/docs/src/modules/components/AppTableOfContents.js index 9f1ea5f9a25b31..22fe6bdb34141b 100644 --- a/docs/src/modules/components/AppTableOfContents.js +++ b/docs/src/modules/components/AppTableOfContents.js @@ -3,11 +3,11 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import throttle from 'lodash/throttle'; import clsx from 'clsx'; -import { useSelector } from 'react-redux'; import { makeStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; import Link from 'docs/src/modules/components/Link'; import PageContext from 'docs/src/modules/components/PageContext'; +import { useTranslate } from 'docs/src/modules/utils/i18n'; const useStyles = makeStyles((theme) => ({ root: { @@ -100,7 +100,7 @@ function useThrottledOnScroll(callback, delay) { export default function AppTableOfContents(props) { const { items } = props; const classes = useStyles(); - const t = useSelector((state) => state.options.t); + const t = useTranslate(); const itemsWithNodeRef = React.useRef([]); React.useEffect(() => { diff --git a/docs/src/modules/components/ComponentLinkHeader.js b/docs/src/modules/components/ComponentLinkHeader.js index 30073ec98c2dc0..5e5393d71af348 100644 --- a/docs/src/modules/components/ComponentLinkHeader.js +++ b/docs/src/modules/components/ComponentLinkHeader.js @@ -1,6 +1,5 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; import Chip from '@material-ui/core/Chip'; import Tooltip from '@material-ui/core/Tooltip'; import SketchIcon from 'docs/src/modules/components/SketchIcon'; @@ -10,6 +9,7 @@ import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined'; import W3CIcon from 'docs/src/modules/components/W3CIcon'; import MaterialDesignIcon from 'docs/src/modules/components/MaterialDesignIcon'; import { makeStyles } from '@material-ui/core/styles'; +import { useTranslate } from 'docs/src/modules/utils/i18n'; const useStyles = makeStyles((theme) => ({ root: { @@ -32,7 +32,7 @@ export default function ComponentLinkHeader(props) { options, } = props; const classes = useStyles(); - const t = useSelector((state) => state.options.t); + const t = useTranslate(); if (headers.materialDesign && options.design === false) { throw new Error('missing design assets'); diff --git a/docs/src/modules/components/Demo.js b/docs/src/modules/components/Demo.js index 84e25faa8423e8..44f8c1725a79fc 100644 --- a/docs/src/modules/components/Demo.js +++ b/docs/src/modules/components/Demo.js @@ -11,6 +11,7 @@ import DemoSandboxed from 'docs/src/modules/components/DemoSandboxed'; import { AdCarbonInline } from 'docs/src/modules/components/AdCarbon'; import getJsxPreview from 'docs/src/modules/utils/getJsxPreview'; import { CODE_VARIANTS } from 'docs/src/modules/constants'; +import { useUserLanguage, useTranslate } from 'docs/src/modules/utils/i18n'; const DemoToolbar = React.lazy(() => import('./DemoToolbar')); // Sync with styles from DemoToolbar @@ -31,7 +32,7 @@ const useDemoToolbarFallbackStyles = makeStyles( ); export function DemoToolbarFallback() { const classes = useDemoToolbarFallbackStyles(); - const t = useSelector((state) => state.options.t); + const t = useTranslate(); return (
@@ -43,7 +44,7 @@ function getDemoName(location) { } function useDemoData(codeVariant, demo, githubLocation) { - const userLanguage = useSelector((state) => state.options.userLanguage); + const userLanguage = useUserLanguage(); const title = `${getDemoName(githubLocation)} Material Demo`; if (codeVariant === CODE_VARIANTS.TS && demo.rawTS) { return { @@ -164,7 +165,7 @@ const useStyles = makeStyles( export default function Demo(props) { const { demo, demoOptions, disableAd, githubLocation } = props; const classes = useStyles(); - const t = useSelector((state) => state.options.t); + const t = useTranslate(); const codeVariant = useSelector((state) => state.options.codeVariant); const demoData = useDemoData(codeVariant, demo, githubLocation); diff --git a/docs/src/modules/components/DemoSandboxed.js b/docs/src/modules/components/DemoSandboxed.js index 0328ac052a3358..9814f3ad84f828 100644 --- a/docs/src/modules/components/DemoSandboxed.js +++ b/docs/src/modules/components/DemoSandboxed.js @@ -4,8 +4,8 @@ import PropTypes from 'prop-types'; import { create } from 'jss'; import { makeStyles, useTheme, jssPreset, StylesProvider } from '@material-ui/core/styles'; import rtl from 'jss-rtl'; -import { useSelector } from 'react-redux'; import DemoErrorBoundary from 'docs/src/modules/components/DemoErrorBoundary'; +import { useTranslate } from 'docs/src/modules/utils/i18n'; function FramedDemo(props) { const { children, document } = props; @@ -107,7 +107,7 @@ function DemoSandboxed(props) { const Sandbox = iframe ? DemoFrame : React.Fragment; const sandboxProps = iframe ? { title: `${name} demo`, ...other } : {}; - const t = useSelector((state) => state.options.t); + const t = useTranslate(); return ( diff --git a/docs/src/modules/components/DemoToolbar.js b/docs/src/modules/components/DemoToolbar.js index 96eaafd5ae54e7..7b4ce430d1225f 100644 --- a/docs/src/modules/components/DemoToolbar.js +++ b/docs/src/modules/components/DemoToolbar.js @@ -2,7 +2,7 @@ import * as React from 'react'; import * as PropTypes from 'prop-types'; import copy from 'clipboard-copy'; import LZString from 'lz-string'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { makeStyles, useTheme } from '@material-ui/core/styles'; import IconButton from '@material-ui/core/IconButton'; import useMediaQuery from '@material-ui/core/useMediaQuery'; @@ -23,6 +23,7 @@ import ResetFocusIcon from '@material-ui/icons/CenterFocusWeak'; import getDemoConfig from 'docs/src/modules/utils/getDemoConfig'; import { getCookie } from 'docs/src/modules/utils/helpers'; import { ACTION_TYPES, CODE_VARIANTS } from 'docs/src/modules/constants'; +import { useTranslate } from 'docs/src/modules/utils/i18n'; function compress(object) { return LZString.compressToBase64(JSON.stringify(object)) @@ -70,7 +71,7 @@ const useDemoToolbarStyles = makeStyles( export function DemoToolbarFallback() { const classes = useDemoToolbarStyles(); - const t = useSelector((state) => state.options.t); + const t = useTranslate(); return (
@@ -225,7 +226,7 @@ export default function DemoToolbar(props) { const classes = useDemoToolbarStyles(); const dispatch = useDispatch(); - const t = useSelector((state) => state.options.t); + const t = useTranslate(); const hasTSVariant = demo.rawTS; const renderedCodeVariant = () => { diff --git a/docs/src/modules/components/DiamondSponsors.js b/docs/src/modules/components/DiamondSponsors.js index 38e6396be9c97d..f4e3f98f6188b4 100644 --- a/docs/src/modules/components/DiamondSponsors.js +++ b/docs/src/modules/components/DiamondSponsors.js @@ -1,9 +1,9 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import Typography from '@material-ui/core/Typography'; -import { useSelector } from 'react-redux'; import { useTheme, makeStyles } from '@material-ui/core/styles'; import AddIcon from '@material-ui/icons/Add'; +import { useTranslate } from 'docs/src/modules/utils/i18n'; const useStyles = makeStyles((theme) => ({ root: { @@ -38,7 +38,7 @@ export default function DiamondSponsors(props) { const classes = useStyles(); const { spot } = props; const theme = useTheme(); - const t = useSelector((state) => state.options.t); + const t = useTranslate(); return (
diff --git a/docs/src/modules/components/EditPage.js b/docs/src/modules/components/EditPage.js index 71c2866f2473fe..946aebd3953d46 100644 --- a/docs/src/modules/components/EditPage.js +++ b/docs/src/modules/components/EditPage.js @@ -1,13 +1,13 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; import Button from '@material-ui/core/Button'; import { SOURCE_CODE_ROOT_URL } from 'docs/src/modules/constants'; +import { useUserLanguage, useTranslate } from 'docs/src/modules/utils/i18n'; export default function EditPage(props) { const { markdownLocation } = props; - const t = useSelector((state) => state.options.t); - const userLanguage = useSelector((state) => state.options.userLanguage); + const t = useTranslate(); + const userLanguage = useUserLanguage(); const LOCALES = { zh: 'zh-CN', pt: 'pt-BR', es: 'es-ES' }; const CROWDIN_ROOT_URL = 'https://translate.material-ui.com/project/material-ui-docs/'; const crowdInLocale = LOCALES[userLanguage] || userLanguage; diff --git a/docs/src/modules/components/Head.js b/docs/src/modules/components/Head.js index 52b246eecb72bc..423e5be207f9f5 100644 --- a/docs/src/modules/components/Head.js +++ b/docs/src/modules/components/Head.js @@ -2,10 +2,10 @@ import * as React from 'react'; import NextHead from 'next/head'; import { useRouter } from 'next/router'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; +import { useUserLanguage, useTranslate } from 'docs/src/modules/utils/i18n'; export default function Head(props) { - const t = useSelector((state) => state.options.t); + const t = useTranslate(); const { card = 'https://material-ui.com/static/logo.png', children, @@ -13,7 +13,7 @@ export default function Head(props) { largeCard = false, title = t('headTitle'), } = props; - const userLanguage = useSelector((state) => state.options.userLanguage); + const userLanguage = useUserLanguage(); const router = useRouter(); return ( diff --git a/docs/src/modules/components/Link.js b/docs/src/modules/components/Link.js index 414be77d14778b..98ce4db3138e7b 100644 --- a/docs/src/modules/components/Link.js +++ b/docs/src/modules/components/Link.js @@ -5,7 +5,7 @@ import clsx from 'clsx'; import { useRouter } from 'next/router'; import NextLink from 'next/link'; import MuiLink from '@material-ui/core/Link'; -import { useSelector } from 'react-redux'; +import { useUserLanguage } from 'docs/src/modules/utils/i18n'; const NextComposed = React.forwardRef(function NextComposed(props, ref) { const { as, href, ...other } = props; @@ -40,7 +40,7 @@ function Link(props) { const router = useRouter(); - const userLanguage = useSelector((state) => state.options.userLanguage); + const userLanguage = useUserLanguage(); const className = clsx(classNameProps, { [activeClassName]: router.pathname === routerHref && activeClassName, }); diff --git a/docs/src/modules/components/MarkdownDocs.js b/docs/src/modules/components/MarkdownDocs.js index 1b145c2d59c618..7b925a63b2f709 100644 --- a/docs/src/modules/components/MarkdownDocs.js +++ b/docs/src/modules/components/MarkdownDocs.js @@ -1,7 +1,6 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; -import { useSelector } from 'react-redux'; import { withStyles } from '@material-ui/core/styles'; import NoSsr from '@material-ui/core/NoSsr'; import { exactProp } from '@material-ui/utils'; @@ -17,6 +16,7 @@ import Ad from 'docs/src/modules/components/Ad'; import AdManager from 'docs/src/modules/components/AdManager'; import AdGuest from 'docs/src/modules/components/AdGuest'; import ComponentLinkHeader from 'docs/src/modules/components/ComponentLinkHeader'; +import { useUserLanguage, useTranslate } from 'docs/src/modules/utils/i18n'; import MarkdownDocsFooter from './MarkdownDocsFooter'; const markdownComponents = { @@ -58,8 +58,8 @@ const styles = (theme) => ({ function MarkdownDocs(props) { const { classes, disableAd = false, disableToc = false, demos = {}, docs, requireDemo } = props; - const t = useSelector((state) => state.options.t); - const userLanguage = useSelector((state) => state.options.userLanguage); + const t = useTranslate(); + const userLanguage = useUserLanguage(); const { description, location, rendered, title, toc, headers } = docs[userLanguage] || docs.en; if (description === undefined) { throw new Error('Missing description in the page'); diff --git a/docs/src/modules/components/MarkdownDocsFooter.js b/docs/src/modules/components/MarkdownDocsFooter.js index e05abbc625c520..2b49c8fedfb716 100644 --- a/docs/src/modules/components/MarkdownDocsFooter.js +++ b/docs/src/modules/components/MarkdownDocsFooter.js @@ -1,7 +1,6 @@ /* eslint-disable no-restricted-globals */ import * as React from 'react'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; import { exactProp } from '@material-ui/utils'; import { withStyles } from '@material-ui/core/styles'; import DialogActions from '@material-ui/core/DialogActions'; @@ -21,6 +20,7 @@ import Snackbar from '@material-ui/core/Snackbar'; import { getCookie, pageToTitleI18n } from 'docs/src/modules/utils/helpers'; import PageContext from 'docs/src/modules/components/PageContext'; import Link from 'docs/src/modules/components/Link'; +import { useUserLanguage, useTranslate } from 'docs/src/modules/utils/i18n'; const styles = (theme) => ({ root: { @@ -143,8 +143,8 @@ function getCurrentRating(pathname) { function MarkdownDocsFooter(props) { const { classes } = props; - const t = useSelector((state) => state.options.t); - const userLanguage = useSelector((state) => state.options.userLanguage); + const t = useTranslate(); + const userLanguage = useUserLanguage(); const { activePage, pages } = React.useContext(PageContext); const [rating, setRating] = React.useState(); const [comment, setComment] = React.useState(''); diff --git a/docs/src/modules/components/Notifications.js b/docs/src/modules/components/Notifications.js index ac422c27765bd2..23aeb347c9861a 100644 --- a/docs/src/modules/components/Notifications.js +++ b/docs/src/modules/components/Notifications.js @@ -17,6 +17,7 @@ import ListItem from '@material-ui/core/ListItem'; import Divider from '@material-ui/core/Divider'; import { getCookie } from 'docs/src/modules/utils/helpers'; import { ACTION_TYPES } from 'docs/src/modules/constants'; +import { useUserLanguage, useTranslate } from 'docs/src/modules/utils/i18n'; const useStyles = makeStyles((theme) => ({ paper: { @@ -46,9 +47,9 @@ export default function Notifications() { const [open, setOpen] = React.useState(false); const [tooltipOpen, setTooltipOpen] = React.useState(false); const anchorRef = React.useRef(null); - const t = useSelector((state) => state.options.t); + const t = useTranslate(); const dispatch = useDispatch(); - const userLanguage = useSelector((state) => state.options.userLanguage); + const userLanguage = useUserLanguage(); const messages = useSelector((state) => state.notifications.messages); const lastSeen = useSelector((state) => state.notifications.lastSeen); diff --git a/docs/src/modules/components/ThemeContext.js b/docs/src/modules/components/ThemeContext.js index 16448cc170c019..766b2b26ee1d41 100644 --- a/docs/src/modules/components/ThemeContext.js +++ b/docs/src/modules/components/ThemeContext.js @@ -6,13 +6,13 @@ import { unstable_createMuiStrictModeTheme as createStrictModeTheme, darken, } from '@material-ui/core/styles'; -import { useSelector } from 'react-redux'; import useMediaQuery from '@material-ui/core/useMediaQuery'; import { enUS, zhCN, faIR, ruRU, ptBR, esES, frFR, deDE, jaJP } from '@material-ui/core/locale'; import { blue, pink } from '@material-ui/core/colors'; import { unstable_useEnhancedEffect as useEnhancedEffect } from '@material-ui/core/utils'; import { getCookie } from 'docs/src/modules/utils/helpers'; import useLazyCSS from 'docs/src/modules/utils/useLazyCSS'; +import { useUserLanguage } from 'docs/src/modules/utils/i18n'; const languageMap = { en: enUS, @@ -178,7 +178,7 @@ export function ThemeProvider(props) { } }, themeInitialOptions); - const userLanguage = useSelector((state) => state.options.userLanguage); + const userLanguage = useUserLanguage(); const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); const preferredMode = prefersDarkMode ? 'dark' : 'light'; const { dense, direction, paletteColors, paletteMode = preferredMode, spacing } = themeOptions; diff --git a/docs/src/modules/redux/optionsReducer.js b/docs/src/modules/redux/optionsReducer.js index f49d0d56ab1a29..09d8056474eeea 100644 --- a/docs/src/modules/redux/optionsReducer.js +++ b/docs/src/modules/redux/optionsReducer.js @@ -1,43 +1,4 @@ import { ACTION_TYPES, CODE_VARIANTS } from 'docs/src/modules/constants'; -import memoize from '@material-ui/system/memoize'; -import mapTranslations from 'docs/src/modules/utils/mapTranslations'; - -const req = require.context('docs/translations', false, /translations.*\.json$/); -const translations = mapTranslations(req, 'json'); - -function getPath(obj, path) { - if (!path || typeof path !== 'string') { - return null; - } - - return path.split('.').reduce((acc, item) => (acc && acc[item] ? acc[item] : null), obj); -} - -const warnOnce = {}; - -const getT = memoize((userLanguage) => (key, options = {}) => { - const { ignoreWarning = false } = options; - const wordings = translations[userLanguage]; - - if (!wordings) { - console.error(`Missing language: ${userLanguage}.`); - return '…'; - } - - const translation = getPath(wordings, key); - - if (!translation) { - const fullKey = `${userLanguage}:${key}`; - // No warnings in CI env - if (!ignoreWarning && !warnOnce[fullKey] && typeof window !== 'undefined') { - console.error(`Missing translation for ${fullKey}.`); - warnOnce[fullKey] = true; - } - return getPath(translations.en, key); - } - - return translation; -}); const mapping = { [ACTION_TYPES.OPTIONS_CHANGE]: (state, action) => { @@ -63,7 +24,5 @@ export default function optionsReducer(state = {}, action) { newState = mapping[action.type](state, action); } - newState.t = getT(newState.userLanguage); - return newState; } diff --git a/docs/src/modules/utils/i18n.js b/docs/src/modules/utils/i18n.js new file mode 100644 index 00000000000000..4497e0b8ef2992 --- /dev/null +++ b/docs/src/modules/utils/i18n.js @@ -0,0 +1,52 @@ +import * as React from 'react'; +import { useSelector } from 'react-redux'; +import mapTranslations from './mapTranslations'; + +const req = require.context('docs/translations', false, /translations.*\.json$/); +const translations = mapTranslations(req, 'json'); + +function getPath(obj, path) { + if (!path || typeof path !== 'string') { + return null; + } + + return path.split('.').reduce((acc, item) => (acc && acc[item] ? acc[item] : null), obj); +} + +const warnOnce = {}; + +export function useTranslate() { + const userLanguage = useSelector((state) => state.options.userLanguage); + + return React.useMemo( + () => + function translate(key, options = {}) { + const { ignoreWarning = false } = options; + const wordings = translations[userLanguage]; + + if (!wordings) { + console.error(`Missing language: ${userLanguage}.`); + return '…'; + } + + const translation = getPath(wordings, key); + + if (!translation) { + const fullKey = `${userLanguage}:${key}`; + // No warnings in CI env + if (!ignoreWarning && !warnOnce[fullKey] && typeof window !== 'undefined') { + console.error(`Missing translation for ${fullKey}.`); + warnOnce[fullKey] = true; + } + return getPath(translations.en, key); + } + + return translation; + }, + [userLanguage], + ); +} + +export function useUserLanguage() { + return useSelector((state) => state.options.userLanguage); +} diff --git a/docs/src/pages/customization/default-theme/DefaultTheme.js b/docs/src/pages/customization/default-theme/DefaultTheme.js index 74cc03a331aa6f..4cd40e1d626d93 100644 --- a/docs/src/pages/customization/default-theme/DefaultTheme.js +++ b/docs/src/pages/customization/default-theme/DefaultTheme.js @@ -1,6 +1,5 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; import url from 'url'; import ExpandIcon from '@material-ui/icons/ExpandMore'; import CollapseIcon from '@material-ui/icons/ChevronRight'; @@ -15,6 +14,7 @@ import { } from '@material-ui/core/styles'; import FormControlLabel from '@material-ui/core/FormControlLabel'; import Switch from '@material-ui/core/Switch'; +import { useTranslate } from 'docs/src/modules/utils/i18n'; /** * @param {unknown} value @@ -273,7 +273,7 @@ function DefaultTheme(props) { const { classes } = props; const [checked, setChecked] = React.useState(false); const [expandPaths, setExpandPaths] = React.useState(null); - const t = useSelector((state) => state.options.t); + const t = useTranslate(); const [darkTheme, setDarkTheme] = React.useState(false); React.useEffect(() => { diff --git a/docs/src/pages/customization/density/DensityTool.js b/docs/src/pages/customization/density/DensityTool.js index 1d97b7bf0f0472..5e7052b1a9e21e 100644 --- a/docs/src/pages/customization/density/DensityTool.js +++ b/docs/src/pages/customization/density/DensityTool.js @@ -10,7 +10,7 @@ import Switch from '@material-ui/core/Switch'; import { DispatchContext } from 'docs/src/modules/components/ThemeContext'; import IncreaseIcon from '@material-ui/icons/AddCircleOutline'; import DecreaseIcon from '@material-ui/icons/RemoveCircleOutline'; -import { useSelector } from 'react-redux'; +import { useTranslate } from 'docs/src/modules/utils/i18n'; const minSpacing = 0; const maxSpacing = 20; @@ -46,7 +46,7 @@ export default function DensityTool() { const theme = useTheme(); const spacingUnit = theme.spacing(1); - const t = useSelector((state) => state.options.t); + const t = useTranslate(); return ( diff --git a/docs/src/pages/discover-more/showcase/Showcase.js b/docs/src/pages/discover-more/showcase/Showcase.js index 8ebf1f7c808315..e836c942582853 100644 --- a/docs/src/pages/discover-more/showcase/Showcase.js +++ b/docs/src/pages/discover-more/showcase/Showcase.js @@ -1,6 +1,5 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; import { withStyles } from '@material-ui/core/styles'; import InputLabel from '@material-ui/core/InputLabel'; import MenuItem from '@material-ui/core/MenuItem'; @@ -12,6 +11,7 @@ import Typography from '@material-ui/core/Typography'; import IconButton from '@material-ui/core/IconButton'; import GitHubIcon from '@material-ui/icons/GitHub'; import Link from 'docs/src/modules/components/Link'; +import { useTranslate } from 'docs/src/modules/utils/i18n'; import appList from './appList'; const styles = (theme) => ({ @@ -71,7 +71,7 @@ function Showcase(props) { const { classes } = props; const [sortFunctionName, setSortFunctionName] = React.useState('dateAdded'); const sortFunction = sortFunctions[sortFunctionName]; - const t = useSelector((state) => state.options.t); + const t = useTranslate(); const handleChangeSort = (event) => { setSortFunctionName(event.target.value); diff --git a/docs/src/pages/getting-started/templates/Templates.js b/docs/src/pages/getting-started/templates/Templates.js index 79a058c61a3b2d..25ae4e4e974f48 100644 --- a/docs/src/pages/getting-started/templates/Templates.js +++ b/docs/src/pages/getting-started/templates/Templates.js @@ -1,6 +1,5 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; import { withStyles } from '@material-ui/core/styles'; import Card from '@material-ui/core/Card'; import CardActions from '@material-ui/core/CardActions'; @@ -9,6 +8,7 @@ import CardMedia from '@material-ui/core/CardMedia'; import Button from '@material-ui/core/Button'; import Grid from '@material-ui/core/Grid'; import Typography from '@material-ui/core/Typography'; +import { useTranslate } from 'docs/src/modules/utils/i18n'; const styles = { item: { @@ -108,7 +108,7 @@ function layouts(t) { function Templates(props) { const { classes } = props; - const t = useSelector((state) => state.options.t); + const t = useTranslate(); return ( diff --git a/docs/src/pages/landing/Pro.js b/docs/src/pages/landing/Pro.js index ecb7834af8cdfe..6ca0033b645f14 100644 --- a/docs/src/pages/landing/Pro.js +++ b/docs/src/pages/landing/Pro.js @@ -2,7 +2,7 @@ import * as React from 'react'; import classNames from 'classnames'; import { makeStyles } from '@material-ui/core/styles'; import Link from 'docs/src/modules/components/Link'; -import { useSelector } from 'react-redux'; +import { useTranslate } from 'docs/src/modules/utils/i18n'; const useStyles = makeStyles( (theme) => ({ @@ -29,7 +29,7 @@ const useStyles = makeStyles( export default function Pro() { const classes = useStyles(); - const t = useSelector((state) => state.options.t); + const t = useTranslate(); return ( state.options.t); + const t = useTranslate(); const backer = backers[Math.floor(backers.length * Math.random())]; return ( diff --git a/docs/src/pages/landing/Quotes.js b/docs/src/pages/landing/Quotes.js index 7db8e54c7bab54..562b1623f305a4 100644 --- a/docs/src/pages/landing/Quotes.js +++ b/docs/src/pages/landing/Quotes.js @@ -1,6 +1,5 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; import { makeStyles } from '@material-ui/core/styles'; import Link from 'docs/src/modules/components/Link'; import Card from '@material-ui/core/Card'; @@ -13,6 +12,7 @@ import TwitterIcon from '@material-ui/icons/Twitter'; import NoSsr from '@material-ui/core/NoSsr'; import Container from '@material-ui/core/Container'; import Divider from '@material-ui/core/Divider'; +import { useTranslate } from 'docs/src/modules/utils/i18n'; const quotes = [ { @@ -228,7 +228,7 @@ for (let i = 0; i < 3; i += 1) { export default function Quotes() { const classes = useStyles(); - const t = useSelector((state) => state.options.t); + const t = useTranslate(); return (
diff --git a/docs/src/pages/landing/Sponsors.js b/docs/src/pages/landing/Sponsors.js index aecc73f86fc156..b955601c6d4a2e 100644 --- a/docs/src/pages/landing/Sponsors.js +++ b/docs/src/pages/landing/Sponsors.js @@ -1,12 +1,12 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; import { makeStyles } from '@material-ui/core/styles'; import NoSsr from '@material-ui/core/NoSsr'; import MarkdownElement from 'docs/src/modules/components/MarkdownElement'; import Container from '@material-ui/core/Container'; import Divider from '@material-ui/core/Divider'; import { prepareMarkdown } from 'docs/src/modules/utils/parseMarkdown'; +import { useUserLanguage } from 'docs/src/modules/utils/i18n'; const useStyles = makeStyles( (theme) => ({ @@ -30,7 +30,7 @@ const useStyles = makeStyles( export default function Sponsors({ docs }) { const classes = useStyles(); - const userLanguage = useSelector((state) => state.options.userLanguage); + const userLanguage = useUserLanguage(); const { rendered } = docs[userLanguage]; return ( diff --git a/docs/src/pages/landing/Steps.js b/docs/src/pages/landing/Steps.js index 381b08bacdef3e..0174750a0b0940 100644 --- a/docs/src/pages/landing/Steps.js +++ b/docs/src/pages/landing/Steps.js @@ -1,6 +1,5 @@ import * as React from 'react'; import clsx from 'clsx'; -import { useSelector } from 'react-redux'; import { makeStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; import Container from '@material-ui/core/Container'; @@ -11,6 +10,7 @@ import { FileDownload as FileDownloadIcon } from '@material-ui/docs'; import BuildIcon from '@material-ui/icons/Build'; import HighlightedCode from 'docs/src/modules/components/HighlightedCode'; import Link from 'docs/src/modules/components/Link'; +import { useTranslate } from 'docs/src/modules/utils/i18n'; const InstallationLink = React.forwardRef((buttonProps, ref) => ( @@ -84,7 +84,7 @@ const useStyles = makeStyles( function HomeSteps() { const classes = useStyles(); - const t = useSelector((state) => state.options.t); + const t = useTranslate(); return ( diff --git a/docs/src/pages/landing/Themes.js b/docs/src/pages/landing/Themes.js index 7bda84c387aede..d00769dae1d666 100644 --- a/docs/src/pages/landing/Themes.js +++ b/docs/src/pages/landing/Themes.js @@ -1,5 +1,4 @@ import * as React from 'react'; -import { useSelector } from 'react-redux'; import { makeStyles, useTheme } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; import Container from '@material-ui/core/Container'; @@ -7,6 +6,7 @@ import Grid from '@material-ui/core/Grid'; import Button from '@material-ui/core/Button'; import NoSsr from '@material-ui/core/NoSsr'; import Link from 'docs/src/modules/components/Link'; +import { useTranslate } from 'docs/src/modules/utils/i18n'; const useStyles = makeStyles( (theme) => ({ @@ -35,7 +35,7 @@ const useStyles = makeStyles( export default function Themes() { const classes = useStyles(); - const t = useSelector((state) => state.options.t); + const t = useTranslate(); const theme = useTheme(); return ( diff --git a/docs/src/pages/landing/Users.js b/docs/src/pages/landing/Users.js index 932ad22f26be71..62ed2085516f51 100644 --- a/docs/src/pages/landing/Users.js +++ b/docs/src/pages/landing/Users.js @@ -1,6 +1,5 @@ import * as React from 'react'; import clsx from 'clsx'; -import { useSelector } from 'react-redux'; import { makeStyles } from '@material-ui/core/styles'; import NoSsr from '@material-ui/core/NoSsr'; import Divider from '@material-ui/core/Divider'; @@ -8,6 +7,7 @@ import Grid from '@material-ui/core/Grid'; import Container from '@material-ui/core/Container'; import Button from '@material-ui/core/Button'; import Typography from '@material-ui/core/Typography'; +import { useTranslate } from 'docs/src/modules/utils/i18n'; const users = [ { @@ -123,7 +123,7 @@ const useStyles = makeStyles( export default function Users() { const classes = useStyles(); - const t = useSelector((state) => state.options.t); + const t = useTranslate(); return (