Skip to content

Commit ee08068

Browse files
authored
docs: shadow sidebar to remain expanded (#208)
1 parent de8864b commit ee08068

File tree

4 files changed

+763
-0
lines changed

4 files changed

+763
-0
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import PropTypes from 'prop-types';
2+
import React, {createContext, useContext, useMemo} from 'react';
3+
import styled from '@emotion/styled';
4+
import {trackCustomEvent} from 'gatsby-plugin-google-analytics';
5+
6+
export const GA_EVENT_CATEGORY_CODE_BLOCK = 'Code Block';
7+
export const MultiCodeBlockContext = createContext({});
8+
export const SelectedLanguageContext = createContext();
9+
10+
const Container = styled.div({
11+
position: 'relative'
12+
});
13+
14+
const langLabels = {
15+
js: 'JavaScript',
16+
ts: 'TypeScript',
17+
'hooks-js': 'Hooks (JS)',
18+
'hooks-ts': 'Hooks (TS)'
19+
};
20+
21+
function getUnifiedLang(language) {
22+
switch (language) {
23+
case 'js':
24+
case 'jsx':
25+
case 'javascript':
26+
return 'js';
27+
case 'ts':
28+
case 'tsx':
29+
case 'typescript':
30+
return 'ts';
31+
default:
32+
return language;
33+
}
34+
}
35+
36+
function getLang(child) {
37+
return getUnifiedLang(child.props['data-language']);
38+
}
39+
40+
export function MultiCodeBlock(props) {
41+
const {codeBlocks, titles} = useMemo(() => {
42+
const defaultState = {
43+
codeBlocks: {},
44+
titles: {}
45+
};
46+
47+
if (!Array.isArray(props.children)) {
48+
return defaultState;
49+
}
50+
51+
return props.children.reduce((acc, child, index, array) => {
52+
const lang = getLang(child);
53+
if (lang) {
54+
return {
55+
...acc,
56+
codeBlocks: {
57+
...acc.codeBlocks,
58+
[lang]: child
59+
}
60+
};
61+
}
62+
63+
if (child.props.className === 'gatsby-code-title') {
64+
const nextNode = array[index + 1];
65+
const title = child.props.children;
66+
const lang = getLang(nextNode);
67+
if (nextNode && title && lang) {
68+
return {
69+
...acc,
70+
titles: {
71+
...acc.titles,
72+
[lang]: title
73+
}
74+
};
75+
}
76+
}
77+
78+
return acc;
79+
}, defaultState);
80+
}, [props.children]);
81+
82+
const languages = useMemo(() => Object.keys(codeBlocks), [codeBlocks]);
83+
const [selectedLanguage, setSelectedLanguage] = useContext(
84+
SelectedLanguageContext
85+
);
86+
87+
if (!languages.length) {
88+
return props.children;
89+
}
90+
91+
function handleLanguageChange(language) {
92+
setSelectedLanguage(language);
93+
trackCustomEvent({
94+
category: GA_EVENT_CATEGORY_CODE_BLOCK,
95+
action: 'Change language',
96+
label: language
97+
});
98+
}
99+
100+
const defaultLanguage = languages[0];
101+
const renderedLanguage =
102+
selectedLanguage in codeBlocks ? selectedLanguage : defaultLanguage;
103+
104+
return (
105+
<Container>
106+
<MultiCodeBlockContext.Provider
107+
value={{
108+
selectedLanguage: renderedLanguage,
109+
languages: languages.map(lang => ({
110+
lang,
111+
label:
112+
// try to find a label or capitalize the provided lang
113+
langLabels[lang] || lang.charAt(0).toUpperCase() + lang.slice(1)
114+
})),
115+
onLanguageChange: handleLanguageChange
116+
}}
117+
>
118+
<div className="gatsby-code-title">{titles[renderedLanguage]}</div>
119+
{codeBlocks[renderedLanguage]}
120+
</MultiCodeBlockContext.Provider>
121+
</Container>
122+
);
123+
}
124+
125+
MultiCodeBlock.propTypes = {
126+
children: PropTypes.node.isRequired
127+
};

0 commit comments

Comments
 (0)