@sit-onyx/nuxt-docs 0.6.0 → 0.7.0-dev-20260513141728
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.
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import type { TocLink } from "@nuxtjs/mdc";
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{
|
|
5
|
+
toc: TocLink[];
|
|
6
|
+
/**
|
|
7
|
+
* Whether the TOC should be hidden completely.
|
|
8
|
+
* By default, the content space is still limited even when the TOC is empty
|
|
9
|
+
* so the layout width is the same when switching between pages with and without a TOC.
|
|
10
|
+
*
|
|
11
|
+
* When set to `true`, the content will be full width.
|
|
12
|
+
*/
|
|
13
|
+
hidden?: boolean;
|
|
14
|
+
}>();
|
|
15
|
+
|
|
16
|
+
defineSlots<{
|
|
17
|
+
/**
|
|
18
|
+
* Page content.
|
|
19
|
+
*/
|
|
20
|
+
default(): unknown;
|
|
21
|
+
}>();
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<div v-if="props.hidden" class="layout__content">
|
|
26
|
+
<slot></slot>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div v-else class="layout">
|
|
30
|
+
<div class="layout__content">
|
|
31
|
+
<slot></slot>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<TableOfContents v-if="props.toc.length" class="layout__toc" :links="props.toc" />
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<style lang="scss" scoped>
|
|
39
|
+
@use "sit-onyx/breakpoints.scss";
|
|
40
|
+
|
|
41
|
+
.layout {
|
|
42
|
+
/** Gap between page content and TOC. Equivalent to one grid column + 2 * grid gutter/gap */
|
|
43
|
+
--onyx-content-toc-gap: calc(2 * var(--onyx-grid-gutter) + (100 / var(--onyx-grid-columns)) * 1%);
|
|
44
|
+
display: grid;
|
|
45
|
+
gap: var(--onyx-content-toc-gap);
|
|
46
|
+
|
|
47
|
+
// see: https://storybook.onyx.schwarz/?path=/docs/navigation-tableofcontents--docs
|
|
48
|
+
grid-template-columns: 1fr minmax(8rem, 15rem);
|
|
49
|
+
|
|
50
|
+
&__toc {
|
|
51
|
+
position: sticky;
|
|
52
|
+
top: var(--onyx-grid-margin-vertical);
|
|
53
|
+
height: calc(100vh - var(--onyx-nav-bar-height) - 2 * var(--onyx-grid-margin-vertical));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// hide TOC on smaller screens
|
|
57
|
+
@include breakpoints.container(max, md) {
|
|
58
|
+
display: flex;
|
|
59
|
+
flex-direction: column;
|
|
60
|
+
|
|
61
|
+
.layout__toc {
|
|
62
|
+
display: none;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
&__content {
|
|
67
|
+
// remove the top margin of the first child since its redundant to the page padding
|
|
68
|
+
:deep(> div > :first-child) {
|
|
69
|
+
margin-top: 0;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
</style>
|
|
@@ -1,24 +1,75 @@
|
|
|
1
1
|
import type { Collections } from "@nuxt/content";
|
|
2
2
|
|
|
3
|
+
export type UseCollectionOptions<TCollection extends keyof Collections = keyof Collections> = {
|
|
4
|
+
/**
|
|
5
|
+
* Collection name to query.
|
|
6
|
+
*/
|
|
7
|
+
collection: MaybeRef<TCollection>;
|
|
8
|
+
/**
|
|
9
|
+
* Path to query the collection item for.
|
|
10
|
+
*
|
|
11
|
+
* @default Current route.
|
|
12
|
+
*/
|
|
13
|
+
path?: MaybeRef<string>;
|
|
14
|
+
};
|
|
15
|
+
|
|
3
16
|
/**
|
|
4
|
-
* Composable for loading the collection
|
|
17
|
+
* Composable for loading the collection data for the current route and locale.
|
|
18
|
+
* Will throw an 404 error if the collection item could not be found.
|
|
19
|
+
* Sets SEO data with `useSeoMeta()` automatically.
|
|
5
20
|
*/
|
|
6
|
-
export const useCollection =
|
|
7
|
-
|
|
21
|
+
export const useCollection = async <TCollection extends keyof Collections = keyof Collections>(
|
|
22
|
+
options: UseCollectionOptions<TCollection>,
|
|
23
|
+
) => {
|
|
24
|
+
const nuxtApp = useNuxtApp();
|
|
8
25
|
const { locale } = useI18n();
|
|
26
|
+
const route = useRoute();
|
|
9
27
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
28
|
+
const collection = computed(() => toValue(options.collection));
|
|
29
|
+
const path = computed(() => {
|
|
30
|
+
let _path = toValue(options.path);
|
|
31
|
+
if (_path) return _path;
|
|
32
|
+
|
|
33
|
+
// get base path of current route (remove a potential locale prefix)
|
|
34
|
+
_path = route.path;
|
|
35
|
+
const localePrefix = `/${locale.value}`;
|
|
36
|
+
|
|
37
|
+
if (_path.startsWith(localePrefix)) return _path.slice(localePrefix.length);
|
|
38
|
+
return _path;
|
|
15
39
|
});
|
|
16
40
|
|
|
17
|
-
|
|
18
|
-
() => `
|
|
19
|
-
() =>
|
|
20
|
-
|
|
21
|
-
|
|
41
|
+
const { data: collectionData } = await useAsyncData(
|
|
42
|
+
() => `collection-${collection.value}-${path.value}`,
|
|
43
|
+
() => queryCollection(collection.value).path(path.value).first(),
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const data = computed<Collections[TCollection] | undefined>(() => {
|
|
47
|
+
return collectionData.value as Collections[TCollection];
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
watch(
|
|
51
|
+
data,
|
|
52
|
+
async (newValue) => {
|
|
53
|
+
// if data is "null", the page content was not found. "undefined" means it is not loaded yet
|
|
54
|
+
if (newValue !== null) return;
|
|
55
|
+
await nuxtApp.runWithContext(() =>
|
|
56
|
+
showError({
|
|
57
|
+
message: "Page not found",
|
|
58
|
+
statusCode: 404,
|
|
59
|
+
fatal: true,
|
|
60
|
+
}),
|
|
61
|
+
);
|
|
22
62
|
},
|
|
63
|
+
{ immediate: true },
|
|
23
64
|
);
|
|
65
|
+
|
|
66
|
+
// runWithContext is needed due to async usage above
|
|
67
|
+
await nuxtApp.runWithContext(() => {
|
|
68
|
+
useSeoMeta({
|
|
69
|
+
title: () => data.value?.seo.title,
|
|
70
|
+
description: () => data.value?.seo.description,
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return { data };
|
|
24
75
|
};
|
|
@@ -34,20 +34,25 @@ export type SidebarNavigationOptions = {
|
|
|
34
34
|
collapsed?: boolean;
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
export
|
|
37
|
+
export type UseSidebarNavigationOptions = {
|
|
38
|
+
/**
|
|
39
|
+
* Collection name to use for querying the navigation.
|
|
40
|
+
*/
|
|
41
|
+
collection: Ref<keyof Collections>;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Composable for querying and mapping the navigation items for a given collection.
|
|
46
|
+
*/
|
|
47
|
+
export const useSidebarNavigation = async (options: UseSidebarNavigationOptions) => {
|
|
38
48
|
const { locale } = useI18n();
|
|
39
49
|
const localePath = useLocalePath();
|
|
40
50
|
const route = useRoute();
|
|
41
51
|
|
|
42
52
|
const { data } = await useAsyncData(
|
|
43
|
-
() => `navigation-${locale.value}`,
|
|
44
|
-
() =>
|
|
45
|
-
|
|
46
|
-
return queryCollectionNavigation(collection as keyof Collections);
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
default: () => [],
|
|
50
|
-
},
|
|
53
|
+
() => `navigation-${options.collection.value}-${locale.value}`,
|
|
54
|
+
() => queryCollectionNavigation(options.collection.value),
|
|
55
|
+
{ default: () => [] },
|
|
51
56
|
);
|
|
52
57
|
|
|
53
58
|
/**
|
package/app/layouts/sidebar.vue
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
+
import type { Collections } from "@nuxt/content";
|
|
2
3
|
import { iconArrowSmallLeft } from "@sit-onyx/icons";
|
|
3
4
|
import type { OnyxPageLayoutProps, OnyxSidebarProps } from "sit-onyx";
|
|
5
|
+
import TableOfContentsLayout from "../components/TableOfContentsLayout.vue";
|
|
4
6
|
import type { SidebarNavigationItem } from "../composables/useSidebarNavigation.js";
|
|
5
7
|
|
|
6
8
|
const props = defineProps<
|
|
@@ -41,10 +43,13 @@ const slots = defineSlots<{
|
|
|
41
43
|
sidebarFooter?(): unknown;
|
|
42
44
|
}>();
|
|
43
45
|
|
|
44
|
-
const {
|
|
46
|
+
const { locale } = useI18n();
|
|
47
|
+
const collectionName = computed(() => `content_${locale.value}` as keyof Collections);
|
|
45
48
|
|
|
46
|
-
const
|
|
47
|
-
|
|
49
|
+
const { navigation, previousRootItem } = await useSidebarNavigation({ collection: collectionName });
|
|
50
|
+
|
|
51
|
+
const { data: collection } = await useCollection({ collection: collectionName });
|
|
52
|
+
const toc = computed(() => collection.value?.body.toc?.links ?? []);
|
|
48
53
|
</script>
|
|
49
54
|
|
|
50
55
|
<template>
|
|
@@ -85,13 +90,9 @@ const toc = computed(() => collection.data.value?.body.toc?.links ?? []);
|
|
|
85
90
|
|
|
86
91
|
<slot name="hero"></slot>
|
|
87
92
|
|
|
88
|
-
<
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
</div>
|
|
92
|
-
|
|
93
|
-
<TableOfContents v-if="toc.length" class="main__toc" :links="toc" />
|
|
94
|
-
</div>
|
|
93
|
+
<TableOfContentsLayout class="onyx-grid-layout" :toc>
|
|
94
|
+
<slot></slot>
|
|
95
|
+
</TableOfContentsLayout>
|
|
95
96
|
|
|
96
97
|
<template v-if="!!slots.footer" #footer>
|
|
97
98
|
<slot name="footer"></slot>
|
|
@@ -104,8 +105,6 @@ const toc = computed(() => collection.data.value?.body.toc?.links ?? []);
|
|
|
104
105
|
</template>
|
|
105
106
|
|
|
106
107
|
<style lang="scss" scoped>
|
|
107
|
-
@use "sit-onyx/breakpoints.scss";
|
|
108
|
-
|
|
109
108
|
.sidebar {
|
|
110
109
|
&__empty {
|
|
111
110
|
max-width: 100%;
|
|
@@ -116,36 +115,4 @@ const toc = computed(() => collection.data.value?.body.toc?.links ?? []);
|
|
|
116
115
|
justify-content: flex-start;
|
|
117
116
|
}
|
|
118
117
|
}
|
|
119
|
-
|
|
120
|
-
.main {
|
|
121
|
-
/** Gap between page content and TOC. Equivalent to one grid column + 2 * grid gutter/gap */
|
|
122
|
-
--onyx-content-toc-gap: calc(2 * var(--onyx-grid-gutter) + (100 / var(--onyx-grid-columns)) * 1%);
|
|
123
|
-
display: grid;
|
|
124
|
-
gap: var(--onyx-content-toc-gap);
|
|
125
|
-
|
|
126
|
-
// see: https://storybook.onyx.schwarz/?path=/docs/navigation-tableofcontents--docs
|
|
127
|
-
grid-template-columns: 1fr minmax(8rem, 15rem);
|
|
128
|
-
|
|
129
|
-
&__toc {
|
|
130
|
-
position: sticky;
|
|
131
|
-
top: var(--onyx-grid-margin-vertical);
|
|
132
|
-
height: calc(100vh - var(--onyx-nav-bar-height) - 2 * var(--onyx-grid-margin-vertical));
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// hide TOC on smaller screens
|
|
136
|
-
@include breakpoints.container(max, md) {
|
|
137
|
-
grid-template-columns: 1fr;
|
|
138
|
-
|
|
139
|
-
.main__toc {
|
|
140
|
-
display: none;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
.content {
|
|
146
|
-
// remove the top margin of the first child since its redundant to the page padding
|
|
147
|
-
:deep(> div > :first-child) {
|
|
148
|
-
margin-top: 0;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
118
|
</style>
|
package/app/pages/[...slug].vue
CHANGED
|
@@ -1,30 +1,16 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const collection = await useCollection();
|
|
2
|
+
import type { Collections } from "@nuxt/content";
|
|
5
3
|
|
|
6
|
-
|
|
7
|
-
() => collection.data.value,
|
|
8
|
-
(data) => {
|
|
9
|
-
// if data is "null", the page content was not found. "undefined" means it is not loaded yet
|
|
10
|
-
if (data !== null) return;
|
|
11
|
-
throw showError({
|
|
12
|
-
message: "Page not found",
|
|
13
|
-
statusCode: 404,
|
|
14
|
-
});
|
|
15
|
-
},
|
|
16
|
-
{ immediate: true },
|
|
17
|
-
);
|
|
4
|
+
definePageMeta({ layout: "sidebar" });
|
|
18
5
|
|
|
19
|
-
const
|
|
6
|
+
const { locale } = useI18n();
|
|
20
7
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
description: computed(() => collection.data.value?.seo.description),
|
|
8
|
+
const { data } = await useCollection({
|
|
9
|
+
collection: computed(() => `content_${locale.value}` as keyof Collections),
|
|
24
10
|
});
|
|
25
11
|
</script>
|
|
26
12
|
|
|
27
|
-
<!-- eslint-disable-next-line vue/no-root-v-if --
|
|
13
|
+
<!-- eslint-disable-next-line vue/no-root-v-if -- The "useCollection" will already redirect to the error page when the data is undefined but the data might still be undefined while e.g. switching to another page -->
|
|
28
14
|
<template>
|
|
29
15
|
<ContentRenderer v-if="data" :value="data" />
|
|
30
16
|
</template>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sit-onyx/nuxt-docs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0-dev-20260513141728",
|
|
4
4
|
"description": "Nuxt layer/template for creating documentations with the onyx design system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Schwarz IT KG",
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"@nuxtjs/i18n": ">= 10",
|
|
34
34
|
"@nuxtjs/mdc": ">= 0.20.2",
|
|
35
35
|
"sass-embedded": ">= 1",
|
|
36
|
-
"@sit-onyx/icons": "^1.9.
|
|
37
|
-
"
|
|
38
|
-
"sit-onyx": "^1.
|
|
36
|
+
"@sit-onyx/icons": "^1.9.1",
|
|
37
|
+
"sit-onyx": "^1.14.0-dev-20260513141728",
|
|
38
|
+
"@sit-onyx/nuxt": "^1.0.1"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@fontsource-variable/source-code-pro": "^5.2.7",
|
|
@@ -49,11 +49,11 @@
|
|
|
49
49
|
"nuxt": "4.4.4",
|
|
50
50
|
"sass-embedded": "1.99.0",
|
|
51
51
|
"typescript": "5.9.3",
|
|
52
|
-
"vue": "3.5.
|
|
53
|
-
"@sit-onyx/icons": "^1.9.0",
|
|
52
|
+
"vue": "3.5.34",
|
|
54
53
|
"@sit-onyx/nuxt": "^1.0.1",
|
|
55
|
-
"@sit-onyx/
|
|
56
|
-
"sit-onyx": "^1.
|
|
54
|
+
"@sit-onyx/icons": "^1.9.1",
|
|
55
|
+
"sit-onyx": "^1.14.0-dev-20260513141728",
|
|
56
|
+
"@sit-onyx/shared": "^0.1.0"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"dev": "pnpm dev:prepare && nuxi dev playground",
|