298 lines
No EOL
12 KiB
JavaScript
298 lines
No EOL
12 KiB
JavaScript
"use strict";
|
|
|
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.default = createStyled;
|
|
exports.shouldForwardProp = shouldForwardProp;
|
|
exports.systemDefaultTheme = void 0;
|
|
var _styledEngine = _interopRequireWildcard(require("@mui/styled-engine"));
|
|
var _deepmerge = require("@mui/utils/deepmerge");
|
|
var _capitalize = _interopRequireDefault(require("@mui/utils/capitalize"));
|
|
var _getDisplayName = _interopRequireDefault(require("@mui/utils/getDisplayName"));
|
|
var _createTheme = _interopRequireDefault(require("../createTheme"));
|
|
var _styleFunctionSx = _interopRequireDefault(require("../styleFunctionSx"));
|
|
var _preprocessStyles = _interopRequireDefault(require("../preprocessStyles"));
|
|
/* eslint-disable no-underscore-dangle */
|
|
/* eslint-disable no-labels */
|
|
/* eslint-disable no-lone-blocks */
|
|
|
|
const systemDefaultTheme = exports.systemDefaultTheme = (0, _createTheme.default)();
|
|
|
|
// Update /system/styled/#api in case if this changes
|
|
function shouldForwardProp(prop) {
|
|
return prop !== 'ownerState' && prop !== 'theme' && prop !== 'sx' && prop !== 'as';
|
|
}
|
|
function shallowLayer(serialized, layerName) {
|
|
if (layerName && serialized && typeof serialized === 'object' && serialized.styles && !serialized.styles.startsWith('@layer') // only add the layer if it is not already there.
|
|
) {
|
|
serialized.styles = `@layer ${layerName}{${String(serialized.styles)}}`;
|
|
}
|
|
return serialized;
|
|
}
|
|
function defaultOverridesResolver(slot) {
|
|
if (!slot) {
|
|
return null;
|
|
}
|
|
return (_props, styles) => styles[slot];
|
|
}
|
|
function attachTheme(props, themeId, defaultTheme) {
|
|
props.theme = isObjectEmpty(props.theme) ? defaultTheme : props.theme[themeId] || props.theme;
|
|
}
|
|
function processStyle(props, style, layerName) {
|
|
/*
|
|
* Style types:
|
|
* - null/undefined
|
|
* - string
|
|
* - CSS style object: { [cssKey]: [cssValue], variants }
|
|
* - Processed style object: { style, variants, isProcessed: true }
|
|
* - Array of any of the above
|
|
*/
|
|
|
|
const resolvedStyle = typeof style === 'function' ? style(props) : style;
|
|
if (Array.isArray(resolvedStyle)) {
|
|
return resolvedStyle.flatMap(subStyle => processStyle(props, subStyle, layerName));
|
|
}
|
|
if (Array.isArray(resolvedStyle?.variants)) {
|
|
let rootStyle;
|
|
if (resolvedStyle.isProcessed) {
|
|
rootStyle = layerName ? shallowLayer(resolvedStyle.style, layerName) : resolvedStyle.style;
|
|
} else {
|
|
const {
|
|
variants,
|
|
...otherStyles
|
|
} = resolvedStyle;
|
|
rootStyle = layerName ? shallowLayer((0, _styledEngine.internal_serializeStyles)(otherStyles), layerName) : otherStyles;
|
|
}
|
|
return processStyleVariants(props, resolvedStyle.variants, [rootStyle], layerName);
|
|
}
|
|
if (resolvedStyle?.isProcessed) {
|
|
return layerName ? shallowLayer((0, _styledEngine.internal_serializeStyles)(resolvedStyle.style), layerName) : resolvedStyle.style;
|
|
}
|
|
return layerName ? shallowLayer((0, _styledEngine.internal_serializeStyles)(resolvedStyle), layerName) : resolvedStyle;
|
|
}
|
|
function processStyleVariants(props, variants, results = [], layerName = undefined) {
|
|
let mergedState; // We might not need it, initialized lazily
|
|
|
|
variantLoop: for (let i = 0; i < variants.length; i += 1) {
|
|
const variant = variants[i];
|
|
if (typeof variant.props === 'function') {
|
|
mergedState ?? (mergedState = {
|
|
...props,
|
|
...props.ownerState,
|
|
ownerState: props.ownerState
|
|
});
|
|
if (!variant.props(mergedState)) {
|
|
continue;
|
|
}
|
|
} else {
|
|
for (const key in variant.props) {
|
|
if (props[key] !== variant.props[key] && props.ownerState?.[key] !== variant.props[key]) {
|
|
continue variantLoop;
|
|
}
|
|
}
|
|
}
|
|
if (typeof variant.style === 'function') {
|
|
mergedState ?? (mergedState = {
|
|
...props,
|
|
...props.ownerState,
|
|
ownerState: props.ownerState
|
|
});
|
|
results.push(layerName ? shallowLayer((0, _styledEngine.internal_serializeStyles)(variant.style(mergedState)), layerName) : variant.style(mergedState));
|
|
} else {
|
|
results.push(layerName ? shallowLayer((0, _styledEngine.internal_serializeStyles)(variant.style), layerName) : variant.style);
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
function createStyled(input = {}) {
|
|
const {
|
|
themeId,
|
|
defaultTheme = systemDefaultTheme,
|
|
rootShouldForwardProp = shouldForwardProp,
|
|
slotShouldForwardProp = shouldForwardProp
|
|
} = input;
|
|
function styleAttachTheme(props) {
|
|
attachTheme(props, themeId, defaultTheme);
|
|
}
|
|
const styled = (tag, inputOptions = {}) => {
|
|
// If `tag` is already a styled component, filter out the `sx` style function
|
|
// to prevent unnecessary styles generated by the composite components.
|
|
(0, _styledEngine.internal_mutateStyles)(tag, styles => styles.filter(style => style !== _styleFunctionSx.default));
|
|
const {
|
|
name: componentName,
|
|
slot: componentSlot,
|
|
skipVariantsResolver: inputSkipVariantsResolver,
|
|
skipSx: inputSkipSx,
|
|
// TODO v6: remove `lowercaseFirstLetter()` in the next major release
|
|
// For more details: https://github.com/mui/material-ui/pull/37908
|
|
overridesResolver = defaultOverridesResolver(lowercaseFirstLetter(componentSlot)),
|
|
...options
|
|
} = inputOptions;
|
|
const layerName = componentName && componentName.startsWith('Mui') || !!componentSlot ? 'components' : 'custom';
|
|
|
|
// if skipVariantsResolver option is defined, take the value, otherwise, true for root and false for other slots.
|
|
const skipVariantsResolver = inputSkipVariantsResolver !== undefined ? inputSkipVariantsResolver :
|
|
// TODO v6: remove `Root` in the next major release
|
|
// For more details: https://github.com/mui/material-ui/pull/37908
|
|
componentSlot && componentSlot !== 'Root' && componentSlot !== 'root' || false;
|
|
const skipSx = inputSkipSx || false;
|
|
let shouldForwardPropOption = shouldForwardProp;
|
|
|
|
// TODO v6: remove `Root` in the next major release
|
|
// For more details: https://github.com/mui/material-ui/pull/37908
|
|
if (componentSlot === 'Root' || componentSlot === 'root') {
|
|
shouldForwardPropOption = rootShouldForwardProp;
|
|
} else if (componentSlot) {
|
|
// any other slot specified
|
|
shouldForwardPropOption = slotShouldForwardProp;
|
|
} else if (isStringTag(tag)) {
|
|
// for string (html) tag, preserve the behavior in emotion & styled-components.
|
|
shouldForwardPropOption = undefined;
|
|
}
|
|
const defaultStyledResolver = (0, _styledEngine.default)(tag, {
|
|
shouldForwardProp: shouldForwardPropOption,
|
|
label: generateStyledLabel(componentName, componentSlot),
|
|
...options
|
|
});
|
|
const transformStyle = style => {
|
|
// - On the server Emotion doesn't use React.forwardRef for creating components, so the created
|
|
// component stays as a function. This condition makes sure that we do not interpolate functions
|
|
// which are basically components used as a selectors.
|
|
// - `style` could be a styled component from a babel plugin for component selectors, This condition
|
|
// makes sure that we do not interpolate them.
|
|
if (style.__emotion_real === style) {
|
|
return style;
|
|
}
|
|
if (typeof style === 'function') {
|
|
return function styleFunctionProcessor(props) {
|
|
return processStyle(props, style, props.theme.modularCssLayers ? layerName : undefined);
|
|
};
|
|
}
|
|
if ((0, _deepmerge.isPlainObject)(style)) {
|
|
const serialized = (0, _preprocessStyles.default)(style);
|
|
return function styleObjectProcessor(props) {
|
|
if (!serialized.variants) {
|
|
return props.theme.modularCssLayers ? shallowLayer(serialized.style, layerName) : serialized.style;
|
|
}
|
|
return processStyle(props, serialized, props.theme.modularCssLayers ? layerName : undefined);
|
|
};
|
|
}
|
|
return style;
|
|
};
|
|
const muiStyledResolver = (...expressionsInput) => {
|
|
const expressionsHead = [];
|
|
const expressionsBody = expressionsInput.map(transformStyle);
|
|
const expressionsTail = [];
|
|
|
|
// Preprocess `props` to set the scoped theme value.
|
|
// This must run before any other expression.
|
|
expressionsHead.push(styleAttachTheme);
|
|
if (componentName && overridesResolver) {
|
|
expressionsTail.push(function styleThemeOverrides(props) {
|
|
const theme = props.theme;
|
|
const styleOverrides = theme.components?.[componentName]?.styleOverrides;
|
|
if (!styleOverrides) {
|
|
return null;
|
|
}
|
|
const resolvedStyleOverrides = {};
|
|
|
|
// TODO: v7 remove iteration and use `resolveStyleArg(styleOverrides[slot])` directly
|
|
// eslint-disable-next-line guard-for-in
|
|
for (const slotKey in styleOverrides) {
|
|
resolvedStyleOverrides[slotKey] = processStyle(props, styleOverrides[slotKey], props.theme.modularCssLayers ? 'theme' : undefined);
|
|
}
|
|
return overridesResolver(props, resolvedStyleOverrides);
|
|
});
|
|
}
|
|
if (componentName && !skipVariantsResolver) {
|
|
expressionsTail.push(function styleThemeVariants(props) {
|
|
const theme = props.theme;
|
|
const themeVariants = theme?.components?.[componentName]?.variants;
|
|
if (!themeVariants) {
|
|
return null;
|
|
}
|
|
return processStyleVariants(props, themeVariants, [], props.theme.modularCssLayers ? 'theme' : undefined);
|
|
});
|
|
}
|
|
if (!skipSx) {
|
|
expressionsTail.push(_styleFunctionSx.default);
|
|
}
|
|
|
|
// This function can be called as a tagged template, so the first argument would contain
|
|
// CSS `string[]` values.
|
|
if (Array.isArray(expressionsBody[0])) {
|
|
const inputStrings = expressionsBody.shift();
|
|
|
|
// We need to add placeholders in the tagged template for the custom functions we have
|
|
// possibly added (attachTheme, overrides, variants, and sx).
|
|
const placeholdersHead = new Array(expressionsHead.length).fill('');
|
|
const placeholdersTail = new Array(expressionsTail.length).fill('');
|
|
let outputStrings;
|
|
// prettier-ignore
|
|
{
|
|
outputStrings = [...placeholdersHead, ...inputStrings, ...placeholdersTail];
|
|
outputStrings.raw = [...placeholdersHead, ...inputStrings.raw, ...placeholdersTail];
|
|
}
|
|
|
|
// The only case where we put something before `attachTheme`
|
|
expressionsHead.unshift(outputStrings);
|
|
}
|
|
const expressions = [...expressionsHead, ...expressionsBody, ...expressionsTail];
|
|
const Component = defaultStyledResolver(...expressions);
|
|
if (tag.muiName) {
|
|
Component.muiName = tag.muiName;
|
|
}
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
Component.displayName = generateDisplayName(componentName, componentSlot, tag);
|
|
}
|
|
return Component;
|
|
};
|
|
if (defaultStyledResolver.withConfig) {
|
|
muiStyledResolver.withConfig = defaultStyledResolver.withConfig;
|
|
}
|
|
return muiStyledResolver;
|
|
};
|
|
return styled;
|
|
}
|
|
function generateDisplayName(componentName, componentSlot, tag) {
|
|
if (componentName) {
|
|
return `${componentName}${(0, _capitalize.default)(componentSlot || '')}`;
|
|
}
|
|
return `Styled(${(0, _getDisplayName.default)(tag)})`;
|
|
}
|
|
function generateStyledLabel(componentName, componentSlot) {
|
|
let label;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
if (componentName) {
|
|
// TODO v6: remove `lowercaseFirstLetter()` in the next major release
|
|
// For more details: https://github.com/mui/material-ui/pull/37908
|
|
label = `${componentName}-${lowercaseFirstLetter(componentSlot || 'Root')}`;
|
|
}
|
|
}
|
|
return label;
|
|
}
|
|
function isObjectEmpty(object) {
|
|
// eslint-disable-next-line
|
|
for (const _ in object) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// https://github.com/emotion-js/emotion/blob/26ded6109fcd8ca9875cc2ce4564fee678a3f3c5/packages/styled/src/utils.js#L40
|
|
function isStringTag(tag) {
|
|
return typeof tag === 'string' &&
|
|
// 96 is one less than the char code
|
|
// for "a" so this is checking that
|
|
// it's a lowercase character
|
|
tag.charCodeAt(0) > 96;
|
|
}
|
|
function lowercaseFirstLetter(string) {
|
|
if (!string) {
|
|
return string;
|
|
}
|
|
return string.charAt(0).toLowerCase() + string.slice(1);
|
|
} |