UNPKG

@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.

285 lines (272 loc) 10.9 kB
"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 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) { /* * 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)); } if (Array.isArray(resolvedStyle?.variants)) { let rootStyle; if (resolvedStyle.isProcessed) { rootStyle = resolvedStyle.style; } else { const { variants, ...otherStyles } = resolvedStyle; rootStyle = otherStyles; } return processStyleVariants(props, resolvedStyle.variants, [rootStyle]); } if (resolvedStyle?.isProcessed) { return resolvedStyle.style; } return resolvedStyle; } function processStyleVariants(props, variants, results = []) { 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(variant.style(mergedState)); } else { results.push(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; // 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. if (typeof style === 'function' && style.__emotion_real !== style) { return function styleFunctionProcessor(props) { return processStyle(props, style); }; } if ((0, _deepmerge.isPlainObject)(style)) { const serialized = (0, _preprocessStyles.default)(style); if (!serialized.variants) { return serialized.style; } return function styleObjectProcessor(props) { return processStyle(props, serialized); }; } 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]); } 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); }); } 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); }