@primer/doctocat-nextjs 0.2.0-rc.eeac884 → 0.3.0-rc.0fe5c68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/components/content/caption/Caption.tsx +1 -1
- package/components/context/color-modes/context.ts +3 -1
- package/components/index.ts +2 -0
- package/components/layout/article/AccessibilityLabel.module.css +21 -0
- package/components/layout/article/AccessibilityLabel.tsx +19 -0
- package/components/layout/article/Article.module.css +22 -1
- package/components/layout/article/Article.tsx +43 -4
- package/components/layout/article/ReadinessLabel.module.css +21 -0
- package/components/layout/article/ReadinessLabel.tsx +15 -0
- package/components/layout/article/SourceLink.module.css +7 -0
- package/components/layout/article/SourceLink.tsx +64 -0
- package/components/layout/code-block/CodeBlock.tsx +18 -0
- package/components/layout/code-block/ReactCodeBlock.module.css +68 -0
- package/components/layout/code-block/ReactCodeBlock.tsx +101 -0
- package/components/layout/code-block/syntax-highlighting-themes.ts +55 -0
- package/components/layout/header/Header.module.css +1 -0
- package/components/layout/header/Header.tsx +13 -15
- package/components/layout/index-cards/IndexCards.tsx +7 -1
- package/components/layout/prop-values/PropValues.tsx +54 -0
- package/components/layout/root-layout/Theme.tsx +135 -112
- package/components/layout/underline-nav/UnderlineNav.tsx +1 -1
- package/css/prose.module.css +88 -48
- package/package.json +12 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @primer/doctocat-nextjs
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#22](https://github.com/primer/doctocat-nextjs/pull/22) [`2b57bc4`](https://github.com/primer/doctocat-nextjs/commit/2b57bc456efc03c99255ca90098ca3e035da1206) Thanks [@rezrah](https://github.com/rezrah)! - Add live code editing and previews for `jsx` code blocks in Markdown. All other code blocks will continue to render as normal.
|
|
8
|
+
|
|
9
|
+
E.g.
|
|
10
|
+
|
|
11
|
+
<pre>
|
|
12
|
+
```jsx
|
|
13
|
+
<p>Your React code here</p>
|
|
14
|
+
```
|
|
15
|
+
</pre>
|
|
16
|
+
|
|
3
17
|
## 0.2.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
|
@@ -2,5 +2,5 @@ import React, {PropsWithChildren} from 'react'
|
|
|
2
2
|
import {Text} from '@primer/react'
|
|
3
3
|
|
|
4
4
|
export function Caption(props: PropsWithChildren) {
|
|
5
|
-
return <Text as="
|
|
5
|
+
return <Text as="span" {...props} sx={{mt: 2, mb: 3, fontSize: 1, color: 'var(--brand-color-text-default)'}} />
|
|
6
6
|
}
|
package/components/index.ts
CHANGED
|
@@ -7,3 +7,5 @@ export * from './library'
|
|
|
7
7
|
export {TableOfContents} from './layout/table-of-contents/TableOfContents'
|
|
8
8
|
export {Article} from './layout/article/Article'
|
|
9
9
|
export {HeadingLink} from './layout/heading-link/HeadingLinks'
|
|
10
|
+
export {CodeBlock} from './layout/code-block/CodeBlock'
|
|
11
|
+
export {PropTableValues} from './layout/prop-values/PropValues'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.reviewedLabel {
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
gap: var(--base-size-8);
|
|
5
|
+
background-color: var(--base-color-scale-purple-0);
|
|
6
|
+
border-color: transparent;
|
|
7
|
+
color: var(--brand-color-text-default);
|
|
8
|
+
font-weight: var(--brand-text-weight-300);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
[data-color-mode='dark'] .reviewedLabel {
|
|
12
|
+
background-color: var(--base-color-scale-purple-8);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.icon {
|
|
16
|
+
fill: var(--base-color-scale-purple-5);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
[data-color-mode='dark'] .icon {
|
|
20
|
+
fill: var(--base-color-scale-purple-1);
|
|
21
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import {Label} from '@primer/react'
|
|
3
|
+
import {AccessibilityInsetIcon} from '@primer/octicons-react'
|
|
4
|
+
import React from 'react'
|
|
5
|
+
|
|
6
|
+
import styles from './AccessibilityLabel.module.css'
|
|
7
|
+
|
|
8
|
+
type AccessibilityLabelProps = {
|
|
9
|
+
short?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function AccessibilityLabel({short}: AccessibilityLabelProps) {
|
|
13
|
+
return (
|
|
14
|
+
<Label size="large" className={styles.reviewedLabel}>
|
|
15
|
+
<AccessibilityInsetIcon className={styles.icon} />
|
|
16
|
+
{short ? 'Reviewed' : 'Reviewed for accessibility'}
|
|
17
|
+
</Label>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
@@ -3,6 +3,27 @@
|
|
|
3
3
|
}
|
|
4
4
|
|
|
5
5
|
.Article--withToc {
|
|
6
|
-
grid-template-columns: 1fr 200px;
|
|
7
6
|
gap: var(--base-size-48);
|
|
8
7
|
}
|
|
8
|
+
|
|
9
|
+
.main {
|
|
10
|
+
order: 1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.aside {
|
|
14
|
+
order: 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@media screen and (min-width: 768px) {
|
|
18
|
+
.main {
|
|
19
|
+
order: 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.aside {
|
|
23
|
+
order: 1;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.Article--withToc {
|
|
27
|
+
grid-template-columns: 1fr 200px;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
'use client'
|
|
1
2
|
import React, {PropsWithChildren} from 'react'
|
|
2
3
|
import {TableOfContents} from '../table-of-contents/TableOfContents'
|
|
3
4
|
import styles from './Article.module.css'
|
|
@@ -5,20 +6,58 @@ import bodyStyles from '../../../css/prose.module.css'
|
|
|
5
6
|
import {Heading as HeadingType} from 'nextra'
|
|
6
7
|
|
|
7
8
|
import clsx from 'clsx'
|
|
9
|
+
import {AccessibilityLabel} from './AccessibilityLabel'
|
|
10
|
+
import {Box, Stack} from '@primer/react-brand'
|
|
11
|
+
import {ReadinessLabel} from './ReadinessLabel'
|
|
12
|
+
import {SourceLink} from './SourceLink'
|
|
8
13
|
|
|
9
14
|
type Props = {
|
|
10
15
|
toc: HeadingType[]
|
|
11
16
|
metadata: {
|
|
12
17
|
layout?: string
|
|
18
|
+
[key: string]: unknown
|
|
19
|
+
figma?: string
|
|
20
|
+
source?: string
|
|
21
|
+
storybook?: string
|
|
13
22
|
}
|
|
14
23
|
}
|
|
15
24
|
|
|
16
25
|
export function Article({children, toc, metadata}: PropsWithChildren<Props>) {
|
|
17
26
|
const hasToc = toc.length > 0
|
|
27
|
+
|
|
28
|
+
const hasMetadata = Boolean(
|
|
29
|
+
metadata.ready || metadata.a11yReviewed || metadata.source || metadata.figma || metadata.storybook,
|
|
30
|
+
)
|
|
31
|
+
|
|
18
32
|
return (
|
|
19
|
-
|
|
20
|
-
<div className={clsx(
|
|
21
|
-
|
|
22
|
-
|
|
33
|
+
<>
|
|
34
|
+
<div className={clsx(styles.Article, hasToc && styles['Article--withToc'])}>
|
|
35
|
+
<div className={styles.main}>
|
|
36
|
+
{hasMetadata ? (
|
|
37
|
+
<Box marginBlockEnd={48}>
|
|
38
|
+
<Stack padding="none" direction="horizontal" justifyContent="space-between">
|
|
39
|
+
{metadata.ready || metadata.a11yReviewed ? (
|
|
40
|
+
<Stack direction="horizontal" gap={8} padding="none">
|
|
41
|
+
{metadata.ready === true && <ReadinessLabel />}
|
|
42
|
+
{typeof metadata.a11yReviewed === 'boolean' && metadata.a11yReviewed && <AccessibilityLabel />}
|
|
43
|
+
</Stack>
|
|
44
|
+
) : null}
|
|
45
|
+
<Stack direction="horizontal" gap={16} padding="none">
|
|
46
|
+
{metadata.source ? <SourceLink type="github" href={metadata.source} /> : null}
|
|
47
|
+
{metadata.figma ? <SourceLink type="figma" href={metadata.figma} /> : null}
|
|
48
|
+
{metadata.storybook ? <SourceLink type="storybook" href={metadata.storybook} /> : null}
|
|
49
|
+
</Stack>
|
|
50
|
+
</Stack>
|
|
51
|
+
</Box>
|
|
52
|
+
) : null}
|
|
53
|
+
<div className={clsx(metadata.layout !== 'custom' && bodyStyles.Prose)}>{children}</div>
|
|
54
|
+
</div>
|
|
55
|
+
{hasToc && (
|
|
56
|
+
<div className={styles.aside}>
|
|
57
|
+
<TableOfContents headings={toc} />
|
|
58
|
+
</div>
|
|
59
|
+
)}
|
|
60
|
+
</div>
|
|
61
|
+
</>
|
|
23
62
|
)
|
|
24
63
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.label {
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
gap: var(--base-size-8);
|
|
5
|
+
background-color: var(--base-color-scale-green-0);
|
|
6
|
+
border-color: transparent;
|
|
7
|
+
color: var(--brand-color-text-default);
|
|
8
|
+
font-weight: var(--brand-text-weight-300);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
[data-color-mode='dark'] .label {
|
|
12
|
+
background-color: var(--base-color-scale-green-8);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.icon {
|
|
16
|
+
fill: var(--base-color-scale-green-5);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
[data-color-mode='dark'] .icon {
|
|
20
|
+
fill: var(--base-color-scale-green-1);
|
|
21
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import {Label} from '@primer/react'
|
|
4
|
+
import {CheckIcon} from '@primer/octicons-react'
|
|
5
|
+
|
|
6
|
+
import styles from './ReadinessLabel.module.css'
|
|
7
|
+
|
|
8
|
+
export function ReadinessLabel() {
|
|
9
|
+
return (
|
|
10
|
+
<Label size="large" className={styles.label}>
|
|
11
|
+
<CheckIcon className={styles.icon} />
|
|
12
|
+
Ready to use
|
|
13
|
+
</Label>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {InlineLink, Stack, Text} from '@primer/react-brand'
|
|
2
|
+
import {BookIcon, MarkGithubIcon} from '@primer/octicons-react'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
|
|
5
|
+
import styles from './SourceLink.module.css'
|
|
6
|
+
|
|
7
|
+
type SourceLinkProps = {
|
|
8
|
+
type: 'github' | 'storybook' | 'figma'
|
|
9
|
+
href: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function SourceLink({href, type}: SourceLinkProps) {
|
|
13
|
+
return (
|
|
14
|
+
<div className="custom-component">
|
|
15
|
+
<Stack padding="none" direction="horizontal" alignItems="center" gap={4}>
|
|
16
|
+
{type === 'github' ? <MarkGithubIcon className={styles.icon} /> : null}
|
|
17
|
+
{type === 'storybook' ? <BookIcon className={styles.icon} /> : null}
|
|
18
|
+
{type === 'figma' ? <FigmaLogo className={styles.icon} /> : null}
|
|
19
|
+
|
|
20
|
+
{type === 'github' ? (
|
|
21
|
+
<Text size="100">
|
|
22
|
+
<InlineLink href={href} className={styles.link} target="_blank">
|
|
23
|
+
GitHub
|
|
24
|
+
</InlineLink>
|
|
25
|
+
</Text>
|
|
26
|
+
) : null}
|
|
27
|
+
{type === 'storybook' ? (
|
|
28
|
+
<Text size="100">
|
|
29
|
+
<InlineLink href={href} className={styles.link} target="_blank">
|
|
30
|
+
Storybook
|
|
31
|
+
</InlineLink>
|
|
32
|
+
</Text>
|
|
33
|
+
) : null}
|
|
34
|
+
{type === 'figma' ? (
|
|
35
|
+
<Text size="100">
|
|
36
|
+
<InlineLink href={href} className={styles.link} target="_blank">
|
|
37
|
+
Figma
|
|
38
|
+
</InlineLink>
|
|
39
|
+
</Text>
|
|
40
|
+
) : null}
|
|
41
|
+
</Stack>
|
|
42
|
+
</div>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function FigmaLogo({className}: {className?: string}) {
|
|
47
|
+
return (
|
|
48
|
+
<svg
|
|
49
|
+
className={className}
|
|
50
|
+
viewBox="0 0 16 16"
|
|
51
|
+
aria-hidden="true"
|
|
52
|
+
width={16}
|
|
53
|
+
height={16}
|
|
54
|
+
fill="currentColor"
|
|
55
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
56
|
+
>
|
|
57
|
+
<path
|
|
58
|
+
fillRule="evenodd"
|
|
59
|
+
clipRule="evenodd"
|
|
60
|
+
d="M5.417 0A3.167 3.167 0 0 0 3.37 5.583 3.16 3.16 0 0 0 2.25 8a3.16 3.16 0 0 0 1.12 2.417 3.167 3.167 0 1 0 5.213 2.417V10.687a3.165 3.165 0 0 0 3.87-4.964A3.167 3.167 0 0 0 10.582 0H5.417Zm4.727 6.333h.21a1.665 1.665 0 1 1-.21 0Zm.25-1.5h.19a1.667 1.667 0 1 0 0-3.333H5.416a1.667 1.667 0 1 0 0 3.333h4.687a3.226 3.226 0 0 1 .29 0ZM3.75 8c0-.92.746-1.667 1.667-1.667h1.666v3.334H5.417C4.497 9.667 3.75 8.92 3.75 8Zm1.667 3.167a1.667 1.667 0 1 0 1.666 1.667v-1.667H5.417Z"
|
|
61
|
+
/>
|
|
62
|
+
</svg>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React, {PropsWithChildren} from 'react'
|
|
3
|
+
|
|
4
|
+
import {ReactCodeBlock} from './ReactCodeBlock'
|
|
5
|
+
import {Pre} from 'nextra/components'
|
|
6
|
+
|
|
7
|
+
type CodeBlockProps = {
|
|
8
|
+
'data-language': string
|
|
9
|
+
jsxScope: Record<string, unknown>
|
|
10
|
+
} & PropsWithChildren<HTMLElement>
|
|
11
|
+
|
|
12
|
+
export function CodeBlock(props: CodeBlockProps) {
|
|
13
|
+
if (['tsx', 'jsx'].includes(props['data-language'])) {
|
|
14
|
+
return <ReactCodeBlock {...props} />
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return <Pre>{props.children}</Pre>
|
|
18
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
.CodeBlock {
|
|
2
|
+
margin-block: var(--base-size-24);
|
|
3
|
+
background-color: var(--brand-color-canvas-default);
|
|
4
|
+
border: var(--brand-borderWidth-thin) solid var(--brand-color-border-default);
|
|
5
|
+
border-radius: var(--brand-borderRadius-large);
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
overflow: hidden;
|
|
9
|
+
width: 100%;
|
|
10
|
+
position: relative;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.Preview {
|
|
14
|
+
display: flex;
|
|
15
|
+
width: 100%;
|
|
16
|
+
padding: var(--base-size-16);
|
|
17
|
+
padding-block: var(--base-size-64);
|
|
18
|
+
min-height: 240px;
|
|
19
|
+
flex: 1 1;
|
|
20
|
+
align-items: center;
|
|
21
|
+
justify-content: center;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.Editor {
|
|
25
|
+
font-size: var(--brand-text-size-100);
|
|
26
|
+
line-height: var(--brand-text-lineHeight-100);
|
|
27
|
+
font-family: var(--brand-fontStack-monospace);
|
|
28
|
+
background-color: var(--brand-color-canvas-subtle);
|
|
29
|
+
overflow: auto;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.Editor pre {
|
|
33
|
+
font-size: inherit;
|
|
34
|
+
border-radius: 0;
|
|
35
|
+
padding: var(--base-size-16) !important;
|
|
36
|
+
background-color: var(--brand-color-canvas-subtle) !important;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.Toolbar {
|
|
40
|
+
display: flex;
|
|
41
|
+
gap: var(--base-size-8);
|
|
42
|
+
padding-inline: var(--base-size-16);
|
|
43
|
+
padding-block-start: var(--base-size-16);
|
|
44
|
+
border-top: var(--brand-borderWidth-thin) solid var(--brand-color-border-default);
|
|
45
|
+
background-color: var(--brand-color-canvas-subtle);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.Error pre {
|
|
49
|
+
margin-block: 0 !important;
|
|
50
|
+
border-radius: 0;
|
|
51
|
+
font-size: var(--brand-text-size-100);
|
|
52
|
+
line-height: var(--brand-text-lineHeight-100);
|
|
53
|
+
font-family: var(--brand-fontStack-monospace);
|
|
54
|
+
color: var(--brand-color-error-fg);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.colorModeMenu {
|
|
58
|
+
display: inline-block;
|
|
59
|
+
position: absolute;
|
|
60
|
+
top: var(--base-size-16);
|
|
61
|
+
right: var(--base-size-2);
|
|
62
|
+
z-index: 19;
|
|
63
|
+
width: 200px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.colorModeButtonActive {
|
|
67
|
+
background-color: var(--brand-color-canvas-subtle);
|
|
68
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React, {PropsWithChildren, useCallback, useState} from 'react'
|
|
3
|
+
import clsx from 'clsx'
|
|
4
|
+
import {LiveProvider, LiveEditor, LiveError, LivePreview} from 'react-live'
|
|
5
|
+
import {useColorMode} from '../../context/color-modes/useColorMode'
|
|
6
|
+
import styles from './ReactCodeBlock.module.css'
|
|
7
|
+
import {ActionBar, Button, ThemeProvider} from '@primer/react'
|
|
8
|
+
import {CopyIcon, MoonIcon, SunIcon, UndoIcon} from '@primer/octicons-react'
|
|
9
|
+
import {colorModes} from '../../context/color-modes/context'
|
|
10
|
+
|
|
11
|
+
import {lightTheme, darkTheme} from './syntax-highlighting-themes'
|
|
12
|
+
|
|
13
|
+
type ReactCodeBlockProps = {
|
|
14
|
+
'data-language': string
|
|
15
|
+
'data-filename'?: string
|
|
16
|
+
jsxScope: Record<string, unknown>
|
|
17
|
+
} & PropsWithChildren<HTMLElement>
|
|
18
|
+
|
|
19
|
+
export function ReactCodeBlock(props: ReactCodeBlockProps) {
|
|
20
|
+
const {colorMode, setColorMode} = useColorMode()
|
|
21
|
+
const initialCode = getCodeFromChildren(props.children)
|
|
22
|
+
const [code, setCode] = useState(initialCode)
|
|
23
|
+
const shouldShowPreview = ['tsx', 'jsx'].includes(props['data-language'])
|
|
24
|
+
|
|
25
|
+
const handleReset = useCallback(() => {
|
|
26
|
+
setCode(initialCode)
|
|
27
|
+
}, [initialCode, setCode])
|
|
28
|
+
|
|
29
|
+
const handleCopy = useCallback(() => {
|
|
30
|
+
navigator.clipboard.writeText(code)
|
|
31
|
+
}, [code])
|
|
32
|
+
|
|
33
|
+
const noInline = props['data-filename'] === 'noinline' || false
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<>
|
|
37
|
+
<LiveProvider code={code} scope={props.jsxScope} noInline={noInline}>
|
|
38
|
+
<div className={clsx(styles.CodeBlock, 'custom-component')}>
|
|
39
|
+
{shouldShowPreview && (
|
|
40
|
+
<div>
|
|
41
|
+
<div className={styles.colorModeMenu}>
|
|
42
|
+
<ActionBar aria-label="Toolbar">
|
|
43
|
+
{colorModes.map((mode, index) => {
|
|
44
|
+
const Icon = mode === 'light' ? SunIcon : MoonIcon
|
|
45
|
+
return (
|
|
46
|
+
<ActionBar.IconButton
|
|
47
|
+
className={clsx(styles.colorModeButton, colorMode === mode && styles.colorModeButtonActive)}
|
|
48
|
+
key={`color-mode-${mode}-${index}`}
|
|
49
|
+
icon={Icon}
|
|
50
|
+
aria-label={mode}
|
|
51
|
+
onClick={() => setColorMode(mode)}
|
|
52
|
+
/>
|
|
53
|
+
)
|
|
54
|
+
})}
|
|
55
|
+
</ActionBar>
|
|
56
|
+
</div>
|
|
57
|
+
<ThemeProvider colorMode={colorMode}>
|
|
58
|
+
<div className="custom-component">
|
|
59
|
+
<LivePreview className={styles.Preview} />
|
|
60
|
+
</div>
|
|
61
|
+
</ThemeProvider>
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
64
|
+
<div className={styles.Toolbar}>
|
|
65
|
+
<Button size="small" leadingVisual={CopyIcon} onClick={handleCopy}>
|
|
66
|
+
Copy
|
|
67
|
+
</Button>
|
|
68
|
+
<Button size="small" leadingVisual={UndoIcon} onClick={handleReset}>
|
|
69
|
+
Reset
|
|
70
|
+
</Button>
|
|
71
|
+
</div>
|
|
72
|
+
<div className={styles.Editor}>
|
|
73
|
+
<LiveEditor theme={colorMode === 'light' ? lightTheme : darkTheme} onChange={setCode} />
|
|
74
|
+
</div>
|
|
75
|
+
{shouldShowPreview && (
|
|
76
|
+
<div className={styles.Error}>
|
|
77
|
+
<LiveError />
|
|
78
|
+
</div>
|
|
79
|
+
)}
|
|
80
|
+
</div>
|
|
81
|
+
</LiveProvider>
|
|
82
|
+
</>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Helper function to turn Nextra <code> children into plain text
|
|
88
|
+
*/
|
|
89
|
+
function getCodeFromChildren(children: React.ReactNode) {
|
|
90
|
+
if (!React.isValidElement(children) || !children.props?.children) return ''
|
|
91
|
+
|
|
92
|
+
// Flattens the nested spans and combine their text content if it's a react child
|
|
93
|
+
const extractText = (node: React.ReactNode): string => {
|
|
94
|
+
if (typeof node === 'string') return node
|
|
95
|
+
if (Array.isArray(node)) return node.map(extractText).join('')
|
|
96
|
+
if (React.isValidElement(node) && node.props?.children) return extractText(node.props.children)
|
|
97
|
+
return ''
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return extractText(children)
|
|
101
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export const lightTheme = {
|
|
2
|
+
plain: {
|
|
3
|
+
backgroundColor: '#ffffff',
|
|
4
|
+
color: '#24292e',
|
|
5
|
+
},
|
|
6
|
+
styles: [
|
|
7
|
+
{
|
|
8
|
+
types: ['comment'],
|
|
9
|
+
style: {
|
|
10
|
+
color: '#6a737d',
|
|
11
|
+
fontStyle: 'italic' as const,
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
types: ['string', 'number', 'builtin', 'variable'],
|
|
16
|
+
style: {
|
|
17
|
+
color: '#032f62',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
types: ['class-name', 'function', 'tag', 'attr-name'],
|
|
22
|
+
style: {
|
|
23
|
+
color: '#005CC5',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const darkTheme = {
|
|
30
|
+
plain: {
|
|
31
|
+
backgroundColor: '#0d1117',
|
|
32
|
+
color: '#c9d1d9',
|
|
33
|
+
},
|
|
34
|
+
styles: [
|
|
35
|
+
{
|
|
36
|
+
types: ['comment'],
|
|
37
|
+
style: {
|
|
38
|
+
color: '#8b949e',
|
|
39
|
+
fontStyle: 'italic' as const,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
types: ['string', 'number', 'builtin', 'variable'],
|
|
44
|
+
style: {
|
|
45
|
+
color: '#a5d6ff',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
types: ['class-name', 'function', 'tag', 'attr-name'],
|
|
50
|
+
style: {
|
|
51
|
+
color: '#d2a8ff',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
padding: var(--base-size-20) var(--base-size-12) var(--base-size-20) var(--base-size-16);
|
|
7
7
|
border-bottom: var(--brand-borderWidth-thin) solid var(--brand-color-border-default);
|
|
8
8
|
background: var(--brand-color-canvas-default);
|
|
9
|
+
z-index: 20;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
@media (max-width: 768px) {
|
|
@@ -3,7 +3,7 @@ import {MarkGithubIcon, MoonIcon, SearchIcon, SunIcon, ThreeBarsIcon, XIcon} fro
|
|
|
3
3
|
import {Box, FormControl, IconButton, TextInput} from '@primer/react'
|
|
4
4
|
import {Heading, Stack, Text} from '@primer/react-brand'
|
|
5
5
|
import {clsx} from 'clsx'
|
|
6
|
-
import {MdxFile,
|
|
6
|
+
import {MdxFile, PageMapItem} from 'nextra'
|
|
7
7
|
import {debounce} from 'lodash'
|
|
8
8
|
|
|
9
9
|
import Link from 'next/link'
|
|
@@ -11,11 +11,11 @@ import styles from './Header.module.css'
|
|
|
11
11
|
import {NavDrawer} from '../nav-drawer/NavDrawer'
|
|
12
12
|
import {useNavDrawerState} from '../nav-drawer/useNavDrawerState'
|
|
13
13
|
import {useColorMode} from '../../context/color-modes/useColorMode'
|
|
14
|
-
import {hasChildren} from '../../../helpers/hasChildren'
|
|
15
14
|
import {DocsItem} from '../../../types'
|
|
16
15
|
|
|
17
16
|
type HeaderProps = {
|
|
18
17
|
pageMap: PageMapItem[]
|
|
18
|
+
flatDocsDirectories: DocsItem[]
|
|
19
19
|
siteTitle: string
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -25,7 +25,7 @@ type SearchResults = {
|
|
|
25
25
|
url: string
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export function Header({pageMap, siteTitle}: HeaderProps) {
|
|
28
|
+
export function Header({pageMap, siteTitle, flatDocsDirectories}: HeaderProps) {
|
|
29
29
|
const {colorMode, setColorMode} = useColorMode()
|
|
30
30
|
const inputRef = useRef<HTMLInputElement | null>(null)
|
|
31
31
|
const searchResultsRef = useRef<HTMLElement | null>(null)
|
|
@@ -74,32 +74,30 @@ export function Header({pageMap, siteTitle}: HeaderProps) {
|
|
|
74
74
|
}, [colorMode])
|
|
75
75
|
|
|
76
76
|
const setSearchResultsDebounced = debounce((data: SearchResults[] | undefined) => setSearchResults(data), 1000)
|
|
77
|
-
|
|
78
77
|
const searchData = useMemo(
|
|
79
78
|
() =>
|
|
80
|
-
|
|
79
|
+
flatDocsDirectories
|
|
81
80
|
.map(item => {
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
if ((item as DocsItem).type === 'doc') {
|
|
86
|
-
return item
|
|
87
|
-
}
|
|
81
|
+
if (item.route === '/') return null // remove homepage
|
|
82
|
+
return item
|
|
88
83
|
})
|
|
89
|
-
.flat()
|
|
90
84
|
.filter(Boolean)
|
|
91
85
|
.map(item => {
|
|
92
86
|
const {frontMatter, route} = item as MdxFile
|
|
93
|
-
|
|
94
87
|
if (!frontMatter) return null
|
|
95
88
|
const result = {
|
|
96
|
-
title:
|
|
89
|
+
title:
|
|
90
|
+
frontMatter['show-tabs'] && frontMatter['tab-label']
|
|
91
|
+
? `${frontMatter.title} | ${frontMatter['tab-label']}`
|
|
92
|
+
: frontMatter.title
|
|
93
|
+
? frontMatter.title
|
|
94
|
+
: '',
|
|
97
95
|
description: frontMatter.description ? frontMatter.description : '',
|
|
98
96
|
url: route,
|
|
99
97
|
}
|
|
100
98
|
return result
|
|
101
99
|
}),
|
|
102
|
-
[
|
|
100
|
+
[flatDocsDirectories],
|
|
103
101
|
)
|
|
104
102
|
|
|
105
103
|
const handleChange = () => {
|
|
@@ -11,7 +11,13 @@ type IndexCardsProps = {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export function IndexCards({route, folderData}: IndexCardsProps) {
|
|
14
|
-
|
|
14
|
+
// We don't want to show children of these pages. E.g. tabbed pages
|
|
15
|
+
const onlyDirectChildren = folderData.filter(
|
|
16
|
+
item => item.route.includes(`${route}/`) && item.route.split('/').length === 3,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
const filteredData = onlyDirectChildren.filter(item => item.type === 'doc' && item.route.includes(`${route}/`))
|
|
20
|
+
|
|
15
21
|
return (
|
|
16
22
|
<Stack direction="vertical" padding="none" gap="spacious">
|
|
17
23
|
{filteredData.map((item: DocsItem) => {
|