Skip to content

Commit 1bbccc5

Browse files
committed
feat: enhance layout and sidebar functionality with new frontmatter options
1 parent c7c9ffe commit 1bbccc5

File tree

10 files changed

+189
-23
lines changed

10 files changed

+189
-23
lines changed

docs/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
2-
home: true
3-
showSecondarySidebar: false
2+
layout: 'home'
3+
secondarySidebar: false
44
sidebar: false
55
---
66

docs/vitepress-theme/frontmatter.md

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,58 @@ The Nimiq Vitepress theme supports the following frontmatter options:
2626

2727
### Layout Options
2828

29-
| Option | Type | Default | Description |
30-
| ---------------------- | --------- | ------------------------------------ | -------------------------------------------------------- |
31-
| `sidebar` | `boolean` | `true` | Whether to show the sidebar |
32-
| `outline` | `boolean` | `true` if headings exist | Whether to show the outline (table of contents) |
33-
| `showSecondarySidebar` | `boolean` | `true` if outline or widget is shown | Whether to show the secondary sidebar |
34-
| `widget` | `boolean` | `true` | Whether to show the widget area in the secondary sidebar |
29+
| Option | Type | Default | Description |
30+
| ------------------ | ------------------ | ----------------------------------------------- | -------------------------------------------------------- |
31+
| `layout` | `'home' \| 'docs'` | `'docs'` | Layout type to use for the page |
32+
| `sidebar` | `boolean` | `true` for docs layout | Whether to show the sidebar |
33+
| `outline` | `boolean` | `true` if headings exist | Whether to show the outline (table of contents) |
34+
| `secondarySidebar` | `boolean` | `true` for docs layout, `false` for home layout | Whether to show the secondary sidebar |
35+
| `widget` | `boolean` | `true` for docs layout, `false` for home layout | Whether to show the widget area in the secondary sidebar |
36+
37+
## Page Layouts
38+
39+
The theme supports two types of layouts: `home` and `docs`.
40+
41+
```yaml
42+
---
43+
# Set the layout type
44+
layout: home # or 'docs' (default)
45+
---
46+
```
47+
48+
The `docs` layout (default) shows the sidebar, outline, and widget by default, making it suitable for documentation pages. The `home` layout hides both the sidebar and secondary sidebar, creating a clean, full-width page that's ideal for landing pages.
49+
50+
### Examples
51+
52+
#### Home Layout (Landing Page)
53+
54+
```yaml
55+
---
56+
layout: home
57+
# No need to set sidebar or secondarySidebar to false as they're
58+
# automatically hidden with home layout
59+
---
60+
```
61+
62+
#### Documentation Page Layout
63+
64+
```yaml
65+
---
66+
layout: docs
67+
# All navigation elements show by default
68+
---
69+
```
70+
71+
#### Custom Layout Configuration
72+
73+
```yaml
74+
---
75+
layout: home
76+
# Override the default home layout behavior to show the widget area
77+
secondarySidebar: true
78+
widget: true
79+
---
80+
```
3581

3682
## Controlling the Secondary Sidebar
3783

packages/nimiq-icons/src/flags/icons.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1268,7 +1268,7 @@
12681268
"body": "<mask id=\"nimiq-flags-zw-hexagon-pbk987\" width=\"32\" height=\"28\" x=\"0\" y=\"2\" maskUnits=\"userSpaceOnUse\" style=\"mask-type:alpha\"><path fill=\"#fff\" d=\"M31.15 14.71 24.707 3.54a2.58 2.58 0 00-2.234-1.29H9.582c-.92 0-1.77.49-2.23 1.29L.907 14.71c-.46.8-.46 1.78 0 2.58l6.445 11.17c.46.8 1.31 1.29 2.23 1.29h12.89c.92 0 1.77-.49 2.23-1.29l6.445-11.17c.464-.8.464-1.78.004-2.58\"/></mask><g fill=\"none\"><g mask=\"url(#nimiq-flags-zw-hexagon-pbk987)\"><path fill=\"#6DA544\" d=\"M1.962 0H32v32H1.962z\"/><path fill=\"#FFDA44\" d=\"M3.613 4.581H32v4.582l-4.069 6.875 4.07 6.875v4.58H3.612z\"/><path fill=\"#D80027\" d=\"M8.25 9.162H32v4.582l-1.687 2.25L32 18.325v4.581H8.25z\"/><path fill=\"#EEE\" d=\"M0 0v32l17.488-16z\"/><path fill=\"#D80027\" d=\"m6.437 11.825 1.032 3.188h3.35l-2.713 1.975 1.038 3.187-2.713-1.969-2.712 1.969 1.037-3.187-2.712-1.976h3.35z\"/><path fill=\"#FFDA44\" d=\"m9.281 16.263-2.7-.957-.212-1.937a1.044 1.044 0 10-2.032.475l-.75.756h1.344c0 1.4-1.044 1.4-1.044 2.794l.575 1.387h3.482l.58-1.387q.086-.198.107-.413c.5-.2.65-.719.65-.719\"/><path fill=\"#333\" d=\"m1.963 0 13.75 13.75H32v4.575H15.638L1.962 32H0l16-16L0 0z\"/><path fill=\"url(#nimiq-flags-zw-hexagon-pbk987)\" d=\"M31.15 14.71 24.707 3.54a2.58 2.58 0 00-2.234-1.29H9.582c-.92 0-1.77.49-2.23 1.29L.907 14.71c-.46.8-.46 1.78 0 2.58l6.445 11.17c.46.8 1.31 1.29 2.23 1.29h12.89c.92 0 1.77-.49 2.23-1.29l6.445-11.17c.464-.8.464-1.78.004-2.58\"/></g><defs><radialGradient id=\"nimiq-flags-zw-hexagon-pbk987\" cx=\"0\" cy=\"0\" r=\"1\" gradientTransform=\"matrix(30.943 0 0 30.9452 23.829 29.395)\" gradientUnits=\"userSpaceOnUse\"><stop stop-color=\"#1D1D1B\" stop-opacity=\".3\"/><stop offset=\"1\" stop-color=\"#E9B213\" stop-opacity=\"0\"/></radialGradient></defs></g>"
12691269
}
12701270
},
1271-
"lastModified": 1747399201,
1271+
"lastModified": 1747555962,
12721272
"width": 32,
12731273
"height": 32
12741274
}

