Skip to content

Commit

Permalink
[pigment-css][react] RTL Support (mui#41570)
Browse files Browse the repository at this point in the history
  • Loading branch information
brijeshb42 authored and pluvio72 committed Mar 28, 2024
1 parent db62e97 commit 52b8ca6
Show file tree
Hide file tree
Showing 15 changed files with 349 additions and 37 deletions.
91 changes: 90 additions & 1 deletion packages/pigment-css-react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ Pigment CSS is a zero-runtime CSS-in-JS library that extracts the colocated sty
- [Styled component as a CSS selector](#styled-component-as-a-css-selector)
- [Typing props](#typing-props)
- [Theming](#theming)
- [Accesing theme values](#accesing-theme-values)
- [Accessing theme values](#accessing-theme-values)
- [CSS variables support](#css-variables-support)
- [Color schemes](#color-schemes)
- [Switching color schemes](#switching-color-schemes)
- [TypeScript](#typescript)
- [How-to guides](#how-to-guides)
- [Coming from Emotion or styled-components](#coming-from-emotion-or-styled-components)
- [RTL Support](#rtl-support)

## Getting started

Expand Down Expand Up @@ -765,3 +766,91 @@ function App() {
)
}
```

## RTL Support

Pigment CSS offers built-in mechanism to automatically output corresponding rtl or ltr CSS. If your app by default caters to ltr direction, you also have an option to configure the plugin to output the CSS for the other direction automatically. To configure Pigment CSS for this, update the bundler config.

### Next.js

```js
const { withPigment } = require('@pigment-css/nextjs-plugin');

// ...
module.exports = withPigment(nextConfig, {
theme: yourCustomTheme,
// CSS output option
css: {
// Specify your default CSS authoring direction
defaultDirection: 'ltr',
// If you want to output CSS for the direction other than
// the `defaultDirection`. Default is `false`.
generateForBothDir: true,
},
});
```

### Vite

```js
import { pigment } from '@pigment-css/vite-plugin';

export default defineConfig({
plugins: [
pigment({
theme: yourTheme,
css: {
// Specify your default CSS authoring direction
defaultDirection: 'ltr',
// If you want to output CSS for the direction other than
// the `defaultDirection`. Default is `false`.
generateForBothDir: true,
},
}),
// ... Your other plugins.
],
});
```

Coming back to the app code, if one of the authored CSS is:

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

const className = css`
margin-left: 10px,
margin-right: 20px,
padding: '0 10px 20px 30px'
`;
```

The output CSS would be:

```css
.cmip3v5 {
margin-left: 10px;
margin-right: 20px;
padding: 0 10px 20px 30px;
}
[dir='rtl'] .cmip3v5 {
margin-right: 10px;
margin-left: 20px;
padding: 0 30px 20px 10px;
}
```

Remember to also add the `dir` attribute on the `html` element or any relevant parent element as per your application logic or user preference.

#### Custom dir selector

The default selector in the output CSS is `[dir=rtl]` or `[dir=ltr]`. You can customize this selector by passing an optional `getDirSelector` method in the `css` property above:

```js
// ...
css: {
getDirSelector(dir: string) {
// return your own selector that you'd like to use.
return `:dir(${dir})`;
}
}
```
12 changes: 11 additions & 1 deletion packages/pigment-css-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
"cssesc": "^3.0.0",
"csstype": "^3.1.3",
"lodash": "^4.17.21",
"stylis": "^4.3.1"
"stylis": "^4.3.1",
"stylis-plugin-rtl": "^2.1.1"
},
"devDependencies": {
"@babel/plugin-syntax-jsx": "^7.23.3",
Expand Down Expand Up @@ -138,6 +139,15 @@
"import": "./build/Box.mjs",
"require": "./build/Box.js",
"default": "./build/Box.js"
},
"./RtlProvider": {
"types": "./build/RtlProvider.d.ts",
"import": {
"types": "./build/RtlProvider.d.mts",
"default": "./build/RtlProvider.mjs"
},
"require": "./build/RtlProvider.js",
"default": "./build/RtlProvider.js"
}
},
"nx": {
Expand Down
25 changes: 25 additions & 0 deletions packages/pigment-css-react/src/RtlProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use client';
/**
* This package has it's own version of RtlProvider to avoid including
* @mui/system in the bundle if someone is not using it.
*/
import * as React from 'react';

type RtlContextType = boolean | undefined;

type RtlProviderProps = {
value?: RtlContextType;
};

const RtlContext = React.createContext<RtlContextType>(false);

function RtlProvider({ value, ...props }: RtlProviderProps) {
return <RtlContext.Provider value={value ?? true} {...props} />;
}

export const useRtl = () => {
const value = React.useContext(RtlContext);
return value ?? false;
};

export default RtlProvider;
24 changes: 22 additions & 2 deletions packages/pigment-css-react/src/utils/cssFnValueToVariable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,28 @@ export type PluginCustomOptions = {
* Object to pass as parameter to the styled css callback functions.
*/
themeArgs?: { theme?: Theme };
/**
* Customize the output CSS. Mainly used for RTL support right now.
*/
css?: {
/**
* To denote that whatever default css is being authored pertains to this
* direction so that when Pigment CSS generates the CSS for the other direction,
* it can revert the direction of the selector accordingly.
* @default 'ltr'
*/
defaultDirection: 'ltr' | 'rtl';
/**
* Pass this as true if you want to output the CSS for both ltr and rtl.
* The css of the non-default direction will be wrapped in a `dir` selector.
*/
generateForBothDir: boolean;
/**
* Pass this callback to customize the selector for the `dir` attribute. The default
* is [dir=ltr] or [dir=rtl].
*/
getDirSelector?: (dir: 'ltr' | 'rtl') => string;
};
};

type CssFnValueToVariableParams = {
Expand All @@ -28,8 +50,6 @@ type CssFnValueToVariableParams = {
themeImportIdentifier?: string;
};

// const expressionCache = new WeakMap<ExpressionValue, Expression>();

// @TODO - Implement default theme argument for non-theme config as well.
function parseAndWrapExpression(
functionString: string,
Expand Down
42 changes: 37 additions & 5 deletions packages/pigment-css-react/src/utils/preprocessor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { Element } from 'stylis';
import { serialize, compile, stringify, middleware } from 'stylis';
import rtlPlugin from 'stylis-plugin-rtl';
import { type PluginCustomOptions } from './cssFnValueToVariable';

function globalSelector(element: Element) {
switch (element.type) {
Expand All @@ -17,13 +19,43 @@ function globalSelector(element: Element) {
}
}

const serializer = middleware([globalSelector, stringify]);
function getSerializer(includeRtl?: boolean) {
if (!includeRtl) {
return middleware([globalSelector, stringify]);
}
return middleware([globalSelector, rtlPlugin, stringify]);
}

const serializer = getSerializer();
const serializerRtl = getSerializer(true);

const stylis = (css: string) => serialize(compile(css), serializer);
const stylis = (css: string, serializerParam = serializer) =>
serialize(compile(css), serializerParam);

export function preprocessor(selector: string, cssText: string) {
const defaultGetDirSelector = (dir: 'ltr' | 'rtl') => `[dir=${dir}]`;

export function preprocessor(
selector: string,
cssText: string,
options?: PluginCustomOptions['css'],
) {
const {
defaultDirection = 'ltr',
generateForBothDir = false,
getDirSelector = defaultGetDirSelector,
} = options || {};
let css = '';
if (cssText.startsWith('@keyframes')) {
return stylis(cssText.replace('@keyframes', `@keyframes ${selector}`));
css += stylis(cssText.replace('@keyframes', `@keyframes ${selector}`));
return css;
}
css += stylis(`${selector}{${cssText}}`);
if (generateForBothDir) {
css += stylis(
`${getDirSelector(defaultDirection === 'ltr' ? 'rtl' : 'ltr')} ${selector}{${cssText}}`,
serializerRtl,
);
}
return stylis(`${selector}{${cssText}}`);

return css;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { styled, keyframes } from '@pigment-css/react';

const rotateKeyframe = keyframes({
from: {
transform: 'translateX(0%)',
},
to: {
transform: 'translateX(100%)',
},
});

const Component = styled.div(({ theme }) => ({
animation: `${rotateKeyframe} 2s ease-out 0s infinite`,
marginLeft: 10,
}));

export const SliderRail = styled('span', {
name: 'MuiSlider',
slot: 'Rail',
})`
display: block;
position: absolute;
left: 0;
top: 0;
border-top-left-radius: 3px;
`;

const SliderRail2 = styled.span`
${SliderRail} {
padding-inline-start: none;
margin: 0px 10px 10px 30px;
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
@keyframes r14vlhb {
from {
transform: translateX(0%);
}
to {
transform: translateX(100%);
}
}
.c194j3ko {
animation: r14vlhb 2s ease-out 0s infinite;
margin-left: 10px;
}
:dir(rtl) .c194j3ko {
animation: r14vlhb 2s ease-out 0s infinite;
margin-right: 10px;
}
.sgip8u5 {
display: block;
position: absolute;
left: 0;
top: 0;
border-top-left-radius: 3px;
}
:dir(rtl) .sgip8u5 {
display: block;
position: absolute;
right: 0;
top: 0;
border-top-right-radius: 3px;
}
.sgip8u5-1 {
font-size: 1.5rem;
}
:dir(rtl) .sgip8u5-1 {
font-size: 1.5rem;
}
.smip3v5 .sgip8u5 {
padding-inline-start: none;
margin: 0px 10px 10px 30px;
}
:dir(rtl) .smip3v5 .sgip8u5 {
padding-inline-start: none;
margin: 0px 30px 10px 10px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { styled as _styled3 } from '@pigment-css/react';
import { styled as _styled2 } from '@pigment-css/react';
import { styled as _styled } from '@pigment-css/react';
import _theme from '@pigment-css/react/theme';
const Component = /*#__PURE__*/ _styled('div')({
classes: ['c194j3ko'],
});
export const SliderRail = /*#__PURE__*/ _styled2('span', {
name: 'MuiSlider',
slot: 'Rail',
})({
classes: ['sgip8u5', 'sgip8u5-1'],
});
const SliderRail2 = /*#__PURE__*/ _styled3('span')({
classes: ['smip3v5'],
});
Loading

0 comments on commit 52b8ca6

Please sign in to comment.