@mui/styles
Version:
MUI Styles - The legacy JSS-based styling solution of Material UI.
232 lines (229 loc) • 7.6 kB
JavaScript
import * as React from 'react';
import { getDynamicStyles } from 'jss';
import mergeClasses from "../mergeClasses/index.js";
import multiKeyStore from "./multiKeyStore.js";
import useTheme from "../useTheme/index.js";
import { StylesContext } from "../StylesProvider/index.js";
import { increment } from "./indexCounter.js";
import getStylesCreator from "../getStylesCreator/index.js";
import noopTheme from "../getStylesCreator/noopTheme.js";
function getClasses({
state,
stylesOptions
}, classes, Component) {
if (stylesOptions.disableGeneration) {
return classes || {};
}
if (!state.cacheClasses) {
state.cacheClasses = {
// Cache for the finalized classes value.
value: null,
// Cache for the last used classes prop pointer.
lastProp: null,
// Cache for the last used rendered classes pointer.
lastJSS: {}
};
}
// Tracks if either the rendered classes or classes prop has changed,
// requiring the generation of a new finalized classes object.
let generate = false;
if (state.classes !== state.cacheClasses.lastJSS) {
state.cacheClasses.lastJSS = state.classes;
generate = true;
}
if (classes !== state.cacheClasses.lastProp) {
state.cacheClasses.lastProp = classes;
generate = true;
}
if (generate) {
state.cacheClasses.value = mergeClasses({
baseClasses: state.cacheClasses.lastJSS,
newClasses: classes,
Component
});
}
return state.cacheClasses.value;
}
function attach({
state,
theme,
stylesOptions,
stylesCreator,
name
}, props) {
if (stylesOptions.disableGeneration) {
return;
}
let sheetManager = multiKeyStore.get(stylesOptions.sheetsManager, stylesCreator, theme);
if (!sheetManager) {
sheetManager = {
refs: 0,
staticSheet: null,
dynamicStyles: null
};
multiKeyStore.set(stylesOptions.sheetsManager, stylesCreator, theme, sheetManager);
}
const options = {
...stylesCreator.options,
...stylesOptions,
theme,
flip: typeof stylesOptions.flip === 'boolean' ? stylesOptions.flip : theme.direction === 'rtl'
};
options.generateId = options.serverGenerateClassName || options.generateClassName;
const sheetsRegistry = stylesOptions.sheetsRegistry;
if (sheetManager.refs === 0) {
let staticSheet;
if (stylesOptions.sheetsCache) {
staticSheet = multiKeyStore.get(stylesOptions.sheetsCache, stylesCreator, theme);
}
const styles = stylesCreator.create(theme, name);
if (!staticSheet) {
staticSheet = stylesOptions.jss.createStyleSheet(styles, {
link: false,
...options
});
staticSheet.attach();
if (stylesOptions.sheetsCache) {
multiKeyStore.set(stylesOptions.sheetsCache, stylesCreator, theme, staticSheet);
}
}
if (sheetsRegistry) {
sheetsRegistry.add(staticSheet);
}
sheetManager.staticSheet = staticSheet;
sheetManager.dynamicStyles = getDynamicStyles(styles);
}
if (sheetManager.dynamicStyles) {
const dynamicSheet = stylesOptions.jss.createStyleSheet(sheetManager.dynamicStyles, {
link: true,
...options
});
dynamicSheet.update(props);
dynamicSheet.attach();
state.dynamicSheet = dynamicSheet;
state.classes = mergeClasses({
baseClasses: sheetManager.staticSheet.classes,
newClasses: dynamicSheet.classes
});
if (sheetsRegistry) {
sheetsRegistry.add(dynamicSheet);
}
} else {
state.classes = sheetManager.staticSheet.classes;
}
sheetManager.refs += 1;
}
function update({
state
}, props) {
if (state.dynamicSheet) {
state.dynamicSheet.update(props);
}
}
function detach({
state,
theme,
stylesOptions,
stylesCreator
}) {
if (stylesOptions.disableGeneration) {
return;
}
const sheetManager = multiKeyStore.get(stylesOptions.sheetsManager, stylesCreator, theme);
sheetManager.refs -= 1;
const sheetsRegistry = stylesOptions.sheetsRegistry;
if (sheetManager.refs === 0) {
multiKeyStore.delete(stylesOptions.sheetsManager, stylesCreator, theme);
stylesOptions.jss.removeStyleSheet(sheetManager.staticSheet);
if (sheetsRegistry) {
sheetsRegistry.remove(sheetManager.staticSheet);
}
}
if (state.dynamicSheet) {
stylesOptions.jss.removeStyleSheet(state.dynamicSheet);
if (sheetsRegistry) {
sheetsRegistry.remove(state.dynamicSheet);
}
}
}
function useSynchronousEffect(func, values) {
const key = React.useRef([]);
let output;
// Store "generation" key. Just returns a new object every time
// TODO: uncomment once we enable eslint-plugin-react-compiler // eslint-disable-next-line react-compiler/react-compiler
const currentKey = React.useMemo(() => ({}), values); // eslint-disable-line react-hooks/exhaustive-deps
// "the first render", or "memo dropped the value"
if (key.current !== currentKey) {
key.current = currentKey;
output = func();
}
React.useEffect(() => () => {
if (output) {
output();
}
}, [currentKey] // eslint-disable-line react-hooks/exhaustive-deps
);
}
export default function makeStyles(stylesOrCreator, options = {}) {
const {
// alias for classNamePrefix, if provided will listen to theme (required for theme.components[name].styleOverrides)
name,
// Help with debuggability.
classNamePrefix: classNamePrefixOption,
Component,
defaultTheme = noopTheme,
...stylesOptions2
} = options;
const stylesCreator = getStylesCreator(stylesOrCreator);
const classNamePrefix = name || classNamePrefixOption || 'makeStyles';
stylesCreator.options = {
index: increment(),
name,
meta: classNamePrefix,
classNamePrefix
};
const useStyles = (props = {}) => {
const theme = useTheme() || defaultTheme;
const stylesOptions = {
...React.useContext(StylesContext),
...stylesOptions2
};
const instance = React.useRef();
const shouldUpdate = React.useRef();
useSynchronousEffect(() => {
const current = {
name,
state: {},
stylesCreator,
stylesOptions,
theme
};
attach(current, props);
shouldUpdate.current = false;
instance.current = current;
return () => {
detach(current);
};
}, [theme, stylesCreator]);
React.useEffect(() => {
if (shouldUpdate.current) {
update(instance.current, props);
}
shouldUpdate.current = true;
});
const classes = getClasses(instance.current, props.classes, Component);
if (process.env.NODE_ENV !== 'production') {
// TODO: uncomment once we enable eslint-plugin-react-compiler // eslint-disable-next-line react-compiler/react-compiler
// eslint-disable-next-line react-hooks/rules-of-hooks -- process.env never changes
React.useDebugValue(classes);
}
if (process.env.NODE_ENV !== 'production') {
const supportedComponents = ['MuiAvatar', 'MuiBadge', 'MuiButton', 'MuiButtonGroup', 'MuiChip', 'MuiDivider', 'MuiFab', 'MuiPaper', 'MuiToolbar', 'MuiTypography', 'MuiAlert', 'MuiPagination', 'MuiPaginationItem', 'MuiSkeleton', 'MuiTimelineDot'];
if (name && supportedComponents.indexOf(name) >= 0 && props.variant && !classes[props.variant]) {
console.error([`MUI: You are using a variant value \`${props.variant}\` for which you didn't define styles.`, `Please create a new variant matcher in your theme for this variant. To learn more about matchers visit https://mui.com/r/custom-component-variants.`].join('\n'));
}
}
return classes;
};
return useStyles;
}