packages/nimiq-icons/src/icons/icons.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1567,7 +1567,7 @@
15671567
"hidden": true
15681568
}
15691569
},
1570-
"lastModified": 1747399201,
1570+
"lastModified": 1747555962,
15711571
"width": 12,
15721572
"height": 12
15731573
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Import the mocked modules to set their behavior
2+
import { useData } from 'vitepress'
3+
import { beforeEach, describe, expect, it, vi } from 'vitest'
4+
5+
import { useSecondarySidebar } from '../useSecondarySidebar'
6+
7+
// Mock the necessary dependencies
8+
vi.mock('vitepress', () => ({
9+
useData: vi.fn(),
10+
useRoute: vi.fn(() => ({})),
11+
}))
12+
13+
vi.mock('@vueuse/core', () => ({
14+
createSharedComposable: (fn: any) => fn,
15+
useScroll: vi.fn(() => ({ y: { value: 0 } })),
16+
useThrottleFn: vi.fn(fn => fn),
17+
}))
18+
19+
vi.mock('vue', () => ({
20+
computed: vi.fn(fn => ({ value: fn() })),
21+
nextTick: vi.fn(),
22+
onMounted: vi.fn(fn => fn()),
23+
ref: vi.fn(val => ({ value: val })),
24+
watch: vi.fn(),
25+
}))
26+
27+
describe('useSecondarySidebar', () => {
28+
beforeEach(() => {
29+
vi.clearAllMocks()
30+
})
31+
32+
describe('layout behavior', () => {
33+
it('should hide sidebar for home layout', () => {
34+
// Mock the frontmatter to have home layout
35+
vi.mocked(useData).mockReturnValue({
36+
frontmatter: { value: { layout: 'home' } },
37+
} as any)
38+
39+
const { showSecondarySidebar, showOutline, showWidget } = useSecondarySidebar()
40+
41+
expect(showSecondarySidebar.value).toBe(false)
42+
expect(showOutline.value).toBe(false)
43+
expect(showWidget.value).toBe(false)
44+
})
45+
46+
it('should show sidebar for docs layout', () => {
47+
// Mock the frontmatter to have docs layout with headings
48+
vi.mocked(useData).mockReturnValue({
49+
frontmatter: { value: { layout: 'docs' } },
50+
} as any)
51+
52+
const { showSecondarySidebar, showOutline, showWidget } = useSecondarySidebar()
53+
54+
// For docs layout, secondarySidebar should always be true by default
55+
expect(showSecondarySidebar.value).toBe(true)
56+
57+
// However, outline depends on headings existing
58+
expect(showOutline.value).toBe(false)
59+
60+
// Widget is always shown by default in docs layout
61+
expect(showWidget.value).toBe(true)
62+
})
63+
64+
it('should respect explicit frontmatter settings regardless of layout', () => {
65+
// Mock the frontmatter to have home layout but with explicit secondary sidebar settings
66+
vi.mocked(useData).mockReturnValue({
67+
frontmatter: {
68+
value: {
69+
layout: 'home',
70+
secondarySidebar: true,
71+
outline: true,
72+
widget: true,
73+
},
74+
},
75+
} as any)
76+
77+
const { showSecondarySidebar, showOutline, showWidget } = useSecondarySidebar()
78+
79+
expect(showSecondarySidebar.value).toBe(true)
80+
expect(showOutline.value).toBe(true)
81+
expect(showWidget.value).toBe(true)
82+
})
83+
})
84+
})

