@brillout/docpress 0.15.10 → 0.15.11
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/components/CodeSnippets/useSelectCodeLang.ts +60 -0
- package/components/CodeSnippets.css +78 -0
- package/components/CodeSnippets.tsx +50 -0
- package/components/Pre.css +51 -0
- package/components/Pre.tsx +72 -0
- package/components/index.ts +1 -0
- package/components/useMDXComponents.tsx +13 -0
- package/css/button.css +23 -0
- package/css/code.css +3 -21
- package/css/index.css +1 -0
- package/css/tooltip.css +10 -2
- package/dist/+config.js +1 -1
- package/dist/NavItemComponent.js +38 -46
- package/dist/components/CodeBlockTransformer.js +2 -3
- package/dist/components/CodeSnippets/useSelectCodeLang.d.ts +7 -0
- package/dist/components/CodeSnippets/useSelectCodeLang.js +50 -0
- package/dist/components/CodeSnippets.d.ts +11 -0
- package/dist/components/CodeSnippets.js +35 -0
- package/dist/components/Comment.js +1 -2
- package/dist/components/FileRemoved.js +4 -6
- package/dist/components/HorizontalLine.js +1 -2
- package/dist/components/ImportMeta.js +2 -3
- package/dist/components/Link.js +34 -50
- package/dist/components/Note.js +17 -29
- package/dist/components/P.js +1 -12
- package/dist/components/RepoLink.js +7 -9
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/determineNavItemsColumnLayout.js +48 -63
- package/dist/parseMarkdownMini.js +5 -17
- package/dist/parsePageSections.js +41 -82
- package/dist/rehypeMetaToProps.d.ts +19 -0
- package/dist/rehypeMetaToProps.js +62 -0
- package/dist/remarkDetype.d.ts +4 -0
- package/dist/remarkDetype.js +146 -0
- package/dist/renderer/usePageContext.js +6 -7
- package/dist/resolvePageContext.js +103 -110
- package/dist/utils/Emoji/Emoji.js +13 -21
- package/dist/utils/assert.js +14 -16
- package/dist/utils/cls.js +1 -1
- package/dist/utils/determineSectionUrlHash.js +5 -5
- package/dist/utils/filter.js +2 -2
- package/dist/utils/getGlobalObject.js +3 -3
- package/dist/vite.config.js +12 -7
- package/index.ts +15 -5
- package/package.json +5 -1
- package/rehypeMetaToProps.ts +69 -0
- package/remarkDetype.ts +172 -0
- package/resolvePageContext.ts +19 -15
- package/tsconfig.json +2 -1
- package/vite.config.ts +9 -4
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export { useSelectCodeLang }
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, useCallback } from 'react'
|
|
4
|
+
import { assertWarning } from '../../utils/assert'
|
|
5
|
+
|
|
6
|
+
const storageKey = 'docpress:code-lang'
|
|
7
|
+
const codeLangDefaultSsr = 'ts'
|
|
8
|
+
const codeLangDefaultClient = 'js'
|
|
9
|
+
|
|
10
|
+
function useSelectCodeLang() {
|
|
11
|
+
const [codeLangSelected, setCodeLangSelected] = useState(codeLangDefaultSsr)
|
|
12
|
+
const updateState = () => {
|
|
13
|
+
setCodeLangSelected(getCodeLangStorage())
|
|
14
|
+
}
|
|
15
|
+
const updateStateOnStorageEvent = (event: StorageEvent) => {
|
|
16
|
+
if (event.key === storageKey) updateState()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const getCodeLangStorage = () => {
|
|
20
|
+
try {
|
|
21
|
+
return window.localStorage.getItem(storageKey) ?? codeLangDefaultClient
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.error(error)
|
|
24
|
+
assertWarning(false, 'Error reading from localStorage')
|
|
25
|
+
return codeLangDefaultClient
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const selectCodeLang = useCallback((value: string) => {
|
|
30
|
+
try {
|
|
31
|
+
window.localStorage.setItem(storageKey, value)
|
|
32
|
+
setCodeLangSelected(value)
|
|
33
|
+
window.dispatchEvent(new CustomEvent('code-lang-storage'))
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error(error)
|
|
36
|
+
assertWarning(false, 'Error setting localStorage')
|
|
37
|
+
}
|
|
38
|
+
}, [])
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
// Initial load from localStorage
|
|
42
|
+
updateState()
|
|
43
|
+
// Update code lang in current tab
|
|
44
|
+
window.addEventListener('code-lang-storage', updateState)
|
|
45
|
+
// Update code lang if changed in another tab
|
|
46
|
+
window.addEventListener('storage', updateStateOnStorageEvent)
|
|
47
|
+
return () => {
|
|
48
|
+
window.removeEventListener('code-lang-storage', updateState)
|
|
49
|
+
window.removeEventListener('storage', updateStateOnStorageEvent)
|
|
50
|
+
}
|
|
51
|
+
}, [])
|
|
52
|
+
|
|
53
|
+
return [codeLangSelected, selectCodeLang] as const
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
declare global {
|
|
57
|
+
interface WindowEventMap {
|
|
58
|
+
'code-lang-storage': CustomEvent
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
.code-snippets {
|
|
2
|
+
position: relative;
|
|
3
|
+
|
|
4
|
+
&:hover {
|
|
5
|
+
.copy-button,
|
|
6
|
+
.code-lang-toggle {
|
|
7
|
+
opacity: 1;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* Hide language toggle for YAML */
|
|
12
|
+
&:has(code[data-language='yaml']) .code-lang-toggle {
|
|
13
|
+
display: none !important;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Language toggle styles */
|
|
17
|
+
.code-lang-toggle {
|
|
18
|
+
position: absolute !important;
|
|
19
|
+
top: 10px;
|
|
20
|
+
right: 42px;
|
|
21
|
+
z-index: 3;
|
|
22
|
+
|
|
23
|
+
/* Checkbox appearance reset */
|
|
24
|
+
appearance: none;
|
|
25
|
+
-webkit-appearance: none;
|
|
26
|
+
-moz-appearance: none;
|
|
27
|
+
|
|
28
|
+
margin: 0;
|
|
29
|
+
padding: 0 4px;
|
|
30
|
+
height: 25px;
|
|
31
|
+
display: flex;
|
|
32
|
+
background-color: #f7f7f7;
|
|
33
|
+
opacity: 0;
|
|
34
|
+
transition: opacity 0.5s ease-in-out, background-color 0.4s ease-in-out;
|
|
35
|
+
|
|
36
|
+
&:not(:hover) {
|
|
37
|
+
background-color: #eee;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Toggle Labels */
|
|
41
|
+
&::before,
|
|
42
|
+
&::after {
|
|
43
|
+
width: 24px;
|
|
44
|
+
display: flex;
|
|
45
|
+
justify-content: center;
|
|
46
|
+
align-items: center;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&::before {
|
|
50
|
+
content: 'JS';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
&::after {
|
|
54
|
+
content: 'TS';
|
|
55
|
+
border-left: none;
|
|
56
|
+
opacity: 0.3;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
&:checked {
|
|
60
|
+
&::before {
|
|
61
|
+
opacity: 0.3;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
&::after {
|
|
65
|
+
opacity: 1;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Code block visibility based on toggle */
|
|
71
|
+
&:has(.code-lang-toggle:checked) figure:first-of-type {
|
|
72
|
+
display: none;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
&:has(.code-lang-toggle:not(:checked)) figure:last-of-type {
|
|
76
|
+
display: none;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Public
|
|
2
|
+
export { TypescriptOnly }
|
|
3
|
+
|
|
4
|
+
// Internal
|
|
5
|
+
export { CodeSnippets }
|
|
6
|
+
|
|
7
|
+
import React, { useEffect, useRef } from 'react'
|
|
8
|
+
import { useSelectCodeLang } from './CodeSnippets/useSelectCodeLang'
|
|
9
|
+
import './CodeSnippets.css'
|
|
10
|
+
|
|
11
|
+
/** Only show if TypeScript is selected */
|
|
12
|
+
function TypescriptOnly({ children }: { children: React.ReactNode }) {
|
|
13
|
+
const [codeLangSelected] = useSelectCodeLang()
|
|
14
|
+
return <div style={{ display: codeLangSelected === 'ts' ? 'block' : 'none' }}>{children}</div>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function CodeSnippets({ children }: { children: React.ReactNode }) {
|
|
18
|
+
const [codeLangSelected, selectCodeLang] = useSelectCodeLang()
|
|
19
|
+
const prevPositionRef = useRef<null | { top: number; el: Element }>(null)
|
|
20
|
+
|
|
21
|
+
// Restores the scroll position of the toggle element after toggling languages.
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!prevPositionRef.current) return
|
|
24
|
+
const { top, el } = prevPositionRef.current
|
|
25
|
+
const delta = el.getBoundingClientRect().top - top
|
|
26
|
+
if (delta !== 0) {
|
|
27
|
+
window.scrollBy(0, delta)
|
|
28
|
+
}
|
|
29
|
+
prevPositionRef.current = null
|
|
30
|
+
}, [codeLangSelected])
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div className="code-snippets">
|
|
34
|
+
<input
|
|
35
|
+
type="checkbox"
|
|
36
|
+
name="code-lang-toggle"
|
|
37
|
+
className="code-lang-toggle raised"
|
|
38
|
+
checked={codeLangSelected === 'ts'}
|
|
39
|
+
onChange={onChange}
|
|
40
|
+
title="Toggle language"
|
|
41
|
+
/>
|
|
42
|
+
{children}
|
|
43
|
+
</div>
|
|
44
|
+
)
|
|
45
|
+
function onChange(e: React.ChangeEvent<HTMLInputElement>) {
|
|
46
|
+
const element = e.target
|
|
47
|
+
prevPositionRef.current = { top: element.getBoundingClientRect().top, el: element }
|
|
48
|
+
selectCodeLang(element.checked ? 'ts' : 'js')
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
pre > code {
|
|
2
|
+
/*
|
|
3
|
+
background-color: #f4f4f4;
|
|
4
|
+
0.043137255 = 1 - (#f4 / #ff)
|
|
5
|
+
*/
|
|
6
|
+
background: rgba(0, 0, 0, 0.043137255);
|
|
7
|
+
font-size: 1em;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* Copy button */
|
|
11
|
+
pre {
|
|
12
|
+
&:has(.copy-button) {
|
|
13
|
+
position: relative;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
&:hover {
|
|
17
|
+
.copy-button {
|
|
18
|
+
opacity: 1;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.copy-button {
|
|
23
|
+
position: absolute !important;
|
|
24
|
+
top: 10px;
|
|
25
|
+
right: 10px;
|
|
26
|
+
z-index: 3;
|
|
27
|
+
margin: 0;
|
|
28
|
+
height: 25px;
|
|
29
|
+
width: 30px;
|
|
30
|
+
background-color: #f7f7f7;
|
|
31
|
+
opacity: 0;
|
|
32
|
+
transition: opacity 0.5s ease-in-out, background-color 0.4s ease-in-out;
|
|
33
|
+
|
|
34
|
+
&:not(:hover) {
|
|
35
|
+
background-color: #eee;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
& svg {
|
|
39
|
+
width: 100%;
|
|
40
|
+
height: 100%;
|
|
41
|
+
fill: none;
|
|
42
|
+
stroke-linecap: round;
|
|
43
|
+
stroke-linejoin: round;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Workaround for shiki regression */
|
|
49
|
+
pre > code:not([data-language]) {
|
|
50
|
+
padding: 16px !important;
|
|
51
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export { Pre }
|
|
2
|
+
|
|
3
|
+
import React, { ComponentPropsWithoutRef, useState } from 'react'
|
|
4
|
+
/* Importing it here chokes the tests. I don't know why.
|
|
5
|
+
import './Pre.css'
|
|
6
|
+
//*/
|
|
7
|
+
|
|
8
|
+
function Pre({ children, ...props }: ComponentPropsWithoutRef<'pre'> & { 'hide-menu'?: string }) {
|
|
9
|
+
return (
|
|
10
|
+
<pre {...props}>
|
|
11
|
+
{!props['hide-menu'] && <CopyButton />}
|
|
12
|
+
{children}
|
|
13
|
+
</pre>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function CopyButton() {
|
|
18
|
+
const [isSuccess, setIsSuccess] = useState(null as null | boolean)
|
|
19
|
+
const onCopy = (success: boolean) => {
|
|
20
|
+
setIsSuccess(success)
|
|
21
|
+
setTimeout(() => {
|
|
22
|
+
setIsSuccess(null)
|
|
23
|
+
}, 900)
|
|
24
|
+
}
|
|
25
|
+
const tooltip = isSuccess === null ? 'Copy to clipboard' : isSuccess ? 'Copied' : 'Failed'
|
|
26
|
+
const icon =
|
|
27
|
+
isSuccess === null ? (
|
|
28
|
+
// Copy icon
|
|
29
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
|
|
30
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
31
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
|
32
|
+
</svg>
|
|
33
|
+
) : isSuccess ? (
|
|
34
|
+
// Green checkmark
|
|
35
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="#28a745" strokeWidth="3">
|
|
36
|
+
<polyline points="20 6 9 17 4 12" />
|
|
37
|
+
</svg>
|
|
38
|
+
) : (
|
|
39
|
+
'❌'
|
|
40
|
+
)
|
|
41
|
+
return (
|
|
42
|
+
<button
|
|
43
|
+
className="copy-button raised"
|
|
44
|
+
aria-label={tooltip}
|
|
45
|
+
data-label-position="top"
|
|
46
|
+
type="button"
|
|
47
|
+
onClick={onClick}
|
|
48
|
+
>
|
|
49
|
+
{icon}
|
|
50
|
+
</button>
|
|
51
|
+
)
|
|
52
|
+
async function onClick(e: React.MouseEvent<HTMLButtonElement>) {
|
|
53
|
+
let success: boolean
|
|
54
|
+
const preEl = e.currentTarget.parentElement!
|
|
55
|
+
let text = preEl.textContent || ''
|
|
56
|
+
text = removeTrailingWhitespaces(text)
|
|
57
|
+
try {
|
|
58
|
+
await navigator.clipboard.writeText(text)
|
|
59
|
+
success = true
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error(error)
|
|
62
|
+
success = false
|
|
63
|
+
}
|
|
64
|
+
onCopy(success)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function removeTrailingWhitespaces(text: string) {
|
|
68
|
+
return text
|
|
69
|
+
.split('\n')
|
|
70
|
+
.map((line) => line.trimEnd())
|
|
71
|
+
.join('\n')
|
|
72
|
+
}
|
package/components/index.ts
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { useMDXComponents }
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import type { UseMdxComponents } from '@mdx-js/mdx'
|
|
5
|
+
import { Pre } from './Pre.js'
|
|
6
|
+
import { CodeSnippets } from './CodeSnippets.js'
|
|
7
|
+
|
|
8
|
+
const useMDXComponents: UseMdxComponents = () => {
|
|
9
|
+
return {
|
|
10
|
+
CodeSnippets,
|
|
11
|
+
pre: (props) => <Pre {...props} />,
|
|
12
|
+
}
|
|
13
|
+
}
|
package/css/button.css
CHANGED
|
@@ -5,3 +5,26 @@ button,
|
|
|
5
5
|
border-radius: 5px;
|
|
6
6
|
cursor: pointer;
|
|
7
7
|
}
|
|
8
|
+
|
|
9
|
+
/* Raised button */
|
|
10
|
+
.raised {
|
|
11
|
+
cursor: pointer;
|
|
12
|
+
border-radius: 5px;
|
|
13
|
+
border-style: solid;
|
|
14
|
+
border-width: 1px 2px 2px 1px;
|
|
15
|
+
border-color: hsl(0, 0%, 75%) hsl(0, 0%, 72%) hsl(0, 0%, 72%) hsl(0, 0%, 75%);
|
|
16
|
+
|
|
17
|
+
&:hover {
|
|
18
|
+
border-color: hsl(0, 0%, 72%) hsl(0, 0%, 66%) hsl(0, 0%, 66%) hsl(0, 0%, 72%);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&:active {
|
|
22
|
+
border-width: 2px 1px 1px 2px;
|
|
23
|
+
border-color: hsl(0, 0%, 66%) hsl(0, 0%, 72%) hsl(0, 0%, 72%) hsl(0, 0%, 66%);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&:disabled {
|
|
27
|
+
border-width: 1px;
|
|
28
|
+
border-color: hsl(0, 0%, 72%);
|
|
29
|
+
}
|
|
30
|
+
}
|
package/css/code.css
CHANGED
|
@@ -2,29 +2,11 @@
|
|
|
2
2
|
@import './code/block.css';
|
|
3
3
|
@import './code/diff.css';
|
|
4
4
|
|
|
5
|
-
code
|
|
6
|
-
border-radius: 4px;
|
|
7
|
-
}
|
|
8
|
-
pre {
|
|
9
|
-
background: none !important;
|
|
10
|
-
}
|
|
5
|
+
/* For code blocks, see Pre.css instead */
|
|
11
6
|
|
|
12
|
-
/* Inline */
|
|
7
|
+
/* Inline <code> */
|
|
13
8
|
code {
|
|
9
|
+
border-radius: 4px;
|
|
14
10
|
background: rgba(0, 0, 0, 0.063137255);
|
|
15
11
|
font-size: 1.1em;
|
|
16
12
|
}
|
|
17
|
-
|
|
18
|
-
/* Block */
|
|
19
|
-
pre > code {
|
|
20
|
-
/*
|
|
21
|
-
background-color: #f4f4f4;
|
|
22
|
-
0.043137255 = 1 - (#f4 / #ff)
|
|
23
|
-
*/
|
|
24
|
-
background: rgba(0, 0, 0, 0.043137255);
|
|
25
|
-
font-size: 1em;
|
|
26
|
-
}
|
|
27
|
-
/* Workaround for shiki regression */
|
|
28
|
-
pre > code:not([data-language]) {
|
|
29
|
-
padding: 16px !important;
|
|
30
|
-
}
|
package/css/index.css
CHANGED
package/css/tooltip.css
CHANGED
|
@@ -17,10 +17,8 @@
|
|
|
17
17
|
font-size: 12px;
|
|
18
18
|
content: attr(aria-label);
|
|
19
19
|
position: absolute;
|
|
20
|
-
top: 100%;
|
|
21
20
|
left: 50%;
|
|
22
21
|
transform: translate(-50%, 0);
|
|
23
|
-
margin-top: 5px;
|
|
24
22
|
background: #fdfdfd;
|
|
25
23
|
padding: 3px 10px;
|
|
26
24
|
box-shadow: rgb(0 0 0 / 8%) 2px 4px 7px 0px;
|
|
@@ -34,4 +32,14 @@
|
|
|
34
32
|
*/
|
|
35
33
|
white-space: nowrap;
|
|
36
34
|
}
|
|
35
|
+
/* Show below */
|
|
36
|
+
[aria-label]:not([data-label-position])::before {
|
|
37
|
+
top: 100%;
|
|
38
|
+
margin-top: 5px;
|
|
39
|
+
}
|
|
40
|
+
/* Show above */
|
|
41
|
+
[aria-label][data-label-position='top']::before {
|
|
42
|
+
bottom: 100%;
|
|
43
|
+
margin-bottom: 7px;
|
|
44
|
+
}
|
|
37
45
|
}
|
package/dist/+config.js
CHANGED
package/dist/NavItemComponent.js
CHANGED
|
@@ -1,53 +1,39 @@
|
|
|
1
|
-
var __assign = (this && this.__assign) || function () {
|
|
2
|
-
__assign = Object.assign || function(t) {
|
|
3
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
-
s = arguments[i];
|
|
5
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
-
t[p] = s[p];
|
|
7
|
-
}
|
|
8
|
-
return t;
|
|
9
|
-
};
|
|
10
|
-
return __assign.apply(this, arguments);
|
|
11
|
-
};
|
|
12
1
|
export { NavItemComponent };
|
|
13
2
|
export { getNavItemsWithComputed };
|
|
14
3
|
import React from 'react';
|
|
15
4
|
import { assert, assertWarning, jsxToTextContent } from './utils/server';
|
|
16
5
|
import './NavItemComponent.css';
|
|
17
6
|
import { parseMarkdownMini } from './parseMarkdownMini';
|
|
18
|
-
function NavItemComponent(
|
|
19
|
-
var _b;
|
|
20
|
-
var _c;
|
|
21
|
-
var navItem = _a.navItem, onClick = _a.onClick;
|
|
7
|
+
function NavItemComponent({ navItem, onClick, }) {
|
|
22
8
|
assert([1, 2, 3, 4].includes(navItem.level), navItem);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
9
|
+
const titleJsx = parseMarkdownMini(navItem.title);
|
|
10
|
+
const titleInNavJsx = parseMarkdownMini(navItem.titleInNav);
|
|
11
|
+
const iconSize = 25;
|
|
12
|
+
const icon = navItem.titleIcon && (React.createElement("img", { src: navItem.titleIcon, style: { height: iconSize, width: iconSize, marginRight: 8, marginLeft: 4, ...navItem.titleIconStyle } }));
|
|
27
13
|
if (navItem.level === 1 || navItem.level === 4) {
|
|
28
14
|
assert(navItem.url === undefined);
|
|
29
15
|
}
|
|
30
16
|
else {
|
|
31
|
-
|
|
17
|
+
const sectionTitle = jsxToTextContent(titleJsx);
|
|
32
18
|
assertWarning(navItem.url, [
|
|
33
|
-
|
|
34
|
-
|
|
19
|
+
`${jsxToTextContent(titleInNavJsx)} is missing a URL hash.`,
|
|
20
|
+
`Add a URL hash with: \`## ${sectionTitle}{#some-hash}\`.`,
|
|
35
21
|
/* TO-DO/eventually: not implemented yet.
|
|
36
22
|
`Use \`<h2 id="url-hash">${sectionTitle}</h2>\` instead of \`## ${sectionTitle}\`.`,
|
|
37
23
|
*/
|
|
38
24
|
].join(' '));
|
|
39
25
|
}
|
|
40
|
-
|
|
26
|
+
let children = titleInNavJsx;
|
|
41
27
|
if (navItem.level === 1) {
|
|
42
28
|
children = (React.createElement(React.Fragment, null,
|
|
43
29
|
icon,
|
|
44
30
|
children,
|
|
45
31
|
React.createElement(Chevron, { className: "collapsible-icon", height: 9 })));
|
|
46
32
|
}
|
|
47
|
-
|
|
48
|
-
href:
|
|
49
|
-
children
|
|
50
|
-
onClick
|
|
33
|
+
const props = {
|
|
34
|
+
href: navItem.url ?? undefined,
|
|
35
|
+
children,
|
|
36
|
+
onClick,
|
|
51
37
|
className: [
|
|
52
38
|
'nav-item',
|
|
53
39
|
'nav-item-level-' + navItem.level,
|
|
@@ -59,45 +45,51 @@ function NavItemComponent(_a) {
|
|
|
59
45
|
.join(' '),
|
|
60
46
|
};
|
|
61
47
|
if (navItem.level === 1) {
|
|
62
|
-
props.style =
|
|
63
|
-
|
|
64
|
-
|
|
48
|
+
props.style = {
|
|
49
|
+
['--category-color']: navItem.color,
|
|
50
|
+
};
|
|
65
51
|
}
|
|
66
52
|
if (navItem.level === 2 || navItem.level === 3) {
|
|
67
|
-
return React.createElement("a",
|
|
53
|
+
return React.createElement("a", { ...props });
|
|
68
54
|
}
|
|
69
55
|
else {
|
|
70
|
-
return React.createElement("span",
|
|
56
|
+
return React.createElement("span", { ...props });
|
|
71
57
|
}
|
|
72
58
|
}
|
|
73
59
|
function getNavItemsWithComputed(navItems, currentUrl) {
|
|
74
|
-
|
|
75
|
-
|
|
60
|
+
let navItemIdx;
|
|
61
|
+
const navItemsWithComputed = navItems.map((navItem, i) => {
|
|
76
62
|
assert([1, 2, 3, 4].includes(navItem.level), navItem);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
63
|
+
const navItemPrevious = navItems[i - 1];
|
|
64
|
+
const navItemNext = navItems[i + 1];
|
|
65
|
+
let isActive = false;
|
|
80
66
|
if (navItem.url === currentUrl) {
|
|
81
|
-
assert(navItem.level === 2, { currentUrl
|
|
67
|
+
assert(navItem.level === 2, { currentUrl });
|
|
82
68
|
assert(navItemIdx === undefined);
|
|
83
69
|
navItemIdx = i;
|
|
84
70
|
isActive = true;
|
|
85
71
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
72
|
+
const isFirstOfItsKind = navItem.level !== navItemPrevious?.level;
|
|
73
|
+
const isLastOfItsKind = navItem.level !== navItemNext?.level;
|
|
74
|
+
const navItemComputed = {
|
|
75
|
+
...navItem,
|
|
76
|
+
isActive,
|
|
77
|
+
isRelevant: false,
|
|
78
|
+
isFirstOfItsKind,
|
|
79
|
+
isLastOfItsKind,
|
|
80
|
+
};
|
|
89
81
|
return navItemComputed;
|
|
90
82
|
});
|
|
91
83
|
// Set `isRelevant`
|
|
92
84
|
if (navItemIdx !== undefined) {
|
|
93
|
-
for (
|
|
94
|
-
|
|
85
|
+
for (let i = navItemIdx; i >= 0; i--) {
|
|
86
|
+
const navItem = navItemsWithComputed[i];
|
|
95
87
|
navItem.isRelevant = true;
|
|
96
88
|
if (navItem.level === 1)
|
|
97
89
|
break;
|
|
98
90
|
}
|
|
99
|
-
for (
|
|
100
|
-
|
|
91
|
+
for (let i = navItemIdx; i < navItemsWithComputed.length; i++) {
|
|
92
|
+
const navItem = navItemsWithComputed[i];
|
|
101
93
|
if (navItem.level === 1)
|
|
102
94
|
break;
|
|
103
95
|
navItem.isRelevant = true;
|
|
@@ -106,6 +98,6 @@ function getNavItemsWithComputed(navItems, currentUrl) {
|
|
|
106
98
|
return navItemsWithComputed;
|
|
107
99
|
}
|
|
108
100
|
function Chevron(props) {
|
|
109
|
-
return (React.createElement("svg",
|
|
101
|
+
return (React.createElement("svg", { viewBox: "0 0 512 292.52", xmlns: "http://www.w3.org/2000/svg", ...props },
|
|
110
102
|
React.createElement("path", { fill: "#aaa", d: "M10.725 82.42L230.125 261.82c6.8 6.8 16.2 10.7 25.9 10.7s19.1-3.9 25.9-10.7l219.4-179.4c14.3-14.3 14.3-37.4 0-51.7s-37.4-14.3-51.7 0l-193.6 153.6-193.6-153.6c-14.3-14.3-37.4-14.3-51.7 0s-14.3 37.5 0 51.7z" })));
|
|
111
103
|
}
|
|
@@ -2,9 +2,8 @@ export { CodeBlockTransformer };
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { assert } from '../utils/server';
|
|
4
4
|
import './CodeBlockTransformer.css';
|
|
5
|
-
function CodeBlockTransformer(
|
|
6
|
-
var children = _a.children, lineBreak = _a.lineBreak;
|
|
5
|
+
function CodeBlockTransformer({ children, lineBreak }) {
|
|
7
6
|
assert(lineBreak === 'white-space' || lineBreak === 'break-word', '`lineBreak` is currently the only use case for <CodeBlockTransformer>');
|
|
8
|
-
|
|
7
|
+
const className = `with-line-break_${lineBreak}`;
|
|
9
8
|
return React.createElement("div", { className: className }, children);
|
|
10
9
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export { useSelectCodeLang };
|
|
2
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
3
|
+
import { assertWarning } from '../../utils/assert';
|
|
4
|
+
const storageKey = 'docpress:code-lang';
|
|
5
|
+
const codeLangDefaultSsr = 'ts';
|
|
6
|
+
const codeLangDefaultClient = 'js';
|
|
7
|
+
function useSelectCodeLang() {
|
|
8
|
+
const [codeLangSelected, setCodeLangSelected] = useState(codeLangDefaultSsr);
|
|
9
|
+
const updateState = () => {
|
|
10
|
+
setCodeLangSelected(getCodeLangStorage());
|
|
11
|
+
};
|
|
12
|
+
const updateStateOnStorageEvent = (event) => {
|
|
13
|
+
if (event.key === storageKey)
|
|
14
|
+
updateState();
|
|
15
|
+
};
|
|
16
|
+
const getCodeLangStorage = () => {
|
|
17
|
+
try {
|
|
18
|
+
return window.localStorage.getItem(storageKey) ?? codeLangDefaultClient;
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
console.error(error);
|
|
22
|
+
assertWarning(false, 'Error reading from localStorage');
|
|
23
|
+
return codeLangDefaultClient;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const selectCodeLang = useCallback((value) => {
|
|
27
|
+
try {
|
|
28
|
+
window.localStorage.setItem(storageKey, value);
|
|
29
|
+
setCodeLangSelected(value);
|
|
30
|
+
window.dispatchEvent(new CustomEvent('code-lang-storage'));
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
console.error(error);
|
|
34
|
+
assertWarning(false, 'Error setting localStorage');
|
|
35
|
+
}
|
|
36
|
+
}, []);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
// Initial load from localStorage
|
|
39
|
+
updateState();
|
|
40
|
+
// Update code lang in current tab
|
|
41
|
+
window.addEventListener('code-lang-storage', updateState);
|
|
42
|
+
// Update code lang if changed in another tab
|
|
43
|
+
window.addEventListener('storage', updateStateOnStorageEvent);
|
|
44
|
+
return () => {
|
|
45
|
+
window.removeEventListener('code-lang-storage', updateState);
|
|
46
|
+
window.removeEventListener('storage', updateStateOnStorageEvent);
|
|
47
|
+
};
|
|
48
|
+
}, []);
|
|
49
|
+
return [codeLangSelected, selectCodeLang];
|
|
50
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { TypescriptOnly };
|
|
2
|
+
export { CodeSnippets };
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import './CodeSnippets.css';
|
|
5
|
+
/** Only show if TypeScript is selected */
|
|
6
|
+
declare function TypescriptOnly({ children }: {
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
}): React.JSX.Element;
|
|
9
|
+
declare function CodeSnippets({ children }: {
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
}): React.JSX.Element;
|