Skip to content

Commit 0ac34b7

Browse files
lex111slorber
andauthored
feat(v2): add support for RTL direction (#4140)
* feat(v2): add support for RTL * Move to i18n config * Move direction to localeConfigs * Create RTL styles for Docusaurus styles (via new hook) * update infima to alpha 19 * fix minor type error * Fix build * Cleanup * polish RTL support * polish RTL support * revert english rtl dir * minor RTL fix * minor doc updates Co-authored-by: slorber <[email protected]>
1 parent 2fb642d commit 0ac34b7

File tree

15 files changed

+126
-29
lines changed

15 files changed

+126
-29
lines changed

packages/docusaurus-module-type-aliases/src/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ declare module '@generated/i18n' {
5252
defaultLocale: string;
5353
locales: [string, ...string[]];
5454
currentLocale: string;
55-
localeConfigs: Record<string, {label: string}>;
55+
localeConfigs: Record<string, {label: string; direction: string}>;
5656
};
5757
export default i18n;
5858
}

packages/docusaurus-theme-classic/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,17 @@
3434
"@types/react-toggle": "^4.0.2",
3535
"clsx": "^1.1.1",
3636
"copy-text-to-clipboard": "^2.2.0",
37-
"infima": "0.2.0-alpha.18",
37+
"infima": "0.2.0-alpha.19",
3838
"joi": "^17.2.1",
3939
"lodash": "^4.17.19",
4040
"parse-numeric-range": "^1.2.0",
41+
"postcss": "^7.0.2",
4142
"prism-react-renderer": "^1.1.1",
4243
"prismjs": "^1.23.0",
4344
"prop-types": "^15.7.2",
4445
"react-router-dom": "^5.2.0",
45-
"react-toggle": "^4.1.1"
46+
"react-toggle": "^4.1.1",
47+
"rtlcss": "^2.6.2"
4648
},
4749
"devDependencies": {
4850
"@docusaurus/module-type-aliases": "2.0.0-alpha.70"

packages/docusaurus-theme-classic/src/index.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {Plugin} from '@docusaurus/types';
99
import {getTranslationFiles, translateThemeConfig} from './translations';
1010
import path from 'path';
1111
import Module from 'module';
12+
import postcss from 'postcss';
13+
import rtlcss from 'rtlcss';
1214

1315
const createRequire = Module.createRequire || Module.createRequireFromPath;
1416
const requireFromDocusaurusCore = createRequire(
@@ -59,15 +61,23 @@ const noFlashColorMode = ({defaultMode, respectPrefersColorScheme}) => {
5961
})();`;
6062
};
6163

64+
function getInfimaCSSFile(direction) {
65+
return `infima/dist/css/default/default${
66+
direction === 'rtl' ? '-rtl' : ''
67+
}.css`;
68+
}
69+
6270
export default function docusaurusThemeClassic(
6371
context,
6472
options,
6573
): Plugin<null, unknown> {
6674
const {
6775
siteConfig: {themeConfig},
76+
i18n: {currentLocale, localeConfigs},
6877
} = context;
6978
const {colorMode, prism: {additionalLanguages = []} = {}} = themeConfig || {};
7079
const {customCss} = options || {};
80+
const {direction} = localeConfigs[currentLocale];
7181

7282
return {
7383
name: 'docusaurus-theme-classic',
@@ -95,7 +105,7 @@ export default function docusaurusThemeClassic(
95105

96106
getClientModules() {
97107
const modules = [
98-
require.resolve('infima/dist/css/default/default.css'),
108+
require.resolve(getInfimaCSSFile(direction)),
99109
path.resolve(__dirname, './prism-include-languages'),
100110
];
101111

@@ -135,6 +145,37 @@ export default function docusaurusThemeClassic(
135145
};
136146
},
137147

148+
configurePostCss(postCssOptions) {
149+
if (direction === 'rtl') {
150+
postCssOptions.plugins.push(
151+
postcss.plugin('RtlCssPlugin', () => {
152+
function isInfimaCSSFile(file) {
153+
return (
154+
file.endsWith(getInfimaCSSFile(direction)) ||
155+
// special case for our own monorepo using symlinks!
156+
file.endsWith(
157+
'infima/packages/core/dist/css/default/default-rtl.css',
158+
)
159+
);
160+
}
161+
162+
return function (root: any) {
163+
const file = root?.source.input.file;
164+
165+
// Skip Infima as we are using the its RTL version.
166+
if (isInfimaCSSFile(file)) {
167+
return;
168+
}
169+
170+
rtlcss.process(root);
171+
};
172+
}),
173+
);
174+
}
175+
176+
return postCssOptions;
177+
},
178+
138179
injectHtmlTags() {
139180
return {
140181
preBodyTags: [

packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,10 @@ function DocPageContent({
9999
role="button"
100100
onKeyDown={toggleSidebar}
101101
onClick={toggleSidebar}>
102-
<IconArrow aria-label="Expand sidebar" />
102+
<IconArrow
103+
aria-label="Expand sidebar"
104+
className={styles.expandSidebarButtonIcon}
105+
/>
103106
</div>
104107
)}
105108
</div>

packages/docusaurus-theme-classic/src/theme/DocPage/styles.module.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@
4444
background-color: var(--ifm-color-emphasis-200);
4545
}
4646

47+
.expandSidebarButtonIcon {
48+
transform: rotate(0);
49+
}
50+
html[dir='rtl'] .expandSidebarButtonIcon {
51+
transform: rotate(180deg);
52+
}
53+
4754
html[data-theme='dark'] .collapsedDocSidebar:hover,
4855
html[data-theme='dark'] .collapsedDocSidebar:focus {
4956
background-color: var(--collapse-button-bg-color-dark);

packages/docusaurus-theme-classic/src/theme/DocSidebar/styles.module.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@
7878
transform: rotate(180deg);
7979
margin-top: 4px;
8080
}
81+
html[dir='rtl'] .collapseSidebarButtonIcon {
82+
transform: rotate(0);
83+
}
8184

8285
html[data-theme='dark'] .collapseSidebarButton {
8386
background-color: var(--collapse-button-bg-color-dark);

packages/docusaurus-theme-classic/src/theme/LayoutHead/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ function CanonicalUrlHeaders({permalink}: {permalink?: string}) {
8383
export default function LayoutHead(props: Props): JSX.Element {
8484
const {
8585
siteConfig,
86-
i18n: {currentLocale},
86+
i18n: {currentLocale, localeConfigs},
8787
} = useDocusaurusContext();
8888
const {
8989
favicon,
@@ -98,11 +98,12 @@ export default function LayoutHead(props: Props): JSX.Element {
9898
// See https://github.com/facebook/docusaurus/issues/3317#issuecomment-754661855
9999
// const htmlLang = currentLocale.split('-')[0];
100100
const htmlLang = currentLocale; // should we allow the user to override htmlLang with localeConfig?
101+
const htmlDir = localeConfigs[currentLocale].direction;
101102

102103
return (
103104
<>
104105
<Head>
105-
<html lang={htmlLang} />
106+
<html lang={htmlLang} dir={htmlDir} />
106107
{metaTitle && <title>{metaTitle}</title>}
107108
{metaTitle && <meta property="og:title" content={metaTitle} />}
108109
{favicon && <link rel="shortcut icon" href={faviconUrl} />}

packages/docusaurus-types/src/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ export type TranslationFiles = TranslationFile[];
9494

9595
export type I18nLocaleConfig = {
9696
label: string;
97+
direction: string;
9798
};
9899

99100
export type I18nConfig = {

packages/docusaurus/src/server/__tests__/i18n.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ describe('loadI18n', () => {
9393
locales: ['en', 'fr', 'de'],
9494
currentLocale: 'de',
9595
localeConfigs: {
96-
fr: {label: 'Français'},
96+
fr: {label: 'Français', direction: 'ltr'},
9797
en: defaultLocaleConfig('en'),
9898
de: defaultLocaleConfig('de'),
9999
},

packages/docusaurus/src/server/configValidation.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ const PresetSchema = Joi.alternatives().try(
7171

7272
const LocaleConfigSchema = Joi.object({
7373
label: Joi.string(),
74+
direction: Joi.string().equal('ltr', 'rtl').default('ltr'),
7475
});
7576

7677
const I18N_CONFIG_SCHEMA = Joi.object<I18nConfig>({

0 commit comments

Comments
 (0)