Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(navigation): allow _dir.yml to filter navigation #1261

Merged
merged 3 commits into from
Jun 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 29 additions & 0 deletions docs/components/content/ReadMore.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<template>
<Alert>
<Icon class="inline-block w-5 h-5" name="heroicons-outline:information-circle" />
Read more in
<NuxtLink :to="link">
{{ computedTitle }}
</NuxtLink>.
</Alert>
</template>

<script setup lang="ts">
import { splitByCase, upperFirst } from 'scule'

const props = defineProps({
link: {
type: String,
required: true
},
title: {
type: String,
required: false,
default: undefined
}
})

const createTitle = (title, link) => (title || link.split('/').filter(Boolean).map(part => splitByCase(part).map(p => upperFirst(p)).join(' ')).join(' > ').replace('Api', 'API'))

const computedTitle = computed(() => createTitle(props.title, props.link))
</script>
189 changes: 141 additions & 48 deletions docs/content/3.guide/2.displaying/3.navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,78 +3,149 @@ title: Navigation
description: 'The fetchContentNavigation utility returns a tree of items based on the content/ directory structure and files.'
---

Use the `title`{lang="ts"} and `path`{lang="ts"} properties of each item to create your application's navigation.
Navigation generation is based on your content sources directory structure.

## Nested navigation
Based on the generated `_id` and `_path` keys, we generate a whole navigation structure for your content.

You can pass a `queryContent()`{lang="ts"} instance to the `fetchContentNavigation()`{lang="ts"} utility to filter the items returned.
It allows you to create advanced navigation components without having to maintain any querying logic related to it.

That allows to create navigation objects for which the starting point is based on a specific content directory.
## Structure

```ts
const query = queryContent({
where: {
_path: { $contains: '/your/navigation/starting/point' }
The navigation object can be seen as a tree representing in a JSON format the structure of your content sources.

It is divided in two type of nodes, **pages** and **directories**.

::code-group

``` [Directory structure]
content/
my-directory/
page.md
```

```json [Directory node]
{
"title": "My Directory",
"_path": "/my-directory",
"children": [
...pagesNodes
]
}
```

```json [Page node]
{
"title": "Page title",
"_path": "/my-directory/my-page",
"_id": "content:my-directory:page.md"
}
```

```json [Complete navigation]
[
{
"title": "My Directory",
"_path": "/my-directory",
"children": [
{
"title": "Page title",
"_path": "/my-directory/my-page",
"_id": "content:my-directory:page.md"
}
]
}
})
]
```

::

## Configuration

Set `navigation: false` in the [front-matter](/guide/writing/markdown) of a page to exclude it in `fetchContentNavigation()` return value.
### Module

The only key available in [module configuration](/api/configuration#navigation) about navigation is `fields`.

`fields` allows you to define the properties that will be included in the navigation items.

::code-group

```ts [nuxt.config.ts]
defineNuxtConfig({
content: {
navigation: {
fields: ['author', 'publishedAt']
}
}
})
```

```md
```md [page.md]
---
navigation: false
title: My Page
author: 'SΓ©bastien Chopin'
publishedAt: '15-06-2022'
---
```

## Example
```json [Navigation node]
{
"title": "My Page",
"author": "SΓ©bastien Chopin",
"publishedAt": "15-06-2022",
"_path": "/page",
"_id": "content:page.md",
}
```

::code-group
::

```Text [Directory structure]
content/
index.md
sub-folder
about.md
### Per-directory

Alternatively, the navigation also allows you to configure directory nodes via `_dir.yml` files.

It allows you to overwrite directory names, and add custom properties to directory nodes in navigation.

## Excluding

Set `navigation: false` in the [front-matter](/guide/writing/markdown) of a page to filter it out of navigation.

```md [page.md]
---
navigation: false
---
```

```vue [app.vue]
<script setup>
const { data: navigation } = await useAsyncData('navigation', () => {
return fetchContentNavigation()
})
</script>
This pattern also works inside `_dir.yml` file, allowing you to filter out content directories and files.

```yaml [_dir.yml]
navigation: false
```

You can also use the `_` content prefix to exclude content directories and files.

::code-group

<template>
<pre>
{{ navigation }}
</pre>
</template>
``` [Directory structure]
content/
_hidden-directory/
page.md
index.md
not-hidden-directory/
_dir.yml
index.md
page.md
```

