Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/mui/pigment-css into pigm…
Browse files Browse the repository at this point in the history
…ent/extend-sx-prop
  • Loading branch information
siriwatknp committed Jun 1, 2024
2 parents b62581f + f014111 commit da9fb0f
Show file tree
Hide file tree
Showing 17 changed files with 741 additions and 332 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,25 @@ function App() {
}
```

### Creating global styles

Use the `globalCss` API to create global styles:

```js
import { globalCss } from '@pigment-css/react';

globalCss`
body {
margin: 0;
padding: 0;
}
`;
```

The `globalCss` function should to be called at the top level of your javascript file, usually from the entry point of the application.

Calling inside a function or a component will not work as expected. Also, the extraction of global styles will always take place regardless of conditional rendering.

### Theming

Theming is an **optional** feature that lets you reuse the same values, such as colors, spacing, and typography, across your application. It is a plain object of any structure that you can define in your config file.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"@mui/internal-docs-utils": "^1.0.4",
"@mui/internal-markdown": "^1.0.1",
"@mui/internal-scripts": "^1.0.3",
"@mui/internal-test-utils": "https://pkg.csb.dev/mui/material-ui/commit/fb183624/@mui/internal-test-utils",
"@mui/internal-test-utils": "1.0.0-dev.20240529-082515-213b5e33ab",
"@mui/monorepo": "github:mui/material-ui#73c88b6c3f71287a4a1f0b1b5d7d37ab268dca49",
"@next/eslint-plugin-next": "^14.1.4",
"@octokit/rest": "^20.1.0",
Expand Down
5 changes: 5 additions & 0 deletions packages/pigment-css-react/exports/globalCss.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Object.defineProperty(exports, '__esModule', {
value: true,
});

exports.default = require('../processors/globalCss').GlobalCssProcessor;
22 changes: 22 additions & 0 deletions packages/pigment-css-react/src/globalCss.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { CSSObjectNoCallback } from './base';
import type { ThemeArgs } from './theme';

type Primitve = string | null | undefined | boolean | number;

type CssArg = ((themeArgs: ThemeArgs) => CSSObjectNoCallback) | CSSObjectNoCallback;
type CssFn = (themeArgs: ThemeArgs) => string | number;

interface GlobalCss {
/**
* @returns {string} The generated css class name to be referenced.
*/
(arg: TemplateStringsArray, ...templateArgs: (Primitve | CssFn)[]): string;
/**
* @returns {string} The generated css class name to be referenced.
*/
(...arg: CssArg[]): string;
}

declare const globalCss: GlobalCss;

export default globalCss;
5 changes: 5 additions & 0 deletions packages/pigment-css-react/src/globalCss.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default function globalCss() {
throw new Error(
`${process.env.PACKAGE_NAME}: You were trying to call "globalCss" function without configuring your bundler. Make sure to install the bundler specific plugin and use it. @pigment-css/vite-plugin for Vite integration or @pigment-css/nextjs-plugin for Next.js integration.`,
);
}
167 changes: 167 additions & 0 deletions packages/pigment-css-react/src/processors/globalCss.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import type { Expression } from '@babel/types';
import type {
CallParam,
TemplateParam,
Params,
TailProcessorParams,
ValueCache,
} from '@wyw-in-js/processor-utils';
import { serializeStyles, Interpolation } from '@emotion/serialize';
import { type Replacements, type Rules, ValueType } from '@wyw-in-js/shared';
import type { CSSInterpolation } from '@emotion/css';
import { validateParams } from '@wyw-in-js/processor-utils';
import BaseProcessor from './base-processor';
import type { IOptions } from './styled';
import { cache } from '../utils/emotion';
import { getGlobalSelector } from '../utils/preprocessor';

export type Primitive = string | number | boolean | null | undefined;

export type TemplateCallback = (params: Record<string, unknown> | undefined) => string | number;

export class GlobalCssProcessor extends BaseProcessor {
callParam: CallParam | TemplateParam;

constructor(params: Params, ...args: TailProcessorParams) {
super([params[0]], ...args);
if (params.length < 2) {
throw BaseProcessor.SKIP;
}
validateParams(
params,
['callee', ['call', 'template']],
`Invalid use of ${this.tagSource.imported} tag.`,
);

const [, callParams] = params;
if (callParams[0] === 'call') {
this.dependencies.push(callParams[1]);
} else if (callParams[0] === 'template') {
callParams[1].forEach((element) => {
if ('kind' in element && element.kind !== ValueType.CONST) {
this.dependencies.push(element);
}
});
}
this.callParam = callParams;
}

build(values: ValueCache) {
if (this.artifacts.length > 0) {
throw new Error(`MUI: "${this.tagSource.imported}" is already built`);
}

const [callType] = this.callParam;

if (callType === 'template') {
this.handleTemplate(this.callParam, values);
} else {
this.handleCall(this.callParam, values);
}
}

private handleTemplate([, callArgs]: TemplateParam, values: ValueCache) {
const templateStrs: string[] = [];
// @ts-ignore @TODO - Fix this. No idea how to initialize a Tagged String array.
templateStrs.raw = [];
const templateExpressions: Primitive[] = [];
const { themeArgs } = this.options as IOptions;

callArgs.forEach((item) => {
if ('kind' in item) {
switch (item.kind) {
case ValueType.FUNCTION: {
const value = values.get(item.ex.name) as TemplateCallback;
templateExpressions.push(value(themeArgs));
break;
}
case ValueType.CONST:
templateExpressions.push(item.value);
break;
case ValueType.LAZY: {
const evaluatedValue = values.get(item.ex.name);
if (typeof evaluatedValue === 'function') {
templateExpressions.push(evaluatedValue(themeArgs));
} else {
templateExpressions.push(evaluatedValue as Primitive);
}
break;
}
default:
break;
}
} else if (item.type === 'TemplateElement') {
templateStrs.push(item.value.cooked as string);
// @ts-ignore
templateStrs.raw.push(item.value.raw);
}
});
this.generateArtifacts(templateStrs, ...templateExpressions);
}

generateArtifacts(styleObjOrTaggged: CSSInterpolation | string[], ...args: Primitive[]) {
const { styles: cssText } = serializeStyles(
args.length > 0
? [styleObjOrTaggged as Interpolation<{}>, ...args]
: [styleObjOrTaggged as Interpolation<{}>],
cache.registered,
);

const rules: Rules = {
[this.asSelector]: {
className: this.className,
cssText,
displayName: this.displayName,
start: this.location?.start ?? null,
},
};
const sourceMapReplacements: Replacements = [
{
length: cssText.length,
original: {
start: {
column: this.location?.start.column ?? 0,
line: this.location?.start.line ?? 0,
},
end: {
column: this.location?.end.column ?? 0,
line: this.location?.end.line ?? 0,
},
},
},
];
this.artifacts.push(['css', [rules, sourceMapReplacements]]);
}

private handleCall([, callArg]: CallParam, values: ValueCache) {
let styleObj: CSSInterpolation;
if (callArg.kind === ValueType.LAZY) {
styleObj = values.get(callArg.ex.name) as CSSInterpolation;
} else if (callArg.kind === ValueType.FUNCTION) {
const { themeArgs } = this.options as IOptions;
const value = values.get(callArg.ex.name) as (
args: Record<string, unknown> | undefined,
) => CSSInterpolation;
styleObj = value(themeArgs);
}
if (styleObj) {
this.generateArtifacts(styleObj);
}
}

doEvaltimeReplacement() {
this.replacer(this.value, false);
}

doRuntimeReplacement() {
this.doEvaltimeReplacement();
}

get asSelector() {
return getGlobalSelector(this.className);
}

get value(): Expression {
return this.astService.nullLiteral();
}
}
12 changes: 9 additions & 3 deletions packages/pigment-css-react/src/utils/preprocessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ const stylis = (css: string, serializerParam = serializer) =>

const defaultGetDirSelector = (dir: 'ltr' | 'rtl') => `[dir=${dir}]`;

export function getGlobalSelector(asSelector: string) {
return `$$GLOBAL-${asSelector}`;
}

export function preprocessor(
selector: string,
cssText: string,
Expand All @@ -45,14 +49,16 @@ export function preprocessor(
getDirSelector = defaultGetDirSelector,
} = options || {};
let css = '';
if (cssText.startsWith('@keyframes')) {
const isGlobal = selector.startsWith(getGlobalSelector(''));

if (!isGlobal && cssText.startsWith('@keyframes')) {
css += stylis(cssText.replace('@keyframes', `@keyframes ${selector}`));
return css;
}
css += stylis(`${selector}{${cssText}}`);
css += stylis(!isGlobal ? `${selector}{${cssText}}` : cssText);
if (generateForBothDir) {
css += stylis(
`${getDirSelector(defaultDirection === 'ltr' ? 'rtl' : 'ltr')} ${selector}{${cssText}}`,
`${getDirSelector(defaultDirection === 'ltr' ? 'rtl' : 'ltr')} ${!isGlobal ? `${selector}{${cssText}}` : cssText}`,
serializerRtl,
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { globalCss } from '@pigment-css/react';

globalCss`
* {
box-sizing: border-box;
}
@font-face {
font-family: 'Patrick Hand SC';
font-style: normal;
font-weight: 400;
color: ${({ theme }) => theme.palette.primary.main};
src: local('Patrick Hand SC'),
local('PatrickHandSC-Regular'),
url(https://fonts.gstatic.com/s/patrickhandsc/v4/OYFWCgfCR-7uHIovjUZXsZ71Uis0Qeb9Gqo8IZV7ckE.woff2)
format('woff2');
unicode-range: U+0100-024f, U+1-1eff,
U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f,
U+A720-A7FF;
}
`;

let inputGlobalStyles = globalCss(({ theme }) => ({
'@keyframes mui-auto-fill': { from: { display: 'block', color: 'transparent' } },
'@keyframes mui-auto-fill-cancel': {
from: { display: 'block', color: theme.palette.primary.main },
},
}));
if (typeof inputGlobalStyles === 'function') {
inputGlobalStyles = inputGlobalStyles();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
* {
box-sizing: border-box;
}
@font-face {
font-family: 'Patrick Hand SC';
font-style: normal;
font-weight: 400;
color: red;
src:
local('Patrick Hand SC'),
local('PatrickHandSC-Regular'),
url(https://fonts.gstatic.com/s/patrickhandsc/v4/OYFWCgfCR-7uHIovjUZXsZ71Uis0Qeb9Gqo8IZV7ckE.woff2)
format('woff2');
unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f, U+A720-A7FF;
}
@keyframes mui-auto-fill {
from {
display: block;
color: transparent;
}
}
@keyframes mui-auto-fill-cancel {
from {
display: block;
color: red;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
null;
let inputGlobalStyles = null;
if (typeof inputGlobalStyles === 'function') {
inputGlobalStyles = inputGlobalStyles();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { globalCss } from '@pigment-css/react';

const green = 'green';

globalCss`
* {
box-sizing: border-box;
}
@font-face {
font-family: 'Patrick Hand SC';
font-style: normal;
font-weight: 400;
color: ${green};
src: local('Patrick Hand SC'),
local('PatrickHandSC-Regular'),
url(https://fonts.gstatic.com/s/patrickhandsc/v4/OYFWCgfCR-7uHIovjUZXsZ71Uis0Qeb9Gqo8IZV7ckE.woff2)
format('woff2');
unicode-range: U+0100-024f, U+1-1eff,
U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f,
U+A720-A7FF;
}
`;

let inputGlobalStyles = globalCss({
'@keyframes mui-auto-fill': { from: { display: 'block' } },
'@keyframes mui-auto-fill-cancel': { from: { display: 'block' } },
});
if (typeof inputGlobalStyles === 'function') {
inputGlobalStyles = inputGlobalStyles();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
* {
box-sizing: border-box;
}
@font-face {
font-family: 'Patrick Hand SC';
font-style: normal;
font-weight: 400;
color: green;
src:
local('Patrick Hand SC'),
local('PatrickHandSC-Regular'),
url(https://fonts.gstatic.com/s/patrickhandsc/v4/OYFWCgfCR-7uHIovjUZXsZ71Uis0Qeb9Gqo8IZV7ckE.woff2)
format('woff2');
unicode-range: U+0100-024f, U+1-1eff, U+20a0-20ab, U+20ad-20cf, U+2c60-2c7f, U+A720-A7FF;
}
@keyframes mui-auto-fill {
from {
display: block;
}
}
@keyframes mui-auto-fill-cancel {
from {
display: block;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
null;
let inputGlobalStyles = null;
if (typeof inputGlobalStyles === 'function') {
inputGlobalStyles = inputGlobalStyles();
}
Loading

0 comments on commit da9fb0f

Please sign in to comment.