@brillout/docpress 0.8.11 → 0.8.13-commit-8740149

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.
Files changed (37) hide show
  1. package/Layout.tsx +35 -23
  2. package/MenuModal.tsx +23 -11
  3. package/config/resolveHeadingsData.ts +2 -6
  4. package/css/colorize-on-hover.css +10 -1
  5. package/dist/Layout.d.ts +11 -0
  6. package/dist/Layout.js +243 -0
  7. package/dist/Links.d.ts +6 -0
  8. package/dist/Links.js +58 -0
  9. package/dist/MenuModal.d.ts +9 -0
  10. package/dist/MenuModal.js +131 -0
  11. package/dist/autoScrollNav.d.ts +3 -0
  12. package/dist/autoScrollNav.js +35 -0
  13. package/dist/components/EditPageNote.d.ts +7 -0
  14. package/dist/components/EditPageNote.js +11 -0
  15. package/dist/config/resolveHeadingsData.d.ts +0 -1
  16. package/dist/config/resolveHeadingsData.js +2 -6
  17. package/dist/config/resolvePageContext.d.ts +0 -1
  18. package/dist/docsearch/SearchLink.d.ts +4 -0
  19. package/dist/docsearch/SearchLink.js +25 -0
  20. package/dist/docsearch/toggleDocsearchModal.d.ts +4 -0
  21. package/dist/docsearch/toggleDocsearchModal.js +26 -0
  22. package/dist/navigation/Navigation.d.ts +2 -1
  23. package/dist/navigation/Navigation.js +69 -38
  24. package/dist/renderer/determineColumnEntries.d.ts +3 -0
  25. package/dist/renderer/{getStyleColumnLayout.js → determineColumnEntries.js} +16 -64
  26. package/dist/utils/PassTrough.d.ts +3 -0
  27. package/dist/utils/PassTrough.js +6 -0
  28. package/dist/utils/getViewportWidth.d.ts +1 -0
  29. package/dist/utils/getViewportWidth.js +4 -0
  30. package/docsearch/SearchLink.tsx +3 -12
  31. package/navigation/Navigation.css +2 -1
  32. package/navigation/Navigation.tsx +94 -63
  33. package/package.json +1 -1
  34. package/renderer/{getStyleColumnLayout.ts → determineColumnEntries.ts} +20 -90
  35. package/renderer/onRenderHtml.tsx +0 -4
  36. package/utils/getViewportWidth.ts +4 -0
  37. package/dist/renderer/getStyleColumnLayout.d.ts +0 -7
@@ -29,17 +29,8 @@ function SearchLink(props: PropsAnchor) {
29
29
  }
30
30
  function SearchIcon() {
31
31
  return (
32
- <svg
33
- style={{ marginRight: 'var(--icon-text-padding)', lineHeight: 0, width: '1.3em' }}
34
- className="decolorize-7"
35
- viewBox="0 0 20 20"
36
- >
37
- <path
38
- d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z"
39
- fill="none"
40
- stroke="currentColor"
41
- strokeWidth="2"
42
- ></path>
43
- </svg>
32
+ <span style={{ marginRight: 'var(--icon-text-padding)', fontSize: '1.1em' }} className="decolorize-7">
33
+ 🔍
34
+ </span>
44
35
  )
45
36
  }
@@ -99,7 +99,7 @@
99
99
  content: "";
100
100
  }
101
101
 
102
- .nav-category {
102
+ :has(> .category-border) {
103
103
  position: relative
104
104
  }
105
105
  .category-border {
@@ -107,6 +107,7 @@
107
107
  top: 0;
108
108
  left: 0;
109
109
  transform: translate(0, 53px);
110
+ height: calc(100% - 53px);
110
111
  width: 3px;
111
112
  z-index: 99;
112
113
  }
@@ -4,13 +4,15 @@ export { NavigationContent }
4
4
  export type { NavItem }
5
5
  export type { NavItemAll }
6
6
 
7
- import React from 'react'
7
+ import React, { useEffect, useState } from 'react'
8
8
  import { assert, assertWarning, jsxToTextContent } from '../utils/server'
9
9
  import './Navigation.css'
10
10
  import { parseTitle } from '../parseTitle'
11
11
  import { usePageContext } from '../renderer/usePageContext'
12
12
  import '@docsearch/css'
13
13
  import '../global.d.ts'
14
+ import { getViewportWidth } from '../utils/getViewportWidth'
15
+ import { navWidthMax, navWidthMin } from '../Layout'
14
16
 
15
17
  type NavItem = {
16
18
  level: number
@@ -21,7 +23,7 @@ type NavItem = {
21
23
  menuModalFullWidth?: true
22
24
  }
23
25
  type NavItemAll = NavItem & {
24
- isColumnLayoutElement?: true
26
+ isColumnEntry?: ColumnMap
25
27
  }
26
28
  function NavigationContent(props: {
27
29
  navItems: NavItem[]
@@ -38,71 +40,76 @@ function NavigationContent(props: {
38
40
  .map((navItem, i) => <NavItemComponent navItem={navItem} key={i} />)
39
41
  } else {
40
42
  assert(!props.showOnlyRelevant)
41
- const navItemsColumnLayout = groupByColumnLayout(navItemsWithComputed)
42
- const paddingBottom = 40
43
- navContent = (
44
- <>
45
- {navItemsColumnLayout.map(({ navItemsColumnEntries, isFullWidth }, i) => (
46
- <div
47
- key={i}
48
- style={{
49
- display: 'flex',
50
- justifyContent: 'center',
51
- }}
52
- >
43
+ navContent = <NavigationColumnLayout navItemsWithComputed={navItemsWithComputed} />
44
+ }
45
+
46
+ return (
47
+ <div className="navigation-content" style={{ marginTop: 10 }}>
48
+ {navContent}
49
+ </div>
50
+ )
51
+ }
52
+
53
+ function NavigationColumnLayout(props: { navItemsWithComputed: NavItemComputed[] }) {
54
+ let [viewportWidth, setViewportWidth] = useState<number | undefined>()
55
+ const updateviewportwidth = () => setViewportWidth(getViewportWidth())
56
+ useEffect(() => {
57
+ updateviewportwidth()
58
+ window.addEventListener('resize', updateviewportwidth, { passive: true })
59
+ })
60
+
61
+ const navItemsByColumnLayouts = getNavItemsByColumnLayouts(props.navItemsWithComputed, viewportWidth)
62
+ const margin = 40
63
+
64
+ return (
65
+ <>
66
+ {navItemsByColumnLayouts.map(({ columns, isFullWidth }, i) => (
67
+ <div
68
+ key={i}
69
+ style={{
70
+ display: 'flex',
71
+ width: columns.length * (navWidthMax + 20),
72
+ justifyContent: 'space-between',
73
+ maxWidth: '100%',
74
+ margin: 'auto',
75
+ marginTop: i === 0 ? -1 * margin : undefined,
76
+ marginBottom: margin,
77
+ }}
78
+ >
79
+ {columns.map((columnEntry, j) => (
53
80
  <div
54
- className={`column-layout-${i}` + (!isFullWidth ? '' : ' nav-category nav-category-full-width')}
81
+ key={j}
55
82
  style={{
56
83
  flexGrow: 1,
57
- columnGap: 20,
58
- paddingBottom: isFullWidth ? paddingBottom : undefined,
84
+ maxWidth: navWidthMax,
85
+ display: 'flex',
86
+ flexDirection: 'column',
87
+ paddingTop: isFullWidth && j !== 0 ? 36 : undefined,
59
88
  }}
60
89
  >
61
- {navItemsColumnEntries.map((navItemColumnEntry, j) => (
62
- <div
63
- key={j}
64
- className={'column-layout-entry' + (isFullWidth ? '' : ' nav-category')}
65
- style={{
66
- breakInside: 'avoid',
67
- paddingBottom: !isFullWidth ? paddingBottom : undefined,
68
- paddingTop: isFullWidth ? undefined : 0,
69
- width: '100%',
70
- }}
71
- >
72
- <NavItemComponent navItem={navItemColumnEntry} />
73
- {navItemColumnEntry.navItemChilds.map((navItem, k) => (
74
- <NavItemComponent navItem={navItem} key={k} />
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} />
75
94
  ))}
76
- <CategoryBorder
77
- navItemLevel1={isFullWidth ? undefined : navItemColumnEntry!}
78
- paddingBottom={paddingBottom}
79
- />
95
+ <CategoryBorder navItemLevel1={isFullWidth ? undefined : navItems[0]!} />
80
96
  </div>
81
97
  ))}
82
- <CategoryBorder
83
- navItemLevel1={!isFullWidth ? undefined : navItemsColumnEntries[0]!}
84
- paddingBottom={paddingBottom}
85
- />
86
98
  </div>
87
- </div>
88
- ))}
89
- </>
90
- )
91
- }
92
-
93
- return (
94
- <div className="navigation-content" style={{ marginTop: 10 }}>
95
- {navContent}
96
- </div>
99
+ ))}
100
+ <CategoryBorder navItemLevel1={!isFullWidth ? undefined : columns[0][0][0]!} />
101
+ </div>
102
+ ))}
103
+ </>
97
104
  )
98
105
  }
99
- function CategoryBorder({ navItemLevel1, paddingBottom }: { navItemLevel1?: NavItemComputed; paddingBottom: number }) {
106
+
107
+ function CategoryBorder({ navItemLevel1 }: { navItemLevel1?: NavItemComputed }) {
100
108
  return !navItemLevel1 ? null : (
101
109
  <div
102
110
  className="category-border"
103
111
  style={{
104
112
  background: navItemLevel1.color!,
105
- height: `calc(100% - ${paddingBottom}px - 53px)`,
106
113
  }}
107
114
  />
108
115
  )
@@ -162,33 +169,57 @@ function NavItemComponent({
162
169
  }
163
170
  }
164
171
 
165
- type NavItemsColumnEntry = NavItemComputed & { navItemChilds: NavItemComputed[] }
166
- function groupByColumnLayout(navItems: NavItemComputed[]) {
167
- const navItemsColumnLayout: { navItemsColumnEntries: NavItemsColumnEntry[]; isFullWidth: boolean }[] = []
168
- let navItemsColumnEntries: NavItemsColumnEntry[] = []
172
+ type NavItemsByColumnLayout = { columns: NavItemComputed[][][]; isFullWidth: boolean }
173
+ function getNavItemsByColumnLayouts(navItems: NavItemComputed[], viewportWidth: number = 0): NavItemsByColumnLayout[] {
174
+ const navItemsByColumnEntries = getNavItemsByColumnEntries(navItems)
175
+ const numberOfColumnsMax = Math.floor(viewportWidth / navWidthMin) || 1
176
+ const navItemsByColumnLayouts: NavItemsByColumnLayout[] = navItemsByColumnEntries.map(
177
+ ({ columnEntries, isFullWidth }) => {
178
+ const numberOfColumns = Math.min(numberOfColumnsMax, columnEntries.length)
179
+ const columns: NavItemComputed[][][] = []
180
+ columnEntries.forEach((columnEntry) => {
181
+ const idx = numberOfColumns === 1 ? 0 : columnEntry.columnMap[numberOfColumns]!
182
+ assert(idx >= 0)
183
+ columns[idx] ??= []
184
+ columns[idx].push(columnEntry.navItems)
185
+ })
186
+ const navItemsByColumnLayout: NavItemsByColumnLayout = { columns, isFullWidth }
187
+ return navItemsByColumnLayout
188
+ },
189
+ )
190
+ return navItemsByColumnLayouts
191
+ }
192
+
193
+ type NavItemsByColumnEntries = { columnEntries: ColumnEntry[]; isFullWidth: boolean }[]
194
+ type ColumnEntry = { navItems: NavItemComputed[]; columnMap: ColumnMap }
195
+ type ColumnMap = Record<number, number>
196
+ function getNavItemsByColumnEntries(navItems: NavItemComputed[]): NavItemsByColumnEntries {
197
+ const navItemsByColumnEntries: NavItemsByColumnEntries = []
198
+ let columnEntries: ColumnEntry[] = []
199
+ let columnEntry: ColumnEntry
169
200
  let isFullWidth: boolean | undefined
170
201
  navItems.forEach((navItem) => {
171
202
  if (navItem.level === 1) {
172
203
  const isFullWidthPrevious = isFullWidth
173
204
  isFullWidth = !!navItem.menuModalFullWidth
174
205
  if (isFullWidthPrevious !== undefined && isFullWidthPrevious !== isFullWidth) {
175
- navItemsColumnLayout.push({ navItemsColumnEntries, isFullWidth: isFullWidthPrevious })
176
- navItemsColumnEntries = []
206
+ navItemsByColumnEntries.push({ columnEntries, isFullWidth: isFullWidthPrevious })
207
+ columnEntries = []
177
208
  }
178
209
  }
179
210
  assert(isFullWidth !== undefined)
180
- if (navItem.isColumnLayoutElement) {
211
+ if (navItem.isColumnEntry) {
181
212
  assert(navItem.level === 1 || navItem.level === 4)
182
- const navItemColumnEntry = { ...navItem, navItemChilds: [] }
183
- navItemsColumnEntries.push(navItemColumnEntry)
213
+ columnEntry = { navItems: [navItem], columnMap: navItem.isColumnEntry }
214
+ columnEntries.push(columnEntry)
184
215
  } else {
185
216
  assert(navItem.level !== 1)
186
- navItemsColumnEntries[navItemsColumnEntries.length - 1].navItemChilds.push(navItem)
217
+ columnEntry.navItems.push(navItem)
187
218
  }
188
219
  })
189
220
  assert(isFullWidth !== undefined)
190
- navItemsColumnLayout.push({ navItemsColumnEntries, isFullWidth })
191
- return navItemsColumnLayout
221
+ navItemsByColumnEntries.push({ columnEntries, isFullWidth })
222
+ return navItemsByColumnEntries
192
223
  }
193
224
 
194
225
  type NavItemComputed = ReturnType<typeof getNavItemsWithComputed>[number]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brillout/docpress",
3
- "version": "0.8.11",
3
+ "version": "0.8.13-commit-8740149",
4
4
  "type": "module",
5
5
  "dependencies": {
6
6
  "@brillout/picocolors": "^1.0.10",
@@ -1,24 +1,14 @@
1
- export { getStyleColumnLayout }
2
- export { determineColumnLayoutEntries }
1
+ export { determineColumnEntries }
3
2
 
4
- // There doens't seem to be as simpler way to have a column layout that uses the whole width real estate.
5
- // - https://stackoverflow.com/questions/9683425/css-column-count-not-respected
6
- // - https://stackoverflow.com/questions/25446921/get-flexbox-column-wrap-to-use-full-width-and-minimize-height
7
- // - https://stackoverflow.com/questions/74873283/how-to-create-a-css-grid-with-3-columns-having-column-flow
8
- // - https://stackoverflow.com/questions/50693793/3-columns-grid-top-to-bottom-using-grid-css
9
- // - https://stackoverflow.com/questions/9119347/html-css-vertical-flow-layout-columnar-style-how-to-implement
10
- // - https://stackoverflow.com/questions/27119691/how-to-start-a-new-column-in-flex-column-wrap-layout
11
- // - https://stackoverflow.com/questions/45264354/is-it-possible-to-place-more-than-one-element-into-a-css-grid-cell-without-overl/49047281#49047281
3
+ // A CSS-only solution doesn't seem to exist.
4
+ // - https://github.com/brillout/docpress/blob/2e41d8b9df098ff8312b02f7e9d41a202548e2b9/src/renderer/getStyleColumnLayout.ts#L4-L26
12
5
 
13
6
  import { type NavItemAll } from '../navigation/Navigation'
14
- import { css } from '../utils/css'
15
7
  import { assert, assertUsage, isBrowser } from '../utils/server'
16
8
  assert(!isBrowser())
17
- const columnWidthMin = 300
18
- const columnWidthMax = 350
19
9
 
20
10
  type NavItemWithLength = NavItemAll & { numberOfHeadings: number | null }
21
- function determineColumnLayoutEntries(navItems: NavItemAll[]): { columnLayouts: number[][] } {
11
+ function determineColumnEntries(navItems: NavItemAll[]): undefined {
22
12
  const navItemsWithLength: NavItemWithLength[] = navItems.map((navItem) => ({
23
13
  ...navItem,
24
14
  numberOfHeadings: navItem.level === 1 || navItem.level === 4 ? 0 : null,
@@ -46,8 +36,9 @@ function determineColumnLayoutEntries(navItems: NavItemAll[]): { columnLayouts:
46
36
  }
47
37
  })
48
38
 
49
- const columnLayouts: number[][] = []
50
- let columns: number[] = []
39
+ type ColumnEntry = { navItemLeader: NavItemAll; numberOfEntries: number }
40
+ const columnLayouts: ColumnEntry[][] = []
41
+ let columnEntries: ColumnEntry[] = []
51
42
  let isFullWidth: boolean | undefined
52
43
  navItemsWithLength.forEach((navItem, i) => {
53
44
  let isFullWidthBegin = false
@@ -56,8 +47,8 @@ function determineColumnLayoutEntries(navItems: NavItemAll[]): { columnLayouts:
56
47
  isFullWidth = !!navItem.menuModalFullWidth
57
48
  if (isFullWidth) isFullWidthBegin = true
58
49
  if (isFullWidthPrevious !== undefined && isFullWidthPrevious !== isFullWidth) {
59
- columnLayouts.push(columns)
60
- columns = []
50
+ columnLayouts.push(columnEntries)
51
+ columnEntries = []
61
52
  }
62
53
  }
63
54
  const navItemPrevious = navItemsWithLength[i - 1]
@@ -82,85 +73,24 @@ function determineColumnLayoutEntries(navItems: NavItemAll[]): { columnLayouts:
82
73
  assert(navItemNext.numberOfHeadings)
83
74
  numberOfHeadings = navItemNext.numberOfHeadings
84
75
  }
85
- columns.push(numberOfHeadings)
86
- navItems[i].isColumnLayoutElement = true
76
+ columnEntries.push({ navItemLeader: navItems[i], numberOfEntries: numberOfHeadings })
87
77
  }
88
78
  })
89
- columnLayouts.push(columns)
79
+ assert(columnEntries!)
80
+ columnLayouts.push(columnEntries)
90
81
 
91
- return { columnLayouts }
92
- }
93
-
94
- function getStyleColumnLayout(columnLayouts: number[][]): string {
95
- let style =
96
- '\n' +
97
- css`
98
- .column-layout-entry {
99
- break-before: avoid;
100
- }
101
- `
102
- style += '\n'
103
- columnLayouts.forEach((columns, i) => {
104
- for (let numberOfColumns = columns.length; numberOfColumns >= 1; numberOfColumns--) {
105
- let styleGivenNumberOfColumns: string[] = []
106
- styleGivenNumberOfColumns.push(
107
- css`
108
- .column-layout-${i} {
109
- column-count: ${numberOfColumns};
110
- max-width: min(100%, ${columnWidthMax * numberOfColumns}px);
111
- }
112
- `,
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,
113
87
  )
114
- const columnsIdMap = determineColumns(columns, numberOfColumns)
115
- const columnBreakPoints = determineColumnBreakPoints(columnsIdMap)
116
- columnBreakPoints.forEach((columnBreakPoint, columnUngroupedId) => {
117
- if (!columnBreakPoint) return
118
- styleGivenNumberOfColumns.push(
119
- css`
120
- .column-layout-${i} .column-layout-entry:nth-child(${columnUngroupedId + 1}) {
121
- break-before: column;
122
- padding-top: 36px;
123
- }
124
- `,
125
- )
88
+ columnEntries.forEach((columnEntry, i) => {
89
+ columnEntry.navItemLeader.isColumnEntry ??= {}
90
+ columnEntry.navItemLeader.isColumnEntry[numberOfColumns] = columnsIdMap[i]
126
91
  })
127
- {
128
- assert(styleGivenNumberOfColumns.length > 0)
129
- const getMaxWidth = (columns: number) => (columns + 1) * columnWidthMin - 1
130
- const isFirst = numberOfColumns === 1
131
- const isLast = numberOfColumns === columns.length
132
- const query = [
133
- !isFirst && `(min-width: ${getMaxWidth(numberOfColumns - 1) + 1}px)`,
134
- !isLast && `(max-width: ${getMaxWidth(numberOfColumns)}px)`,
135
- ]
136
- .filter(Boolean)
137
- .join(' and ')
138
- if (query) {
139
- styleGivenNumberOfColumns = [`@container ${query} {`, ...styleGivenNumberOfColumns, `}`]
140
- }
141
- }
142
- style += styleGivenNumberOfColumns.join('\n') + '\n'
143
92
  }
144
93
  })
145
- return style
146
- }
147
-
148
- function determineColumnBreakPoints(columnsIdMap: number[]): boolean[] {
149
- assert(columnsIdMap[0] === 0)
150
- let columnGroupedIdBefore = 0
151
- const columnBreakPoints = columnsIdMap.map((columnGroupedId) => {
152
- assert(
153
- [
154
- //
155
- columnGroupedIdBefore,
156
- columnGroupedIdBefore + 1,
157
- ].includes(columnGroupedId),
158
- )
159
- const val = columnGroupedId !== columnGroupedIdBefore
160
- columnGroupedIdBefore = columnGroupedId
161
- return val
162
- })
163
- return columnBreakPoints
164
94
  }
165
95
 
166
96
  function determineColumns(columnsUnmerged: number[], numberOfColumns: number): number[] {
@@ -6,7 +6,6 @@ import { assert } from '../utils/server'
6
6
  import type { PageContextResolved } from '../config/resolvePageContext'
7
7
  import { getPageElement } from './getPageElement'
8
8
  import type { OnRenderHtmlAsync } from 'vike/types'
9
- import { getStyleColumnLayout } from './getStyleColumnLayout'
10
9
 
11
10
  const onRenderHtml: OnRenderHtmlAsync = async (
12
11
  pageContext,
@@ -16,8 +15,6 @@ Promise<Awaited<ReturnType<OnRenderHtmlAsync>>> => {
16
15
 
17
16
  const page = getPageElement(pageContext, pageContextResolved)
18
17
 
19
- const styleMenuModalLayout = getStyleColumnLayout(pageContextResolved.columnLayouts)
20
-
21
18
  const descriptionTag = pageContextResolved.isLandingPage
22
19
  ? dangerouslySkipEscape(`<meta name="description" content="${pageContextResolved.meta.tagline}" />`)
23
20
  : ''
@@ -33,7 +30,6 @@ Promise<Awaited<ReturnType<OnRenderHtmlAsync>>> => {
33
30
  ${descriptionTag}
34
31
  <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />
35
32
  ${getOpenGraphTags(pageContext.urlPathname, pageContextResolved.documentTitle, pageContextResolved.meta)}
36
- <style>${dangerouslySkipEscape(styleMenuModalLayout)}</style>
37
33
  <meta name="docsearch:category" content="${pageContextResolved.activeCategory}" />
38
34
  </head>
39
35
  <body>
@@ -0,0 +1,4 @@
1
+ export function getViewportWidth(): number {
2
+ // `window.innerWidth` inlcudes scrollbar width: https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes
3
+ return document.documentElement.clientWidth
4
+ }
@@ -1,7 +0,0 @@
1
- export { getStyleColumnLayout };
2
- export { determineColumnLayoutEntries };
3
- import { type NavItemAll } from '../navigation/Navigation';
4
- declare function determineColumnLayoutEntries(navItems: NavItemAll[]): {
5
- columnLayouts: number[][];
6
- };
7
- declare function getStyleColumnLayout(columnLayouts: number[][]): string;