@mui/system 5.0.6 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/LICENSE +21 -21
  3. package/breakpoints.js +41 -8
  4. package/createBox.d.ts +5 -1
  5. package/createBox.js +5 -3
  6. package/createTheme/createBreakpoints.js +2 -2
  7. package/cssVars/createCssVarsProvider.d.ts +88 -38
  8. package/cssVars/createCssVarsProvider.js +83 -61
  9. package/cssVars/cssVarsParser.d.ts +14 -3
  10. package/cssVars/cssVarsParser.js +41 -11
  11. package/cssVars/getInitColorSchemeScript.d.ts +7 -2
  12. package/cssVars/getInitColorSchemeScript.js +27 -5
  13. package/cssVars/useCurrentColorScheme.d.ts +50 -0
  14. package/cssVars/useCurrentColorScheme.js +235 -0
  15. package/esm/breakpoints.js +39 -8
  16. package/esm/createBox.js +5 -3
  17. package/esm/createTheme/createBreakpoints.js +2 -2
  18. package/esm/cssVars/createCssVarsProvider.js +82 -63
  19. package/esm/cssVars/cssVarsParser.js +40 -11
  20. package/esm/cssVars/getInitColorSchemeScript.js +24 -3
  21. package/esm/cssVars/useCurrentColorScheme.js +217 -0
  22. package/esm/styleFunctionSx/extendSxProp.js +20 -1
  23. package/esm/styleFunctionSx/styleFunctionSx.js +45 -35
  24. package/index.js +1 -1
  25. package/legacy/breakpoints.js +39 -8
  26. package/legacy/createBox.js +6 -3
  27. package/legacy/createTheme/createBreakpoints.js +2 -2
  28. package/legacy/cssVars/createCssVarsProvider.js +83 -70
  29. package/legacy/cssVars/cssVarsParser.js +37 -9
  30. package/legacy/cssVars/getInitColorSchemeScript.js +13 -4
  31. package/legacy/cssVars/useCurrentColorScheme.js +231 -0
  32. package/legacy/index.js +1 -1
  33. package/legacy/styleFunctionSx/extendSxProp.js +21 -1
  34. package/legacy/styleFunctionSx/styleFunctionSx.js +44 -34
  35. package/modern/breakpoints.js +39 -8
  36. package/modern/createBox.js +5 -3
  37. package/modern/createTheme/createBreakpoints.js +2 -2
  38. package/modern/cssVars/createCssVarsProvider.js +82 -63
  39. package/modern/cssVars/cssVarsParser.js +40 -11
  40. package/modern/cssVars/getInitColorSchemeScript.js +24 -3
  41. package/modern/cssVars/useCurrentColorScheme.js +217 -0
  42. package/modern/index.js +1 -1
  43. package/modern/styleFunctionSx/extendSxProp.js +20 -1
  44. package/modern/styleFunctionSx/styleFunctionSx.js +45 -35
  45. package/package.json +7 -7
  46. package/styleFunctionSx/extendSxProp.js +21 -1
  47. package/styleFunctionSx/styleFunctionSx.d.ts +2 -1
  48. package/styleFunctionSx/styleFunctionSx.js +46 -36
  49. package/styleFunctionSx/styleFunctionSx.spec.d.ts +1 -0
@@ -1,17 +1,38 @@
1
1
  import * as React from 'react';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- export const DEFAULT_STORAGE_KEY = 'mui-color-scheme';
3
+ export const DEFAULT_MODE_STORAGE_KEY = 'mui-mode';
4
+ export const DEFAULT_COLOR_SCHEME_STORAGE_KEY = 'mui-color-scheme';
4
5
  export const DEFAULT_ATTRIBUTE = 'data-mui-color-scheme';