```Text [Output]
```json [Navigation object]
[
{
"title": "Hello Content V2",
"_path": "/",
"children": [
{
"_id": "content:index.md",
"title": "Hello Content V2",
"_path": "/"
}
]
},
{
"title": "Sub Directory",
"_path": "/sub-directory",
"title": "Not Hidden Directory",
"_path": "/not-hidden-directory",
"children": [
{
"_id": "content:sub-directory:about.md",
"title": "About Content V2",
"_path": "/sub-directory/about"
"title": "Page",
"_id": "content:not-hidden-directory:page.md",
"_path": "/not-hidden-directory/page"
}
]
}
Expand All @@ -83,4 +154,26 @@ const { data: navigation } = await useAsyncData('navigation', () => {

::

:ReadMore{link="/examples/navigation/fetch-content-navigation"}

## Nested navigation

You can pass a `queryContent()`{lang="ts"} instance to the `fetchContentNavigation()`{lang="ts"} utility to filter the items returned.

That allows to create navigation objects for which the starting point is based on a specific content directory.

```ts
const query = queryContent({
where: {
_path: { $contains: '/your/navigation/starting/point' }
}
})
```

---

::alert
Go deeper in the **API** section:

- [fetchContentNavigation()](/api/composables/fetch-content-navigation) composable to fetch navigation in `<script>`
- [ContentNavigation](/api/components/content-navigation) component made to shorten access to navigation in `<template>`
::
55 changes: 54 additions & 1 deletion docs/content/4.api/2.composables/2.fetch-content-navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,64 @@ title: 'fetchContentNavigation()'
description: 'The fetchContentNavigation utility returns a tree of items based on the content/ directory structure and files.'
---

## Usage

::code-group

```Text [Directory structure]
content/
index.md
sub-folder
about.md
```

```vue [app.vue]
<script setup>
const { data: navigation } = await useAsyncData('navigation', fetchContentNavigation())
</script>

<template>
<pre>
{{ navigation }}
</pre>
</template>
```

```json [Output]
[
{
"title": "Hello Content V2",
"_path": "/",
"children": [
{
"title": "Hello Content V2",
"_id": "content:index.md",
"_path": "/"
}
]
},
{
"title": "Sub Directory",
"_path": "/sub-directory",
"children": [
{
"title": "About Content V2",
"_id": "content:sub-directory:about.md",
"_path": "/sub-directory/about"
}
]
}
]
```

::

## Arguments

- `queryBuilder`{lang=ts}
- Type: `QueryBuilder`{lang=ts}
- Definition: Any query built via `queryContent()`{lang=ts}
- Default: `/`{lang=ts}

:ReadMore{link="/examples/navigation/fetch-content-navigation"}
::ReadMore{link="/examples/navigation/fetch-content-navigation"}
::
12 changes: 10 additions & 2 deletions docs/content/4.api/3.configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,10 +242,18 @@ Options for yaml parser.

## `navigation`

- Type: `Boolean`{lang=ts}
- Type: `false or Object`{lang=ts}
- Default: `true`{lang=ts}

Enable/Disable content navigation.
Configure the navigation feature.

Can be set to `false` to disable the feature completely.

### `navigation` options

| Option | Type | Description |
| ----------------- | :--------: | :-------- |
| `fields` | `string[]` | A list of fields to inherit from front-matter to navigation nodes. |

## `locales`

Expand Down
6 changes: 4 additions & 2 deletions docs/content/5.examples/1.essentials/1.hello-world.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ toc: false
title: 'Hello world'
---

:ReadMore{link="/get-started"}
::ReadMore{link="/get-started"}
::

:sandbox{repo="nuxt/content" branch="main" dir="examples/essentials/hello-world" file="app.vue"}
::sandbox{repo="nuxt/content" branch="main" dir="examples/essentials/hello-world" file="app.vue"}
::
6 changes: 4 additions & 2 deletions docs/content/5.examples/2.mdc/1.inline-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Inline components are components that don't contain any slot.

After creating them in the `components/` directory, declare them in your markdown files with the `:` MDC syntax.

:ReadMore{link="/guide/writing/mdc#inline-components"}
::ReadMore{link="/guide/writing/mdc#inline-components"}
::

:sandbox{repo="nuxt/content" branch="main" dir="examples/mdc/inline-component" file="content/index.md"}
::sandbox{repo="nuxt/content" branch="main" dir="examples/mdc/inline-component" file="content/index.md"}
::
5 changes: 3 additions & 2 deletions docs/content/5.examples/2.mdc/2.nested-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The MDC syntax allow you to nest components within a parent slot using indentati

The `components/AppNested.vue` component uses the `<Markdown>` component as markdown-rendering slot.

:ReadMore{link="/guide/writing/mdc#nesting"}
::ReadMore{link="/guide/writing/mdc#nesting"}

:sandbox{repo="nuxt/content" branch="main" dir="examples/mdc/nested-components" file="content/index.md"}
::sandbox{repo="nuxt/content" branch="main" dir="examples/mdc/nested-components" file="content/index.md"}
::
6 changes: 4 additions & 2 deletions docs/content/5.examples/2.mdc/3.props.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ description: ''

Pass props to your components in Markdown files using the `{}` MDC syntax.

:ReadMore{link="/guide/writing/mdc#props"}
::ReadMore{link="/guide/writing/mdc#props"}
::

:sandbox{repo="nuxt/content" branch="main" dir="examples/mdc/props" file="content/index.md"}
::sandbox{repo="nuxt/content" branch="main" dir="examples/mdc/props" file="content/index.md"}
::
6 changes: 4 additions & 2 deletions docs/content/5.examples/2.mdc/4.slots.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ You can fill a component's default slot by inserting content between `::`.

Use the `#` MDC syntax to fill a named slot inside a component.

:ReadMore{link="/guide/writing/mdc#slots"}
::ReadMore{link="/guide/writing/mdc#slots"}
::

:sandbox{repo="nuxt/content" branch="main" dir="examples/mdc/slots" file="content/index.md"}
::sandbox{repo="nuxt/content" branch="main" dir="examples/mdc/slots" file="content/index.md"}
::