209 lines
6.5 KiB
JavaScript
209 lines
6.5 KiB
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; |