@brillout/docpress 0.1.14 → 0.1.15
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/dist/{chunk-GBXQT242.js → chunk-J7PKG3ZA.js} +38 -13
- package/dist/chunk-J7PKG3ZA.js.map +1 -0
- package/dist/{devServer-CXZI7V2M.js → devServer-RUTLBMIJ.js} +2 -5
- package/dist/{devServer-CXZI7V2M.js.map → devServer-RUTLBMIJ.js.map} +1 -1
- package/dist/index.d.ts +0 -136
- package/dist/index.js +27 -484
- package/dist/index.js.map +1 -1
- package/package.json +8 -11
- package/src/MobileHeader.tsx +68 -0
- package/src/PageLayout.css +41 -0
- package/src/PageLayout.tsx +37 -0
- package/src/algolia/DocSearch.css +29 -0
- package/src/algolia/DocSearch.ts +37 -0
- package/src/autoScrollNav.ts +22 -0
- package/src/cli/devServer.ts +31 -0
- package/src/cli/index.ts +29 -0
- package/src/components/CodeBlock.tsx +22 -0
- package/src/components/DocLink.tsx +108 -0
- package/src/components/EditPageNote.tsx +18 -0
- package/src/components/HorizontalLine.tsx +20 -0
- package/src/components/ImportMeta.tsx +11 -0
- package/src/components/Info.tsx +12 -0
- package/src/components/Link.tsx +18 -0
- package/src/components/Note.tsx +31 -0
- package/src/components/P.css +8 -0
- package/src/components/P.tsx +8 -0
- package/src/components/ReadingRecommendation.tsx +53 -0
- package/src/components/RepoLink.tsx +24 -0
- package/src/components/Sponsors/companyLogos/ccoli-logo.svg +1 -0
- package/src/components/Sponsors/companyLogos/ccoli-text.svg +1 -0
- package/{dist/ccoli-CHW3TQKS.svg → src/components/Sponsors/companyLogos/ccoli.svg} +0 -0
- package/{dist/contra-WLZBOPBV.svg → src/components/Sponsors/companyLogos/contra.svg} +0 -0
- package/{dist/mfqs-2EAEE7N6.svg → src/components/Sponsors/companyLogos/mfqs.svg} +0 -0
- package/src/components/Sponsors/label.draft.svg +108 -0
- package/{dist/label-MP75CTIA.svg → src/components/Sponsors/label.svg} +0 -0
- package/{dist/medalBronze-CO4CTUR4.svg → src/components/Sponsors/medalBronze.svg} +0 -0
- package/{dist/medalGold-UP6A73FL.svg → src/components/Sponsors/medalGold.svg} +0 -0
- package/{dist/medalSilver-FAPGGOBN.svg → src/components/Sponsors/medalSilver.svg} +0 -0
- package/src/components/Sponsors.tsx +242 -0
- package/{dist → src}/components/features/FeatureList.css +7 -2
- package/src/components/features/FeatureList.tsx +114 -0
- package/{dist/chevron-R2IYJD62.svg → src/components/features/chevron.svg} +0 -0
- package/src/components/features/initFeatureList.ts +66 -0
- package/src/components/index.ts +13 -0
- package/src/config/Config.ts +30 -0
- package/src/config/getConfig.ts +18 -0
- package/src/config/resolveConfig/resolveHeading.ts +0 -0
- package/src/config/resolvePageContext.ts +157 -0
- package/{dist/Inter-Var-IOAEQULN.ttf → src/css/Inter-Var.ttf} +0 -0
- package/src/css/button.css +7 -0
- package/src/css/code/block.css +36 -0
- package/src/css/code/inline.css +27 -0
- package/src/css/code.css +20 -0
- package/src/css/colorize-on-hover.css +29 -0
- package/src/css/font.css +19 -0
- package/src/css/heading.css +25 -0
- package/src/css/index.css +11 -0
- package/src/css/link.css +17 -0
- package/src/css/note.css +26 -0
- package/src/css/reset.css +12 -0
- package/src/css/table.css +14 -0
- package/src/css/tooltip.css +11 -0
- package/src/headings.ts +200 -0
- package/{dist/changelog-IPI5F42D.svg → src/icons/changelog.svg} +0 -0
- package/{dist/discord-JD33TUSF.svg → src/icons/discord.svg} +0 -0
- package/{dist/github-P5ZSKN2N.svg → src/icons/github.svg} +0 -0
- package/{dist/heart-OINVKOXO.svg → src/icons/heart.svg} +0 -0
- package/{dist/twitter-I7DXDN3J.svg → src/icons/twitter.svg} +0 -0
- package/src/index.ts +3 -0
- package/src/installSectionUrlHashs.ts +50 -0
- package/src/markdownHeadingsVitePlugin.ts +128 -0
- package/src/navigation/Navigation-highlight.css +41 -0
- package/src/navigation/Navigation-items.css +122 -0
- package/src/navigation/Navigation-layout.css +118 -0
- package/src/navigation/Navigation.client.old.ts +303 -0
- package/src/navigation/Navigation.client.ts +19 -0
- package/src/navigation/Navigation.css +12 -0
- package/src/navigation/Navigation.tsx +228 -0
- package/src/navigation/NavigationHeader.tsx +97 -0
- package/src/navigation/navigation-fullscreen/NavigationFullscreenButton.css +32 -0
- package/src/navigation/navigation-fullscreen/NavigationFullscreenButton.tsx +44 -0
- package/{dist/chevron-K3WPYLOP.svg → src/navigation/navigation-fullscreen/chevron.svg} +0 -0
- package/{dist/close-IQXTDOHV.svg → src/navigation/navigation-fullscreen/close.svg} +0 -0
- package/src/navigation/navigation-fullscreen/initNavigationFullscreen.ts +115 -0
- package/src/parseEmojis.ts +33 -0
- package/src/renderer/_default.page.client.ts +4 -0
- package/src/renderer/_default.page.server.tsx +69 -0
- package/src/renderer/usePageContext.tsx +25 -0
- package/src/types.ts +2 -0
- package/src/utils/Emoji/Emoji.ts +216 -0
- package/src/utils/Emoji/assets.ts +9 -0
- package/{dist/compass-2RWQU3E4.svg → src/utils/Emoji/compass.svg} +0 -0
- package/{dist/engine-6Q6VSCVA.png → src/utils/Emoji/engine.png} +0 -0
- package/src/utils/Emoji/index.ts +1 -0
- package/{dist/mechanical-arm-TR7IQQMG.svg → src/utils/Emoji/mechanical-arm.svg} +0 -0
- package/src/utils/Emoji/mountain.svg +1 -0
- package/{dist/road-fork-3WZLW3HB.svg → src/utils/Emoji/road-fork.svg} +0 -0
- package/{dist/shield-CU45RG5C.svg → src/utils/Emoji/shield.svg} +0 -0
- package/{dist/typescript-ALIPKLRM.svg → src/utils/Emoji/typescript.svg} +0 -0
- package/src/utils/assert.ts +39 -0
- package/src/utils/client.ts +2 -0
- package/src/utils/crawlAllFiles.ts +17 -0
- package/src/utils/determineSectionUrlHash.ts +35 -0
- package/src/utils/filesystemPathHandling.ts +42 -0
- package/src/utils/filter.ts +12 -0
- package/src/utils/isBrowser.ts +5 -0
- package/src/utils/jsxToTextContent.ts +11 -0
- package/src/utils/objectAssign.ts +9 -0
- package/src/utils/server.ts +8 -0
- package/src/vite.config.ts +52 -0
- package/dist/chunk-7HKDCMSZ.js +0 -154
- package/dist/chunk-7HKDCMSZ.js.map +0 -1
- package/dist/chunk-G2A5MZJA.js +0 -48
- package/dist/chunk-G2A5MZJA.js.map +0 -1
- package/dist/chunk-GBXQT242.js.map +0 -1
- package/dist/chunk-JS5BGVDK.js +0 -178
- package/dist/chunk-JS5BGVDK.js.map +0 -1
- package/dist/chunk-OEVBWUR6.js +0 -92
- package/dist/chunk-OEVBWUR6.js.map +0 -1
- package/dist/chunk-TTLAZ2T2.js +0 -8
- package/dist/chunk-TTLAZ2T2.js.map +0 -1
- package/dist/cli/index.d.ts +0 -1
- package/dist/cli/index.js +0 -35
- package/dist/cli/index.js.map +0 -1
- package/dist/components/features/FeatureList.css.map +0 -1
- package/dist/components/features/FeatureList.d.ts +0 -13
- package/dist/components/features/FeatureList.js +0 -8
- package/dist/components/features/FeatureList.js.map +0 -1
- package/dist/components/features/initFeatureList.d.ts +0 -3
- package/dist/components/features/initFeatureList.js +0 -60
- package/dist/components/features/initFeatureList.js.map +0 -1
- package/dist/index.css +0 -121
- package/dist/index.css.map +0 -1
- package/dist/renderer/_default.page.client.css +0 -263
- package/dist/renderer/_default.page.client.css.map +0 -1
- package/dist/renderer/_default.page.client.d.ts +0 -1
- package/dist/renderer/_default.page.client.js +0 -180
- package/dist/renderer/_default.page.client.js.map +0 -1
- package/dist/renderer/_default.page.server.css +0 -312
- package/dist/renderer/_default.page.server.css.map +0 -1
- package/dist/renderer/_default.page.server.d.ts +0 -22
- package/dist/renderer/_default.page.server.js +0 -602
- package/dist/renderer/_default.page.server.js.map +0 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { initNavigationFullscreen } from './navigation-fullscreen/initNavigationFullscreen'
|
|
2
|
+
|
|
3
|
+
activateNavigationMask()
|
|
4
|
+
activateMenuToggle()
|
|
5
|
+
initNavigationFullscreen()
|
|
6
|
+
|
|
7
|
+
function activateMenuToggle() {
|
|
8
|
+
const menuToggle = document.getElementById('menu-toggle')!
|
|
9
|
+
menuToggle.onclick = toggleNavigation
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function activateNavigationMask() {
|
|
13
|
+
const navigationMask = document.getElementById('navigation-mask')!
|
|
14
|
+
navigationMask.onclick = toggleNavigation
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function toggleNavigation() {
|
|
18
|
+
document.body.classList.toggle('show-menu')
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
@import './Navigation-layout.css';
|
|
2
|
+
@import './Navigation-items.css';
|
|
3
|
+
@import './Navigation-highlight.css';
|
|
4
|
+
|
|
5
|
+
#navigation-container {
|
|
6
|
+
--background-color: #f0f0f0;
|
|
7
|
+
padding-bottom: 70px;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
html.navigation-fullscreen #detached-note {
|
|
11
|
+
display: none;
|
|
12
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
export { Navigation }
|
|
2
|
+
export { NavigationMask }
|
|
3
|
+
|
|
4
|
+
import React from 'react'
|
|
5
|
+
import { NavigationHeader } from './NavigationHeader'
|
|
6
|
+
import { Heading } from '../headings'
|
|
7
|
+
import { assert, Emoji } from '../utils/server'
|
|
8
|
+
import './Navigation.css'
|
|
9
|
+
import { NavigationFullscreenClose } from './navigation-fullscreen/NavigationFullscreenButton'
|
|
10
|
+
|
|
11
|
+
function Navigation({
|
|
12
|
+
pageContext
|
|
13
|
+
}: {
|
|
14
|
+
pageContext: {
|
|
15
|
+
headingsWithSubHeadings: Heading[]
|
|
16
|
+
urlPathname: string
|
|
17
|
+
isDetachedPage: boolean
|
|
18
|
+
}
|
|
19
|
+
}) {
|
|
20
|
+
const { isDetachedPage } = pageContext
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
<div id="navigation-container">
|
|
24
|
+
<NavigationHeader />
|
|
25
|
+
{isDetachedPage && <DetachedPageNote />}
|
|
26
|
+
<NavigationContent pageContext={pageContext} />
|
|
27
|
+
{/* <ScrollOverlay /> */}
|
|
28
|
+
<NavigationFullscreenClose />
|
|
29
|
+
</div>
|
|
30
|
+
</>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function NavigationMask() {
|
|
35
|
+
return <div id="navigation-mask" />
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function NavigationContent({
|
|
39
|
+
pageContext
|
|
40
|
+
}: {
|
|
41
|
+
pageContext: {
|
|
42
|
+
headingsWithSubHeadings: Heading[]
|
|
43
|
+
urlPathname: string
|
|
44
|
+
isDetachedPage: boolean
|
|
45
|
+
}
|
|
46
|
+
}) {
|
|
47
|
+
const headings = getHeadingsWithComputedProps(pageContext)
|
|
48
|
+
const headingsGrouped = groupHeadings(headings)
|
|
49
|
+
return (
|
|
50
|
+
<div id="navigation-content">
|
|
51
|
+
<div className="nav-column" style={{ position: 'relative' }}>
|
|
52
|
+
{headingsGrouped.map((headingsH1, i) => (
|
|
53
|
+
<div className="nav-h1-group" key={i}>
|
|
54
|
+
<Heading heading={headingsH1} />
|
|
55
|
+
{headingsH1.headings.map((heading, j) => (
|
|
56
|
+
<Heading heading={heading} key={j} />
|
|
57
|
+
))}
|
|
58
|
+
</div>
|
|
59
|
+
))}
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function Heading({
|
|
66
|
+
heading
|
|
67
|
+
}: {
|
|
68
|
+
heading: {
|
|
69
|
+
level: number
|
|
70
|
+
url?: string
|
|
71
|
+
titleInNav: string | JSX.Element
|
|
72
|
+
computed: {
|
|
73
|
+
isActive: boolean
|
|
74
|
+
isActiveFirst: boolean
|
|
75
|
+
isActiveLast: boolean
|
|
76
|
+
isChildOfListHeading: boolean
|
|
77
|
+
isFirstOfItsKind: boolean
|
|
78
|
+
isLastOfItsKind: boolean
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}) {
|
|
82
|
+
assert([1, 2, 3, 4].includes(heading.level), heading)
|
|
83
|
+
return (
|
|
84
|
+
<a
|
|
85
|
+
className={[
|
|
86
|
+
'nav-item',
|
|
87
|
+
'nav-item-h' + heading.level,
|
|
88
|
+
heading.computed.isActive && ' is-active',
|
|
89
|
+
heading.computed.isActiveFirst && ' is-active-first',
|
|
90
|
+
heading.computed.isActiveLast && ' is-active-last',
|
|
91
|
+
heading.computed.isChildOfListHeading && 'nav-item-parent-is-list-heading',
|
|
92
|
+
heading.computed.isFirstOfItsKind && 'nav-item-first-of-its-kind',
|
|
93
|
+
heading.computed.isLastOfItsKind && 'nav-item-last-of-its-kind'
|
|
94
|
+
]
|
|
95
|
+
.filter(Boolean)
|
|
96
|
+
.join(' ')}
|
|
97
|
+
href={heading.url || undefined}
|
|
98
|
+
>
|
|
99
|
+
{/* <span className="nav-item-text">{heading.titleInNav}</span> */}
|
|
100
|
+
{heading.titleInNav}
|
|
101
|
+
</a>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function groupHeadings<T extends { level: number }>(headings: T[]) {
|
|
106
|
+
const headingsGrouped: (T & { headings: T[] })[] = []
|
|
107
|
+
headings.forEach((heading) => {
|
|
108
|
+
if (heading.level === 1) {
|
|
109
|
+
headingsGrouped.push({ ...heading, headings: [] })
|
|
110
|
+
} else {
|
|
111
|
+
headingsGrouped[headingsGrouped.length - 1].headings.push(heading)
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
return headingsGrouped
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function getHeadingsWithComputedProps(pageContext: {
|
|
118
|
+
headingsWithSubHeadings: Heading[]
|
|
119
|
+
urlPathname: string
|
|
120
|
+
isDetachedPage: boolean
|
|
121
|
+
}) {
|
|
122
|
+
const { headingsWithSubHeadings, urlPathname } = pageContext
|
|
123
|
+
return headingsWithSubHeadings.map((heading, i) => {
|
|
124
|
+
assert([1, 2, 3, 4].includes(heading.level), heading)
|
|
125
|
+
|
|
126
|
+
const headingPrevious = headingsWithSubHeadings[i - 1]
|
|
127
|
+
const headingNext = headingsWithSubHeadings[i + 1]
|
|
128
|
+
|
|
129
|
+
let isActiveFirst = false
|
|
130
|
+
let isActiveLast = false
|
|
131
|
+
let isActive = false
|
|
132
|
+
if (heading.url === urlPathname) {
|
|
133
|
+
assert(heading.level === 2, { urlPathname })
|
|
134
|
+
isActive = true
|
|
135
|
+
isActiveFirst = true
|
|
136
|
+
if (headingNext?.level !== 3) {
|
|
137
|
+
isActiveLast = true
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (heading.level === 3) {
|
|
141
|
+
isActive = true
|
|
142
|
+
if (headingNext?.level !== 3) {
|
|
143
|
+
isActiveLast = true
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const isFirstOfItsKind = heading.level !== headingPrevious?.level
|
|
148
|
+
const isLastOfItsKind = heading.level !== headingNext?.level
|
|
149
|
+
const isChildOfListHeading = !!heading.parentHeadings[0]?.isListTitle
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
...heading,
|
|
153
|
+
computed: {
|
|
154
|
+
isActive,
|
|
155
|
+
isActiveFirst,
|
|
156
|
+
isActiveLast,
|
|
157
|
+
isFirstOfItsKind,
|
|
158
|
+
isLastOfItsKind,
|
|
159
|
+
isChildOfListHeading
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function ScrollOverlay() {
|
|
166
|
+
// const width = '1px'
|
|
167
|
+
// const color = '#aaa'
|
|
168
|
+
return (
|
|
169
|
+
<div
|
|
170
|
+
id="scroll-overlay"
|
|
171
|
+
style={{
|
|
172
|
+
pointerEvents: 'none',
|
|
173
|
+
position: 'absolute',
|
|
174
|
+
left: '0',
|
|
175
|
+
width: '100%',
|
|
176
|
+
/*
|
|
177
|
+
background: `linear-gradient(to right, ${color} ${width}, transparent ${width}) 0 0,
|
|
178
|
+
linear-gradient(to right, ${color} ${width}, transparent ${width}) 0 100%,
|
|
179
|
+
linear-gradient(to left, ${color} ${width}, transparent ${width}) 100% 0,
|
|
180
|
+
linear-gradient(to left, ${color} ${width}, transparent ${width}) 100% 100%,
|
|
181
|
+
linear-gradient(to bottom, ${color} ${width}, transparent ${width}) 0 0,
|
|
182
|
+
linear-gradient(to bottom, ${color} ${width}, transparent ${width}) 100% 0,
|
|
183
|
+
linear-gradient(to top, ${color} ${width}, transparent ${width}) 0 100%,
|
|
184
|
+
linear-gradient(to top, ${color} ${width}, transparent ${width}) 100% 100%`,
|
|
185
|
+
//*/
|
|
186
|
+
//borderRight: `5px solid ${color}`,
|
|
187
|
+
borderRight: `3px solid #666`,
|
|
188
|
+
//border: `1px solid ${color}`,
|
|
189
|
+
boxSizing: 'border-box',
|
|
190
|
+
// backgroundColor: 'rgba(0,0,0,0.03)',
|
|
191
|
+
backgroundRepeat: 'no-repeat',
|
|
192
|
+
|
|
193
|
+
backgroundSize: '10px 10px'
|
|
194
|
+
}}
|
|
195
|
+
/>
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function DetachedPageNote() {
|
|
200
|
+
return (
|
|
201
|
+
<div
|
|
202
|
+
id="detached-note"
|
|
203
|
+
style={{
|
|
204
|
+
backgroundColor: 'var(--background-color)',
|
|
205
|
+
textAlign: 'left',
|
|
206
|
+
marginLeft: 10,
|
|
207
|
+
marginRight: 10,
|
|
208
|
+
marginTop: 30,
|
|
209
|
+
marginBottom: -8,
|
|
210
|
+
borderRadius: 5,
|
|
211
|
+
padding: 10
|
|
212
|
+
}}
|
|
213
|
+
>
|
|
214
|
+
<Emoji name="info" />{' '}
|
|
215
|
+
<b>
|
|
216
|
+
<em>Detached</em>
|
|
217
|
+
</b>
|
|
218
|
+
<span
|
|
219
|
+
style={{
|
|
220
|
+
opacity: 0.8
|
|
221
|
+
}}
|
|
222
|
+
>
|
|
223
|
+
{' '}
|
|
224
|
+
— this page isn't listed in the navigation menu below.
|
|
225
|
+
</span>
|
|
226
|
+
</div>
|
|
227
|
+
)
|
|
228
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import iconGithub from '../icons/github.svg'
|
|
3
|
+
import iconTwitter from '../icons/twitter.svg'
|
|
4
|
+
import iconDiscord from '../icons/discord.svg'
|
|
5
|
+
import iconChangelog from '../icons/changelog.svg'
|
|
6
|
+
import { usePageContext } from '../renderer/usePageContext'
|
|
7
|
+
|
|
8
|
+
export { NavigationHeader }
|
|
9
|
+
|
|
10
|
+
function NavigationHeader() {
|
|
11
|
+
const pageContext = usePageContext()
|
|
12
|
+
return (
|
|
13
|
+
<div
|
|
14
|
+
id="navigation-header"
|
|
15
|
+
style={{
|
|
16
|
+
display: 'flex',
|
|
17
|
+
flexDirection: 'column',
|
|
18
|
+
alignItems: 'center',
|
|
19
|
+
marginBottom: -5
|
|
20
|
+
}}
|
|
21
|
+
>
|
|
22
|
+
<a
|
|
23
|
+
id="navigation-header-logo"
|
|
24
|
+
style={{
|
|
25
|
+
display: 'flex',
|
|
26
|
+
alignItems: 'center',
|
|
27
|
+
color: 'inherit',
|
|
28
|
+
justifyContent: 'left',
|
|
29
|
+
textDecoration: 'none',
|
|
30
|
+
paddingTop: 12,
|
|
31
|
+
paddingBottom: 7
|
|
32
|
+
}}
|
|
33
|
+
href="/"
|
|
34
|
+
>
|
|
35
|
+
{pageContext.config.navHeader}
|
|
36
|
+
</a>
|
|
37
|
+
<Links />
|
|
38
|
+
</div>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function Links() {
|
|
43
|
+
const pageContext = usePageContext()
|
|
44
|
+
const { projectInfo } = pageContext.config
|
|
45
|
+
return (
|
|
46
|
+
<div
|
|
47
|
+
style={{
|
|
48
|
+
display: 'flex',
|
|
49
|
+
alignItems: 'center',
|
|
50
|
+
paddingTop: 0,
|
|
51
|
+
justifyContent: 'left'
|
|
52
|
+
}}
|
|
53
|
+
>
|
|
54
|
+
<SocialLink className="decolorize-4" icon={iconGithub} href={projectInfo.githubRepository} />
|
|
55
|
+
<SocialLink className="decolorize-6" icon={iconDiscord} href={projectInfo.discordInvite} />
|
|
56
|
+
<SocialLink className="decolorize-7" icon={iconTwitter} href={projectInfo.twitterProfile} />
|
|
57
|
+
<div id="docsearch-desktop" />
|
|
58
|
+
<ChangelogButton />
|
|
59
|
+
</div>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function ChangelogButton() {
|
|
64
|
+
const pageContext = usePageContext()
|
|
65
|
+
const { projectInfo } = pageContext.config
|
|
66
|
+
return (
|
|
67
|
+
<a
|
|
68
|
+
href={`${projectInfo.githubRepository}/blob/main/CHANGELOG.md`}
|
|
69
|
+
className="button colorize-on-hover"
|
|
70
|
+
style={{
|
|
71
|
+
display: 'flex',
|
|
72
|
+
alignItems: 'center',
|
|
73
|
+
padding: '1px 7px',
|
|
74
|
+
marginLeft: 2,
|
|
75
|
+
fontSize: '0.97em',
|
|
76
|
+
color: 'inherit'
|
|
77
|
+
}}
|
|
78
|
+
>
|
|
79
|
+
<span className="decolorize-7">v{projectInfo.projectVersion}</span>
|
|
80
|
+
<img className="decolorize-6" src={iconChangelog} height={16} style={{ marginLeft: 5 }} />
|
|
81
|
+
</a>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function SocialLink({ className, icon, href }: { className: string; icon: string; href: string }) {
|
|
86
|
+
return (
|
|
87
|
+
<>
|
|
88
|
+
<a
|
|
89
|
+
className={'colorize-on-hover ' + className}
|
|
90
|
+
href={href}
|
|
91
|
+
style={{ padding: 3, display: 'inline-block', lineHeight: 0 }}
|
|
92
|
+
>
|
|
93
|
+
<img src={icon} height="20" style={{}} />
|
|
94
|
+
</a>
|
|
95
|
+
</>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--navigation-fullscreen-button-width: 20px;
|
|
3
|
+
}
|
|
4
|
+
#navigation-fullscreen-button {
|
|
5
|
+
width: var(--navigation-fullscreen-button-width);
|
|
6
|
+
position: relative;
|
|
7
|
+
z-index: 2;
|
|
8
|
+
}
|
|
9
|
+
#navigation-wrapper:hover + #navigation-fullscreen-button > div > div,
|
|
10
|
+
#navigation-fullscreen-button:hover > div > div {
|
|
11
|
+
left: 0px;
|
|
12
|
+
}
|
|
13
|
+
#navigation-fullscreen-button > div > div {
|
|
14
|
+
transition: all 0.3s ease-in-out;
|
|
15
|
+
left: -20px;
|
|
16
|
+
position: absolute;
|
|
17
|
+
height: 100%;
|
|
18
|
+
width: 100%;
|
|
19
|
+
background-color: #eee !important;
|
|
20
|
+
background: url('./chevron.svg');
|
|
21
|
+
background-repeat: no-repeat;
|
|
22
|
+
background-position: center center;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
html:not(.navigation-fullscreen) #navigation-fullscreen-close {
|
|
26
|
+
display: none;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
html.navigation-fullscreen #page-content {
|
|
30
|
+
/* Make `Ctrl-F` browser search only crawl the navigation menu */
|
|
31
|
+
visibility: hidden;
|
|
32
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export { NavigationFullscreenButton }
|
|
2
|
+
export { NavigationFullscreenClose }
|
|
3
|
+
|
|
4
|
+
import React from 'react'
|
|
5
|
+
import './NavigationFullscreenButton.css'
|
|
6
|
+
import closeIcon from './close.svg'
|
|
7
|
+
|
|
8
|
+
function NavigationFullscreenButton() {
|
|
9
|
+
return (
|
|
10
|
+
<>
|
|
11
|
+
<a id="navigation-fullscreen-button">
|
|
12
|
+
<div
|
|
13
|
+
style={{
|
|
14
|
+
position: 'fixed',
|
|
15
|
+
cursor: 'pointer',
|
|
16
|
+
height: '100vh',
|
|
17
|
+
width: 20,
|
|
18
|
+
overflow: 'hidden'
|
|
19
|
+
}}
|
|
20
|
+
>
|
|
21
|
+
<div></div>
|
|
22
|
+
</div>
|
|
23
|
+
<div
|
|
24
|
+
style={{ position: 'fixed', height: '100vh', width: 20 }}
|
|
25
|
+
aria-label="Press <Esc>"
|
|
26
|
+
data-balloon-pos="right"
|
|
27
|
+
></div>
|
|
28
|
+
</a>
|
|
29
|
+
</>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function NavigationFullscreenClose() {
|
|
34
|
+
return (
|
|
35
|
+
<a
|
|
36
|
+
id="navigation-fullscreen-close"
|
|
37
|
+
style={{ position: 'fixed', top: 11, right: 15, zIndex: 10 }}
|
|
38
|
+
aria-label="Press <Esc>"
|
|
39
|
+
data-balloon-pos="left"
|
|
40
|
+
>
|
|
41
|
+
<img src={closeIcon} height={50} width={50} style={{ display: 'block' }} />
|
|
42
|
+
</a>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export { initNavigationFullscreen }
|
|
2
|
+
|
|
3
|
+
import { assert } from '../../utils/client'
|
|
4
|
+
|
|
5
|
+
let scrollPositionBeforeToggle: number = 0
|
|
6
|
+
|
|
7
|
+
function initNavigationFullscreen() {
|
|
8
|
+
updateColumnWidth()
|
|
9
|
+
window.addEventListener('resize', updateColumnWidth, { passive: true })
|
|
10
|
+
document.getElementById('navigation-fullscreen-button')!.onclick = toggleNavExpend
|
|
11
|
+
document.getElementById('navigation-fullscreen-close')!.onclick = toggleNavExpend
|
|
12
|
+
document.addEventListener(
|
|
13
|
+
// We don't use keydown to not interfere with user pressing `<Esc>` for closing the browser's `<Ctrl-F>` search diablog, see https://stackoverflow.com/questions/66595035/how-to-detect-escape-key-if-search-bar-of-browser-is-open
|
|
14
|
+
'keydown',
|
|
15
|
+
(ev) => {
|
|
16
|
+
if (ev.key === 'Escape') toggleNavExpend()
|
|
17
|
+
},
|
|
18
|
+
false
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function toggleNavExpend() {
|
|
23
|
+
const navContainer = document.getElementById('navigation-container')!
|
|
24
|
+
const scrollPos = navContainer.scrollTop
|
|
25
|
+
document.documentElement.classList.toggle('navigation-fullscreen')
|
|
26
|
+
if (scrollPositionBeforeToggle !== undefined) {
|
|
27
|
+
navContainer.scrollTop = scrollPositionBeforeToggle
|
|
28
|
+
}
|
|
29
|
+
scrollPositionBeforeToggle = scrollPos
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function updateColumnWidth() {
|
|
33
|
+
const navMinWidth = 299
|
|
34
|
+
const navH1Groups = Array.from(document.querySelectorAll('.nav-h1-group'))
|
|
35
|
+
const numberOfColumnsMax = navH1Groups.length
|
|
36
|
+
|
|
37
|
+
const widthAvailable = getViewportWidth()
|
|
38
|
+
const numberOfColumns = Math.max(1, Math.min(numberOfColumnsMax, Math.floor(widthAvailable / navMinWidth)))
|
|
39
|
+
|
|
40
|
+
let columns = navH1Groups.map((navH1Group) => {
|
|
41
|
+
const column = [
|
|
42
|
+
{
|
|
43
|
+
element: navH1Group,
|
|
44
|
+
elementHeight: navH1Group.children.length
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
return column
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
mergeColumns(columns, numberOfColumns)
|
|
51
|
+
|
|
52
|
+
const navContent = document.getElementById('navigation-content')!
|
|
53
|
+
|
|
54
|
+
Array.from(navContent.children).forEach((child) => {
|
|
55
|
+
assert(child.className === 'nav-column')
|
|
56
|
+
})
|
|
57
|
+
navContent.innerHTML = ''
|
|
58
|
+
|
|
59
|
+
columns.forEach((column) => {
|
|
60
|
+
const columnEl = document.createElement('div')
|
|
61
|
+
columnEl.className = 'nav-column'
|
|
62
|
+
column.forEach(({ element }) => {
|
|
63
|
+
columnEl.appendChild(element)
|
|
64
|
+
})
|
|
65
|
+
navContent.appendChild(columnEl)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const navItemMaxWidth = 350
|
|
69
|
+
navContent.style.maxWidth = `${numberOfColumns * navItemMaxWidth}px`
|
|
70
|
+
}
|
|
71
|
+
function getViewportWidth(): number {
|
|
72
|
+
// `window.innerWidth` inlcudes scrollbar width: https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes
|
|
73
|
+
return document.documentElement.clientWidth
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function mergeColumns<T>(columns: { element: T; elementHeight: number }[][], maxNumberOfColumns: number) {
|
|
77
|
+
assert(columns.length > 0)
|
|
78
|
+
assert(maxNumberOfColumns > 0)
|
|
79
|
+
if (columns.length <= maxNumberOfColumns) {
|
|
80
|
+
return columns
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let mergeCandidate = {
|
|
84
|
+
i: -1,
|
|
85
|
+
mergeHeight: Infinity
|
|
86
|
+
}
|
|
87
|
+
for (let i = 0; i <= columns.length - 2; i++) {
|
|
88
|
+
const column1 = columns[i + 0]
|
|
89
|
+
const column2 = columns[i + 1]
|
|
90
|
+
const column1Height = sum(column1.map((c) => c.elementHeight))
|
|
91
|
+
const column2Height = sum(column2.map((c) => c.elementHeight))
|
|
92
|
+
const mergeHeight = column1Height + column2Height
|
|
93
|
+
if (mergeCandidate.mergeHeight > mergeHeight) {
|
|
94
|
+
mergeCandidate = {
|
|
95
|
+
i,
|
|
96
|
+
mergeHeight
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
{
|
|
102
|
+
const { i } = mergeCandidate
|
|
103
|
+
assert(-1 < i && i < columns.length - 1, { i, columnsLength: columns.length, maxNumberOfColumns })
|
|
104
|
+
columns[i] = [...columns[i], ...columns[i + 1]]
|
|
105
|
+
columns.splice(i + 1, 1)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
mergeColumns(columns, maxNumberOfColumns)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function sum(arr: number[]): number {
|
|
112
|
+
let total = 0
|
|
113
|
+
arr.forEach((n) => (total += n))
|
|
114
|
+
return total
|
|
115
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export { parseEmojis }
|
|
2
|
+
|
|
3
|
+
import twemoji from 'twemoji'
|
|
4
|
+
|
|
5
|
+
const emojiList = {
|
|
6
|
+
// https://emojipedia.org/no-entry/
|
|
7
|
+
':no_entry:': 0x26d4,
|
|
8
|
+
// https://emojipedia.org/warning/
|
|
9
|
+
':warning:': 0x26a0,
|
|
10
|
+
// https://emojipedia.org/trophy/
|
|
11
|
+
':trophy:': 0x1f3c6
|
|
12
|
+
/*
|
|
13
|
+
// https://emojipedia.org/red-heart/
|
|
14
|
+
':heart:': 0x2764,
|
|
15
|
+
*/
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function parseEmojis(html: string) {
|
|
19
|
+
Object.entries(emojiList).forEach(([shortcode, codepoint]) => {
|
|
20
|
+
if (!html.includes(shortcode)) {
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
const emojiStr = twemoji.convert.fromCodePoint(codepoint as any)
|
|
24
|
+
let emojiImg: any = twemoji.parse(emojiStr, {
|
|
25
|
+
folder: 'svg',
|
|
26
|
+
ext: '.svg'
|
|
27
|
+
})
|
|
28
|
+
const style = 'height: 1.275em; width: 1.275em; vertical-align: -20%'
|
|
29
|
+
emojiImg = emojiImg.replace('<img class="emoji" ', `<img style="${style}" `)
|
|
30
|
+
html = html.split(shortcode).join(emojiImg)
|
|
31
|
+
})
|
|
32
|
+
return html
|
|
33
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import ReactDOMServer from 'react-dom/server'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { escapeInject, dangerouslySkipEscape } from 'vite-plugin-ssr'
|
|
4
|
+
import { PageLayout } from '../PageLayout'
|
|
5
|
+
import { resolvePageContext, PageContextOriginal } from '../config/resolvePageContext'
|
|
6
|
+
import { getDocSearchJS, getDocSearchCSS } from '../algolia/DocSearch'
|
|
7
|
+
import { parseEmojis } from '../parseEmojis'
|
|
8
|
+
import { assert } from '../utils/server'
|
|
9
|
+
|
|
10
|
+
export { render }
|
|
11
|
+
|
|
12
|
+
async function render(pageContextOriginal: PageContextOriginal) {
|
|
13
|
+
const { Page } = pageContextOriginal
|
|
14
|
+
const pageContextResolved = resolvePageContext(pageContextOriginal)
|
|
15
|
+
|
|
16
|
+
const page = (
|
|
17
|
+
<PageLayout pageContext={pageContextResolved}>
|
|
18
|
+
<Page />
|
|
19
|
+
</PageLayout>
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
const descriptionTag = pageContextResolved.isLandingPage
|
|
23
|
+
? dangerouslySkipEscape(`<meta name="description" content="${pageContextResolved.meta.tagline}" />`)
|
|
24
|
+
: ''
|
|
25
|
+
|
|
26
|
+
const docSearchJS = getDocSearchJS(pageContextResolved)
|
|
27
|
+
const docSearchCSS = getDocSearchCSS(pageContextResolved)
|
|
28
|
+
|
|
29
|
+
let pageHtml = ReactDOMServer.renderToString(page)
|
|
30
|
+
pageHtml = parseEmojis(pageHtml)
|
|
31
|
+
|
|
32
|
+
return escapeInject`<!DOCTYPE html>
|
|
33
|
+
<html>
|
|
34
|
+
<head>
|
|
35
|
+
<meta charset="UTF-8" />
|
|
36
|
+
<link rel="icon" href="${pageContextResolved.meta.faviconUrl}" />
|
|
37
|
+
<title>${pageContextResolved.meta.title}</title>
|
|
38
|
+
${descriptionTag}
|
|
39
|
+
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />
|
|
40
|
+
${docSearchCSS}
|
|
41
|
+
${getOpenGraphTags(pageContextOriginal.urlPathname, pageContextResolved.meta)}
|
|
42
|
+
</head>
|
|
43
|
+
<body>
|
|
44
|
+
<div id="page-view">${dangerouslySkipEscape(pageHtml)}</div>
|
|
45
|
+
${docSearchJS}
|
|
46
|
+
</body>
|
|
47
|
+
</html>`
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getOpenGraphTags(
|
|
51
|
+
url: string,
|
|
52
|
+
meta: { title: string; tagline: string; websiteUrl: string; twitterHandle: string; bannerUrl?: string }
|
|
53
|
+
) {
|
|
54
|
+
const { title, tagline, websiteUrl, twitterHandle, bannerUrl } = meta
|
|
55
|
+
|
|
56
|
+
assert(url.startsWith('/'))
|
|
57
|
+
if (url !== '/' || !bannerUrl) return ''
|
|
58
|
+
|
|
59
|
+
// See view-source:https://vitejs.dev/
|
|
60
|
+
return escapeInject`
|
|
61
|
+
<meta property="og:type" content="website">
|
|
62
|
+
<meta property="og:title" content="${title}">
|
|
63
|
+
<meta property="og:image" content="${bannerUrl}">
|
|
64
|
+
<meta property="og:url" content="${websiteUrl}">
|
|
65
|
+
<meta property="og:description" content="${tagline}">
|
|
66
|
+
<meta name="twitter:card" content="summary_large_image">
|
|
67
|
+
<meta name="twitter:site" content="${twitterHandle}">
|
|
68
|
+
`
|
|
69
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// `usePageContext` allows us to access `pageContext` in any React component.
|
|
2
|
+
// More infos: https://vite-plugin-ssr.com/pageContext-anywhere
|
|
3
|
+
|
|
4
|
+
import React, { useContext } from 'react'
|
|
5
|
+
import type { PageContextResolved } from '../config/resolvePageContext'
|
|
6
|
+
|
|
7
|
+
export { PageContextProvider }
|
|
8
|
+
export { usePageContext }
|
|
9
|
+
|
|
10
|
+
const Context = React.createContext<PageContextResolved>(undefined as any)
|
|
11
|
+
|
|
12
|
+
function PageContextProvider({
|
|
13
|
+
pageContext,
|
|
14
|
+
children
|
|
15
|
+
}: {
|
|
16
|
+
pageContext: PageContextResolved
|
|
17
|
+
children: React.ReactNode
|
|
18
|
+
}) {
|
|
19
|
+
return <Context.Provider value={pageContext}>{children}</Context.Provider>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function usePageContext(): PageContextResolved {
|
|
23
|
+
const pageContext = useContext(Context)
|
|
24
|
+
return pageContext
|
|
25
|
+
}
|
package/src/types.ts
ADDED