@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
JavaScript
'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
};
}
;