createThemeNoVars.js
6.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import _formatErrorMessage from "@mui/utils/formatMuiErrorMessage";
import deepmerge from '@mui/utils/deepmerge';
import styleFunctionSx, { unstable_defaultSxConfig as defaultSxConfig } from '@mui/system/styleFunctionSx';
import systemCreateTheme from '@mui/system/createTheme';
import { alpha as systemAlpha, lighten as systemLighten, darken as systemDarken } from '@mui/system/colorManipulator';
import generateUtilityClass from '@mui/utils/generateUtilityClass';
import createMixins from "./createMixins.js";
import createPalette from "./createPalette.js";
import createTypography from "./createTypography.js";
import shadows from "./shadows.js";
import createTransitions from "./createTransitions.js";
import zIndex from "./zIndex.js";
import { stringifyTheme } from "./stringifyTheme.js";
function coefficientToPercentage(coefficient) {
if (typeof coefficient === 'number') {
return `${(coefficient * 100).toFixed(0)}%`;
}
return `calc((${coefficient}) * 100%)`;
}
// This can be removed when moved to `color-mix()` entirely.
const parseAddition = str => {
if (!Number.isNaN(+str)) {
return +str;
}
const numbers = str.match(/\d*\.?\d+/g);
if (!numbers) {
return 0;
}
let sum = 0;
for (let i = 0; i < numbers.length; i += 1) {
sum += +numbers[i];
}
return sum;
};
function attachColorManipulators(theme) {
Object.assign(theme, {
alpha(color, coefficient) {
const obj = this || theme;
if (obj.colorSpace) {
return `oklch(from ${color} l c h / ${typeof coefficient === 'string' ? `calc(${coefficient})` : coefficient})`;
}
if (obj.vars) {
// To preserve the behavior of the CSS theme variables
// In the future, this could be replaced by `color-mix` (when https://caniuse.com/?search=color-mix reaches 95%).
return `rgba(${color.replace(/var\(--([^,\s)]+)(?:,[^)]+)?\)+/g, 'var(--$1Channel)')} / ${typeof coefficient === 'string' ? `calc(${coefficient})` : coefficient})`;
}
return systemAlpha(color, parseAddition(coefficient));
},
lighten(color, coefficient) {
const obj = this || theme;
if (obj.colorSpace) {
return `color-mix(in ${obj.colorSpace}, ${color}, #fff ${coefficientToPercentage(coefficient)})`;
}
return systemLighten(color, coefficient);
},
darken(color, coefficient) {
const obj = this || theme;
if (obj.colorSpace) {
return `color-mix(in ${obj.colorSpace}, ${color}, #000 ${coefficientToPercentage(coefficient)})`;
}
return systemDarken(color, coefficient);
}
});
}
function createThemeNoVars(options = {}, ...args) {
const {
breakpoints: breakpointsInput,
mixins: mixinsInput = {},
spacing: spacingInput,
palette: paletteInput = {},
transitions: transitionsInput = {},
typography: typographyInput = {},
shape: shapeInput,
colorSpace,
...other
} = options;
if (options.vars &&
// The error should throw only for the root theme creation because user is not allowed to use a custom node `vars`.
// `generateThemeVars` is the closest identifier for checking that the `options` is a result of `createTheme` with CSS variables so that user can create new theme for nested ThemeProvider.
options.generateThemeVars === undefined) {
throw new Error(process.env.NODE_ENV !== "production" ? 'MUI: `vars` is a private field used for CSS variables support.\n' +
// #host-reference
'Please use another name or follow the [docs](https://mui.com/material-ui/customization/css-theme-variables/usage/) to enable the feature.' : _formatErrorMessage(20));
}
const palette = createPalette({
...paletteInput,
colorSpace
});
const systemTheme = systemCreateTheme(options);
let muiTheme = deepmerge(systemTheme, {
mixins: createMixins(systemTheme.breakpoints, mixinsInput),
palette,
// Don't use [...shadows] until you've verified its transpiled code is not invoking the iterator protocol.
shadows: shadows.slice(),
typography: createTypography(palette, typographyInput),
transitions: createTransitions(transitionsInput),
zIndex: {
...zIndex
}
});
muiTheme = deepmerge(muiTheme, other);
muiTheme = args.reduce((acc, argument) => deepmerge(acc, argument), muiTheme);
if (process.env.NODE_ENV !== 'production') {
// TODO v6: Refactor to use globalStateClassesMapping from @mui/utils once `readOnly` state class is used in Rating component.
const stateClasses = ['active', 'checked', 'completed', 'disabled', 'error', 'expanded', 'focused', 'focusVisible', 'required', 'selected'];
const traverse = (node, component) => {
let key;
// eslint-disable-next-line guard-for-in
for (key in node) {
const child = node[key];
if (stateClasses.includes(key) && Object.keys(child).length > 0) {
if (process.env.NODE_ENV !== 'production') {
const stateClass = generateUtilityClass('', key);
console.error([`MUI: The \`${component}\` component increases ` + `the CSS specificity of the \`${key}\` internal state.`, 'You can not override it like this: ', JSON.stringify(node, null, 2), '', `Instead, you need to use the '&.${stateClass}' syntax:`, JSON.stringify({
root: {
[`&.${stateClass}`]: child
}
}, null, 2), '', 'https://mui.com/r/state-classes-guide'].join('\n'));
}
// Remove the style to prevent global conflicts.
node[key] = {};
}
}
};
Object.keys(muiTheme.components).forEach(component => {
const styleOverrides = muiTheme.components[component].styleOverrides;
if (styleOverrides && component.startsWith('Mui')) {
traverse(styleOverrides, component);
}
});
}
muiTheme.unstable_sxConfig = {
...defaultSxConfig,
...other?.unstable_sxConfig
};
muiTheme.unstable_sx = function sx(props) {
return styleFunctionSx({
sx: props,
theme: this
});
};
muiTheme.toRuntimeSource = stringifyTheme; // for Pigment CSS integration
attachColorManipulators(muiTheme);
return muiTheme;
}
export default createThemeNoVars;