Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/components/content/ExampleTheTitle.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<h1 class="text-4xl">
<Markdown :use="$slots.default" unwrap="p" />
<ContentSlot :use="$slots.default" unwrap="p" />
</h1>
</template>
2 changes: 1 addition & 1 deletion docs/components/content/MyButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defineProps({

<template>
<button :class="type">
<Markdown :use="$slots.default" unwrap="p" />
<ContentSlot :use="$slots.default" unwrap="p" />
</button>
</template>

Expand Down
14 changes: 7 additions & 7 deletions docs/content/3.guide/1.writing/3.mdc.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Any component in the `components/content/` directory or [made available globally
The component must contain either:

- A `<slot />`{lang="html"} to accept raw text or another component.
- The `<Markdown />`{lang="html"} component to accept formatted text.
- The `<ContentSlot />`{lang="html"} component to accept formatted text.

In a markdown file, use the component with the **`::`** identifier.

Expand Down Expand Up @@ -107,15 +107,15 @@ You can add more `::::` when nesting components as a visual reminder.

### Markdown rendering

The `<Markdown />`{lang="html"} component is auto-imported by Nuxt Content. It acts as a special slot that accepts rich text rendered by Markdown.
The `<ContentSlot />`{lang="html"} component is auto-imported by Nuxt Content. It acts as a special slot that accepts rich text rendered by Markdown.

The `unwrap` prop accepts an HTML tag that will be used to unwrap the content, useful when using tags such as title tags (`<h1>`{lang="html"}, `<h2>`{lang="html"}, ...) or inline tags (`<button>`{lang="html"}, `<a>`{lang="html"}, ...).

::code-group
```html [TheTitle.vue]
<template>
<h1 class="text-4xl">
<Markdown :use="$slots.default" unwrap="p" />
<ContentSlot :use="$slots.default" unwrap="p" />
</h1>
</template>
```
Expand All @@ -131,15 +131,15 @@ The `unwrap` prop accepts an HTML tag that will be used to unwrap the content, u
::
::

The `<Markdown />` component can act as a named slot with the `use` property:
The `<ContentSlot />` component can act as a named slot with the `use` property:

```html
<Markdown :use="$slots.description" unwrap="p">
<ContentSlot :use="$slots.description" unwrap="p">
```

## Inline components

Inline components are components without slots or `<Markdown />`.
Inline components are components without slots or `<ContentSlot />`.

They can be used with the `:` identifier.

Expand Down Expand Up @@ -189,7 +189,7 @@ defineProps(['type'])

<template>
<div :class="[type]">
<Markdown :use="$slots.default" unwrap="p" />
<ContentSlot :use="$slots.default" unwrap="p" />
</div>
</template>
```
Expand Down
4 changes: 4 additions & 0 deletions docs/content/4.api/1.components/5.markdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ title: '<Markdown>'
description: 'The fastest way to inject Markdown into your Vue components.'
---

::alert{type=danger}
**NOTE**: As of [email protected] this component is deprecated and replaced by [`<ContentSlot>`](./content-slot).
::

The `<Markdown>`{lang=html} component makes it easier to use Markdown syntax in your Vue components.

It is useful when creating components that you want to use in your Markdown content.
Expand Down
32 changes: 32 additions & 0 deletions docs/content/4.api/1.components/6.content-slot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: '<ContentSlot>'
description: 'The fastest way to inject Markdown into your Vue components.'
---

The `<ContentSlot>`{lang=html} component makes it easier to use Markdown syntax in your Vue components.

It is useful when creating components that you want to use in your Markdown content.

## Props

- `use`{lang=ts}: The slot to bind on, you must provide a `use`{lang=ts} via `$slots.{your_slot}`{lang=ts} in `<template>`{lang=html}.
- Type: Vue slot `Function`
- Example: `$slots.default`{lang=ts}
- `unwrap`{lang=ts}: Whether to unwrap the content or not. This is useful when you want to extract the content nested in native Markdown syntax. Each specified tag will get removed from AST.
- Type: `Boolean`{lang=ts} or `String`{lang=ts}
- Default: `false`{lang=ts}
- Example: `'ul li'`{lang=ts}

## Example

```html [components/FancyHeader.vue]
<template>
<h2 class="fancy-header"><ContentSlot :use="$slots.default" unwrap="p" /></h2>
</template>
```

```md [content/index.md]
::fancy-header
That text paragraph will be unwrapped.
::
```
2 changes: 1 addition & 1 deletion docs/content/4.api/2.composables/3.unwrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
title: 'useUnwrap()'
---

It can be used to achieve a similar behavior as `unwrap`{lang=ts} prop from `<Markdown>`{lang=html} component.
It can be used to achieve a similar behavior as `unwrap`{lang=ts} prop from `<ContentSlot>`{lang=html} component.
2 changes: 1 addition & 1 deletion docs/content/5.examples/3.mdc/2.nested-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: 'Nested components'
description: 'The MDC syntax allow you to nest components within a parent slot using indentation and the :: syntax.'
---

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

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

Expand Down
2 changes: 1 addition & 1 deletion docs/content/6.blog/1.announcing-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ MDC is Markdown, so nothing changes and you can keep using the `.md` extension.

<template>
<button :class="type">
<Markdown :use="$slots.default" unwrap="p" />
<ContentSlot :use="$slots.default" unwrap="p" />
</button>
</template>
```
Expand Down
2 changes: 1 addition & 1 deletion examples/mdc/nested-components/components/AppNested.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<aside>
<h2>Nested component</h2>
<Markdown unwrap="p" />
<ContentSlot unwrap="p" />
</aside>
</template>
4 changes: 2 additions & 2 deletions playground/document-driven/components/content/Alert.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
</div>
<div>
<h3 style="margin: 0">
<Markdown :use="$slots.title" unwrap="p" />
<ContentSlot :use="$slots.title" unwrap="p" />
</h3>
<div>
<Markdown :use="$slots.default" unwrap="p" />
<ContentSlot :use="$slots.default" unwrap="p" />
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion playground/document-driven/components/content/List.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const { flatUnwrap } = useUnwrap()
<ul>
<li v-for="(item, index) of flatUnwrap($slots.default(), ['ul'])" :key="index">
β˜‘οΈŽ
<span><Markdown :use="() => item" unwrap="li" /></span>
<span><ContentSlot :use="() => item" unwrap="li" /></span>
</li>
</ul>
</template>
2 changes: 1 addition & 1 deletion playground/shared/components/content/Alert.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div class="alert">
<Markdown :use="$slots.default" unwrap="p" />
<ContentSlot :use="$slots.default" unwrap="p" />
</div>
</template>

Expand Down
4 changes: 2 additions & 2 deletions playground/shared/components/content/MarkdownBetween.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<Markdown unwrap="ul li">
<ContentSlot :use="$slots.default" unwrap="ul li">
<template #between>
<br>
</template>
</Markdown>
</ContentSlot>
</template>
85 changes: 85 additions & 0 deletions src/runtime/components/ContentSlot.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<script lang="ts">
import type { Slot } from 'vue'
import { defineComponent, getCurrentInstance, useSlots, computed, useUnwrap, h } from '#imports'

/**
* ContentSlot component
*/
export default defineComponent({
name: 'ContentSlot',
functional: true,
props: {
/**
* A slot name or function
*/
use: {
type: Function,
default: undefined
},
/**
* Tags to unwrap separated by spaces
* Example: 'ul li'
*/
unwrap: {
type: [Boolean, String],
default: false
}
},
setup (props) {
const { parent } = getCurrentInstance()
const { between, default: fallbackSlot } = useSlots()

const tags = computed(() => {
if (typeof props.unwrap === 'string') { return props.unwrap.split(' ') }
return ['*']
})

return {
fallbackSlot,
tags,
between,
parent
}
},
render ({ use, unwrap, fallbackSlot, between, tags, parent }) {
try {
let slot: Slot = use
if (typeof use === 'string') {
slot = parent?.slots[use] || parent?.parent?.slots[use]
// eslint-disable-next-line no-console
console.warn(`Please set :use="$slots.${use}" in <ContentSlot> component to enable reactivity`)
}

if (!slot) { return fallbackSlot ? fallbackSlot() : h('div') }

if (!unwrap) { return [slot()] }

const { flatUnwrap } = useUnwrap()

const unwrapped = flatUnwrap(slot(), tags)

if (between) {
return unwrapped.flatMap(
(vnode, index) => index === 0 ? [vnode] : [between(), vnode]
)
}

return unwrapped.reduce((acc, item) => {
if (typeof item.children === 'string') {
if (typeof acc[acc.length - 1] === 'string') {
acc[acc.length - 1] += item.children
} else {
acc.push(item.children)
}
} else {
acc.push(item)
}
return acc
}, [])
} catch (e) {
// Catching errors to allow content reactivity
return h('div')
}
}
})
</script>
67 changes: 7 additions & 60 deletions src/runtime/components/Markdown.vue
Original file line number Diff line number Diff line change
@@ -1,32 +1,19 @@
<script lang="ts">
import type { Slot } from 'vue'
import { defineComponent, getCurrentInstance, useSlots, computed, useUnwrap, h } from '#imports'
import ContentSlot from './ContentSlot'
import { defineComponent, getCurrentInstance, useSlots, computed } from '#imports'

/**
* Markdown component
*/
export default defineComponent({
// eslint-disable-next-line vue/multi-word-component-names
name: 'Markdown',
functional: true,
props: {
/**
* A slot name or function
*/
use: {
type: Function,
default: undefined
},
/**
* Tags to unwrap separated by spaces
* Example: 'ul li'
*/
unwrap: {
type: [Boolean, String],
default: false
}
},
extends: ContentSlot,
setup (props) {
if (process.dev) {
// eslint-disable-next-line no-console
console.warn('[deprecation] <Markdown> component is deprecated. Please use <ContentSlot> instead.')
}
const { parent } = getCurrentInstance()
const { between, default: fallbackSlot } = useSlots()

Expand All @@ -41,46 +28,6 @@ export default defineComponent({
between,
parent
}
},
render ({ use, unwrap, fallbackSlot, between, tags, parent }) {
try {
let slot: Slot = use
if (typeof use === 'string') {
slot = parent?.slots[use] || parent?.parent?.slots[use]
// eslint-disable-next-line no-console
console.warn(`Please set :use="$slots.${use}" in <Markdown> component to enable reactivity`)
}

if (!slot) { return fallbackSlot ? fallbackSlot() : h('div') }

if (!unwrap) { return [slot()] }

const { flatUnwrap } = useUnwrap()

const unwrapped = flatUnwrap(slot(), tags)

if (between) {
return unwrapped.flatMap(
(vnode, index) => index === 0 ? [vnode] : [between(), vnode]
)
}

return unwrapped.reduce((acc, item) => {
if (typeof item.children === 'string') {
if (typeof acc[acc.length - 1] === 'string') {
acc[acc.length - 1] += item.children
} else {
acc.push(item.children)
}
} else {
acc.push(item)
}
return acc
}, [])
} catch (e) {
// Catching errors to allow content reactivity
return h('div')
}
}
})
</script>
2 changes: 1 addition & 1 deletion test/fixtures/basic/components/content/Alert.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div>
<Markdown :use="$slots.default" />
<ContentSlot :use="$slots.default" />
</div>
</template>
8 changes: 4 additions & 4 deletions test/fixtures/basic/components/content/TestMarkdown.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<template>
<div>
<h1 class="text-3xl font-bold text-primary-900 dark:text-primary-100">
<Markdown :use="$slots.title" unwrap="p">
<ContentSlot :use="$slots.title" unwrap="p">
Default title
</Markdown>
</ContentSlot>
</h1>
<p>
<Markdown :use="$slots.default" unwrap="p">
<ContentSlot :use="$slots.default" unwrap="p">
Default paragraph
</Markdown>
</ContentSlot>
</p>
</div>
</template>