init project
Some checks failed
No response / noResponse (push) Has been cancelled
CI / Continuous releases (push) Has been cancelled
CI / test-dev (macos-latest) (push) Has been cancelled
CI / test-dev (ubuntu-latest) (push) Has been cancelled
CI / test-dev (windows-latest) (push) Has been cancelled
Maintenance / main (push) Has been cancelled
Scorecards supply-chain security / Scorecards analysis (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled

This commit is contained in:
how2ice
2025-12-12 14:26:25 +09:00
commit 005cf56baf
43188 changed files with 1079531 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
# @mui/stylis-plugin-rtl
Stylis RTL plugin for Material UI.
> Note: this is a fork of [stylis-plugin-rtl](https://github.com/styled-components/stylis-plugin-rtl) to fix issues with CSS layers and to support the latest version of Stylis.
## Installation
```bash
npm install @mui/stylis-plugin-rtl @emotion/cache stylis
```
## Usage
```js
import * as React from 'react';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import TextField from '@mui/material/TextField';
import rtlPlugin from '@mui/stylis-plugin-rtl';
import { prefixer } from 'stylis';
import { CacheProvider } from '@emotion/react';
import createCache from '@emotion/cache';
const theme = createTheme({
direction: 'rtl',
});
const cacheRtl = createCache({
key: 'muirtl',
stylisPlugins: [prefixer, rtlPlugin],
});
export default function RtlDemo() {
return (
<CacheProvider value={cacheRtl}>
<ThemeProvider theme={theme}>
<div dir="rtl">
<TextField
label="ملصق"
placeholder="العنصر النائب"
helperText="هذا نص مساعد"
variant="outlined"
/>
</div>
</ThemeProvider>
</CacheProvider>
);
}
```
For more information, see the [RTL documentation](https://mui.com/material-ui/guides/right-to-left/).

View File

@@ -0,0 +1,61 @@
{
"name": "@mui/stylis-plugin-rtl",
"version": "7.3.6",
"author": "MUI Team",
"description": "A plugin for Material UI that provides RTL (right-to-left) support.",
"keywords": [
"react",
"react-component",
"mui",
"rtl"
],
"repository": {
"type": "git",
"url": "git+https://github.com/mui/material-ui.git",
"directory": "packages/mui-stylis-plugin-rtl"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/mui/material-ui/issues"
},
"homepage": "https://github.com/mui/material-ui/tree/master/packages/mui-utils",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"scripts": {
"build": "code-infra build",
"release": "pnpm build && pnpm publish",
"test": "pnpm --workspace-root test:unit --project \"*:@mui/stylis-plugin-rtl\"",
"typescript": "tsc -p tsconfig.json",
"attw": "attw --pack ./build --exclude-entrypoints esm modern"
},
"dependencies": {
"@babel/runtime": "^7.28.4",
"cssjanus": "^2.3.0"
},
"devDependencies": {
"@mui/internal-test-utils": "workspace:^",
"@types/chai": "^5.2.3",
"@types/node": "^20.19.25",
"@types/sinon": "^17.0.4",
"@types/stylis": "4.2.7",
"chai": "^6.0.1",
"sinon": "^21.0.0",
"stylis": "4.3.6"
},
"peerDependencies": {
"stylis": "4.x"
},
"sideEffects": false,
"publishConfig": {
"access": "public",
"directory": "build"
},
"engines": {
"node": ">=14.0.0"
},
"exports": {
".": "./src/index.ts"
}
}

View File

@@ -0,0 +1,173 @@
import { expect } from 'chai';
import { compile, Middleware, middleware, prefixer, serialize, stringify } from 'stylis';
import muiRtlPlugin from '@mui/stylis-plugin-rtl';
const stylis = (css: string, extraPlugins: Middleware[] = []) =>
serialize(compile(css), middleware([...extraPlugins, muiRtlPlugin, stringify]));
describe('integration test with stylis', () => {
it('flips simple rules', () => {
expect(
stylis(
`.a {
padding-left: 5px;
margin-right: 5px;
border-left: 1px solid red;
}
`,
),
).to.equal(`.a{padding-right:5px;margin-left:5px;border-right:1px solid red;}`);
});
it('flips shorthands', () => {
expect(
stylis(
`.a {
padding: 0 5px 0 0;
margin: 0 0 0 5px;
}
`,
),
).to.equal(`.a{padding:0 0 0 5px;margin:0 5px 0 0;}`);
});
it('handles noflip directives', () => {
expect(
stylis(
`
.a {
/* @noflip */
padding: 0 5px 0 0;
margin: 0 0 0 5px;
}
`,
),
).to.equal(`.a{padding:0 5px 0 0;margin:0 5px 0 0;}`);
});
it('flips keyframes', () => {
expect(
stylis(
`@keyframes a {
0% { left: 0px; }
100% { left: 100px; }
}
`,
),
).to.equal(`@keyframes a{0%{right:0px;}100%{right:100px;}}`);
});
it('flips media queries', () => {
expect(
stylis(
`@media (min-width: 500px) {
.a {
padding-left: 5px;
margin-right: 5px;
border-left: 1px solid red;
}
}
`,
),
).to.equal(
`@media (min-width: 500px){.a{padding-right:5px;margin-left:5px;border-right:1px solid red;}}`,
);
});
it('flips supports queries', () => {
expect(
stylis(
`@supports (display: flex) {
.a {
padding-left: 5px;
margin-right: 5px;
border-left: 1px solid red;
}
}
`,
),
).to.equal(
`@supports (display: flex){.a{padding-right:5px;margin-left:5px;border-right:1px solid red;}}`,
);
});
it('works in tandem with prefixer', () => {
expect(
stylis(
`@keyframes a {
0% { left: 0px; }
100% { left: 100px; }
}
`,
[prefixer],
),
).to.equal(
`@-webkit-keyframes a{0%{right:0px;}100%{right:100px;}}@keyframes a{0%{right:0px;}100%{right:100px;}}`,
);
});
it("doesn't crash on empty rules", () => {
// this generates nodes for:
// .cls{}
// .cls .nested{color:hotpink;}
expect(
stylis(`
.cls {
& .nested {
color:hotpink;
}
}
`),
).to.equal(`.cls .nested{color:hotpink;}`);
});
it('works for nested rules', () => {
expect(
stylis(`
.cls {
margin-right: 32px;
& .first-child {
margin-right: 32px;
}
}
`),
).to.equal(`.cls{margin-left:32px;}.cls .first-child{margin-left:32px;}`);
});
it('works for layer rules', () => {
expect(
stylis(`
@layer default {
.cls {
margin-right: 32px;
& .first-child {
margin-right: 32px;
}
}
}
`),
).to.equal(`@layer default{.cls{margin-left:32px;}.cls .first-child{margin-left:32px;}}`);
});
it('works for nested layer rules', () => {
expect(
stylis(`
@layer root {
.foo {
margin-right: 32px;
}
@layer default {
.cls {
margin-right: 32px;
& .first-child {
margin-right: 32px;
}
}
}
}
`),
).to.equal(
`@layer root{.foo{margin-left:32px;}@layer default{.cls{margin-left:32px;}.cls .first-child{margin-left:32px;}}}`,
);
});
});

View File

@@ -0,0 +1,85 @@
/* eslint-disable default-case, no-return-assign, curly, prefer-template, @typescript-eslint/no-unused-vars */
/**
* Copied from https://github.com/styled-components/stylis-plugin-rtl/blob/main/src/stylis-rtl.ts
* with a modification at line 67 to handle layer rules.
*/
// @ts-nocheck
import cssjanus from 'cssjanus';
import {
COMMENT,
compile,
DECLARATION,
IMPORT,
RULESET,
serialize,
strlen,
Middleware,
KEYFRAMES,
MEDIA,
SUPPORTS,
LAYER,
} from 'stylis';
type MiddlewareParams = Parameters<Middleware>;
function stringifyPreserveComments(
element: MiddlewareParams[0],
index: MiddlewareParams[1],
children: MiddlewareParams[2],
): string {
switch (element.type) {
case IMPORT:
case DECLARATION:
case COMMENT:
return (element.return = element.return || element.value);
case RULESET: {
element.value = Array.isArray(element.props) ? element.props.join(',') : element.props;
if (Array.isArray(element.children)) {
element.children.forEach((x) => {
if (x.type === COMMENT) x.children = x.value;
});
}
}
}
const serializedChildren = serialize(
Array.prototype.concat(element.children),
stringifyPreserveComments,
);
return strlen(serializedChildren)
? (element.return = element.value + '{' + serializedChildren + '}')
: '';
}
function stylisRTLPlugin(
element: MiddlewareParams[0],
index: MiddlewareParams[1],
children: MiddlewareParams[2],
callback: MiddlewareParams[3],
): string | void {
if (
element.type === KEYFRAMES ||
element.type === SUPPORTS ||
(element.type === RULESET &&
(!element.parent ||
element.parent.type === MEDIA ||
element.parent.type === RULESET ||
element.parent.type === LAYER))
) {
const stringified = cssjanus.transform(stringifyPreserveComments(element, index, children));
element.children = stringified ? compile(stringified)[0].children : [];
element.return = '';
}
}
// stable identifier that will not be dropped by minification unless the whole module
// is unused
Object.defineProperty(stylisRTLPlugin, 'name', { value: 'stylisRTLPlugin' });
export default stylisRTLPlugin;

View File

@@ -0,0 +1,16 @@
{
// This config is for emitting declarations (.d.ts) only
// Actual .ts source files are transpiled via babel
"extends": "./tsconfig.json",
"compilerOptions": {
"composite": true,
"declaration": true,
"noEmit": false,
"emitDeclarationOnly": true,
"outDir": "build/esm",
"rootDir": "./src",
"types": ["react", "node"]
},
"include": ["src/**/*.ts"],
"exclude": ["src/**/*.test.ts*", "src/**/*.spec.ts*"]
}

View File

@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"allowJs": false,
"types": ["react", "vitest/globals", "node"]
},
"include": ["src/**/*"]
}

View File

@@ -0,0 +1,4 @@
// eslint-disable-next-line import/no-relative-packages
import sharedConfig from '../../vitest.shared.mts';
export default sharedConfig(import.meta.url, { jsdom: true });