Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(v2): add filename in CodeBlock #2346

Merged
merged 11 commits into from
Mar 25, 2020
84 changes: 53 additions & 31 deletions packages/docusaurus-theme-classic/src/theme/CodeBlock/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import useThemeContext from '@theme/hooks/useThemeContext';
import styles from './styles.module.css';

const highlightLinesRangeRegex = /{([\d,-]+)}/;
const codeBlockTitleRegex = /title=".*"/;

export default ({children, className: languageClassName, metastring}) => {
const {
Expand All @@ -41,6 +42,7 @@ export default ({children, className: languageClassName, metastring}) => {
const target = useRef(null);
const button = useRef(null);
let highlightLines = [];
let codeBlockTitle = '';

const {isDarkTheme} = useThemeContext();
const lightModeTheme = prism.theme || defaultTheme;
Expand All @@ -52,6 +54,13 @@ export default ({children, className: languageClassName, metastring}) => {
highlightLines = rangeParser.parse(highlightLinesRange).filter(n => n > 0);
}

if (metastring && codeBlockTitleRegex.test(metastring)) {
codeBlockTitle = metastring
.match(codeBlockTitleRegex)[0]
.split('title=')[1]
.replace(/"+/g, '');
}

useEffect(() => {
let clipboard;

Expand Down Expand Up @@ -90,38 +99,51 @@ export default ({children, className: languageClassName, metastring}) => {
code={children.replace(/\n$/, '')}
language={language}>
{({className, style, tokens, getLineProps, getTokenProps}) => (
<pre className={classnames(className, styles.codeBlock)}>
<button
ref={button}
type="button"
aria-label="Copy code to clipboard"
className={styles.copyButton}
onClick={handleCopyCode}>
{showCopied ? 'Copied' : 'Copy'}
</button>

<div ref={target} className={styles.codeBlockLines} style={style}>
{tokens.map((line, i) => {
if (line.length === 1 && line[0].content === '') {
line[0].content = '\n'; // eslint-disable-line no-param-reassign
}

const lineProps = getLineProps({line, key: i});

if (highlightLines.includes(i + 1)) {
lineProps.className = `${lineProps.className} docusaurus-highlight-code-line`;
}

return (
<div key={i} {...lineProps}>
{line.map((token, key) => (
<span key={key} {...getTokenProps({token, key})} />
))}
</div>
);
})}
<>
{codeBlockTitle && (
<div style={style} className={styles.codeBlockTitle}>
{codeBlockTitle}
</div>
)}
<div className={styles.codeBlockContent}>
<button
ref={button}
type="button"
aria-label="Copy code to clipboard"
className={classnames(styles.copyButton, {
[styles.copyButtonWithTitle]: codeBlockTitle,
})}
onClick={handleCopyCode}>
{showCopied ? 'Copied' : 'Copy'}
</button>
<pre
className={classnames(className, styles.codeBlock, {
[styles.codeBlockWithTitle]: codeBlockTitle,
})}>
<div ref={target} className={styles.codeBlockLines} style={style}>
{tokens.map((line, i) => {
if (line.length === 1 && line[0].content === '') {
line[0].content = '\n'; // eslint-disable-line no-param-reassign
}

const lineProps = getLineProps({line, key: i});

if (highlightLines.includes(i + 1)) {
lineProps.className = `${lineProps.className} docusaurus-highlight-code-line`;
}

return (
<div key={i} {...lineProps}>
{line.map((token, key) => (
<span key={key} {...getTokenProps({token, key})} />
))}
</div>
);
})}
</div>
</pre>
</div>
</pre>
</>
)}
</Highlight>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,31 @@
* LICENSE file in the root directory of this source tree.
*/

.codeBlockContent {
position: relative;
}

.codeBlockTitle {
border-top-left-radius: var(--ifm-pre-border-radius);
border-top-right-radius: var(--ifm-pre-border-radius);
font-weight: bold;
padding: 4px 12px;
border-bottom: 1px solid;
width: 100%;
}

.codeBlock {
overflow: auto;
display: block;
padding: 0;
margin: 0;
}

.codeBlockWithTitle {
border-top-left-radius: 0;
border-top-right-radius: 0;
}

.copyButton {
background: rgb(1, 22, 39);
border: 1px solid rgb(214, 222, 235);
Expand All @@ -30,7 +48,12 @@
bottom 200ms ease-in-out;
}

.codeBlock:hover > .copyButton {
.copyButtonWithTitle {
top: calc(var(--ifm-pre-padding));
}

.codeBlockTitle:hover + .codeBlockContent .copyButton,
.codeBlockContent:hover > .copyButton {
visibility: visible;
opacity: 1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import Playground from '@theme/Playground';
import styles from './styles.module.css';

const highlightLinesRangeRegex = /{([\d,-]+)}/;
const codeBlockTitleRegex = /title=".*"/;

export default ({
children,
Expand Down Expand Up @@ -48,6 +49,7 @@ export default ({
const target = useRef(null);
const button = useRef(null);
let highlightLines = [];
let codeBlockTitle = '';

const {isDarkTheme} = useThemeContext();
const lightModeTheme = prism.theme || defaultTheme;
Expand All @@ -59,6 +61,13 @@ export default ({
highlightLines = rangeParser.parse(highlightLinesRange).filter(n => n > 0);
}

if (metastring && codeBlockTitleRegex.test(metastring)) {
codeBlockTitle = metastring
.match(codeBlockTitleRegex)[0]
.split('title=')[1]
.replace(/"+/g, '');
}

useEffect(() => {
let clipboard;

Expand Down Expand Up @@ -109,38 +118,51 @@ export default ({
code={children.replace(/\n$/, '')}
language={language}>
{({className, style, tokens, getLineProps, getTokenProps}) => (
<pre className={classnames(className, styles.codeBlock)}>
<button
ref={button}
type="button"
aria-label="Copy code to clipboard"
className={styles.copyButton}
onClick={handleCopyCode}>
{showCopied ? 'Copied' : 'Copy'}
</button>

<div ref={target} className={styles.codeBlockLines} style={style}>
{tokens.map((line, i) => {
if (line.length === 1 && line[0].content === '') {
line[0].content = '\n'; // eslint-disable-line no-param-reassign
}

const lineProps = getLineProps({line, key: i});

if (highlightLines.includes(i + 1)) {
lineProps.className = `${lineProps.className} docusaurus-highlight-code-line`;
}

return (
<div key={i} {...lineProps}>
{line.map((token, key) => (
<span key={key} {...getTokenProps({token, key})} />
))}
</div>
);
})}
<>
{codeBlockTitle && (
<div style={style} className={styles.codeBlockTitle}>
{codeBlockTitle}
</div>
)}
<div className={styles.codeBlockContent}>
<button
ref={button}
type="button"
aria-label="Copy code to clipboard"
className={classnames(styles.copyButton, {
[styles.copyButtonWithTitle]: codeBlockTitle,
})}
onClick={handleCopyCode}>
{showCopied ? 'Copied' : 'Copy'}
</button>
<pre
className={classnames(className, styles.codeBlock, {
[styles.codeBlockWithTitle]: codeBlockTitle,
})}>
<div ref={target} className={styles.codeBlockLines} style={style}>
{tokens.map((line, i) => {
if (line.length === 1 && line[0].content === '') {
line[0].content = '\n'; // eslint-disable-line no-param-reassign
}

const lineProps = getLineProps({line, key: i});

if (highlightLines.includes(i + 1)) {
lineProps.className = `${lineProps.className} docusaurus-highlight-code-line`;
}

return (
<div key={i} {...lineProps}>
{line.map((token, key) => (
<span key={key} {...getTokenProps({token, key})} />
))}
</div>
);
})}
</div>
</pre>
</div>
</pre>
</>
)}
</Highlight>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,31 @@
* LICENSE file in the root directory of this source tree.
*/

.codeBlockContent {
position: relative;
}

.codeBlockTitle {
border-top-left-radius: var(--ifm-pre-border-radius);
border-top-right-radius: var(--ifm-pre-border-radius);
font-weight: bold;
padding: 4px 12px;
border-bottom: 1px solid;
width: 100%;
}

.codeBlock {
overflow: auto;
display: block;
padding: 0;
margin: 0;
}

.codeBlockWithTitle {
border-top-left-radius: 0;
border-top-right-radius: 0;
}

.copyButton {
background: rgb(1, 22, 39);
border: 1px solid rgb(214, 222, 235);
Expand All @@ -30,7 +48,12 @@
bottom 200ms ease-in-out;
}

.codeBlock:hover > .copyButton {
.copyButtonWithTitle {
top: calc(var(--ifm-pre-padding));
}

.codeBlockTitle:hover + .codeBlockContent .copyButton,
.codeBlockContent:hover > .copyButton {
visibility: visible;
opacity: 1;
}
Expand Down
17 changes: 17 additions & 0 deletions website/docs/markdown-features.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,23 @@ function MyComponent(props) {
export default MyComponent;
```

### Code title

You can add title to code block by adding `title` key after the language (leave a space between them).

```jsx title="src/components/HelloCodeTitle.js"
function HelloCodeTitle(props) {
return <h1>Hello, {props.name}</h1>;
}
```

```jsx title="src/components/HelloCodeTitle.js"
function HelloCodeTitle(props) {
return <h1>Hello, {props.name}</h1>;
}
```


### Interactive code editor

(Powered by [React Live](https://github.com/FormidableLabs/react-live))
Expand Down
3 changes: 1 addition & 2 deletions website/docs/migrating-from-v1-to-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ This provides a clear distinction between Docusaurus' official packages and comm

Meanwhile, the default doc site functionalities provided by Docusaurus 1 are now provided by `@docusaurus/preset-classic`. Therefore, we need to add this dependency as well:

```json
// package.json
```json title="package.json"
{
dependencies: {
- "docusaurus": "^1.x.x",
Expand Down