Skip to content

Commit 97fd86a

Browse files
committed
WIP: use monaco editor for code samples
1 parent 11931ca commit 97fd86a

20 files changed

+2693
-66
lines changed

apps/virtuoso.dev/docs/guides/getting-started.md

+3-18
Original file line numberDiff line numberDiff line change
@@ -54,27 +54,12 @@ Then follow the installation section in the [Virtuoso Message List](/virtuoso-me
5454

5555
Add the Component to your application.
5656

57-
```jsx
58-
import * as React from 'react'
59-
import * as ReactDOM from 'react-dom'
57+
```tsx live
6058
import { Virtuoso } from 'react-virtuoso'
6159

62-
const App = () => (
63-
<Virtuoso
64-
style={{ height: '400px' }}
65-
totalCount={200}
66-
itemContent={index => <div>Item {index}</div>}
67-
/>
68-
)
69-
70-
```
71-
72-
This is how it looks live:
73-
74-
```tsx live
75-
function App() {
60+
export default function App() {
7661
return <Virtuoso
77-
style={{ height: "400px" }}
62+
style={{ height: "100%" }}
7863
totalCount={200}
7964
itemContent={(index) => <div>Item {index}</div>}
8065
/>

apps/virtuoso.dev/docusaurus.config.ts

+35-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { themes as prismThemes } from "prism-react-renderer";
22
import type { Config } from "@docusaurus/types";
33
import dotenv from "dotenv";
44
import type * as Preset from "@docusaurus/preset-classic";
5+
import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin'
56

67
if (process.env.NODE_ENV === "development") {
78
dotenv.config({ path: ".env.local" });
@@ -36,9 +37,41 @@ const config: Config = {
3637
locales: ["en"],
3738
},
3839

39-
themes: ["@docusaurus/theme-live-codeblock"],
40+
// themes: ["@docusaurus/theme-live-codeblock"],
4041

4142
plugins: [
43+
() => {
44+
return {
45+
name: "monaco-webpack-plugin",
46+
configureWebpack() {
47+
return {
48+
plugins: [
49+
new MonacoWebpackPlugin({
50+
// available options are documented at https://github.com/microsoft/monaco-editor/blob/main/webpack-plugin/README.md#options
51+
languages: ['json', 'typescript'],
52+
})
53+
],
54+
};
55+
},
56+
};
57+
},
58+
// () => {
59+
// return {
60+
// name: "radix-ui-css",
61+
// configureWebpack() {
62+
// return {
63+
// module: {
64+
// rules: [
65+
// {
66+
// test: /@radix-ui\/themes\/styles.css$/i,
67+
// use: ["style-loader", "css-loader"],
68+
// },
69+
// ],
70+
// }
71+
// };
72+
// },
73+
// };
74+
// },
4275
// @ts-expect-error Not sure why docusaurus does not like the return value, but it works
4376
() => {
4477
return {
@@ -169,7 +202,7 @@ const config: Config = {
169202
anonymizeIP: true,
170203
},
171204
theme: {
172-
customCss: "./src/css/custom.css",
205+
customCss: ["./src/css/custom.css", "../../node_modules/@radix-ui/themes/styles.css"],
173206
},
174207
} satisfies Preset.Options,
175208
],

apps/virtuoso.dev/package.json

+8
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,24 @@
3030
"@mui/material": "^5.15.14",
3131
"@ngneat/falso": "^7.2.0",
3232
"@paddle/paddle-js": "^1.0.3",
33+
"@radix-ui/themes": "^3.2.0",
3334
"@tanstack/react-table": "^8.15.0",
3435
"@virtuoso.dev/message-list": "^1.9.6",
3536
"clsx": "^2.0.0",
37+
"css-loader": "^7.1.2",
3638
"docusaurus-plugin-typedoc": "^1.2.3",
3739
"dotenv": "^16.4.5",
40+
"esbuild-wasm": "^0.25.0",
41+
"monaco-editor": "^0.52.2",
42+
"monaco-editor-webpack-plugin": "^7.1.0",
3843
"prism-react-renderer": "^2.3.0",
44+
"raw-loader": "^4.0.2",
3945
"react": "^18.0.0",
4046
"react-dom": "^18.0.0",
47+
"react-monaco-editor": "^0.58.0",
4148
"react-virtuoso": "*",
4249
"search-insights": "^2.13.0",
50+
"style-loader": "^4.0.0",
4351
"typedoc": "^0.27.6",
4452
"typedoc-plugin-markdown": "4.4.1",
4553
"typedoc-plugin-no-inherit": "^1.5.0",

apps/virtuoso.dev/src/css/custom.css

+6
Original file line numberDiff line numberDiff line change
@@ -285,3 +285,9 @@ h2.newLabel::after,
285285
li.newLabel > div > a {
286286
color: var(--ifm-color-primary-lightest);
287287
}
288+
289+
.live-code-block-wrapper {
290+
border-radius: var(--radius-3);
291+
border: 1px solid var(--base-card-classic-border-color);
292+
padding: var(--space-3);
293+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React, {type ComponentProps, type ReactNode} from 'react';
2+
import clsx from 'clsx';
3+
import {ThemeClassNames, usePrismTheme} from '@docusaurus/theme-common';
4+
import {getPrismCssVariables} from '@docusaurus/theme-common/internal';
5+
import styles from './styles.module.css';
6+
7+
export default function CodeBlockContainer<T extends 'div' | 'pre'>({
8+
as: As,
9+
...props
10+
}: {as: T} & ComponentProps<T>): ReactNode {
11+
const prismTheme = usePrismTheme();
12+
const prismCssVariables = getPrismCssVariables(prismTheme);
13+
return (
14+
<As
15+
// Polymorphic components are hard to type, without `oneOf` generics
16+
{...(props as any)}
17+
style={prismCssVariables}
18+
className={clsx(
19+
props.className,
20+
styles.codeBlockContainer,
21+
ThemeClassNames.common.codeBlock,
22+
)}
23+
/>
24+
);
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.codeBlockContainer {
2+
background: var(--prism-background-color);
3+
color: var(--prism-color);
4+
margin-bottom: var(--ifm-leading);
5+
box-shadow: var(--ifm-global-shadow-lw);
6+
border-radius: var(--ifm-code-border-radius);
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React, {type ReactNode} from 'react';
2+
import clsx from 'clsx';
3+
import Container from '@theme/CodeBlock/Container';
4+
import type {Props} from '@theme/CodeBlock/Content/Element';
5+
6+
import styles from './styles.module.css';
7+
8+
// <pre> tags in markdown map to CodeBlocks. They may contain JSX children. When
9+
// the children is not a simple string, we just return a styled block without
10+
// actually highlighting.
11+
export default function CodeBlockJSX({children, className}: Props): ReactNode {
12+
return (
13+
<Container
14+
as="pre"
15+
tabIndex={0}
16+
className={clsx(styles.codeBlockStandalone, 'thin-scrollbar', className)}>
17+
<code className={styles.codeBlockLines}>{children}</code>
18+
</Container>
19+
);
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import React, {type ReactNode} from 'react';
2+
import clsx from 'clsx';
3+
import {useThemeConfig, usePrismTheme} from '@docusaurus/theme-common';
4+
import {
5+
parseCodeBlockTitle,
6+
parseLanguage,
7+
parseLines,
8+
containsLineNumbers,
9+
useCodeWordWrap,
10+
} from '@docusaurus/theme-common/internal';
11+
import {Highlight, type Language} from 'prism-react-renderer';
12+
import Line from '@theme/CodeBlock/Line';
13+
import CopyButton from '@theme/CodeBlock/CopyButton';
14+
import WordWrapButton from '@theme/CodeBlock/WordWrapButton';
15+
import Container from '@theme/CodeBlock/Container';
16+
import type {Props} from '@theme/CodeBlock/Content/String';
17+
18+
import styles from './styles.module.css';
19+
20+
// Prism languages are always lowercase
21+
// We want to fail-safe and allow both "php" and "PHP"
22+
// See https://github.com/facebook/docusaurus/issues/9012
23+
function normalizeLanguage(language: string | undefined): string | undefined {
24+
return language?.toLowerCase();
25+
}
26+
27+
export default function CodeBlockString({
28+
children,
29+
className: blockClassName = '',
30+
metastring,
31+
title: titleProp,
32+
showLineNumbers: showLineNumbersProp,
33+
language: languageProp,
34+
}: Props): ReactNode {
35+
const {
36+
prism: {defaultLanguage, magicComments},
37+
} = useThemeConfig();
38+
const language = normalizeLanguage(
39+
languageProp ?? parseLanguage(blockClassName) ?? defaultLanguage,
40+
);
41+
42+
const prismTheme = usePrismTheme();
43+
const wordWrap = useCodeWordWrap();
44+
45+
// We still parse the metastring in case we want to support more syntax in the
46+
// future. Note that MDX doesn't strip quotes when parsing metastring:
47+
// "title=\"xyz\"" => title: "\"xyz\""
48+
const title = parseCodeBlockTitle(metastring) || titleProp;
49+
50+
const {lineClassNames, code} = parseLines(children, {
51+
metastring,
52+
language,
53+
magicComments,
54+
});
55+
const showLineNumbers =
56+
showLineNumbersProp ?? containsLineNumbers(metastring);
57+
58+
return (
59+
<Container
60+
as="div"
61+
className={clsx(
62+
blockClassName,
63+
language &&
64+
!blockClassName.includes(`language-${language}`) &&
65+
`language-${language}`,
66+
)}>
67+
{title && <div className={styles.codeBlockTitle}>{title}</div>}
68+
<div className={styles.codeBlockContent}>
69+
<Highlight
70+
theme={prismTheme}
71+
code={code}
72+
language={(language ?? 'text') as Language}>
73+
{({className, style, tokens, getLineProps, getTokenProps}) => (
74+
<pre
75+
/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */
76+
tabIndex={0}
77+
ref={wordWrap.codeBlockRef}
78+
className={clsx(className, styles.codeBlock, 'thin-scrollbar')}
79+
style={style}>
80+
<code
81+
className={clsx(
82+
styles.codeBlockLines,
83+
showLineNumbers && styles.codeBlockLinesWithNumbering,
84+
)}>
85+
{tokens.map((line, i) => (
86+
<Line
87+
key={i}
88+
line={line}
89+
getLineProps={getLineProps}
90+
getTokenProps={getTokenProps}
91+
classNames={lineClassNames[i]}
92+
showLineNumbers={showLineNumbers}
93+
/>
94+
))}
95+
</code>
96+
</pre>
97+
)}
98+
</Highlight>
99+
<div className={styles.buttonGroup}>
100+
{(wordWrap.isEnabled || wordWrap.isCodeScrollable) && (
101+
<WordWrapButton
102+
className={styles.codeButton}
103+
onClick={() => wordWrap.toggle()}
104+
isEnabled={wordWrap.isEnabled}
105+
/>
106+
)}
107+
<CopyButton className={styles.codeButton} code={code} />
108+
</div>
109+
</div>
110+
</Container>
111+
);
112+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
.codeBlockContent {
2+
position: relative;
3+
/* rtl:ignore */
4+
direction: ltr;
5+
border-radius: inherit;
6+
}
7+
8+
.codeBlockTitle {
9+
border-bottom: 1px solid var(--ifm-color-emphasis-300);
10+
font-size: var(--ifm-code-font-size);
11+
font-weight: 500;
12+
padding: 0.75rem var(--ifm-pre-padding);
13+
border-top-left-radius: inherit;
14+
border-top-right-radius: inherit;
15+
}
16+
17+
.codeBlock {
18+
--ifm-pre-background: var(--prism-background-color);
19+
margin: 0;
20+
padding: 0;
21+
}
22+
23+
.codeBlockTitle + .codeBlockContent .codeBlock {
24+
border-top-left-radius: 0;
25+
border-top-right-radius: 0;
26+
}
27+
28+
.codeBlockStandalone {
29+
padding: 0;
30+
}
31+
32+
.codeBlockLines {
33+
font: inherit;
34+
/* rtl:ignore */
35+
float: left;
36+
min-width: 100%;
37+
padding: var(--ifm-pre-padding);
38+
}
39+
40+
.codeBlockLinesWithNumbering {
41+
display: table;
42+
padding: var(--ifm-pre-padding) 0;
43+
}
44+
45+
@media print {
46+
.codeBlockLines {
47+
white-space: pre-wrap;
48+
}
49+
}
50+
51+
.buttonGroup {
52+
display: flex;
53+
column-gap: 0.2rem;
54+
position: absolute;
55+
/* rtl:ignore */
56+
right: calc(var(--ifm-pre-padding) / 2);
57+
top: calc(var(--ifm-pre-padding) / 2);
58+
}
59+
60+
.buttonGroup button {
61+
display: flex;
62+
align-items: center;
63+
background: var(--prism-background-color);
64+
color: var(--prism-color);
65+
border: 1px solid var(--ifm-color-emphasis-300);
66+
border-radius: var(--ifm-global-radius);
67+
padding: 0.4rem;
68+
line-height: 0;
69+
transition: opacity var(--ifm-transition-fast) ease-in-out;
70+
opacity: 0;
71+
}
72+
73+
.buttonGroup button:focus-visible,
74+
.buttonGroup button:hover {
75+
opacity: 1 !important;
76+
}
77+
78+
:global(.theme-code-block:hover) .buttonGroup button {
79+
opacity: 0.4;
80+
}

0 commit comments

Comments
 (0)