mirror of
https://github.com/facebook/docusaurus.git
synced 2025-06-14 00:32:47 +02:00
feat(v2): add filename in CodeBlock (#2346)
* feat: add filename in CodeBlock * Fix code to use Regex to find title from markdown and Update style * Fix reviewed point - Delete unnecessary template literals - Delete unnecessary "important!" from css * Add title in live codeblock * Just edit code order * Add demo for code title * Add docs about code title in markdown-features.mdx * Make code title height scalable * Rename codeBlockWrapper to codeBlockContent * Make copyButton appear when hovering codeTitle * Fix docs description about code title
This commit is contained in:
parent
201c663318
commit
5e0d11dbaf
6 changed files with 166 additions and 60 deletions
|
@ -17,6 +17,7 @@ import useThemeContext from '@theme/hooks/useThemeContext';
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
const highlightLinesRangeRegex = /{([\d,-]+)}/;
|
const highlightLinesRangeRegex = /{([\d,-]+)}/;
|
||||||
|
const codeBlockTitleRegex = /title=".*"/;
|
||||||
|
|
||||||
export default ({children, className: languageClassName, metastring}) => {
|
export default ({children, className: languageClassName, metastring}) => {
|
||||||
const {
|
const {
|
||||||
|
@ -41,6 +42,7 @@ export default ({children, className: languageClassName, metastring}) => {
|
||||||
const target = useRef(null);
|
const target = useRef(null);
|
||||||
const button = useRef(null);
|
const button = useRef(null);
|
||||||
let highlightLines = [];
|
let highlightLines = [];
|
||||||
|
let codeBlockTitle = '';
|
||||||
|
|
||||||
const {isDarkTheme} = useThemeContext();
|
const {isDarkTheme} = useThemeContext();
|
||||||
const lightModeTheme = prism.theme || defaultTheme;
|
const lightModeTheme = prism.theme || defaultTheme;
|
||||||
|
@ -52,6 +54,13 @@ export default ({children, className: languageClassName, metastring}) => {
|
||||||
highlightLines = rangeParser.parse(highlightLinesRange).filter(n => n > 0);
|
highlightLines = rangeParser.parse(highlightLinesRange).filter(n => n > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (metastring && codeBlockTitleRegex.test(metastring)) {
|
||||||
|
codeBlockTitle = metastring
|
||||||
|
.match(codeBlockTitleRegex)[0]
|
||||||
|
.split('title=')[1]
|
||||||
|
.replace(/"+/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let clipboard;
|
let clipboard;
|
||||||
|
|
||||||
|
@ -90,38 +99,51 @@ export default ({children, className: languageClassName, metastring}) => {
|
||||||
code={children.replace(/\n$/, '')}
|
code={children.replace(/\n$/, '')}
|
||||||
language={language}>
|
language={language}>
|
||||||
{({className, style, tokens, getLineProps, getTokenProps}) => (
|
{({className, style, tokens, getLineProps, getTokenProps}) => (
|
||||||
<pre className={classnames(className, styles.codeBlock)}>
|
<>
|
||||||
<button
|
{codeBlockTitle && (
|
||||||
ref={button}
|
<div style={style} className={styles.codeBlockTitle}>
|
||||||
type="button"
|
{codeBlockTitle}
|
||||||
aria-label="Copy code to clipboard"
|
</div>
|
||||||
className={styles.copyButton}
|
)}
|
||||||
onClick={handleCopyCode}>
|
<div className={styles.codeBlockContent}>
|
||||||
{showCopied ? 'Copied' : 'Copy'}
|
<button
|
||||||
</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
|
||||||
|
}
|
||||||
|
|
||||||
<div ref={target} className={styles.codeBlockLines} style={style}>
|
const lineProps = getLineProps({line, key: i});
|
||||||
{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`;
|
||||||
|
}
|
||||||
|
|
||||||
if (highlightLines.includes(i + 1)) {
|
return (
|
||||||
lineProps.className = `${lineProps.className} docusaurus-highlight-code-line`;
|
<div key={i} {...lineProps}>
|
||||||
}
|
{line.map((token, key) => (
|
||||||
|
<span key={key} {...getTokenProps({token, key})} />
|
||||||
return (
|
))}
|
||||||
<div key={i} {...lineProps}>
|
</div>
|
||||||
{line.map((token, key) => (
|
);
|
||||||
<span key={key} {...getTokenProps({token, key})} />
|
})}
|
||||||
))}
|
</div>
|
||||||
</div>
|
</pre>
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</pre>
|
</>
|
||||||
)}
|
)}
|
||||||
</Highlight>
|
</Highlight>
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,6 +5,19 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* 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 {
|
.codeBlock {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -12,6 +25,11 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.codeBlockWithTitle {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.copyButton {
|
.copyButton {
|
||||||
background: rgb(1, 22, 39);
|
background: rgb(1, 22, 39);
|
||||||
border: 1px solid rgb(214, 222, 235);
|
border: 1px solid rgb(214, 222, 235);
|
||||||
|
@ -30,7 +48,12 @@
|
||||||
bottom 200ms ease-in-out;
|
bottom 200ms ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.codeBlock:hover > .copyButton {
|
.copyButtonWithTitle {
|
||||||
|
top: calc(var(--ifm-pre-padding));
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeBlockTitle:hover + .codeBlockContent .copyButton,
|
||||||
|
.codeBlockContent:hover > .copyButton {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import Playground from '@theme/Playground';
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
const highlightLinesRangeRegex = /{([\d,-]+)}/;
|
const highlightLinesRangeRegex = /{([\d,-]+)}/;
|
||||||
|
const codeBlockTitleRegex = /title=".*"/;
|
||||||
|
|
||||||
export default ({
|
export default ({
|
||||||
children,
|
children,
|
||||||
|
@ -48,6 +49,7 @@ export default ({
|
||||||
const target = useRef(null);
|
const target = useRef(null);
|
||||||
const button = useRef(null);
|
const button = useRef(null);
|
||||||
let highlightLines = [];
|
let highlightLines = [];
|
||||||
|
let codeBlockTitle = '';
|
||||||
|
|
||||||
const {isDarkTheme} = useThemeContext();
|
const {isDarkTheme} = useThemeContext();
|
||||||
const lightModeTheme = prism.theme || defaultTheme;
|
const lightModeTheme = prism.theme || defaultTheme;
|
||||||
|
@ -59,6 +61,13 @@ export default ({
|
||||||
highlightLines = rangeParser.parse(highlightLinesRange).filter(n => n > 0);
|
highlightLines = rangeParser.parse(highlightLinesRange).filter(n => n > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (metastring && codeBlockTitleRegex.test(metastring)) {
|
||||||
|
codeBlockTitle = metastring
|
||||||
|
.match(codeBlockTitleRegex)[0]
|
||||||
|
.split('title=')[1]
|
||||||
|
.replace(/"+/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let clipboard;
|
let clipboard;
|
||||||
|
|
||||||
|
@ -109,38 +118,51 @@ export default ({
|
||||||
code={children.replace(/\n$/, '')}
|
code={children.replace(/\n$/, '')}
|
||||||
language={language}>
|
language={language}>
|
||||||
{({className, style, tokens, getLineProps, getTokenProps}) => (
|
{({className, style, tokens, getLineProps, getTokenProps}) => (
|
||||||
<pre className={classnames(className, styles.codeBlock)}>
|
<>
|
||||||
<button
|
{codeBlockTitle && (
|
||||||
ref={button}
|
<div style={style} className={styles.codeBlockTitle}>
|
||||||
type="button"
|
{codeBlockTitle}
|
||||||
aria-label="Copy code to clipboard"
|
</div>
|
||||||
className={styles.copyButton}
|
)}
|
||||||
onClick={handleCopyCode}>
|
<div className={styles.codeBlockContent}>
|
||||||
{showCopied ? 'Copied' : 'Copy'}
|
<button
|
||||||
</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
|
||||||
|
}
|
||||||
|
|
||||||
<div ref={target} className={styles.codeBlockLines} style={style}>
|
const lineProps = getLineProps({line, key: i});
|
||||||
{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`;
|
||||||
|
}
|
||||||
|
|
||||||
if (highlightLines.includes(i + 1)) {
|
return (
|
||||||
lineProps.className = `${lineProps.className} docusaurus-highlight-code-line`;
|
<div key={i} {...lineProps}>
|
||||||
}
|
{line.map((token, key) => (
|
||||||
|
<span key={key} {...getTokenProps({token, key})} />
|
||||||
return (
|
))}
|
||||||
<div key={i} {...lineProps}>
|
</div>
|
||||||
{line.map((token, key) => (
|
);
|
||||||
<span key={key} {...getTokenProps({token, key})} />
|
})}
|
||||||
))}
|
</div>
|
||||||
</div>
|
</pre>
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</pre>
|
</>
|
||||||
)}
|
)}
|
||||||
</Highlight>
|
</Highlight>
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,6 +5,19 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* 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 {
|
.codeBlock {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -12,6 +25,11 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.codeBlockWithTitle {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.copyButton {
|
.copyButton {
|
||||||
background: rgb(1, 22, 39);
|
background: rgb(1, 22, 39);
|
||||||
border: 1px solid rgb(214, 222, 235);
|
border: 1px solid rgb(214, 222, 235);
|
||||||
|
@ -30,7 +48,12 @@
|
||||||
bottom 200ms ease-in-out;
|
bottom 200ms ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.codeBlock:hover > .copyButton {
|
.copyButtonWithTitle {
|
||||||
|
top: calc(var(--ifm-pre-padding));
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeBlockTitle:hover + .codeBlockContent .copyButton,
|
||||||
|
.codeBlockContent:hover > .copyButton {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -314,6 +314,23 @@ function MyComponent(props) {
|
||||||
export default MyComponent;
|
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
|
### Interactive code editor
|
||||||
|
|
||||||
(Powered by [React Live](https://github.com/FormidableLabs/react-live))
|
(Powered by [React Live](https://github.com/FormidableLabs/react-live))
|
||||||
|
|
|
@ -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:
|
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
|
```json title="package.json"
|
||||||
// package.json
|
|
||||||
{
|
{
|
||||||
dependencies: {
|
dependencies: {
|
||||||
- "docusaurus": "^1.x.x",
|
- "docusaurus": "^1.x.x",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue