@brillout/docpress 0.8.14 → 0.8.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/Layout.tsx +254 -177
- package/MenuModal.tsx +42 -48
- package/{Links.tsx → NavSecondaryContent.tsx} +5 -8
- package/components/HorizontalLine.tsx +4 -3
- package/config/resolveHeadingsData.ts +7 -10
- package/css/code/block.css +5 -5
- package/css/code/inline.css +1 -1
- package/css/colorize-on-hover.css +2 -2
- package/dist/Layout.d.ts +10 -6
- package/dist/Layout.js +165 -120
- package/dist/MenuModal.js +34 -47
- package/dist/{Links.d.ts → NavSecondaryContent.d.ts} +3 -2
- package/dist/{Links.js → NavSecondaryContent.js} +5 -7
- package/dist/components/HorizontalLine.d.ts +1 -1
- package/dist/components/HorizontalLine.js +3 -2
- package/dist/config/resolveHeadingsData.d.ts +3 -4
- package/dist/config/resolveHeadingsData.js +5 -8
- package/dist/config/resolvePageContext.d.ts +2 -3
- package/dist/docsearch/SearchLink.js +1 -1
- package/dist/navigation/Collapsible.d.ts +10 -0
- package/dist/navigation/Collapsible.js +35 -0
- package/dist/navigation/Navigation.d.ts +0 -3
- package/dist/navigation/Navigation.js +106 -55
- package/dist/renderer/determineNavItemsColumnLayout.d.ts +3 -0
- package/dist/renderer/{determineColumnEntries.js → determineNavItemsColumnLayout.js} +34 -28
- package/dist/renderer/usePageContext.d.ts +2 -2
- package/dist/renderer/usePageContext.js +2 -4
- package/dist/utils/Style.d.ts +5 -0
- package/dist/utils/Style.js +6 -0
- package/dist/utils/cls.d.ts +3 -0
- package/dist/utils/cls.js +5 -0
- package/dist/utils/throttle.d.ts +1 -0
- package/dist/utils/throttle.js +14 -0
- package/docsearch/SearchLink.tsx +1 -1
- package/global.d.ts +1 -1
- package/navigation/Collapsible.css +11 -0
- package/navigation/Collapsible.tsx +64 -0
- package/navigation/Navigation.css +12 -6
- package/navigation/Navigation.tsx +191 -80
- package/package.json +1 -1
- package/renderer/{determineColumnEntries.ts → determineNavItemsColumnLayout.ts} +35 -29
- package/renderer/initOnNavigation.ts +37 -0
- package/renderer/onRenderClient.tsx +2 -0
- package/renderer/usePageContext.tsx +2 -5
- package/utils/Style.tsx +7 -0
- package/utils/cls.ts +8 -0
- package/utils/throttle.ts +10 -0
- package/dist/renderer/determineColumnEntries.d.ts +0 -3
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
padding-left: var(--padding-left-global);
|
|
21
21
|
padding-right: 4px;
|
|
22
22
|
}
|
|
23
|
-
.nav-item-level-
|
|
23
|
+
.nav-item-level-4:first-child {
|
|
24
24
|
padding-top: 0;
|
|
25
25
|
margin-top: -2px;
|
|
26
26
|
}
|
|
@@ -59,9 +59,6 @@
|
|
|
59
59
|
padding: var(--padding) 0;
|
|
60
60
|
padding-left: calc(var(--padding-left-global) + 5px);
|
|
61
61
|
}
|
|
62
|
-
#menu-modal .nav-item-level-3 {
|
|
63
|
-
border-right: 4px solid #eee;
|
|
64
|
-
}
|
|
65
62
|
.nav-item-level-3 {
|
|
66
63
|
--shadow-size: 9px;
|
|
67
64
|
--shadow-color: rgba(0, 0, 0, 0.11);
|
|
@@ -106,8 +103,17 @@
|
|
|
106
103
|
position: absolute;
|
|
107
104
|
top: 0;
|
|
108
105
|
left: 0;
|
|
109
|
-
|
|
110
|
-
height: calc(100% - 53px);
|
|
106
|
+
height: 100%;
|
|
111
107
|
width: 3px;
|
|
112
108
|
z-index: 99;
|
|
113
109
|
}
|
|
110
|
+
|
|
111
|
+
:has(> .collapsible-icon) {
|
|
112
|
+
position: relative;
|
|
113
|
+
}
|
|
114
|
+
.collapsible-icon {
|
|
115
|
+
position: absolute;
|
|
116
|
+
top: calc(100% / 2 - 6px);
|
|
117
|
+
margin-left: 9px;
|
|
118
|
+
vertical-align: middle;
|
|
119
|
+
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
export { NavigationContent }
|
|
3
3
|
// TODO/refactor: do this only on the server side?
|
|
4
4
|
export type { NavItem }
|
|
5
|
-
export type { NavItemAll }
|
|
6
5
|
|
|
7
6
|
import React, { useEffect, useState } from 'react'
|
|
8
7
|
import { assert, assertWarning, jsxToTextContent } from '../utils/server'
|
|
@@ -12,7 +11,9 @@ import { usePageContext } from '../renderer/usePageContext'
|
|
|
12
11
|
import '@docsearch/css'
|
|
13
12
|
import '../global.d.ts'
|
|
14
13
|
import { getViewportWidth } from '../utils/getViewportWidth'
|
|
15
|
-
import {
|
|
14
|
+
import { navLeftWidthMax, navLeftWidthMin } from '../Layout'
|
|
15
|
+
import { throttle } from '../utils/throttle'
|
|
16
|
+
import { Collapsible } from './Collapsible'
|
|
16
17
|
|
|
17
18
|
type NavItem = {
|
|
18
19
|
level: number
|
|
@@ -21,8 +22,6 @@ type NavItem = {
|
|
|
21
22
|
title: string
|
|
22
23
|
titleInNav: string
|
|
23
24
|
menuModalFullWidth?: true
|
|
24
|
-
}
|
|
25
|
-
type NavItemAll = NavItem & {
|
|
26
25
|
isColumnEntry?: ColumnMap
|
|
27
26
|
}
|
|
28
27
|
function NavigationContent(props: {
|
|
@@ -35,12 +34,12 @@ function NavigationContent(props: {
|
|
|
35
34
|
|
|
36
35
|
let navContent: React.ReactNode
|
|
37
36
|
if (!props.columnLayout) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
let navItemsRelevant = navItemsWithComputed
|
|
38
|
+
if (props.showOnlyRelevant) navItemsRelevant = navItemsRelevant.filter((navItemGroup) => navItemGroup.isRelevant)
|
|
39
|
+
navContent = navItemsRelevant.map((navItem, i) => <NavItemComponent navItem={navItem} key={i} />)
|
|
41
40
|
} else {
|
|
42
41
|
assert(!props.showOnlyRelevant)
|
|
43
|
-
navContent = <
|
|
42
|
+
navContent = <NavigationWithColumnLayout navItemsWithComputed={navItemsWithComputed} />
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
return (
|
|
@@ -50,75 +49,124 @@ function NavigationContent(props: {
|
|
|
50
49
|
)
|
|
51
50
|
}
|
|
52
51
|
|
|
53
|
-
function
|
|
52
|
+
function NavigationWithColumnLayout(props: { navItemsWithComputed: NavItemComputed[] }) {
|
|
54
53
|
let [viewportWidth, setViewportWidth] = useState<number | undefined>()
|
|
55
54
|
const updateviewportwidth = () => setViewportWidth(getViewportWidth())
|
|
56
55
|
useEffect(() => {
|
|
57
56
|
updateviewportwidth()
|
|
58
|
-
window.addEventListener('resize', updateviewportwidth, { passive: true })
|
|
57
|
+
window.addEventListener('resize', throttle(updateviewportwidth, 300), { passive: true })
|
|
59
58
|
})
|
|
60
|
-
|
|
61
59
|
const navItemsByColumnLayouts = getNavItemsByColumnLayouts(props.navItemsWithComputed, viewportWidth)
|
|
62
|
-
const margin = 40
|
|
63
|
-
|
|
64
60
|
return (
|
|
65
61
|
<>
|
|
66
|
-
{navItemsByColumnLayouts.map((
|
|
67
|
-
<div
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
>
|
|
90
|
-
{columnEntry.map((navItems, k) => (
|
|
91
|
-
<div key={k} style={{ marginTop: isFullWidth ? undefined : margin }}>
|
|
92
|
-
{navItems.map((navItem, l) => (
|
|
93
|
-
<NavItemComponent navItem={navItem} key={l} />
|
|
94
|
-
))}
|
|
95
|
-
<CategoryBorder navItemLevel1={isFullWidth ? undefined : navItems[0]!} />
|
|
96
|
-
</div>
|
|
97
|
-
))}
|
|
62
|
+
{navItemsByColumnLayouts.map((columnLayout, i) => (
|
|
63
|
+
<div key={i}>
|
|
64
|
+
{columnLayout.isFullWidthCategory ? (
|
|
65
|
+
<div style={{ marginTop: 0 }}>
|
|
66
|
+
<ColumnsWrapper numberOfColumns={columnLayout.columns.length}>
|
|
67
|
+
<Collapsible
|
|
68
|
+
head={(onClick) => <NavItemComponent navItem={columnLayout.navItemLevel1} onClick={onClick} />}
|
|
69
|
+
disabled={columnLayout.columns.length > 1}
|
|
70
|
+
collapsedInit={!columnLayout.navItemLevel1.isRelevant}
|
|
71
|
+
marginBottomOnExpand={10}
|
|
72
|
+
>
|
|
73
|
+
<ColumnsLayout className="collapsible">
|
|
74
|
+
{columnLayout.columns.map((column, j) => (
|
|
75
|
+
<Column key={j}>
|
|
76
|
+
{column.navItems.map((navItem, k) => (
|
|
77
|
+
<NavItemComponent key={k} navItem={navItem} />
|
|
78
|
+
))}
|
|
79
|
+
</Column>
|
|
80
|
+
))}
|
|
81
|
+
<CategoryBorder navItemLevel1={columnLayout.navItemLevel1} />
|
|
82
|
+
</ColumnsLayout>
|
|
83
|
+
</Collapsible>
|
|
84
|
+
</ColumnsWrapper>
|
|
98
85
|
</div>
|
|
99
|
-
)
|
|
100
|
-
|
|
86
|
+
) : (
|
|
87
|
+
<ColumnsWrapper numberOfColumns={columnLayout.columns.length}>
|
|
88
|
+
<ColumnsLayout>
|
|
89
|
+
{columnLayout.columns.map((column, j) => (
|
|
90
|
+
<Column key={j}>
|
|
91
|
+
{column.categories.map((category, k) => (
|
|
92
|
+
<div key={k} style={{ marginBottom: 0 }}>
|
|
93
|
+
<Collapsible
|
|
94
|
+
head={(onClick) => <NavItemComponent navItem={category.navItemLevel1} onClick={onClick} />}
|
|
95
|
+
disabled={columnLayout.columns.length > 1}
|
|
96
|
+
collapsedInit={!category.navItemLevel1.isRelevant}
|
|
97
|
+
marginBottomOnExpand={40}
|
|
98
|
+
>
|
|
99
|
+
{category.navItems.map((navItem, l) => (
|
|
100
|
+
<NavItemComponent key={l} navItem={navItem} />
|
|
101
|
+
))}
|
|
102
|
+
<CategoryBorder navItemLevel1={category.navItemLevel1} />
|
|
103
|
+
</Collapsible>
|
|
104
|
+
</div>
|
|
105
|
+
))}
|
|
106
|
+
</Column>
|
|
107
|
+
))}
|
|
108
|
+
</ColumnsLayout>
|
|
109
|
+
</ColumnsWrapper>
|
|
110
|
+
)}
|
|
101
111
|
</div>
|
|
102
112
|
))}
|
|
103
113
|
</>
|
|
104
114
|
)
|
|
105
115
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return !navItemLevel1 ? null : (
|
|
116
|
+
function Column({ children }: { children: React.ReactNode }) {
|
|
117
|
+
return (
|
|
109
118
|
<div
|
|
110
|
-
className="category-border"
|
|
111
119
|
style={{
|
|
112
|
-
|
|
120
|
+
flexGrow: 1,
|
|
121
|
+
maxWidth: navLeftWidthMax,
|
|
122
|
+
display: 'flex',
|
|
123
|
+
flexDirection: 'column',
|
|
113
124
|
}}
|
|
114
|
-
|
|
125
|
+
>
|
|
126
|
+
{children}
|
|
127
|
+
</div>
|
|
115
128
|
)
|
|
116
129
|
}
|
|
130
|
+
function ColumnsWrapper({ children, numberOfColumns }: { children: React.ReactNode; numberOfColumns: number }) {
|
|
131
|
+
return (
|
|
132
|
+
<div
|
|
133
|
+
style={{
|
|
134
|
+
width: numberOfColumns * (navLeftWidthMax + 20),
|
|
135
|
+
maxWidth: '100%',
|
|
136
|
+
margin: 'auto',
|
|
137
|
+
}}
|
|
138
|
+
>
|
|
139
|
+
{children}
|
|
140
|
+
</div>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
function ColumnsLayout({ children, className }: { children: React.ReactNode; className?: string }) {
|
|
144
|
+
return (
|
|
145
|
+
<div
|
|
146
|
+
className={className}
|
|
147
|
+
style={{
|
|
148
|
+
display: 'flex',
|
|
149
|
+
justifyContent: 'space-between',
|
|
150
|
+
}}
|
|
151
|
+
>
|
|
152
|
+
{children}
|
|
153
|
+
</div>
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
function CategoryBorder({ navItemLevel1 }: { navItemLevel1: NavItemComputed }) {
|
|
157
|
+
assert(navItemLevel1.level === 1)
|
|
158
|
+
return <div className="category-border" style={{ background: navItemLevel1.color! }} />
|
|
159
|
+
}
|
|
117
160
|
|
|
161
|
+
type PropsNavItem = PropsAnchor & PropsSpan
|
|
162
|
+
type PropsAnchor = React.HTMLProps<HTMLAnchorElement>
|
|
163
|
+
type PropsSpan = React.HTMLProps<HTMLSpanElement>
|
|
118
164
|
function NavItemComponent({
|
|
119
165
|
navItem,
|
|
166
|
+
onClick,
|
|
120
167
|
}: {
|
|
121
168
|
navItem: NavItemComputed
|
|
169
|
+
onClick?: PropsNavItem['onClick']
|
|
122
170
|
}) {
|
|
123
171
|
assert([1, 2, 3, 4].includes(navItem.level), navItem)
|
|
124
172
|
|
|
@@ -141,9 +189,20 @@ function NavItemComponent({
|
|
|
141
189
|
)
|
|
142
190
|
}
|
|
143
191
|
|
|
144
|
-
|
|
192
|
+
let children: JSX.Element = titleInNavJsx
|
|
193
|
+
if (navItem.level === 1) {
|
|
194
|
+
children = (
|
|
195
|
+
<>
|
|
196
|
+
{children}
|
|
197
|
+
<Chevron className="collapsible-icon" height={9} />
|
|
198
|
+
</>
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const props: PropsNavItem = {
|
|
145
203
|
href: navItem.url ?? undefined,
|
|
146
|
-
children
|
|
204
|
+
children,
|
|
205
|
+
onClick,
|
|
147
206
|
className: [
|
|
148
207
|
'nav-item',
|
|
149
208
|
'nav-item-level-' + navItem.level,
|
|
@@ -159,8 +218,6 @@ function NavItemComponent({
|
|
|
159
218
|
['--category-color']: navItem.color!,
|
|
160
219
|
}
|
|
161
220
|
}
|
|
162
|
-
type PropsAnchor = React.HTMLProps<HTMLAnchorElement>
|
|
163
|
-
type PropsSpan = React.HTMLProps<HTMLSpanElement>
|
|
164
221
|
|
|
165
222
|
if (navItem.level === 2 || navItem.level === 3) {
|
|
166
223
|
return <a {...props} />
|
|
@@ -169,45 +226,88 @@ function NavItemComponent({
|
|
|
169
226
|
}
|
|
170
227
|
}
|
|
171
228
|
|
|
172
|
-
type NavItemsByColumnLayout =
|
|
229
|
+
type NavItemsByColumnLayout =
|
|
230
|
+
| {
|
|
231
|
+
columns: {
|
|
232
|
+
categories: {
|
|
233
|
+
navItemLevel1: NavItemComputed
|
|
234
|
+
navItems: NavItemComputed[]
|
|
235
|
+
}[]
|
|
236
|
+
}[]
|
|
237
|
+
isFullWidthCategory: false
|
|
238
|
+
}
|
|
239
|
+
| {
|
|
240
|
+
navItemLevel1: NavItemComputed
|
|
241
|
+
columns: { navItems: NavItemComputed[] }[]
|
|
242
|
+
isFullWidthCategory: true
|
|
243
|
+
}
|
|
244
|
+
type NavItemsByColumnLayout2 = { columns: NavItemComputed[][][]; isFullWidthCategory: boolean }
|
|
173
245
|
function getNavItemsByColumnLayouts(navItems: NavItemComputed[], viewportWidth: number = 0): NavItemsByColumnLayout[] {
|
|
174
246
|
const navItemsByColumnEntries = getNavItemsByColumnEntries(navItems)
|
|
175
|
-
const numberOfColumnsMax = Math.floor(viewportWidth /
|
|
247
|
+
const numberOfColumnsMax = Math.floor(viewportWidth / navLeftWidthMin) || 1
|
|
176
248
|
const navItemsByColumnLayouts: NavItemsByColumnLayout[] = navItemsByColumnEntries.map(
|
|
177
|
-
({ columnEntries,
|
|
249
|
+
({ columnEntries, isFullWidthCategory }) => {
|
|
178
250
|
const numberOfColumns = Math.min(numberOfColumnsMax, columnEntries.length)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
251
|
+
if (!isFullWidthCategory) {
|
|
252
|
+
const columns: {
|
|
253
|
+
categories: {
|
|
254
|
+
navItemLevel1: NavItemComputed
|
|
255
|
+
navItems: NavItemComputed[]
|
|
256
|
+
}[]
|
|
257
|
+
}[] = []
|
|
258
|
+
columnEntries.forEach((columnEntry) => {
|
|
259
|
+
const idx = numberOfColumns === 1 ? 0 : columnEntry.columnMap[numberOfColumns]!
|
|
260
|
+
assert(idx >= 0)
|
|
261
|
+
columns[idx] ??= { categories: [] }
|
|
262
|
+
const navItemLevel1 = columnEntry.navItems[0]
|
|
263
|
+
const navItems = columnEntry.navItems.slice(1)
|
|
264
|
+
columns[idx].categories.push({ navItemLevel1, navItems })
|
|
265
|
+
})
|
|
266
|
+
const navItemsByColumnLayout: NavItemsByColumnLayout = { columns, isFullWidthCategory }
|
|
267
|
+
return navItemsByColumnLayout
|
|
268
|
+
} else {
|
|
269
|
+
let navItemLevel1: NavItemComputed
|
|
270
|
+
const columns: { navItems: NavItemComputed[] }[] = []
|
|
271
|
+
columnEntries.forEach((columnEntry, i) => {
|
|
272
|
+
const idx = numberOfColumns === 1 ? 0 : columnEntry.columnMap[numberOfColumns]!
|
|
273
|
+
assert(idx >= 0)
|
|
274
|
+
columns[idx] ??= { navItems: [] }
|
|
275
|
+
let { navItems } = columnEntry
|
|
276
|
+
if (i === 0) {
|
|
277
|
+
navItemLevel1 = navItems[0]
|
|
278
|
+
navItems = navItems.slice(1)
|
|
279
|
+
}
|
|
280
|
+
columns[idx].navItems.push(...navItems)
|
|
281
|
+
})
|
|
282
|
+
const navItemsByColumnLayout: NavItemsByColumnLayout = {
|
|
283
|
+
columns,
|
|
284
|
+
navItemLevel1: navItemLevel1!,
|
|
285
|
+
isFullWidthCategory,
|
|
286
|
+
}
|
|
287
|
+
return navItemsByColumnLayout
|
|
288
|
+
}
|
|
188
289
|
},
|
|
189
290
|
)
|
|
190
291
|
return navItemsByColumnLayouts
|
|
191
292
|
}
|
|
192
|
-
|
|
193
|
-
type NavItemsByColumnEntries = { columnEntries: ColumnEntry[]; isFullWidth: boolean }[]
|
|
293
|
+
type NavItemsByColumnEntries = { columnEntries: ColumnEntry[]; isFullWidthCategory: boolean }[]
|
|
194
294
|
type ColumnEntry = { navItems: NavItemComputed[]; columnMap: ColumnMap }
|
|
195
295
|
type ColumnMap = Record<number, number>
|
|
196
296
|
function getNavItemsByColumnEntries(navItems: NavItemComputed[]): NavItemsByColumnEntries {
|
|
197
297
|
const navItemsByColumnEntries: NavItemsByColumnEntries = []
|
|
198
298
|
let columnEntries: ColumnEntry[] = []
|
|
199
299
|
let columnEntry: ColumnEntry
|
|
200
|
-
let
|
|
300
|
+
let isFullWidthCategory: boolean | undefined
|
|
201
301
|
navItems.forEach((navItem) => {
|
|
202
302
|
if (navItem.level === 1) {
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
if (
|
|
206
|
-
navItemsByColumnEntries.push({ columnEntries,
|
|
303
|
+
const isFullWidthCategoryPrevious = isFullWidthCategory
|
|
304
|
+
isFullWidthCategory = !!navItem.menuModalFullWidth
|
|
305
|
+
if (isFullWidthCategoryPrevious !== undefined && isFullWidthCategoryPrevious !== isFullWidthCategory) {
|
|
306
|
+
navItemsByColumnEntries.push({ columnEntries, isFullWidthCategory: isFullWidthCategoryPrevious })
|
|
207
307
|
columnEntries = []
|
|
208
308
|
}
|
|
209
309
|
}
|
|
210
|
-
assert(
|
|
310
|
+
assert(isFullWidthCategory !== undefined)
|
|
211
311
|
if (navItem.isColumnEntry) {
|
|
212
312
|
assert(navItem.level === 1 || navItem.level === 4)
|
|
213
313
|
columnEntry = { navItems: [navItem], columnMap: navItem.isColumnEntry }
|
|
@@ -217,13 +317,13 @@ function getNavItemsByColumnEntries(navItems: NavItemComputed[]): NavItemsByColu
|
|
|
217
317
|
columnEntry.navItems.push(navItem)
|
|
218
318
|
}
|
|
219
319
|
})
|
|
220
|
-
assert(
|
|
221
|
-
navItemsByColumnEntries.push({ columnEntries,
|
|
320
|
+
assert(isFullWidthCategory !== undefined)
|
|
321
|
+
navItemsByColumnEntries.push({ columnEntries, isFullWidthCategory })
|
|
222
322
|
return navItemsByColumnEntries
|
|
223
323
|
}
|
|
224
324
|
|
|
225
325
|
type NavItemComputed = ReturnType<typeof getNavItemsWithComputed>[number]
|
|
226
|
-
function getNavItemsWithComputed(navItems:
|
|
326
|
+
function getNavItemsWithComputed(navItems: NavItem[], currentUrl: string) {
|
|
227
327
|
let navItemIdx: number | undefined
|
|
228
328
|
const navItemsWithComputed = navItems.map((navItem, i) => {
|
|
229
329
|
assert([1, 2, 3, 4].includes(navItem.level), navItem)
|
|
@@ -269,3 +369,14 @@ function getNavItemsWithComputed(navItems: NavItemAll[], currentUrl: string) {
|
|
|
269
369
|
|
|
270
370
|
return navItemsWithComputed
|
|
271
371
|
}
|
|
372
|
+
|
|
373
|
+
function Chevron(props: React.HTMLProps<SVGSVGElement>) {
|
|
374
|
+
return (
|
|
375
|
+
<svg viewBox="0 0 512 292.52" xmlns="http://www.w3.org/2000/svg" {...props}>
|
|
376
|
+
<path
|
|
377
|
+
fill="#aaa"
|
|
378
|
+
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"
|
|
379
|
+
/>
|
|
380
|
+
</svg>
|
|
381
|
+
)
|
|
382
|
+
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,30 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { determineNavItemsColumnLayout }
|
|
2
2
|
|
|
3
3
|
// A CSS-only solution doesn't seem to exist.
|
|
4
4
|
// - https://github.com/brillout/docpress/blob/2e41d8b9df098ff8312b02f7e9d41a202548e2b9/src/renderer/getStyleColumnLayout.ts#L4-L26
|
|
5
5
|
|
|
6
|
-
import { type
|
|
6
|
+
import { type NavItem } from '../navigation/Navigation'
|
|
7
7
|
import { assert, assertUsage, isBrowser } from '../utils/server'
|
|
8
8
|
assert(!isBrowser())
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
function determineNavItemsColumnLayout(navItems: NavItem[]): undefined {
|
|
11
|
+
const columnLayouts = getColumnEntries(navItems)
|
|
12
|
+
columnLayouts.forEach((columnEntries) => {
|
|
13
|
+
for (let numberOfColumns = columnEntries.length; numberOfColumns >= 1; numberOfColumns--) {
|
|
14
|
+
const columnMapping = determineColumnLayout(
|
|
15
|
+
columnEntries.map((columnEntry) => columnEntry.numberOfEntries),
|
|
16
|
+
numberOfColumns,
|
|
17
|
+
)
|
|
18
|
+
columnEntries.forEach((columnEntry, i) => {
|
|
19
|
+
columnEntry.navItemLeader.isColumnEntry ??= {}
|
|
20
|
+
columnEntry.navItemLeader.isColumnEntry[numberOfColumns] = columnMapping[i]
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getColumnEntries(navItems: NavItem[]) {
|
|
27
|
+
type NavItemWithLength = NavItem & { numberOfHeadings: number | null }
|
|
12
28
|
const navItemsWithLength: NavItemWithLength[] = navItems.map((navItem) => ({
|
|
13
29
|
...navItem,
|
|
14
30
|
numberOfHeadings: navItem.level === 1 || navItem.level === 4 ? 0 : null,
|
|
@@ -36,17 +52,17 @@ function determineColumnEntries(navItems: NavItemAll[]): undefined {
|
|
|
36
52
|
}
|
|
37
53
|
})
|
|
38
54
|
|
|
39
|
-
type ColumnEntry = { navItemLeader:
|
|
55
|
+
type ColumnEntry = { navItemLeader: NavItem; numberOfEntries: number }
|
|
40
56
|
const columnLayouts: ColumnEntry[][] = []
|
|
41
57
|
let columnEntries: ColumnEntry[] = []
|
|
42
|
-
let
|
|
58
|
+
let isFullWidthCategory: boolean | undefined
|
|
43
59
|
navItemsWithLength.forEach((navItem, i) => {
|
|
44
|
-
let
|
|
60
|
+
let isFullWidthCategoryBegin = false
|
|
45
61
|
if (navItem.level === 1) {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
if (
|
|
62
|
+
const isFullWidthCategoryPrevious = isFullWidthCategory
|
|
63
|
+
isFullWidthCategory = !!navItem.menuModalFullWidth
|
|
64
|
+
if (isFullWidthCategory) isFullWidthCategoryBegin = true
|
|
65
|
+
if (isFullWidthCategoryPrevious !== undefined && isFullWidthCategoryPrevious !== isFullWidthCategory) {
|
|
50
66
|
columnLayouts.push(columnEntries)
|
|
51
67
|
columnEntries = []
|
|
52
68
|
}
|
|
@@ -54,16 +70,18 @@ function determineColumnEntries(navItems: NavItemAll[]): undefined {
|
|
|
54
70
|
const navItemPrevious = navItemsWithLength[i - 1]
|
|
55
71
|
const navItemNext = navItemsWithLength[i + 1]
|
|
56
72
|
if (
|
|
57
|
-
!
|
|
73
|
+
!isFullWidthCategory
|
|
74
|
+
? navItem.level === 1
|
|
75
|
+
: (navItem.level === 4 && navItemPrevious!.level !== 1) || isFullWidthCategoryBegin
|
|
58
76
|
) {
|
|
59
|
-
if (
|
|
60
|
-
assert(navItem.level === 4 || (navItem.level === 1 &&
|
|
77
|
+
if (isFullWidthCategory) {
|
|
78
|
+
assert(navItem.level === 4 || (navItem.level === 1 && isFullWidthCategoryBegin))
|
|
61
79
|
} else {
|
|
62
80
|
assert(navItem.level === 1)
|
|
63
81
|
}
|
|
64
82
|
let { numberOfHeadings } = navItem
|
|
65
83
|
assert(numberOfHeadings !== null)
|
|
66
|
-
if (
|
|
84
|
+
if (isFullWidthCategoryBegin) {
|
|
67
85
|
assert(navItem.level === 1)
|
|
68
86
|
assertUsage(
|
|
69
87
|
navItemNext && navItemNext.level === 4,
|
|
@@ -78,22 +96,10 @@ function determineColumnEntries(navItems: NavItemAll[]): undefined {
|
|
|
78
96
|
})
|
|
79
97
|
assert(columnEntries!)
|
|
80
98
|
columnLayouts.push(columnEntries)
|
|
81
|
-
|
|
82
|
-
columnLayouts.forEach((columnEntries) => {
|
|
83
|
-
for (let numberOfColumns = columnEntries.length; numberOfColumns >= 1; numberOfColumns--) {
|
|
84
|
-
const columnsIdMap = determineColumns(
|
|
85
|
-
columnEntries.map((columnEntry) => columnEntry.numberOfEntries),
|
|
86
|
-
numberOfColumns,
|
|
87
|
-
)
|
|
88
|
-
columnEntries.forEach((columnEntry, i) => {
|
|
89
|
-
columnEntry.navItemLeader.isColumnEntry ??= {}
|
|
90
|
-
columnEntry.navItemLeader.isColumnEntry[numberOfColumns] = columnsIdMap[i]
|
|
91
|
-
})
|
|
92
|
-
}
|
|
93
|
-
})
|
|
99
|
+
return columnLayouts
|
|
94
100
|
}
|
|
95
101
|
|
|
96
|
-
function
|
|
102
|
+
function determineColumnLayout(columnsUnmerged: number[], numberOfColumns: number): number[] {
|
|
97
103
|
assert(numberOfColumns <= columnsUnmerged.length)
|
|
98
104
|
const columnsMergingInit: ColumnMerging[] = columnsUnmerged.map((columnHeight, i) => ({
|
|
99
105
|
columnIdsMerged: [i],
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export { initOnNavigation }
|
|
2
|
+
|
|
3
|
+
import { isBrowser } from '../utils/isBrowser'
|
|
4
|
+
import { closeMenuModal } from '../MenuModal'
|
|
5
|
+
import { unexpandNav } from '../Layout'
|
|
6
|
+
|
|
7
|
+
function onNavigation() {
|
|
8
|
+
closeMenuModal()
|
|
9
|
+
unexpandNav()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function initOnNavigation() {
|
|
13
|
+
if (!isBrowser()) return
|
|
14
|
+
document.addEventListener('click', (ev) => onLinkClick(ev, onNavigation))
|
|
15
|
+
// It's redundant as onLinkClick() should be enough, but just to be sure.
|
|
16
|
+
addEventListener('hashchange', onNavigation)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function onLinkClick(ev: MouseEvent, callback: () => void) {
|
|
20
|
+
if (ev.altKey || ev.ctrlKey || ev.metaKey || ev.shiftKey) return
|
|
21
|
+
const linkTag = findLinkTag(ev.target as HTMLElement)
|
|
22
|
+
if (!linkTag) return
|
|
23
|
+
const href = linkTag.getAttribute('href')
|
|
24
|
+
if (!href) return
|
|
25
|
+
if (!href.startsWith('/') && !href.startsWith('#')) return
|
|
26
|
+
callback()
|
|
27
|
+
}
|
|
28
|
+
function findLinkTag(target: HTMLElement): null | HTMLElement {
|
|
29
|
+
while (target.tagName !== 'A') {
|
|
30
|
+
const { parentNode } = target
|
|
31
|
+
if (!parentNode) {
|
|
32
|
+
return null
|
|
33
|
+
}
|
|
34
|
+
target = parentNode as HTMLElement
|
|
35
|
+
}
|
|
36
|
+
return target
|
|
37
|
+
}
|
|
@@ -11,6 +11,7 @@ import { autoScrollNav } from '../autoScrollNav'
|
|
|
11
11
|
import { installSectionUrlHashs } from '../installSectionUrlHashs'
|
|
12
12
|
import { getGlobalObject } from '../utils/client'
|
|
13
13
|
import { initKeyBindings } from '../initKeyBindings'
|
|
14
|
+
import { initOnNavigation } from './initOnNavigation'
|
|
14
15
|
|
|
15
16
|
const globalObject = getGlobalObject<{
|
|
16
17
|
root?: ReactDOM.Root
|
|
@@ -19,6 +20,7 @@ const globalObject = getGlobalObject<{
|
|
|
19
20
|
|
|
20
21
|
addEcosystemStamp()
|
|
21
22
|
initKeyBindings()
|
|
23
|
+
initOnNavigation()
|
|
22
24
|
|
|
23
25
|
async function onRenderClient(pageContext: PageContextClient) {
|
|
24
26
|
onRenderStart()
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export { usePageContext }
|
|
2
|
+
export { PageContextProvider }
|
|
3
3
|
|
|
4
4
|
import React, { useContext } from 'react'
|
|
5
5
|
import type { PageContextResolved } from '../config/resolvePageContext'
|
|
6
6
|
import type { PageContext } from 'vike/types'
|
|
7
7
|
|
|
8
|
-
export { PageContextProvider }
|
|
9
|
-
export { usePageContext }
|
|
10
|
-
|
|
11
8
|
const Context = React.createContext<PageContextResolved>(undefined as any)
|
|
12
9
|
|
|
13
10
|
function PageContextProvider({
|
package/utils/Style.tsx
ADDED
package/utils/cls.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function throttle<T extends (...args: any[]) => void>(func: T, limit: number): T {
|
|
2
|
+
let inThrottle: boolean
|
|
3
|
+
return function (this: any, ...args: Parameters<T>) {
|
|
4
|
+
if (!inThrottle) {
|
|
5
|
+
func.apply(this, args)
|
|
6
|
+
inThrottle = true
|
|
7
|
+
setTimeout(() => (inThrottle = false), limit)
|
|
8
|
+
}
|
|
9
|
+
} as T
|
|
10
|
+
}
|