Skip to content

Commit 70ced20

Browse files
authored
Fix styling issues (#152)
1 parent 0eff93b commit 70ced20

File tree

7 files changed

+141
-31
lines changed

7 files changed

+141
-31
lines changed

.changeset/fifty-rockets-perform.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'docusaurus-theme-redoc': patch
3+
---
4+
5+
Fix nav sometimes hidden when logo is not present

packages/docusaurus-theme-redoc/src/theme/ApiSchema/ApiSchema.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22
import clsx from 'clsx';
33
import { ThemeProvider } from 'styled-components';
44
import { SchemaDefinition } from 'redoc';
5-
import { useSpec } from '../../hooks/useSpec';
5+
import { useSpec } from '../../utils/useSpec';
66
import { useSpecData } from '../useSpecData';
77
import { ApiSchemaProps as Props } from '../../types/common';
88
import '../Redoc/styles.css';

packages/docusaurus-theme-redoc/src/theme/Redoc/Redoc.tsx

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import React from 'react';
2+
import clsx from 'clsx';
23
import { Redoc as RedocComponent } from 'redoc';
34
import { SpecProps } from '../../types/common';
4-
import { useSpec } from '../../hooks/useSpec';
5+
import { useSpec } from '../../utils/useSpec';
56
import { ServerStyles } from './Styles';
67
import './styles.css';
78

@@ -12,12 +13,14 @@ import './styles.css';
1213
* Released under the MIT License
1314
*/
1415
function Redoc(props: SpecProps): JSX.Element {
15-
const { store } = useSpec(props);
16+
const { store, darkStore, lightStore, hasLogo } = useSpec(props);
1617

1718
return (
1819
<>
19-
<ServerStyles store={store} />
20-
<div className="redocusaurus">
20+
<ServerStyles lightStore={lightStore} darkStore={darkStore} />
21+
<div
22+
className={clsx(['redocusaurus', hasLogo && 'redocusaurus-has-logo'])}
23+
>
2124
<RedocComponent store={store} />
2225
</div>
2326
</>

packages/docusaurus-theme-redoc/src/theme/Redoc/ServerStyles.tsx

+87-10
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,94 @@ import { AppStore, Redoc } from 'redoc';
44
import { renderToString } from 'react-dom/server';
55
import { ServerStyleSheet } from 'styled-components';
66

7-
export function ServerStyles({ store }: { store: AppStore }) {
8-
const sheet = new ServerStyleSheet();
9-
renderToString(sheet.collectStyles(React.createElement(Redoc, { store })));
10-
const css = sheet.getStyleTags();
7+
/**
8+
* @see https://stackoverflow.com/a/54077142
9+
*/
10+
const prefixCssSelectors = function (rules: string, className: string) {
11+
const classLen = className.length;
12+
let char, nextChar, isAt, isIn;
13+
14+
// makes sure the className will not concatenate the selector
15+
className += ' ';
16+
17+
// removes comments
18+
rules = rules.replace(/\/\*(?:(?!\*\/)[\s\S])*\*\/|[\r\n\t]+/g, '');
19+
20+
// makes sure nextChar will not target a space
21+
rules = rules.replace(/}(\s*)@/g, '}@');
22+
rules = rules.replace(/}(\s*)}/g, '}}');
23+
24+
for (let i = 0; i < rules.length - 2; i++) {
25+
char = rules[i];
26+
nextChar = rules[i + 1];
27+
28+
if (char === '@' && nextChar !== 'f') isAt = true;
29+
if (!isAt && char === '{') isIn = true;
30+
if (isIn && char === '}') isIn = false;
31+
32+
if (
33+
!isIn &&
34+
nextChar !== '@' &&
35+
nextChar !== '}' &&
36+
(char === '}' || char === ',' || ((char === '{' || char === ';') && isAt))
37+
) {
38+
rules = rules.slice(0, i + 1) + className + rules.slice(i + 1);
39+
i += classLen;
40+
isAt = false;
41+
}
42+
}
43+
44+
// prefix the first select if it is not `@media` and if it is not yet prefixed
45+
if (rules.indexOf(className) !== 0 && rules.indexOf('@') !== 0)
46+
rules = className + rules;
47+
48+
return rules;
49+
};
50+
51+
const LIGHT_MODE_PREFIX = "html:not([data-theme='dark'])";
52+
const DARK_MODE_PREFIX = "html([data-theme='dark'])";
53+
54+
export function ServerStyles({
55+
lightStore,
56+
darkStore,
57+
}: {
58+
lightStore: AppStore;
59+
darkStore: AppStore;
60+
}) {
61+
const css = {
62+
light: '',
63+
dark: '',
64+
};
65+
const lightSheet = new ServerStyleSheet();
66+
renderToString(
67+
lightSheet.collectStyles(React.createElement(Redoc, { store: lightStore })),
68+
);
69+
const lightStyleTag = lightSheet.getStyleTags();
70+
let lightCss = lightStyleTag.slice(lightStyleTag.indexOf('>') + 1);
71+
lightCss = lightCss.slice(0, lightCss.indexOf('<style'));
72+
css.light = prefixCssSelectors(lightCss, LIGHT_MODE_PREFIX);
73+
74+
const darkSheet = new ServerStyleSheet();
75+
renderToString(
76+
darkSheet.collectStyles(React.createElement(Redoc, { store: darkStore })),
77+
);
78+
const darkStyleTag = darkSheet.getStyleTags();
79+
let darkCss = darkStyleTag.slice(darkStyleTag.indexOf('>') + 1);
80+
darkCss = darkCss.slice(0, darkCss.indexOf('<style'));
81+
css.dark = prefixCssSelectors(darkCss, DARK_MODE_PREFIX).slice(
82+
DARK_MODE_PREFIX.length + 1,
83+
);
1184

1285
return (
13-
<div
14-
className="redocusaurus-styles"
15-
dangerouslySetInnerHTML={{
16-
__html: css,
17-
}}
18-
/>
86+
<div className="redocusaurus-styles">
87+
<style
88+
key="light-mode-styles"
89+
dangerouslySetInnerHTML={{ __html: css.light }}
90+
/>
91+
<style
92+
key="dark-mode-styles"
93+
dangerouslySetInnerHTML={{ __html: css.dark }}
94+
/>
95+
</div>
1996
);
2097
}

packages/docusaurus-theme-redoc/src/theme/Redoc/Styles.tsx

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1+
import React from 'react';
12
import type { AppStore } from 'redoc';
23

3-
function ClientStyles(_props: { store: AppStore }) {
4-
return null;
4+
/**
5+
* Don't hydrate/replace server styles
6+
* @see https://github.com/facebook/react/issues/10923#issuecomment-338715787
7+
*/
8+
export function ServerStyles(_props: {
9+
lightStore: AppStore;
10+
darkStore: AppStore;
11+
}) {
12+
return <div className="redocusaurus-styles"></div>;
513
}
6-
7-
export { ClientStyles as ServerStyles };

packages/docusaurus-theme-redoc/src/theme/Redoc/styles.css

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
.redocusaurus-styles {
2+
display: none;
3+
}
4+
15
.redocusaurus .redoc-wrap {
26
border-bottom: 1px solid var(--ifm-toc-border-color);
37
}
@@ -33,8 +37,11 @@ html[data-theme='dark'] .redocusaurus h2 > a:nth-child(1)::before {
3337
border-right: 1px solid var(--ifm-toc-border-color);
3438
}
3539

36-
/* Hide Logo as already in navbar*/
37-
.redocusaurus .menu-content div:first-child {
40+
/**
41+
* Hide Logo as already in navbar
42+
* @see https://github.com/rohit-gohri/redocusaurus/issues/123
43+
*/
44+
.redocusaurus-has-logo .menu-content div:first-child {
3845
display: none;
3946
}
4047

Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useMemo } from 'react';
22
import useBaseUrl from '@docusaurus/useBaseUrl';
3+
import useIsBrowser from '@docusaurus/useIsBrowser';
34
import { usePluginData } from '@docusaurus/useGlobalData';
45
import { useColorMode } from '@docusaurus/theme-common';
56
import { AppStore, RedocRawOptions } from 'redoc';
@@ -14,28 +15,39 @@ import { GlobalData } from '../types/options';
1415
*/
1516
export function useSpec({ spec, url }: SpecProps) {
1617
const fullUrl = useBaseUrl(url);
18+
const isBrowser = useIsBrowser();
1719
const { isDarkTheme } = useColorMode();
1820
const themeOptions = usePluginData<GlobalData>('docusaurus-theme-redoc');
1921

2022
const result = useMemo(() => {
2123
const { lightTheme, darkTheme, options: redocOptions } = themeOptions;
22-
const theme = isDarkTheme ? darkTheme : lightTheme;
2324

24-
const options: RedocRawOptions = {
25-
...redocOptions,
26-
theme,
25+
const commonOptions: Partial<RedocRawOptions> = {
2726
// Disable offset when server rendering
28-
scrollYOffset:
29-
typeof window === 'undefined' ? 0 : redocOptions.scrollYOffset,
27+
scrollYOffset: isBrowser ? redocOptions.scrollYOffset : 0,
3028
};
29+
3130
// eslint-disable-next-line @typescript-eslint/no-explicit-any
32-
const store = new AppStore(spec as any, fullUrl, options);
31+
const lightStore = new AppStore(spec as any, fullUrl, {
32+
...redocOptions,
33+
...commonOptions,
34+
theme: lightTheme,
35+
});
36+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
37+
const darkStore = new AppStore(spec as any, fullUrl, {
38+
...redocOptions,
39+
...commonOptions,
40+
theme: darkTheme,
41+
});
3342

3443
return {
35-
store,
36-
options,
44+
// @ts-expect-error extra prop
45+
hasLogo: !!spec.info?.['x-logo'],
46+
lightStore,
47+
darkStore,
48+
store: isBrowser && isDarkTheme ? darkStore : lightStore,
3749
};
38-
}, [spec, fullUrl, themeOptions, isDarkTheme]);
50+
}, [isBrowser, spec, fullUrl, themeOptions, isDarkTheme]);
3951

4052
return result;
4153
}

0 commit comments

Comments
 (0)