@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.
106 lines (104 loc) • 3.93 kB
JavaScript
'use client';
import * as React from 'react';
import { jsx as _jsx } from "react/jsx-runtime";
export const DEFAULT_MODE_STORAGE_KEY = 'mode';
export const DEFAULT_COLOR_SCHEME_STORAGE_KEY = 'color-scheme';
export const DEFAULT_ATTRIBUTE = 'data-color-scheme';
// React 17 has no `useSyncExternalStore`; spread into a plain object so reading the
// missing property returns `undefined` instead of throwing under strict ESM. See #41190 (comment).
const safeReact = {
...React
};
const maybeReactUseSyncExternalStore = safeReact.useSyncExternalStore;
const subscribe = () => () => {};
/**
* `true` during the server render and the matching hydration render, `false`
* on every client render afterwards. React warns when a `<script>` is
* created during a client render (such scripts never execute), so the inline
* script is only emitted on the server pass and dropped after hydration — the
* attribute it already set on the document persists. React <18 has no
* `useSyncExternalStore` and no such warning, so the script is always emitted.
*/
function useIsServerRender() {
if (maybeReactUseSyncExternalStore === undefined) {
return true;
}
return maybeReactUseSyncExternalStore(subscribe, () => false, () => true);
}
export function buildInitColorSchemeScript(options) {
const {
defaultMode = 'system',
defaultLightColorScheme = 'light',
defaultDarkColorScheme = 'dark',
modeStorageKey = DEFAULT_MODE_STORAGE_KEY,
colorSchemeStorageKey = DEFAULT_COLOR_SCHEME_STORAGE_KEY,
attribute: initialAttribute = DEFAULT_ATTRIBUTE,
colorSchemeNode = 'document.documentElement',
nonce
} = options || {};
let setter = '';
let attribute = initialAttribute;
if (initialAttribute === 'class') {
attribute = '.%s';
}
if (initialAttribute === 'data') {
attribute = '[data-%s]';
}
if (attribute.startsWith('.')) {
const selector = attribute.substring(1);
setter += `${colorSchemeNode}.classList.remove('${selector}'.replace('%s', light), '${selector}'.replace('%s', dark));
${colorSchemeNode}.classList.add('${selector}'.replace('%s', colorScheme));`;
}
const matches = attribute.match(/\[([^[\]]+)\]/); // case [data-color-scheme='%s'] or [data-color-scheme]
if (matches) {
const [attr, value] = matches[1].split('=');
if (!value) {
setter += `${colorSchemeNode}.removeAttribute('${attr}'.replace('%s', light));
${colorSchemeNode}.removeAttribute('${attr}'.replace('%s', dark));`;
}
setter += `
${colorSchemeNode}.setAttribute('${attr}'.replace('%s', colorScheme), ${value ? `${value}.replace('%s', colorScheme)` : '""'});`;
} else if (attribute !== '.%s') {
setter += `${colorSchemeNode}.setAttribute('${attribute}', colorScheme);`;
}
return /*#__PURE__*/_jsx("script", {
suppressHydrationWarning: true,
nonce: typeof window === 'undefined' ? nonce : ''
// eslint-disable-next-line react/no-danger
,
dangerouslySetInnerHTML: {
__html: `(function() {
try {
let colorScheme = '';
const mode = localStorage.getItem('${modeStorageKey}') || '${defaultMode}';
const dark = localStorage.getItem('${colorSchemeStorageKey}-dark') || '${defaultDarkColorScheme}';
const light = localStorage.getItem('${colorSchemeStorageKey}-light') || '${defaultLightColorScheme}';
if (mode === 'system') {
// handle system mode
const mql = window.matchMedia('(prefers-color-scheme: dark)');
if (mql.matches) {
colorScheme = dark
} else {
colorScheme = light
}
}
if (mode === 'light') {
colorScheme = light;
}
if (mode === 'dark') {
colorScheme = dark;
}
if (colorScheme) {
${setter}
}
} catch(e){}})();`
}
}, "mui-color-scheme-init");
}
export default function InitColorSchemeScript(options) {
const isServerRender = useIsServerRender();
if (!isServerRender) {
return null;
}
return buildInitColorSchemeScript(options);
}