@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.
209 lines (205 loc) • 6.67 kB
JavaScript
import PropTypes from 'prop-types';
import isObjectEmpty from '@mui/utils/isObjectEmpty';
import fastDeepAssign from '@mui/utils/fastDeepAssign';
import deepmerge from '@mui/utils/deepmerge';
import merge from "../merge/index.mjs";
import { isCqShorthand, getContainerQuery } from "../cssContainerQueries/index.mjs";
import createBreakpoints from "../createBreakpoints/createBreakpoints.mjs";
const EMPTY_THEME = {};
// The breakpoint **start** at this value.
// For instance with the first breakpoint xs: [xs, sm[.
/** @internal */
export const values = {
xs: 0,
// phone
sm: 600,
// tablet
md: 900,
// small laptop
lg: 1200,
// desktop
xl: 1536 // large screen
};
export const DEFAULT_BREAKPOINTS = createBreakpoints({
values: values
});
const defaultContainerQueries = {
containerQueries: containerName => ({
up: key => {
let result = typeof key === 'number' ? key : values[key] || key;
if (typeof result === 'number') {
result = `${result}px`;
}
return containerName ? `@container ${containerName} (min-width:${result})` : `@container (min-width:${result})`;
}
})
};
export function handleBreakpoints(props, propValue, styleFromPropValue) {
const result = {};
return iterateBreakpoints(result, props.theme, propValue, (mediaKey, value, initialKey) => {
const finalValue = styleFromPropValue(value, initialKey);
if (mediaKey) {
result[mediaKey] = finalValue;
} else {
fastDeepAssign(result, finalValue);
}
});
}
export function iterateBreakpoints(target, theme, propValue, callback) {
theme ??= EMPTY_THEME;
if (Array.isArray(propValue)) {
const breakpoints = theme.breakpoints ?? DEFAULT_BREAKPOINTS;
for (let i = 0; i < propValue.length; i += 1) {
buildBreakpoint(target, breakpoints.up(breakpoints.keys[i]), propValue[i], undefined, callback);
}
return target;
}
if (typeof propValue === 'object') {
const breakpoints = theme.breakpoints ?? DEFAULT_BREAKPOINTS;
const breakpointValues = breakpoints.values ?? values;
for (const key in propValue) {
if (isCqShorthand(breakpoints.keys, key)) {
const containerKey = getContainerQuery(theme.containerQueries ? theme : defaultContainerQueries, key);
if (containerKey) {
buildBreakpoint(target, containerKey, propValue[key], key, callback);
}
} else if (key in breakpointValues) {
const mediaKey = breakpoints.up(key);
buildBreakpoint(target, mediaKey, propValue[key], key, callback);
} else {
const cssKey = key;
target[cssKey] = propValue[cssKey];
}
}
return target;
}
callback(undefined, propValue);
return target;
}
function buildBreakpoint(target, mediaKey, value, initialKey, callback) {
target[mediaKey] ??= {};
callback(mediaKey, value, initialKey);
}
function setupBreakpoints(styleFunction) {
const newStyleFunction = props => {
const theme = props.theme || {};
const base = styleFunction(props);
const themeBreakpoints = theme.breakpoints || DEFAULT_BREAKPOINTS;
const extended = themeBreakpoints.keys.reduce((acc, key) => {
if (props[key]) {
acc = acc || {};
acc[themeBreakpoints.up(key)] = styleFunction({
theme,
...props[key]
});
}
return acc;
}, null);
return merge(base, extended);
};
newStyleFunction.propTypes = process.env.NODE_ENV !== 'production' ? {
...styleFunction.propTypes,
xs: PropTypes.object,
sm: PropTypes.object,
md: PropTypes.object,
lg: PropTypes.object,
xl: PropTypes.object
} : {};
newStyleFunction.filterProps = ['xs', 'sm', 'md', 'lg', 'xl', ...styleFunction.filterProps];
return newStyleFunction;
}
/** @internal */
export function createEmptyBreakpointObject(breakpoints = DEFAULT_BREAKPOINTS) {
const {
internal_mediaKeys: mediaKeys
} = breakpoints;
const result = {};
for (let i = 0; i < mediaKeys.length; i += 1) {
result[mediaKeys[i]] = {};
}
return result;
}
/** @internal */
export function removeUnusedBreakpoints(breakpoints, style) {
const breakpointKeys = breakpoints.internal_mediaKeys;
for (let i = 0; i < breakpointKeys.length; i += 1) {
const key = breakpointKeys[i];
if (isObjectEmpty(style[key])) {
delete style[key];
}
}
return style;
}
export function mergeBreakpointsInOrder(breakpoints, ...styles) {
const emptyBreakpoints = createEmptyBreakpointObject(breakpoints);
const mergedOutput = [emptyBreakpoints, ...styles].reduce((prev, next) => deepmerge(prev, next), {});
return removeUnusedBreakpoints(breakpoints, mergedOutput);
}
/** @internal */
export function computeBreakpointsBase(breakpointValues, themeBreakpoints) {
if (typeof breakpointValues !== 'object') {
return {};
}
const base = {};
const breakpointsKeys = Object.keys(themeBreakpoints);
if (Array.isArray(breakpointValues)) {
breakpointsKeys.forEach((breakpoint, i) => {
if (i < breakpointValues.length) {
base[breakpoint] = true;
}
});
} else {
breakpointsKeys.forEach(breakpoint => {
if (breakpointValues[breakpoint] != null) {
base[breakpoint] = true;
}
});
}
return base;
}
export function resolveBreakpointValues(options) {
const {
values: breakpointValues,
breakpoints: themeBreakpoints,
base: customBase
} = options;
const base = customBase || computeBreakpointsBase(breakpointValues, themeBreakpoints);
const keys = Object.keys(base);
if (keys.length === 0) {
return breakpointValues;
}
let previous;
return keys.reduce((acc, breakpoint, i) => {
if (Array.isArray(breakpointValues)) {
acc[breakpoint] = breakpointValues[i] != null ? breakpointValues[i] : breakpointValues[previous];
previous = i;
} else if (typeof breakpointValues === 'object' && breakpointValues) {
const bv = breakpointValues;
acc[breakpoint] = bv[breakpoint] != null ? bv[breakpoint] : bv[previous];
previous = breakpoint;
} else {
acc[breakpoint] = breakpointValues;
}
return acc;
}, {});
}
export function hasBreakpoint(breakpoints, value) {
if (Array.isArray(value)) {
return true;
}
if (typeof value === 'object' && value !== null) {
for (let i = 0; i < breakpoints.keys.length; i += 1) {
if (breakpoints.keys[i] in value) {
return true;
}
}
const valueKeys = Object.keys(value);
for (let i = 0; i < valueKeys.length; i += 1) {
if (isCqShorthand(breakpoints.keys, valueKeys[i])) {
return true;
}
}
}
return false;
}
export default setupBreakpoints;