diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index 6dae83fb6a85b5..78dff475afaed9 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -14,7 +14,8 @@ "packages/material-ui-utils", "packages/material-ui-unstyled", "packages/material-ui-styled-engine", - "packages/material-ui-styled-engine-sc" + "packages/material-ui-styled-engine-sc", + "packages/material-ui-styled-engine-goober" ], "publishDirectory": { "@material-ui/codemod": "packages/material-ui-codemod/build", @@ -24,6 +25,7 @@ "@material-ui/styles": "packages/material-ui-styles/build", "@material-ui/styled-engine": "packages/material-ui-styled-engine/build", "@material-ui/styled-engine-sc": "packages/material-ui-styled-engine-sc/build", + "@material-ui/styled-engine-goober": "packages/material-ui-styled-engine-goober/build", "@material-ui/system": "packages/material-ui-system/build", "@material-ui/private-theming": "packages/material-ui-private-theming/build", "@material-ui/types": "packages/material-ui-types/build", diff --git a/babel.config.js b/babel.config.js index 7c3ba507ed0bee..09f50bfceb0fb3 100644 --- a/babel.config.js +++ b/babel.config.js @@ -15,6 +15,9 @@ const defaultAlias = { '@material-ui/lab': resolveAliasPath('./packages/material-ui-lab/src'), '@material-ui/styled-engine': resolveAliasPath('./packages/material-ui-styled-engine/src'), '@material-ui/styled-engine-sc': resolveAliasPath('./packages/material-ui-styled-engine-sc/src'), + '@material-ui/styled-engine-goober': resolveAliasPath( + './packages/material-ui-styled-engine-goober/src', + ), '@material-ui/styles': resolveAliasPath('./packages/material-ui-styles/src'), '@material-ui/system': resolveAliasPath('./packages/material-ui-system/src'), '@material-ui/private-theming': resolveAliasPath('./packages/material-ui-private-theming/src'), diff --git a/docs/babel.config.js b/docs/babel.config.js index c9e98edcca5209..843ba61d0debde 100644 --- a/docs/babel.config.js +++ b/docs/babel.config.js @@ -19,10 +19,10 @@ const alias = { '@material-ui/icons': '../packages/material-ui-icons/lib', '@material-ui/lab': '../packages/material-ui-lab/src', '@material-ui/styles': '../packages/material-ui-styles/src', - '@material-ui/styled-engine-sc': '../packages/material-ui-styled-engine-sc/src', // Swap the comments on the next two lines for using the styled-components as style engine - '@material-ui/styled-engine': '../packages/material-ui-styled-engine/src', + // '@material-ui/styled-engine': '../packages/material-ui-styled-engine/src', // '@material-ui/styled-engine': '../packages/material-ui-styled-engine-sc/src', + '@material-ui/styled-engine': '../packages/material-ui-styled-engine-goober/src', '@material-ui/system': '../packages/material-ui-system/src', '@material-ui/private-theming': '../packages/material-ui-private-theming/src', '@material-ui/utils': '../packages/material-ui-utils/src', diff --git a/docs/package.json b/docs/package.json index 0a82208bcbee35..f6e26674f5a33b 100644 --- a/docs/package.json +++ b/docs/package.json @@ -37,6 +37,7 @@ "@material-ui/lab": "5.0.0-alpha.43", "@material-ui/styled-engine": "5.0.0-beta.4", "@material-ui/styled-engine-sc": "5.0.0-beta.1", + "@material-ui/styled-engine-goober": "5.0.0-beta.1", "@material-ui/styles": "5.0.0-beta.4", "@material-ui/system": "5.0.0-beta.4", "@material-ui/types": "6.0.2", @@ -77,6 +78,7 @@ "final-form": "^4.18.5", "flexsearch": "^0.7.0", "fs-extra": "^10.0.0", + "goober": "^2.0.0", "json2mq": "^0.2.0", "jss": "^10.0.3", "jss-plugin-template": "^10.0.3", diff --git a/packages/material-ui-styled-engine-goober/README.md b/packages/material-ui-styled-engine-goober/README.md new file mode 100644 index 00000000000000..384d65d45011e2 --- /dev/null +++ b/packages/material-ui-styled-engine-goober/README.md @@ -0,0 +1,23 @@ +# @material-ui/styled-engine-goober + +This package is a wrapper around the `goober` package. +It is created to be used as alias for the `@material-ui/styled-engine` package, for the developers who would like to use `goober` as a styled engine instead of `@emotion/styled`. + +## Installation + +The installation of the dependency in your package is slightly different from the usual. +You need to alias the default `emotion` implementation to the `goober` one. +Depending on the bundler you are using, you made add it like this: + +### webpack + +```js +module.exports = { + //... + resolve: { + alias: { + '@material-ui/styled-engine': '@material-ui/styled-engine-goober', + }, + }, +}; +``` diff --git a/packages/material-ui-styled-engine-goober/package.json b/packages/material-ui-styled-engine-goober/package.json new file mode 100644 index 00000000000000..30d281c12f1179 --- /dev/null +++ b/packages/material-ui-styled-engine-goober/package.json @@ -0,0 +1,54 @@ +{ + "name": "@material-ui/styled-engine-goober", + "version": "5.0.0-beta.1", + "private": false, + "author": "Material-UI Team", + "description": "styled() API wrapper package for goober.", + "main": "./src/index.js", + "keywords": [ + "react", + "react-component", + "material-ui", + "goober" + ], + "repository": { + "type": "git", + "url": "https://github.com/mui-org/material-ui.git", + "directory": "packages/material-ui-styled-engine-goober" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/mui-org/material-ui/issues" + }, + "homepage": "https://next.material-ui.com/guides/styled-engine/", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/material-ui" + }, + "scripts": { + "build": "yarn build:legacy && yarn build:modern && yarn build:node && yarn build:stable && yarn build:copy-files", + "build:legacy": "node ../../scripts/build legacy", + "build:modern": "node ../../scripts/build modern", + "build:node": "node ../../scripts/build node", + "build:stable": "node ../../scripts/build stable", + "build:copy-files": "node ../../scripts/copy-files.js", + "prebuild": "rimraf build", + "release": "yarn build && npm publish build --tag next", + "test": "cd ../../ && cross-env NODE_ENV=test mocha 'packages/material-ui-styled-engine-goober/**/*.test.{js,ts,tsx}'", + "typescript": "tslint -p tsconfig.json \"{src,test}/**/*.{spec,d}.{ts,tsx}\" && tsc -p tsconfig.json" + }, + "dependencies": { + "@emotion/unitless": "^0.7.5", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "goober": "^5.0.0" + }, + "sideEffects": false, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=12.0.0" + } +} diff --git a/packages/material-ui-styled-engine-goober/src/GlobalStyles/GlobalStyles.ts b/packages/material-ui-styled-engine-goober/src/GlobalStyles/GlobalStyles.ts new file mode 100644 index 00000000000000..fbdbb00276ab0c --- /dev/null +++ b/packages/material-ui-styled-engine-goober/src/GlobalStyles/GlobalStyles.ts @@ -0,0 +1,23 @@ +import PropTypes from 'prop-types'; +import { createGlobalStyles } from 'goober/global'; + +function isEmpty(obj) { + return obj === undefined || obj === null || Object.keys(obj).length === 0; +} + +const GlobalStyles = createGlobalStyles((props) => { + const { styles, defaultTheme = {} } = props; + + if (typeof styles === 'function') { + return styles(isEmpty(props.theme) ? defaultTheme : props.theme); + } + + return styles; +}); + +GlobalStyles.propTypes = { + defaultTheme: PropTypes.object, + styles: PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.func]), +}; + +export default GlobalStyles; diff --git a/packages/material-ui-styled-engine-goober/src/GlobalStyles/index.ts b/packages/material-ui-styled-engine-goober/src/GlobalStyles/index.ts new file mode 100644 index 00000000000000..a815f5c4a572b2 --- /dev/null +++ b/packages/material-ui-styled-engine-goober/src/GlobalStyles/index.ts @@ -0,0 +1,2 @@ +export { default } from './GlobalStyles'; +export * from './GlobalStyles'; diff --git a/packages/material-ui-styled-engine-goober/src/StyledEngineProvider/StyledEngineProvider.tsx b/packages/material-ui-styled-engine-goober/src/StyledEngineProvider/StyledEngineProvider.tsx new file mode 100644 index 00000000000000..087e736373a426 --- /dev/null +++ b/packages/material-ui-styled-engine-goober/src/StyledEngineProvider/StyledEngineProvider.tsx @@ -0,0 +1,3 @@ +export default function StyledEngineProvider(props) { + return props.children; +} diff --git a/packages/material-ui-styled-engine-goober/src/StyledEngineProvider/index.ts b/packages/material-ui-styled-engine-goober/src/StyledEngineProvider/index.ts new file mode 100644 index 00000000000000..7fdd272b28e6ca --- /dev/null +++ b/packages/material-ui-styled-engine-goober/src/StyledEngineProvider/index.ts @@ -0,0 +1,2 @@ +export { default } from './StyledEngineProvider'; +export * from './StyledEngineProvider'; diff --git a/packages/material-ui-styled-engine-goober/src/index.tsx b/packages/material-ui-styled-engine-goober/src/index.tsx new file mode 100644 index 00000000000000..7be9304407e14a --- /dev/null +++ b/packages/material-ui-styled-engine-goober/src/index.tsx @@ -0,0 +1,83 @@ +import * as React from 'react'; +import { styled as styledGoober, setup } from 'goober'; +import unitless from '@emotion/unitless'; +import { prefix } from 'goober/prefixer'; + +export const ThemeContext = React.createContext({}); + +// TODO drop hard coded function +function shouldForwardProp(prop: string) { + return prop !== 'styleProps' && prop !== 'theme' && prop !== 'sx' && prop !== 'as'; +} + +const useTheme = () => React.useContext(ThemeContext); + +function forwardProps(props: object) { + Object.keys(props).forEach((prop) => { + // Or any other conditions. + // This could also check if this is a dev build and not remove the props + if (!shouldForwardProp(prop)) { + // @ts-ignore + delete props[prop]; + } + }); +} + +function isCustomProperty(property: string) { + // 45 is - + return property.charCodeAt(1) === 45; +} + +function camelize(str: string) { + return str.replace(/-./g, (chunk) => chunk[1].toUpperCase()); +} + +function plugins(property: string, inValue: any) { + // Add default px unit when needed + let value = inValue; + const key = camelize(property); + if ( + unitless[key] !== 1 && + !isCustomProperty(property) && + typeof value === 'number' && + value !== 0 + ) { + value = `${value}px`; + } + + return prefix(property, value); +} + +// @ts-expect-error Wrong type https://github.com/cristianbote/goober/pull/364 +setup(React.createElement, plugins, useTheme, forwardProps); + +// TODO hanle options +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export default function styled(tag: any, options: any) { + const stylesFactory = styledGoober(tag, React.forwardRef); + + return (...styles: any[]) => { + if (process.env.NODE_ENV !== 'production') { + const component = typeof tag === 'string' ? `"${tag}"` : 'component'; + if (styles.length === 0) { + console.error( + [ + `Material-UI: Seems like you called \`styled(${component})()\` without a \`style\` argument.`, + 'You must provide a `styles` argument: `styled("div")(styleYouForgotToPass)`.', + ].join('\n'), + ); + } else if (styles.some((style) => style === undefined)) { + console.error( + `Material-UI: the styled(${component})(...args) API requires all its args to be defined.`, + ); + } + } + + // @ts-ignore + return stylesFactory(styles); + }; +} + +export { keyframes, css } from 'goober'; +export { default as StyledEngineProvider } from './StyledEngineProvider'; +export { default as GlobalStyles } from './GlobalStyles'; diff --git a/packages/material-ui-styled-engine-goober/tsconfig.json b/packages/material-ui-styled-engine-goober/tsconfig.json new file mode 100644 index 00000000000000..57220954971e1e --- /dev/null +++ b/packages/material-ui-styled-engine-goober/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig", + "include": ["src/**/*", "test/**/*"] +} diff --git a/packages/material-ui-system/src/createStyled.js b/packages/material-ui-system/src/createStyled.js index 892d08d8d77c7a..75d9106c588d9d 100644 --- a/packages/material-ui-system/src/createStyled.js +++ b/packages/material-ui-system/src/createStyled.js @@ -5,7 +5,7 @@ import styleFunctionSx from './styleFunctionSx'; import propsToClassKey from './propsToClassKey'; function isEmpty(obj) { - return Object.keys(obj).length === 0; + return obj == null || Object.keys(obj).length === 0; } const getStyleOverrides = (name, theme) => { diff --git a/test/karma.tests.js b/test/karma.tests.js index 0d9e3477dd9e56..bc9638dd454160 100644 --- a/test/karma.tests.js +++ b/test/karma.tests.js @@ -36,6 +36,13 @@ const styledEngineSCContext = require.context( ); styledEngineSCContext.keys().forEach(styledEngineSCContext); +const styledEngineGooberContext = require.context( + '../packages/material-ui-styled-engine-goober/src/', + true, + /\.test\.(js|ts|tsx)$/, +); +styledEngineGooberContext.keys().forEach(styledEngineGooberContext); + const systemContext = require.context( '../packages/material-ui-system/src/', true, diff --git a/tsconfig.json b/tsconfig.json index ded72cb01a3a2d..0563e0f195dc41 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,6 +23,8 @@ "@material-ui/styled-engine/*": ["./packages/material-ui-styled-engine/src/*"], "@material-ui/styled-engine-sc": ["./packages/material-ui-styled-engine-sc/src"], "@material-ui/styled-engine-sc/*": ["./packages/material-ui-styled-engine-sc/src/*"], + "@material-ui/styled-engine-goober": ["./packages/material-ui-styled-engine-goober/src"], + "@material-ui/styled-engine-goober/*": ["./packages/material-ui-styled-engine-goober/src/*"], "@material-ui/styles": ["./packages/material-ui-styles/src"], "@material-ui/styles/*": ["./packages/material-ui-styles/src/*"], "@material-ui/system": ["./packages/material-ui-system/src"], diff --git a/webpackBaseConfig.js b/webpackBaseConfig.js index d41bffde4cd26a..d7f0151cb04bb3 100644 --- a/webpackBaseConfig.js +++ b/webpackBaseConfig.js @@ -21,6 +21,10 @@ module.exports = { __dirname, './packages/material-ui-styled-engine-sc/src', ), + '@material-ui/styled-engine-goober': path.resolve( + __dirname, + './packages/material-ui-styled-engine-goober/src', + ), '@material-ui/styles': path.resolve(__dirname, './packages/material-ui-styles/src'), '@material-ui/system': path.resolve(__dirname, './packages/material-ui-system/src'), '@material-ui/private-theming': path.resolve( diff --git a/yarn.lock b/yarn.lock index d8fd2b5940d645..93d1ced622c350 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8674,6 +8674,11 @@ gonzales-pe@^4.3.0: dependencies: minimist "^1.2.5" +goober@^2.0.0: + version "2.0.40" + resolved "https://registry.yarnpkg.com/goober/-/goober-2.0.40.tgz#7f3d4cafdf6854da55460a639e9b3a15848aaaf3" + integrity sha512-LnYclgtz+t2Q9fDEIEdMKCoh5+JyBwG6ORJme52Av18ZrajhqEtzACXWhL/+IIB0SegvwXLcGG7FH+BJ/Z3Ljw== + good-listener@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"