5
6
  export default function getInitColorSchemeScript(options) {
6
7
  const {
7
- storageKey = DEFAULT_STORAGE_KEY,
8
+ defaultMode = 'light',
9
+ defaultLightColorScheme = 'light',
10
+ defaultDarkColorScheme = 'dark',
11
+ modeStorageKey = DEFAULT_MODE_STORAGE_KEY,
12
+ colorSchemeStorageKey = DEFAULT_COLOR_SCHEME_STORAGE_KEY,
8
13
  attribute = DEFAULT_ATTRIBUTE
9
14
  } = options || {};
10
15
  return /*#__PURE__*/_jsx("script", {
11
16
  // eslint-disable-next-line react/no-danger
12
17
  dangerouslySetInnerHTML: {
13
18
  __html: `(function() { try {
14
- var colorScheme = localStorage.getItem('${storageKey}');
19
+ var mode = localStorage.getItem('${modeStorageKey}');
20
+ var colorScheme = '';
21
+ if (mode === 'system' || (!mode && ${defaultMode} === 'system')) {
22
+ // handle system mode
23
+ var mql = window.matchMedia('(prefers-color-scheme: dark)');
24
+ if (mql.matches) {
25
+ colorScheme = localStorage.getItem('${colorSchemeStorageKey}-dark') || ${defaultLightColorScheme};
26
+ } else {
27
+ colorScheme = localStorage.getItem('${colorSchemeStorageKey}-light') || ${defaultDarkColorScheme};
28
+ }
29
+ }
30
+ if (mode === 'light') {
31
+ colorScheme = localStorage.getItem('${colorSchemeStorageKey}-light') || ${defaultLightColorScheme};
32
+ }
33
+ if (mode === 'dark') {
34
+ colorScheme = localStorage.getItem('${colorSchemeStorageKey}-dark') || ${defaultDarkColorScheme};
35
+ }
15
36
  if (colorScheme) {
16
37
  document.body.setAttribute('${attribute}', colorScheme);
17
38
  }
@@ -0,0 +1,217 @@
1
+ import _extends from "@babel/runtime/helpers/esm/extends";
2
+ import * as React from 'react';
3
+ import { DEFAULT_MODE_STORAGE_KEY, DEFAULT_COLOR_SCHEME_STORAGE_KEY } from './getInitColorSchemeScript';
4
+ export function getSystemMode(mode) {
5
+ if (typeof window !== 'undefined' && mode === 'system') {
6
+ const mql = window.matchMedia('(prefers-color-scheme: dark)');
7
+
8
+ if (mql.matches) {
9
+ return 'dark';
10
+ }
11
+
12
+ return 'light';
13
+ }
14
+
15
+ return undefined;
16
+ }
17
+
18
+ function processState(state, callback) {
19
+ if (state.mode === 'light' || state.mode === 'system' && state.systemMode === 'light') {
20
+ return callback('light');
21
+ }
22
+
23
+ if (state.mode === 'dark' || state.mode === 'system' && state.systemMode === 'dark') {
24
+ return callback('dark');
25
+ }
26
+
27
+ return undefined;
28
+ }
29
+
30
+ export function getColorScheme(state) {
31
+ return processState(state, mode => {
32
+ if (mode === 'light') {
33
+ return state.lightColorScheme;
34
+ }
35
+
36
+ if (mode === 'dark') {
37
+ return state.darkColorScheme;
38
+ }
39
+
40
+ return undefined;
41
+ });
42
+ }
43
+
44
+ function resolveValue(key, defaultValue) {
45
+ if (typeof window === 'undefined') {
46
+ return undefined;
47
+ }
48
+
49
+ let value;
50
+
51
+ try {
52
+ value = localStorage.getItem(key) || undefined;
53
+ } catch (e) {// Unsupported
54
+ }
55
+
56
+ return value || defaultValue;
57
+ }
58
+
59
+ export default function useCurrentColorScheme(options) {
60
+ const {
61
+ defaultMode = 'light',
62
+ defaultLightColorScheme,
63
+ defaultDarkColorScheme,
64
+ supportedColorSchemes = [],
65
+ modeStorageKey = DEFAULT_MODE_STORAGE_KEY,
66
+ colorSchemeStorageKey = DEFAULT_COLOR_SCHEME_STORAGE_KEY
67
+ } = options;
68
+ const joinedColorSchemes = supportedColorSchemes.join(',');
69
+ const [state, setState] = React.useState(() => {
70
+ const initialMode = resolveValue(modeStorageKey, defaultMode);
71
+ return {
72
+ mode: initialMode,
73
+ systemMode: getSystemMode(initialMode),
74
+ lightColorScheme: resolveValue(`${colorSchemeStorageKey}-light`) || defaultLightColorScheme,
75
+ darkColorScheme: resolveValue(`${colorSchemeStorageKey}-dark`) || defaultDarkColorScheme
76
+ };
77
+ });
78
+ const colorScheme = getColorScheme(state);
79
+ const setMode = React.useCallback(mode => {
80
+ setState(currentState => {
81
+ const newMode = !mode ? defaultMode : mode;
82
+
83
+ if (typeof localStorage !== 'undefined') {
84
+ localStorage.setItem(modeStorageKey, newMode);
85
+ }
86
+
87
+ return _extends({}, currentState, {
88
+ mode: newMode,
89
+ systemMode: getSystemMode(newMode)
90
+ });
91
+ });
92
+ }, [modeStorageKey, defaultMode]);
93
+ const setColorScheme = React.useCallback(value => {
94
+ if (!value || typeof value === 'string') {
95
+ if (value && !supportedColorSchemes.includes(value)) {
96
+ console.error(`\`${value}\` does not exist in \`theme.colorSchemes\`.`);
97
+ } else {
98
+ setState(currentState => {
99
+ const newState = _extends({}, currentState);
100
+
101
+ if (!value) {
102
+ // reset to default color scheme
103
+ newState.lightColorScheme = defaultLightColorScheme;
104
+ newState.darkColorScheme = defaultDarkColorScheme;
105
+ return newState;
106
+ }
107
+
108
+ processState(currentState, mode => {
109
+ localStorage.setItem(`${colorSchemeStorageKey}-${mode}`, value);
110
+
111
+ if (mode === 'light') {
112
+ newState.lightColorScheme = value;
113
+ }
114
+
115
+ if (mode === 'dark') {
116
+ newState.darkColorScheme = value;
117
+ }
118
+ });
119
+ return newState;
120
+ });
121
+ }
122
+ } else if (value.light && !supportedColorSchemes.includes(value.light) || value.dark && !supportedColorSchemes.includes(value.dark)) {
123
+ console.error(`\`${value}\` does not exist in \`theme.colorSchemes\`.`);
124
+ } else {
125
+ setState(currentState => {
126
+ const newState = _extends({}, currentState);
127
+
128
+ if (value.light || value.light === null) {
129
+ newState.lightColorScheme = value.light === null ? defaultLightColorScheme : value.light;
130
+ }
131
+
132
+ if (value.dark || value.dark === null) {
133
+ newState.darkColorScheme = value.dark === null ? defaultDarkColorScheme : value.dark;
134
+ }
135
+
136
+ return newState;
137
+ });
138
+
139
+ if (value.light) {
140
+ localStorage.setItem(`${colorSchemeStorageKey}-light`, value.light);
141
+ }
142
+
143
+ if (value.dark) {
144
+ localStorage.setItem(`${colorSchemeStorageKey}-dark`, value.dark);
145
+ }
146
+ }
147
+ }, [colorSchemeStorageKey, supportedColorSchemes, defaultLightColorScheme, defaultDarkColorScheme]);
148
+ const handleMediaQuery = React.useCallback(e => {
149
+ if (state.mode === 'system') {
150
+ setState(currentState => _extends({}, currentState, {
151
+ systemMode: e.matches ? 'dark' : 'light'
152
+ }));
153
+ }
154
+ }, [state.mode]); // Ref hack to avoid adding handleMediaQuery as a dep
155
+
156
+ const mediaListener = React.useRef(handleMediaQuery);
157
+ mediaListener.current = handleMediaQuery;
158
+ React.useEffect(() => {
159
+ const handler = (...args) => mediaListener.current(...args); // Always listen to System preference
160
+
161
+
162
+ const media = window.matchMedia('(prefers-color-scheme: dark)'); // Intentionally use deprecated listener methods to support iOS & old browsers
163
+
164
+ media.addListener(handler);
165
+ handler(media);
166
+ return () => media.removeListener(handler);
167
+ }, []); // Save mode, lightColorScheme & darkColorScheme to localStorage
168
+
169
+ React.useEffect(() => {
170
+ if (state.mode) {
171
+ localStorage.setItem(modeStorageKey, state.mode);
172
+ }
173
+
174
+ processState(state, mode => {
175
+ if (mode === 'light') {
176
+ localStorage.setItem(`${colorSchemeStorageKey}-light`, state.lightColorScheme);
177
+ }
178
+
179
+ if (mode === 'dark') {
180
+ localStorage.setItem(`${colorSchemeStorageKey}-dark`, state.darkColorScheme);
181
+ }
182
+ });
183
+ }, [state, colorSchemeStorageKey, modeStorageKey]); // Handle when localStorage has changed
184
+
185
+ React.useEffect(() => {
186
+ const handleStorage = event => {
187
+ const value = event.newValue;
188
+
189
+ if (typeof event.key === 'string' && event.key.startsWith(colorSchemeStorageKey) && (!value || joinedColorSchemes.match(value))) {
190
+ // If the key is deleted, value will be null then reset color scheme to the default one.
191
+ if (event.key.endsWith('light')) {
192
+ setColorScheme({
193
+ light: value
194
+ });
195
+ }
196
+
197
+ if (event.key.endsWith('dark')) {
198
+ setColorScheme({
199
+ dark: value
200
+ });
201
+ }
202
+ }
203
+
204
+ if (event.key === modeStorageKey && (!value || ['light', 'dark', 'system'].includes(value))) {
205
+ setMode(value || defaultMode);
206
+ }
207
+ };
208
+
209
+ window.addEventListener('storage', handleStorage);
210
+ return () => window.removeEventListener('storage', handleStorage);
211
+ }, [setColorScheme, setMode, modeStorageKey, colorSchemeStorageKey, joinedColorSchemes, defaultMode]);
212
+ return _extends({}, state, {
213
+ colorScheme,
214
+ setMode,
215
+ setColorScheme
216
+ });
217
+ }
@@ -1,6 +1,7 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
3
3
  const _excluded = ["sx"];
