UNPKG

@mui/system

Version:

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.

346 lines (338 loc) 14.6 kB
"use strict"; 'use client'; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.DISABLE_CSS_TRANSITION = void 0; exports.default = createCssVarsProvider; var React = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _styledEngine = require("@mui/styled-engine"); var _privateTheming = require("@mui/private-theming"); var _useEnhancedEffect = _interopRequireDefault(require("@mui/utils/useEnhancedEffect")); var _ThemeProvider = _interopRequireDefault(require("../ThemeProvider")); var _InitColorSchemeScript = _interopRequireWildcard(require("../InitColorSchemeScript/InitColorSchemeScript")); var _useCurrentColorScheme = _interopRequireDefault(require("./useCurrentColorScheme")); var _jsxRuntime = require("react/jsx-runtime"); const DISABLE_CSS_TRANSITION = exports.DISABLE_CSS_TRANSITION = '*{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}'; function createCssVarsProvider(options) { const { themeId, /** * This `theme` object needs to follow a certain structure to * be used correctly by the finel `CssVarsProvider`. It should have a * `colorSchemes` key with the light and dark (and any other) palette. * It should also ideally have a vars object created using `prepareCssVars`. */ theme: defaultTheme = {}, modeStorageKey: defaultModeStorageKey = _InitColorSchemeScript.DEFAULT_MODE_STORAGE_KEY, colorSchemeStorageKey: defaultColorSchemeStorageKey = _InitColorSchemeScript.DEFAULT_COLOR_SCHEME_STORAGE_KEY, disableTransitionOnChange: designSystemTransitionOnChange = false, defaultColorScheme, resolveTheme } = options; const defaultContext = { allColorSchemes: [], colorScheme: undefined, darkColorScheme: undefined, lightColorScheme: undefined, mode: undefined, setColorScheme: () => {}, setMode: () => {}, systemMode: undefined }; const ColorSchemeContext = /*#__PURE__*/React.createContext(undefined); if (process.env.NODE_ENV !== 'production') { ColorSchemeContext.displayName = 'ColorSchemeContext'; } const useColorScheme = () => React.useContext(ColorSchemeContext) || defaultContext; const defaultColorSchemes = {}; const defaultComponents = {}; function CssVarsProvider(props) { const { children, theme: themeProp, modeStorageKey = defaultModeStorageKey, colorSchemeStorageKey = defaultColorSchemeStorageKey, disableTransitionOnChange = designSystemTransitionOnChange, storageManager, storageWindow = typeof window === 'undefined' ? undefined : window, documentNode = typeof document === 'undefined' ? undefined : document, colorSchemeNode = typeof document === 'undefined' ? undefined : document.documentElement, disableNestedContext = false, disableStyleSheetGeneration = false, defaultMode: initialMode = 'system', forceThemeRerender = false, noSsr } = props; const hasMounted = React.useRef(false); const upperTheme = (0, _privateTheming.useTheme)(); const ctx = React.useContext(ColorSchemeContext); const nested = !!ctx && !disableNestedContext; const initialTheme = React.useMemo(() => { if (themeProp) { return themeProp; } return typeof defaultTheme === 'function' ? defaultTheme() : defaultTheme; }, [themeProp]); const scopedTheme = initialTheme[themeId]; const restThemeProp = scopedTheme || initialTheme; const { colorSchemes = defaultColorSchemes, components = defaultComponents, cssVarPrefix } = restThemeProp; const joinedColorSchemes = Object.keys(colorSchemes).filter(k => !!colorSchemes[k]).join(','); const allColorSchemes = React.useMemo(() => joinedColorSchemes.split(','), [joinedColorSchemes]); const defaultLightColorScheme = typeof defaultColorScheme === 'string' ? defaultColorScheme : defaultColorScheme.light; const defaultDarkColorScheme = typeof defaultColorScheme === 'string' ? defaultColorScheme : defaultColorScheme.dark; const defaultMode = colorSchemes[defaultLightColorScheme] && colorSchemes[defaultDarkColorScheme] ? initialMode : colorSchemes[restThemeProp.defaultColorScheme]?.palette?.mode || restThemeProp.palette?.mode; // 1. Get the data about the `mode`, `colorScheme`, and setter functions. const { mode: stateMode, setMode, systemMode, lightColorScheme, darkColorScheme, colorScheme: stateColorScheme, setColorScheme } = (0, _useCurrentColorScheme.default)({ supportedColorSchemes: allColorSchemes, defaultLightColorScheme, defaultDarkColorScheme, modeStorageKey, colorSchemeStorageKey, defaultMode, storageManager, storageWindow, noSsr }); let mode = stateMode; let colorScheme = stateColorScheme; if (nested) { mode = ctx.mode; colorScheme = ctx.colorScheme; } if (process.env.NODE_ENV !== 'production') { if (forceThemeRerender && !restThemeProp.vars) { console.warn(['MUI: The `forceThemeRerender` prop should only be used with CSS theme variables.', 'Note that it will slow down the app when changing between modes, so only do this when you cannot find a better solution.'].join('\n')); } } // `colorScheme` is undefined on the server and hydration phase let calculatedColorScheme = colorScheme || restThemeProp.defaultColorScheme; if (restThemeProp.vars && !forceThemeRerender) { calculatedColorScheme = restThemeProp.defaultColorScheme; } const memoTheme = React.useMemo(() => { // 2. get the `vars` object that refers to the CSS custom properties const themeVars = restThemeProp.generateThemeVars?.() || restThemeProp.vars; // 3. Start composing the theme object const theme = { ...restThemeProp, components, colorSchemes, cssVarPrefix, vars: themeVars }; if (typeof theme.generateSpacing === 'function') { theme.spacing = theme.generateSpacing(); } // 4. Resolve the color scheme and merge it to the theme if (calculatedColorScheme) { const scheme = colorSchemes[calculatedColorScheme]; if (scheme && typeof scheme === 'object') { // 4.1 Merge the selected color scheme to the theme Object.keys(scheme).forEach(schemeKey => { if (scheme[schemeKey] && typeof scheme[schemeKey] === 'object') { // shallow merge the 1st level structure of the theme. theme[schemeKey] = { ...theme[schemeKey], ...scheme[schemeKey] }; } else { theme[schemeKey] = scheme[schemeKey]; } }); } } return resolveTheme ? resolveTheme(theme) : theme; }, [restThemeProp, calculatedColorScheme, components, colorSchemes, cssVarPrefix]); // 5. Declaring effects // 5.1 Updates the selector value to use the current color scheme which tells CSS to use the proper stylesheet. const colorSchemeSelector = restThemeProp.colorSchemeSelector; (0, _useEnhancedEffect.default)(() => { if (colorScheme && colorSchemeNode && colorSchemeSelector && colorSchemeSelector !== 'media') { const selector = colorSchemeSelector; let rule = colorSchemeSelector; if (selector === 'class') { rule = `.%s`; } if (selector === 'data') { rule = `[data-%s]`; } if (selector?.startsWith('data-') && !selector.includes('%s')) { // 'data-mui-color-scheme' -> '[data-mui-color-scheme="%s"]' rule = `[${selector}="%s"]`; } if (rule.startsWith('.')) { colorSchemeNode.classList.remove(...allColorSchemes.map(scheme => rule.substring(1).replace('%s', scheme))); colorSchemeNode.classList.add(rule.substring(1).replace('%s', colorScheme)); } else { const matches = rule.replace('%s', colorScheme).match(/\[([^\]]+)\]/); if (matches) { const [attr, value] = matches[1].split('='); if (!value) { // for attributes like `data-theme-dark`, `data-theme-light` // remove all the existing data attributes before setting the new one allColorSchemes.forEach(scheme => { colorSchemeNode.removeAttribute(attr.replace(colorScheme, scheme)); }); } colorSchemeNode.setAttribute(attr, value ? value.replace(/"|'/g, '') : ''); } else { colorSchemeNode.setAttribute(rule, colorScheme); } } } }, [colorScheme, colorSchemeSelector, colorSchemeNode, allColorSchemes]); // 5.2 Remove the CSS transition when color scheme changes to create instant experience. // credit: https://github.com/pacocoursey/next-themes/blob/b5c2bad50de2d61ad7b52a9c5cdc801a78507d7a/index.tsx#L313 React.useEffect(() => { let timer; if (disableTransitionOnChange && hasMounted.current && documentNode) { const css = documentNode.createElement('style'); css.appendChild(documentNode.createTextNode(DISABLE_CSS_TRANSITION)); documentNode.head.appendChild(css); // Force browser repaint (() => window.getComputedStyle(documentNode.body))(); timer = setTimeout(() => { documentNode.head.removeChild(css); }, 1); } return () => { clearTimeout(timer); }; }, [colorScheme, disableTransitionOnChange, documentNode]); React.useEffect(() => { hasMounted.current = true; return () => { hasMounted.current = false; }; }, []); const contextValue = React.useMemo(() => ({ allColorSchemes, colorScheme, darkColorScheme, lightColorScheme, mode, setColorScheme, setMode: process.env.NODE_ENV === 'production' ? setMode : newMode => { if (memoTheme.colorSchemeSelector === 'media') { console.error(['MUI: The `setMode` function has no effect if `colorSchemeSelector` is `media` (`media` is the default value).', 'To toggle the mode manually, please configure `colorSchemeSelector` to use a class or data attribute.', 'To learn more, visit https://mui.com/material-ui/customization/css-theme-variables/configuration/#toggling-dark-mode-manually'].join('\n')); } setMode(newMode); }, systemMode }), [allColorSchemes, colorScheme, darkColorScheme, lightColorScheme, mode, setColorScheme, setMode, systemMode, memoTheme.colorSchemeSelector]); let shouldGenerateStyleSheet = true; if (disableStyleSheetGeneration || restThemeProp.cssVariables === false || nested && upperTheme?.cssVarPrefix === cssVarPrefix) { shouldGenerateStyleSheet = false; } const element = /*#__PURE__*/(0, _jsxRuntime.jsxs)(React.Fragment, { children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_ThemeProvider.default, { themeId: scopedTheme ? themeId : undefined, theme: memoTheme, children: children }), shouldGenerateStyleSheet && /*#__PURE__*/(0, _jsxRuntime.jsx)(_styledEngine.GlobalStyles, { styles: memoTheme.generateStyleSheets?.() || [] })] }); if (nested) { return element; } return /*#__PURE__*/(0, _jsxRuntime.jsx)(ColorSchemeContext.Provider, { value: contextValue, children: element }); } process.env.NODE_ENV !== "production" ? CssVarsProvider.propTypes = { /** * The component tree. */ children: _propTypes.default.node, /** * The node used to attach the color-scheme attribute */ colorSchemeNode: _propTypes.default.any, /** * localStorage key used to store `colorScheme` */ colorSchemeStorageKey: _propTypes.default.string, /** * The default mode when the storage is empty, * require the theme to have `colorSchemes` with light and dark. */ defaultMode: _propTypes.default.string, /** * If `true`, the provider creates its own context and generate stylesheet as if it is a root `CssVarsProvider`. */ disableNestedContext: _propTypes.default.bool, /** * If `true`, the style sheet won't be generated. * * This is useful for controlling nested CssVarsProvider behavior. */ disableStyleSheetGeneration: _propTypes.default.bool, /** * Disable CSS transitions when switching between modes or color schemes. */ disableTransitionOnChange: _propTypes.default.bool, /** * The document to attach the attribute to. */ documentNode: _propTypes.default.any, /** * If `true`, theme values are recalculated when the mode changes. */ forceThemeRerender: _propTypes.default.bool, /** * The key in the local storage used to store current color scheme. */ modeStorageKey: _propTypes.default.string, /** * If `true`, the mode will be the same value as the storage without an extra rerendering after the hydration. * You should use this option in conjuction with `InitColorSchemeScript` component. */ noSsr: _propTypes.default.bool, /** * The storage manager to be used for storing the mode and color scheme * @default using `window.localStorage` */ storageManager: _propTypes.default.func, /** * The window that attaches the 'storage' event listener. * @default window */ storageWindow: _propTypes.default.any, /** * The calculated theme object that will be passed through context. */ theme: _propTypes.default.object } : void 0; const defaultLightColorScheme = typeof defaultColorScheme === 'string' ? defaultColorScheme : defaultColorScheme.light; const defaultDarkColorScheme = typeof defaultColorScheme === 'string' ? defaultColorScheme : defaultColorScheme.dark; const getInitColorSchemeScript = params => (0, _InitColorSchemeScript.default)({ colorSchemeStorageKey: defaultColorSchemeStorageKey, defaultLightColorScheme, defaultDarkColorScheme, modeStorageKey: defaultModeStorageKey, ...params }); return { CssVarsProvider, useColorScheme, getInitColorSchemeScript }; }