@brillout/docpress 0.15.10-commit-e9efbd3 → 0.15.10-commit-af2d9bc
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 +61 -0
- package/components/CodeSnippets.css +47 -0
- package/components/CodeSnippets.tsx +80 -45
- package/css/tooltip.css +10 -2
- package/detypePlugin.ts +73 -48
- 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 +51 -0
- package/dist/components/CodeSnippets.d.ts +10 -6
- package/dist/components/CodeSnippets.js +66 -94
- 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/determineNavItemsColumnLayout.js +48 -63
- package/dist/detypePlugin.js +84 -120
- package/dist/parseMarkdownMini.js +5 -17
- package/dist/parsePageSections.js +41 -82
- package/dist/renderer/usePageContext.js +6 -7
- package/dist/resolvePageContext.js +91 -103
- 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 +7 -7
- package/package.json +1 -1
- package/tsconfig.json +2 -1
- package/dist/utils/useSelectedLanguage.d.ts +0 -7
- package/dist/utils/useSelectedLanguage.js +0 -49
- package/utils/useSelectedLanguage.ts +0 -61
|
@@ -0,0 +1,61 @@
|
|
|
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) return
|
|
17
|
+
updateState()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const getCodeLangStorage = () => {
|
|
21
|
+
try {
|
|
22
|
+
return window.localStorage.getItem(storageKey) ?? codeLangDefaultClient
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error(error)
|
|
25
|
+
assertWarning(false, 'Error reading from localStorage')
|
|
26
|
+
return codeLangDefaultClient
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const selectCodeLang = useCallback((value: string) => {
|
|
31
|
+
try {
|
|
32
|
+
window.localStorage.setItem(storageKey, value)
|
|
33
|
+
setCodeLangSelected(value)
|
|
34
|
+
window.dispatchEvent(new CustomEvent('code-lang-storage'))
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error(error)
|
|
37
|
+
assertWarning(false, 'Error setting localStorage')
|
|
38
|
+
}
|
|
39
|
+
}, [])
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
// Initial load from localStorage
|
|
43
|
+
updateState()
|
|
44
|
+
// Update code lang in current tab
|
|
45
|
+
window.addEventListener('code-lang-storage', updateState)
|
|
46
|
+
// Update code lang if changed in another tab
|
|
47
|
+
window.addEventListener('storage', updateStateOnStorageEvent)
|
|
48
|
+
return () => {
|
|
49
|
+
window.removeEventListener('code-lang-storage', updateState)
|
|
50
|
+
window.removeEventListener('storage', updateStateOnStorageEvent)
|
|
51
|
+
}
|
|
52
|
+
}, [])
|
|
53
|
+
|
|
54
|
+
return [codeLangSelected, selectCodeLang] as const
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
declare global {
|
|
58
|
+
interface WindowEventMap {
|
|
59
|
+
'code-lang-storage': CustomEvent
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/* Language select */
|
|
2
|
+
.code-lang-select {
|
|
3
|
+
right: 42px;
|
|
4
|
+
padding-left: 6px;
|
|
5
|
+
padding-right: 21px;
|
|
6
|
+
font-size: 14px;
|
|
7
|
+
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 2 1'><path fill='%23666' d='M0 0l1 1 1-1z'/></svg>");
|
|
8
|
+
background-repeat: no-repeat;
|
|
9
|
+
background-position: right 7px center;
|
|
10
|
+
background-size: 10px;
|
|
11
|
+
/* Remove default arrow in some browsers */
|
|
12
|
+
appearance: none;
|
|
13
|
+
-webkit-appearance: none;
|
|
14
|
+
-moz-appearance: none;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* Copy button */
|
|
18
|
+
.copy-button {
|
|
19
|
+
display: inline-flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
right: 10px;
|
|
22
|
+
width: 30px;
|
|
23
|
+
& svg {
|
|
24
|
+
width: 100%;
|
|
25
|
+
height: 100%;
|
|
26
|
+
stroke-linecap: round;
|
|
27
|
+
stroke-linejoin: round;
|
|
28
|
+
fill: none;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Common style */
|
|
33
|
+
.copy-button,
|
|
34
|
+
.code-lang-select {
|
|
35
|
+
position: absolute !important;
|
|
36
|
+
height: 25px;
|
|
37
|
+
top: 10px;
|
|
38
|
+
z-index: 3;
|
|
39
|
+
outline: none;
|
|
40
|
+
cursor: pointer;
|
|
41
|
+
border: 1px solid #ccc;
|
|
42
|
+
border-radius: 5px;
|
|
43
|
+
background-color: #f7f7f7;
|
|
44
|
+
&:hover {
|
|
45
|
+
background-color: #eee;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -1,72 +1,107 @@
|
|
|
1
|
-
|
|
1
|
+
// Public
|
|
2
|
+
export { TypescriptOnly }
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
// Internal
|
|
5
|
+
export { CodeSnippets }
|
|
6
|
+
export { CodeSnippet }
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
import React, { useState } from 'react'
|
|
9
|
+
import { useSelectCodeLang } from './CodeSnippets/useSelectCodeLang'
|
|
10
|
+
import './CodeSnippets.css'
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
/** Only show if TypeScript is selected */
|
|
13
|
+
function TypescriptOnly({ children }: { children: React.ReactNode }) {
|
|
14
|
+
const [codeLangSelected] = useSelectCodeLang()
|
|
15
|
+
return <div style={{ display: codeLangSelected === 'ts' ? 'block' : 'none' }}>{children}</div>
|
|
16
|
+
}
|
|
12
17
|
|
|
18
|
+
function CodeSnippets({ children }: { children: React.ReactNode }) {
|
|
19
|
+
const [codeLangSelected, selectCodeLang] = useSelectCodeLang()
|
|
13
20
|
return (
|
|
14
21
|
<div>
|
|
15
22
|
<form style={{ position: 'relative' }}>
|
|
16
|
-
<select
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
onChange={handleOnChange}
|
|
20
|
-
value={selectedLang}
|
|
21
|
-
style={{ position: 'absolute', top: '10px', right: '60px', zIndex: 3 }}
|
|
22
|
-
>
|
|
23
|
-
<option value="js">Javascript</option>
|
|
24
|
-
<option value="ts">Typescript</option>
|
|
23
|
+
<select className="code-lang-select" onChange={onChange} value={codeLangSelected}>
|
|
24
|
+
<option value="js">JavaScript</option>
|
|
25
|
+
<option value="ts">TypeScript</option>
|
|
25
26
|
</select>
|
|
26
27
|
</form>
|
|
27
28
|
{children}
|
|
28
29
|
</div>
|
|
29
30
|
)
|
|
31
|
+
function onChange(e: React.ChangeEvent<HTMLSelectElement>) {
|
|
32
|
+
selectCodeLang(e.target.value)
|
|
33
|
+
}
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
function CodeSnippet({
|
|
33
37
|
children,
|
|
34
|
-
|
|
38
|
+
codeLang,
|
|
35
39
|
tsOnly = false,
|
|
36
|
-
}: { children: React.ReactNode;
|
|
37
|
-
const [
|
|
40
|
+
}: { children: React.ReactNode; codeLang: string; tsOnly: boolean }) {
|
|
41
|
+
const [codeLangSelected] = useSelectCodeLang()
|
|
38
42
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
const copyToClipboard = async (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
42
|
-
try {
|
|
43
|
-
const figureEl = e.currentTarget.nextElementSibling
|
|
44
|
-
if (figureEl?.tagName === 'FIGURE') {
|
|
45
|
-
await navigator.clipboard.writeText(figureEl.textContent ?? '')
|
|
46
|
-
console.log('Copied to clipboard!')
|
|
47
|
-
}
|
|
48
|
-
} catch (error) {
|
|
49
|
-
console.warn('Copy failed', error)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
43
|
+
const displayStyle = tsOnly ? {} : { display: codeLangSelected === codeLang ? 'block' : 'none' }
|
|
52
44
|
|
|
53
45
|
return (
|
|
54
|
-
<div style={{ ...
|
|
55
|
-
<
|
|
56
|
-
type="button"
|
|
57
|
-
style={{ position: 'absolute', top: '10px', right: '10px', zIndex: 3 }}
|
|
58
|
-
onClick={copyToClipboard}
|
|
59
|
-
>
|
|
60
|
-
Copy
|
|
61
|
-
</button>
|
|
46
|
+
<div style={{ ...displayStyle, position: 'relative' }}>
|
|
47
|
+
<CopyButton />
|
|
62
48
|
{children}
|
|
63
49
|
</div>
|
|
64
50
|
)
|
|
65
51
|
}
|
|
66
52
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
53
|
+
function CopyButton() {
|
|
54
|
+
const [isSuccess, setIsSuccess] = useState(null as null | boolean)
|
|
55
|
+
const onCopy = (success: boolean) => {
|
|
56
|
+
setIsSuccess(success)
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
setIsSuccess(null)
|
|
59
|
+
}, 900)
|
|
60
|
+
}
|
|
61
|
+
const tooltip = isSuccess === null ? 'Copy to clipboard' : isSuccess ? 'Copied' : 'Failed'
|
|
62
|
+
const text =
|
|
63
|
+
isSuccess === null ? (
|
|
64
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
|
|
65
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
66
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
|
67
|
+
</svg>
|
|
68
|
+
) : isSuccess ? (
|
|
69
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="#28a745" strokeWidth="3">
|
|
70
|
+
<polyline points="20 6 9 17 4 12" />
|
|
71
|
+
</svg>
|
|
72
|
+
) : (
|
|
73
|
+
'❌'
|
|
74
|
+
)
|
|
75
|
+
return (
|
|
76
|
+
<button className="copy-button" aria-label={tooltip} data-label-position="top" type="button" onClick={onClick}>
|
|
77
|
+
{text}
|
|
78
|
+
</button>
|
|
79
|
+
)
|
|
80
|
+
async function onClick(e: React.MouseEvent<HTMLButtonElement>) {
|
|
81
|
+
let success: boolean
|
|
82
|
+
try {
|
|
83
|
+
await copyToClipboard(e)
|
|
84
|
+
success = true
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error(error)
|
|
87
|
+
success = false
|
|
88
|
+
}
|
|
89
|
+
onCopy(success)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function copyToClipboard(e: React.MouseEvent<HTMLButtonElement>) {
|
|
94
|
+
const figureEl = e.currentTarget.nextElementSibling
|
|
95
|
+
if (figureEl?.tagName === 'FIGURE') {
|
|
96
|
+
let text = figureEl.textContent ?? ''
|
|
97
|
+
text = removeTrailingWhitespaces(text)
|
|
98
|
+
await navigator.clipboard.writeText(text)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
70
101
|
|
|
71
|
-
|
|
102
|
+
function removeTrailingWhitespaces(text: string) {
|
|
103
|
+
return text
|
|
104
|
+
.split('\n')
|
|
105
|
+
.map((line) => line.trimEnd())
|
|
106
|
+
.join('\n')
|
|
72
107
|
}
|
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/detypePlugin.ts
CHANGED
|
@@ -2,96 +2,121 @@ export { detypePlugin }
|
|
|
2
2
|
|
|
3
3
|
import type { PluginOption } from 'vite'
|
|
4
4
|
import module from 'node:module'
|
|
5
|
-
|
|
5
|
+
import { assertUsage } from './utils/assert.js'
|
|
6
|
+
import pc from '@brillout/picocolors'
|
|
6
7
|
// Cannot use `import { transform } from 'detype'` as it results in errors,
|
|
7
8
|
// and the package has no default export. Using `module.createRequire` instead.
|
|
8
|
-
const { transform } = module.createRequire(import.meta.url)('detype') as typeof import('detype')
|
|
9
|
+
const { transform: detype } = module.createRequire(import.meta.url)('detype') as typeof import('detype')
|
|
10
|
+
|
|
11
|
+
const prettierOptions: NonNullable<Parameters<typeof detype>[2]>['prettierOptions'] = {
|
|
12
|
+
semi: false,
|
|
13
|
+
singleQuote: true,
|
|
14
|
+
printWidth: 100,
|
|
15
|
+
trailingComma: 'none',
|
|
16
|
+
}
|
|
17
|
+
// RegExp to find TypeScript code blocks.
|
|
18
|
+
//
|
|
19
|
+
// For example:
|
|
20
|
+
// ~~~mdx
|
|
21
|
+
// ```ts
|
|
22
|
+
// const hello: string = 'world'
|
|
23
|
+
// ```
|
|
24
|
+
// ~~~
|
|
25
|
+
//
|
|
26
|
+
// But also indented code blocks:
|
|
27
|
+
// ~~~mdx
|
|
28
|
+
// > Also works:
|
|
29
|
+
// > - In blockquotes
|
|
30
|
+
// > - In bullet points
|
|
31
|
+
// > ```ts
|
|
32
|
+
// > const hello: string = 'world'
|
|
33
|
+
// > ```
|
|
34
|
+
// ~~~
|
|
35
|
+
const codeBlockRE = /^(.*)```(tsx?|vue)[^\n]*\n([\s\S]*?)```/gm
|
|
9
36
|
|
|
10
37
|
function detypePlugin(): PluginOption {
|
|
11
38
|
return {
|
|
12
39
|
name: '@brillout/docpress:detypePlugin',
|
|
13
40
|
enforce: 'pre',
|
|
14
|
-
transform: async (code: string,
|
|
15
|
-
if (!
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
const codeNew = await transformCode(code)
|
|
19
|
-
|
|
41
|
+
transform: async (code: string, moduleId: string) => {
|
|
42
|
+
if (!moduleId.endsWith('.mdx')) return
|
|
43
|
+
const codeNew = await transformCode(code, moduleId)
|
|
20
44
|
return codeNew
|
|
21
45
|
},
|
|
22
46
|
}
|
|
23
47
|
}
|
|
24
48
|
|
|
25
|
-
|
|
26
|
-
const prettierOptions = {
|
|
27
|
-
semi: false,
|
|
28
|
-
singleQuote: true,
|
|
29
|
-
printWidth: 100,
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async function transformCode(code: string) {
|
|
49
|
+
async function transformCode(code: string, moduleId: string) {
|
|
33
50
|
const matches = Array.from(code.matchAll(codeBlockRE))
|
|
34
|
-
if (matches.length === 0)
|
|
35
|
-
return code
|
|
36
|
-
}
|
|
51
|
+
if (matches.length === 0) return
|
|
37
52
|
|
|
38
53
|
let codeNew = `import { CodeSnippets, CodeSnippet } from '@brillout/docpress';\n\n`
|
|
39
54
|
let lastIndex = 0
|
|
40
55
|
|
|
41
56
|
for (const match of matches) {
|
|
42
|
-
|
|
43
|
-
const tsOpeningCode = fullMatch.split('\n')[0].slice(startsWith.length)
|
|
57
|
+
const [codeBlockOuterStr, codeBlockIndent, codeBlockLang, codeBlockContentWithIndent] = match
|
|
44
58
|
|
|
45
|
-
|
|
46
|
-
const
|
|
59
|
+
// Remove indentation
|
|
60
|
+
const codeBlockOpen = codeBlockOuterStr.split('\n')[0].slice(codeBlockIndent.length)
|
|
61
|
+
const codeBlockContent = removeCodeBlockIndent(codeBlockContentWithIndent, codeBlockIndent, moduleId)
|
|
47
62
|
|
|
48
|
-
|
|
63
|
+
const blockStartIndex = match.index
|
|
64
|
+
const blockEndIndex = blockStartIndex + codeBlockOuterStr.length
|
|
65
|
+
codeNew += code.slice(lastIndex, blockStartIndex)
|
|
49
66
|
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (tsOpeningCode.includes('ts-only')) {
|
|
55
|
-
codeNew += `${startsWith}<CodeSnippet language={'ts'} tsOnly={'true'}>\n${fullMatch}\n${startsWith}</CodeSnippet>`
|
|
67
|
+
if (codeBlockOpen.includes('ts-only')) {
|
|
68
|
+
codeNew += `${codeBlockIndent}<CodeSnippet codeLang="ts" tsOnly>\n${codeBlockOuterStr}\n${codeBlockIndent}</CodeSnippet>`
|
|
56
69
|
} else {
|
|
57
|
-
|
|
70
|
+
// someFileName.ts => someFileName.js
|
|
71
|
+
let codeBlockContentJs = codeBlockContent.replaceAll('.ts', '.js')
|
|
72
|
+
// Remove TypeScript
|
|
73
|
+
codeBlockContentJs = await detype(codeBlockContentJs, `some-dummy-filename.${codeBlockLang}`, {
|
|
58
74
|
removeTsComments: true,
|
|
59
75
|
prettierOptions,
|
|
60
76
|
})
|
|
61
|
-
const jsLang = lang === 'vue' ? 'vue' : lang.replace('t', 'j') // ts => js | tsx => jsx
|
|
62
|
-
const jsOpeningCode = tsOpeningCode.replace(lang, jsLang)
|
|
63
|
-
const closing = `\`\`\``
|
|
64
77
|
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
78
|
+
const codeBlockLangJs =
|
|
79
|
+
codeBlockLang === 'vue'
|
|
80
|
+
? 'vue'
|
|
81
|
+
: // ts => js | tsx => jsx
|
|
82
|
+
codeBlockLang.replace('t', 'j')
|
|
83
|
+
const codeBlockOpenJs = codeBlockOpen.replace(codeBlockLang, codeBlockLangJs)
|
|
84
|
+
const codeBlockClose = '```'
|
|
85
|
+
|
|
86
|
+
const codeSnippetTs = `<CodeSnippet codeLang="ts">\n${codeBlockOpen}\n${codeBlockContent}${codeBlockClose}\n</CodeSnippet>`
|
|
87
|
+
const codeSnippetJs = `<CodeSnippet codeLang="js">\n${codeBlockOpenJs}\n${codeBlockContentJs}${codeBlockClose}\n</CodeSnippet>`
|
|
88
|
+
const codeSnippets = restoreCodeBlockIndent(
|
|
89
|
+
`<CodeSnippets>\n${codeSnippetJs}\n${codeSnippetTs}\n</CodeSnippets>`,
|
|
90
|
+
codeBlockIndent,
|
|
70
91
|
)
|
|
71
92
|
|
|
72
93
|
codeNew += codeSnippets
|
|
73
94
|
}
|
|
74
95
|
|
|
75
|
-
lastIndex =
|
|
96
|
+
lastIndex = blockEndIndex
|
|
76
97
|
}
|
|
77
98
|
codeNew += code.slice(lastIndex)
|
|
78
99
|
|
|
79
100
|
return codeNew
|
|
80
101
|
}
|
|
81
102
|
|
|
82
|
-
function
|
|
103
|
+
function removeCodeBlockIndent(code: string, codeBlockIndent: string, moduleId: string) {
|
|
104
|
+
if (!codeBlockIndent.length) return code
|
|
83
105
|
return code
|
|
84
106
|
.split('\n')
|
|
85
|
-
.map((line) =>
|
|
107
|
+
.map((line) => {
|
|
108
|
+
assertUsage(
|
|
109
|
+
line.startsWith(codeBlockIndent.trimEnd()),
|
|
110
|
+
`In ${pc.bold(pc.blue(moduleId))} the line ${pc.bold(line)} must start with ${pc.bold(codeBlockIndent)}`,
|
|
111
|
+
)
|
|
112
|
+
return line.slice(codeBlockIndent.length)
|
|
113
|
+
})
|
|
86
114
|
.join('\n')
|
|
87
115
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (!startsWith.length) {
|
|
91
|
-
return code
|
|
92
|
-
}
|
|
116
|
+
function restoreCodeBlockIndent(code: string, codeBlockIndent: string) {
|
|
117
|
+
if (!codeBlockIndent.length) return code
|
|
93
118
|
return code
|
|
94
119
|
.split('\n')
|
|
95
|
-
.map((line) => `${
|
|
120
|
+
.map((line) => `${codeBlockIndent}${line}`)
|
|
96
121
|
.join('\n')
|
|
97
122
|
}
|
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
|
}
|