4
+ import { isPlainObject } from '@mui/utils';
4
5
  import { propToStyleFunction } from '../getThemeValue';
5
6
 
6
7
  const splitProps = props => {
@@ -28,7 +29,25 @@ export default function extendSxProp(props) {
28
29
  systemProps,
29
30
  otherProps
30
31
  } = splitProps(other);
32
+ let finalSx;
33
+
34
+ if (Array.isArray(inSx)) {
35
+ finalSx = [systemProps, ...inSx];
36
+ } else if (typeof inSx === 'function') {
37
+ finalSx = (...args) => {
38
+ const result = inSx(...args);
39
+
40
+ if (!isPlainObject(result)) {
41
+ return systemProps;
42
+ }
43
+
44
+ return _extends({}, systemProps, result);
45
+ };
46
+ } else {
47
+ finalSx = _extends({}, systemProps, inSx);
48
+ }
49
+
31
50
  return _extends({}, otherProps, {
32
- sx: _extends({}, systemProps, inSx)
51
+ sx: finalSx
33
52
  });
34
53
  }
@@ -14,53 +14,63 @@ function callIfFn(maybeFn, arg) {
14
14
 
15
15
  function styleFunctionSx(props) {
16
16
  const {
17
- sx: styles,
17
+ sx,
18
18
  theme = {}
19
19
  } = props || {};
20
20
 
21
- if (!styles) {
22
- return null;
21
+ if (!sx) {
22
+ return null; // emotion & styled-components will neglect null
23
23
  }
24
+ /*
25
+ * Receive `sxInput` as object or callback
26
+ * and then recursively check keys & values to create media query object styles.
27
+ * (the result will be used in `styled`)
28
+ */
24
29
 
25
- let stylesObject = styles;
26
30
 
27
- if (typeof styles === 'function') {
28
- stylesObject = styles(theme);
29
- } else if (typeof styles !== 'object') {
30
- // value
31
- return styles;
32
- }
31
+ function traverse(sxInput) {
32
+ let sxObject = sxInput;
33
33
 
34
- const emptyBreakpoints = createEmptyBreakpointObject(theme.breakpoints);
35
- const breakpointsKeys = Object.keys(emptyBreakpoints);
36
- let css = emptyBreakpoints;
37
- Object.keys(stylesObject).forEach(styleKey => {
38
- const value = callIfFn(stylesObject[styleKey], theme);
34
+ if (typeof sxInput === 'function') {
35
+ sxObject = sxInput(theme);
36
+ } else if (typeof sxInput !== 'object') {
37
+ // value
38
+ return sxInput;
39
+ }
39
40
 
40
- if (typeof value === 'object') {
41
- if (propToStyleFunction[styleKey]) {
42
- css = merge(css, getThemeValue(styleKey, value, theme));
43
- } else {
44
- const breakpointsValues = handleBreakpoints({
45
- theme
46
- }, value, x => ({
47
- [styleKey]: x
48
- }));
41
+ const emptyBreakpoints = createEmptyBreakpointObject(theme.breakpoints);
42
+ const breakpointsKeys = Object.keys(emptyBreakpoints);
43
+ let css = emptyBreakpoints;
44
+ Object.keys(sxObject).forEach(styleKey => {
45
+ const value = callIfFn(sxObject[styleKey], theme);
49
46
 
50
- if (objectsHaveSameKeys(breakpointsValues, value)) {
51
- css[styleKey] = styleFunctionSx({
52
- sx: value,
53
- theme
54
- });
47
+ if (typeof value === 'object') {
48
+ if (propToStyleFunction[styleKey]) {
49
+ css = merge(css, getThemeValue(styleKey, value, theme));
55
50
  } else {
56
- css = merge(css, breakpointsValues);
51
+ const breakpointsValues = handleBreakpoints({
52
+ theme
53
+ }, value, x => ({
54
+ [styleKey]: x
55
+ }));
56
+
57
+ if (objectsHaveSameKeys(breakpointsValues, value)) {
58
+ css[styleKey] = styleFunctionSx({
59
+ sx: value,
60
+ theme
61
+ });
62
+ } else {
63
+ css = merge(css, breakpointsValues);
64
+ }
57
65
  }
66
+ } else {
67
+ css = merge(css, getThemeValue(styleKey, value, theme));
58
68
  }
59
- } else {
60
- css = merge(css, getThemeValue(styleKey, value, theme));
61
- }
62
- });
63
- return removeUnusedBreakpoints(breakpointsKeys, css);
69
+ });
70
+ return removeUnusedBreakpoints(breakpointsKeys, css);
71
+ }
72
+
73
+ return Array.isArray(sx) ? sx.map(traverse) : traverse(sx);
64
74
  }
65
75
 
66
76
  styleFunctionSx.filterProps = ['sx'];
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /** @license MUI v5.0.6
1
+ /** @license MUI v5.1.0
2
2
  *
3
3
  * This source code is licensed under the MIT license found in the
4
4
  * LICENSE file in the root directory of this source tree.
@@ -10,12 +10,12 @@ export var values = {
10
10
  xs: 0,
11
11
  // phone
12
12
  sm: 600,
13
- // tablets
13
+ // tablet
14
14
  md: 900,
15
15
  // small laptop
16
16
  lg: 1200,
17
17
  // desktop
18
- xl: 1536 // large screens
18
+ xl: 1536 // large screen
19
19
 
20
20
  };
21
21
  var defaultBreakpoints = {
@@ -122,10 +122,40 @@ export function mergeBreakpointsInOrder(breakpointsInput) {
122
122
  return deepmerge(prev, next);
123
123
  }, {});
124
124
  return removeUnusedBreakpoints(Object.keys(emptyBreakpoints), mergedOutput);
125
+ } // compute base for responsive values; e.g.,
126
+ // [1,2,3] => {xs: true, sm: true, md: true}
127
+ // {xs: 1, sm: 2, md: 3} => {xs: true, sm: true, md: true}
128
+
129
+ export function computeBreakpointsBase(breakpointValues, themeBreakpoints) {
130
+ // fixed value
131
+ if (_typeof(breakpointValues) !== 'object') {
132
+ return {};
133
+ }
134
+
135
+ var base = {};
136
+ var breakpointsKeys = Object.keys(themeBreakpoints);
137
+
138
+ if (Array.isArray(breakpointValues)) {
139
+ breakpointsKeys.forEach(function (breakpoint, i) {
140
+ if (i < breakpointValues.length) {
141
+ base[breakpoint] = true;
142
+ }
143
+ });
144
+ } else {
145
+ breakpointsKeys.forEach(function (breakpoint) {
146
+ if (breakpointValues[breakpoint] != null) {
147
+ base[breakpoint] = true;
148
+ }
149
+ });
150
+ }
151
+
152
+ return base;
125
153
  }
126
154
  export function resolveBreakpointValues(_ref) {
127
155
  var breakpointValues = _ref.values,
128
- base = _ref.base;
156
+ themeBreakpoints = _ref.breakpoints,
157
+ customBase = _ref.base;
158
+ var base = customBase || computeBreakpointsBase(breakpointValues, themeBreakpoints);
129
159
  var keys = Object.keys(base);
130
160
 
131
161
  if (keys.length === 0) {
@@ -133,14 +163,15 @@ export function resolveBreakpointValues(_ref) {
133
163
  }
134
164
 
135
165
  var previous;
136
- return keys.reduce(function (acc, breakpoint) {
137
- if (_typeof(breakpointValues) === 'object') {
138
- acc[breakpoint] = breakpointValues[breakpoint] != null ? breakpointValues[breakpoint] : breakpointValues[previous];
166
+ return keys.reduce(function (acc, breakpoint, i) {
167
+ if (Array.isArray(breakpointValues)) {
168
+ acc[breakpoint] = breakpointValues[i] != null ? breakpointValues[i] : breakpointValues[previous];
169
+ previous = i;
139
170
  } else {
140
- acc[breakpoint] = breakpointValues;
171
+ acc[breakpoint] = breakpointValues[breakpoint] != null ? breakpointValues[breakpoint] : breakpointValues[previous] || breakpointValues;
172
+ previous = breakpoint;
141
173
  }
142
174
 
143
- previous = breakpoint;
144
175
  return acc;
145
176
  }, {});
146
177
  }
@@ -9,7 +9,10 @@ import useTheme from './useTheme';
9
9
  import { jsx as _jsx } from "react/jsx-runtime";
10
10
  export default function createBox() {
11
11
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
12
- var defaultTheme = options.defaultTheme;
12
+ var defaultTheme = options.defaultTheme,
13
+ _options$defaultClass = options.defaultClassName,
14
+ defaultClassName = _options$defaultClass === void 0 ? 'MuiBox-root' : _options$defaultClass,
15
+ generateClassName = options.generateClassName;
13
16
  var BoxRoot = styled('div')(styleFunctionSx);
14
17
  var Box = /*#__PURE__*/React.forwardRef(function Box(inProps, ref) {
15
18
  var theme = useTheme(defaultTheme);
@@ -23,7 +26,7 @@ export default function createBox() {
23
26
  return /*#__PURE__*/_jsx(BoxRoot, _extends({
24
27
  as: component,
25
28
  ref: ref,
26
- className: clsx(className, 'MuiBox-root'),
29
+ className: clsx(className, generateClassName ? generateClassName(defaultClassName) : defaultClassName),
27
30
  theme: theme
28
31
  }, other));
29
32
  });
@@ -49,7 +52,7 @@ export default function createBox() {
49
52
  /**
50
53
  * @ignore
51
54
  */
52
- sx: PropTypes.object
55
+ sx: PropTypes.oneOfType([PropTypes.object, PropTypes.array])
53
56
  } : void 0;
54
57
  return Box;
55
58
  }
@@ -10,12 +10,12 @@ export default function createBreakpoints(breakpoints) {
10
10
  xs: 0,
11
11
  // phone
12
12
  sm: 600,
13
- // tablets
13
+ // tablet
14
14
  md: 900,
15
15
  // small laptop
16
16
  lg: 1200,
17
17
  // desktop
18
- xl: 1536 // large screens
18
+ xl: 1536 // large screen
19
19
 
20
20
  } : _breakpoints$values,
21
21
  _breakpoints$unit = breakpoints.unit,