Skip to content

Commit

Permalink
refactor(theme-classic): split CodeBlock (#7175)
Browse files Browse the repository at this point in the history
* extract CodeBlockLine

* stable refactor

* stable refactor

* stable refactor

* add CodeBlockContainer

* refactor

* refactor

* do the actual split

Co-authored-by: Joshua Chen <[email protected]>
  • Loading branch information
slorber and Josh-Cena authored Apr 15, 2022
1 parent 0f5f6f3 commit 5273a53
Show file tree
Hide file tree
Showing 10 changed files with 340 additions and 200 deletions.
54 changes: 52 additions & 2 deletions packages/docusaurus-theme-classic/src/theme-classic.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,10 @@ declare module '@theme/BlogLayout' {
}

declare module '@theme/CodeBlock' {
import type {ReactElement} from 'react';
import type {ReactNode} from 'react';

export interface Props {
readonly children: string | ReactElement;
readonly children: ReactNode;
readonly className?: string;
readonly metastring?: string;
readonly title?: string;
Expand All @@ -170,6 +170,56 @@ declare module '@theme/CodeBlock/CopyButton' {
export default function CopyButton(props: Props): JSX.Element;
}

declare module '@theme/CodeBlock/Container' {
import type {ComponentProps} from 'react';

export default function CodeBlockContainer<T extends 'div' | 'pre'>({
as: As,
...props
}: {as: T} & ComponentProps<T>): JSX.Element;
}

declare module '@theme/CodeBlock/Content/Element' {
import type {Props} from '@theme/CodeBlock';

export type {Props};

export default function CodeBlockElementContent(props: Props): JSX.Element;
}

declare module '@theme/CodeBlock/Content/String' {
import type {Props as CodeBlockProps} from '@theme/CodeBlock';

export interface Props extends Omit<CodeBlockProps, 'children'> {
readonly children: string;
}

export default function CodeBlockStringContent(props: Props): JSX.Element;
}

declare module '@theme/CodeBlock/Line' {
import type {ComponentProps} from 'react';
import type Highlight from 'prism-react-renderer';

// Lib does not make this easy
type RenderProps = Parameters<
ComponentProps<typeof Highlight>['children']
>[0];
type GetLineProps = RenderProps['getLineProps'];
type GetTokenProps = RenderProps['getTokenProps'];
type Token = RenderProps['tokens'][number][number];

export interface Props {
readonly line: Token[];
readonly highlight: boolean;
readonly showLineNumbers: boolean;
readonly getLineProps: GetLineProps;
readonly getTokenProps: GetTokenProps;
}

export default function CodeBlockLine(props: Props): JSX.Element;
}

declare module '@theme/DocCard' {
import type {PropSidebarItem} from '@docusaurus/plugin-content-docs';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React, {type ComponentProps} from 'react';
import clsx from 'clsx';
import {
usePrismTheme,
getPrismCssVariables,
ThemeClassNames,
} from '@docusaurus/theme-common';
import styles from './styles.module.css';

export default function CodeBlockContainer<T extends 'div' | 'pre'>({
as: As,
...props
}: {as: T} & ComponentProps<T>): JSX.Element {
const prismTheme = usePrismTheme();
const prismCssVariables = getPrismCssVariables(prismTheme);
return (
<As
// Polymorphic components are hard to type, without `oneOf` generics
{...(props as never)}
style={prismCssVariables}
className={clsx(
props.className,
styles.codeBlockContainer,
ThemeClassNames.common.codeBlock,
)}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

.codeBlockContainer {
background: var(--prism-background-color);
color: var(--prism-color);
margin-bottom: var(--ifm-leading);
box-shadow: var(--ifm-global-shadow-lw);
border-radius: var(--ifm-code-border-radius);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import Container from '@theme/CodeBlock/Container';
import clsx from 'clsx';
import type {Props} from '@theme/CodeBlock/Content/Element';

import styles from './styles.module.css';

// <pre> tags in markdown map to CodeBlocks. They may contain JSX children. When
// the children is not a simple string, we just return a styled block without
// actually highlighting.
export default function CodeBlockJSX({
children,
className,
}: Props): JSX.Element {
return (
<Container
as="pre"
tabIndex={0}
className={clsx(styles.codeBlockStandalone, 'thin-scrollbar', className)}>
<code className={styles.codeBlockLines}>{children}</code>
</Container>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import {
useThemeConfig,
parseCodeBlockTitle,
parseLanguage,
parseLines,
containsLineNumbers,
usePrismTheme,
} from '@docusaurus/theme-common';
import clsx from 'clsx';
import Highlight, {defaultProps, type Language} from 'prism-react-renderer';
import Line from '@theme/CodeBlock/Line';
import CopyButton from '@theme/CodeBlock/CopyButton';
import Container from '@theme/CodeBlock/Container';
import type {Props} from '@theme/CodeBlock/Content/String';

import styles from './styles.module.css';

export default function CodeBlockString({
children,
className: blockClassName = '',
metastring,
title: titleProp,
showLineNumbers: showLineNumbersProp,
language: languageProp,
}: Props): JSX.Element {
const {
prism: {defaultLanguage},
} = useThemeConfig();
const language =
languageProp ?? parseLanguage(blockClassName) ?? defaultLanguage;
const prismTheme = usePrismTheme();

// We still parse the metastring in case we want to support more syntax in the
// future. Note that MDX doesn't strip quotes when parsing metastring:
// "title=\"xyz\"" => title: "\"xyz\""
const title = parseCodeBlockTitle(metastring) || titleProp;

const {highlightLines, code} = parseLines(children, metastring, language);
const showLineNumbers =
showLineNumbersProp || containsLineNumbers(metastring);

return (
<Container
as="div"
className={clsx(
blockClassName,
language &&
!blockClassName.includes(`language-${language}`) &&
`language-${language}`,
)}>
{title && <div className={styles.codeBlockTitle}>{title}</div>}
<div className={styles.codeBlockContent}>
<Highlight
{...defaultProps}
theme={prismTheme}
code={code}
language={(language ?? 'text') as Language}>
{({className, tokens, getLineProps, getTokenProps}) => (
<pre
/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */
tabIndex={0}
className={clsx(className, styles.codeBlock, 'thin-scrollbar')}>
<code
className={clsx(
styles.codeBlockLines,
showLineNumbers && styles.codeBlockLinesWithNumbering,
)}>
{tokens.map((line, i) => (
<Line
key={i}
line={line}
getLineProps={getLineProps}
getTokenProps={getTokenProps}
highlight={highlightLines.includes(i)}
showLineNumbers={showLineNumbers}
/>
))}
</code>
</pre>
)}
</Highlight>
<CopyButton code={code} />
</div>
</Container>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/

.codeBlockContainer {
background: var(--prism-background-color);
color: var(--prism-color);
margin-bottom: var(--ifm-leading);
box-shadow: var(--ifm-global-shadow-lw);
border-radius: var(--ifm-code-border-radius);
}

.codeBlockContent {
position: relative;
/* rtl:ignore */
Expand Down Expand Up @@ -62,31 +54,3 @@
white-space: pre-wrap;
}
}

.codeLine {
display: table-row;
counter-increment: line-count;
}

.codeLineNumber {
display: table-cell;
text-align: right;
width: 1%;
position: sticky;
left: 0;
padding: 0 var(--ifm-pre-padding);
background: var(--ifm-pre-background);
}

.codeLineNumber::before {
content: counter(line-count);
opacity: 0.4;
}

:global(.docusaurus-highlight-code-line) .codeLineNumber::before {
opacity: 0.8;
}

.codeLineContent {
padding-right: var(--ifm-pre-padding);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import type {Props} from '@theme/CodeBlock/Line';
import styles from './styles.module.css';

export default function CodeBlockLine({
line,
highlight,
showLineNumbers,
getLineProps,
getTokenProps,
}: Props): JSX.Element {
if (line.length === 1 && line[0]!.content === '\n') {
line[0]!.content = '';
}

const lineProps = getLineProps({
line,
...(showLineNumbers && {className: styles.codeLine}),
});

if (highlight) {
lineProps.className += ' docusaurus-highlight-code-line';
}

const lineTokens = line.map((token, key) => (
<span key={key} {...getTokenProps({token, key})} />
));

return (
<span {...lineProps}>
{showLineNumbers ? (
<>
<span className={styles.codeLineNumber} />
<span className={styles.codeLineContent}>{lineTokens}</span>
</>
) : (
<>
{lineTokens}
<br />
</>
)}
</span>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

.codeLine {
display: table-row;
counter-increment: line-count;
}

.codeLineNumber {
display: table-cell;
text-align: right;
width: 1%;
position: sticky;
left: 0;
padding: 0 var(--ifm-pre-padding);
background: var(--ifm-pre-background);
}

.codeLineNumber::before {
content: counter(line-count);
opacity: 0.4;
}

:global(.docusaurus-highlight-code-line) .codeLineNumber::before {
opacity: 0.8;
}

.codeLineContent {
padding-right: var(--ifm-pre-padding);
}
Loading

0 comments on commit 5273a53

Please sign in to comment.