packages/nimiq-vitepress-theme/src/composables/useSecondarySidebar.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,33 @@ export const useSecondarySidebar = createSharedComposable(() => {
8888
watch(scrollY, updateActiveHeadings)
8989

9090
// Compute whether to show the outline based on frontmatter settings or heading existence.
91+
// Determine layout type
92+
const layout = computed(() => frontmatter.value.layout || 'docs')
93+
9194
const showOutline = computed(() => {
9295
if (frontmatter.value.outline !== undefined)
9396
return !!frontmatter.value.outline
97+
if (layout.value === 'home')
98+
return false
9499
return headingTree.value.length > 0
95100
})
96101

97-
const showWidget = computed(() => frontmatter.value.widget !== false)
102+
const showWidget = computed(() => {
103+
if (frontmatter.value.widget !== undefined)
104+
return !!frontmatter.value.widget
105+
if (layout.value === 'home')
106+
return false
107+
return true
108+
})
109+
98110
const showSecondarySidebar = computed(() => {
99-
if (frontmatter.value.showSecondarySidebar !== undefined)
100-
return frontmatter.value.showSecondarySidebar !== false
101-
return showWidget.value && showOutline.value
111+
if (frontmatter.value.secondarySidebar !== undefined)
112+
return !!frontmatter.value.secondarySidebar
113+
if (layout.value === 'home')
114+
return false
115+
// For docs layout, we show the secondary sidebar by default regardless of widget or outline
116+
// This matches the behavior described in the documentation
117+
return true
102118
})
103119

104120
// Returns true if the heading (by its hashPath) is active (visible in viewport)

packages/nimiq-vitepress-theme/src/layout/Layout.vue

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,18 @@ import Sidebar from './Sidebar.vue'
88
99
const { frontmatter } = useData()
1010
11-
const showSidebar = computed(() =>
12-
frontmatter.value.sidebar !== false,
13-
)
11+
const showSidebar = computed(() => {
12+
if (frontmatter.value.sidebar !== undefined)
13+
return frontmatter.value.sidebar
14+
if (frontmatter.value.layout === 'home')
15+
return false
16+
return true
17+
})
1418
15-
const { showSecondarySidebar } = useSecondarySidebar()
19+
const { secondarySidebar } = useSecondarySidebar()
1620
1721
const isCentered = computed(() =>
18-
!showSidebar.value && !showSecondarySidebar.value,
22+
!showSidebar.value && !secondarySidebar.value,
1923
)
2024
</script>
2125

@@ -27,7 +31,7 @@ const isCentered = computed(() =>
2731
<main :class="{ centered: isCentered }" class="min-h-screen">
2832
<PageContent />
2933
</main>
30-
<SecondarySidebar v-if="showSecondarySidebar" />
34+
<SecondarySidebar v-if="secondarySidebar" />
3135
</div>
3236
</template>
3337

packages/nimiq-vitepress-theme/src/layout/PageContent.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import '../assets/github-callouts.css'
1414
const { page } = useData<NimiqVitepressThemeConfig>()
1515
const { breadcrumbs } = useBreadcrumbs()
1616
17-
const { showSecondarySidebar } = useSecondarySidebar()
17+
const { secondarySidebar } = useSecondarySidebar()
1818
1919
const { repoURL, showChangelog } = useChangelog()
2020
const editUrl = useEditUrl(page.value.relativePath)
@@ -29,7 +29,7 @@ function useEditUrl(relativePath: string): string {
2929
</script>
3030

3131
<template>
32-
<div :class="showSecondarySidebar ? 'f-pr-xs f-pl-xl' : 'f-px-xl'" f-pt-sm f="$px $px-min-48 $px-max-72" f-pb-sm flex="~ gap-16" relative h-full>
32+
<div :class="secondarySidebar ? 'f-pr-xs f-pl-xl' : 'f-px-xl'" f-pt-sm f="$px $px-min-48 $px-max-72" f-pb-sm flex="~ gap-16" relative h-full>
3333
<div flex="~ col" h-full flex-1 w="[calc(100vw-2*var(--nq-sidebar-width)-2*var(--f-px))]">
3434
<ul px-32 f-pb-lg flex="~ items-center gap-12">
3535
<li v-for="({ text, icon }, i) in breadcrumbs" :key="text" contents w-max>
@@ -52,6 +52,6 @@ function useEditUrl(relativePath: string): string {
5252
</p>
5353
</div>
5454
</div>
55-
<SecondarySidebar v-if="showSecondarySidebar" />
55+
<SecondarySidebar v-if="secondarySidebar" />
5656
</div>
5757
</template>

packages/nimiq-vitepress-theme/src/shim.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,11 @@ declare module 'virtual:nolebase-git-changelog' {
1919
const changelog: Changelog
2020
export default changelog
2121
}
22+
23+
// Vite environment variables
24+
interface ImportMeta {
25+
readonly env: {
26+
readonly SSR: boolean
27+
readonly [key: string]: string | boolean | undefined
28+
}
29+
}

packages/nimiq-vitepress-theme/src/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ export interface NimiqVitepressThemeConfig {
4040
showEditContent?: boolean
4141
}
4242

43+
export interface NimiqVitepressFrontmatter {
44+
layout?: 'home' | 'docs'
45+
sidebar?: boolean
46+
outline?: boolean
47+
secondarySidebar?: boolean
48+
widget?: boolean
49+
}
50+
4351
export interface DefineThemeNqVpOptions {
4452
enhanceApp?: Theme['enhanceApp']
4553
}

0 commit comments

Comments
 (0)