mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 15:15:19 +00:00
feat(nuxt3): support custom
prop for <nuxt-link>
(#4249)
Co-authored-by: Daniel Roe <daniel@roe.dev> Co-authored-by: pooya parsa <pyapar@gmail.com>
This commit is contained in:
parent
c88e1716ab
commit
38e0fa6e1e
@ -81,6 +81,7 @@ In this example, we use `<NuxtLink>` with `target`, `rel`, and `noRel` props.
|
||||
- **replace**: Works the same as [Vue Router's `replace` prop](https://router.vuejs.org/api/#replace) on internal links
|
||||
- **ariaCurrentValue**: An `aria-current` attribute value to apply on exact active links. Works the same as [Vue Router's `aria-current-value` prop](https://router.vuejs.org/api/#aria-current-value) on internal links
|
||||
- **external**: Forces the link to be considered as external (`true`) or internal (`false`). This is helpful to handle edge-cases
|
||||
- **custom**: Whether `<NuxtLink>` should wrap its content in an `<a>` element. It allows taking full control of how a link is rendered and how navigation works when it is clicked. Works the same as [Vue Router's `custom` prop](https://router.vuejs.org/api/#custom)
|
||||
|
||||
::alert{icon=👉}
|
||||
Defaults can be overwritten, see [overwriting defaults](#overwriting-defaults) if you want to change them.
|
||||
|
@ -3,9 +3,17 @@
|
||||
<NuxtLink to="/about">
|
||||
About page
|
||||
</NuxtLink>
|
||||
<NuxtLink v-slot="{ navigate }" to="/about" custom>
|
||||
<button @click="navigate">
|
||||
Custom about page
|
||||
</button>
|
||||
</NuxtLink>
|
||||
<NuxtLink to="https://nuxtjs.org">
|
||||
Nuxt website
|
||||
</NuxtLink>
|
||||
<NuxtLink v-slot="{ href, target }" to="https://nuxtjs.org" custom>
|
||||
<a :href="href" :target="target">Go to {{ href }}</a>
|
||||
</NuxtLink>
|
||||
<NuxtLink to="https://twitter.com/nuxt_js" target="_blank">
|
||||
Nuxt Twitter with a blank target
|
||||
</NuxtLink>
|
||||
|
@ -23,6 +23,13 @@ const timer = useState('timer', () => 0)
|
||||
<NuxtLink to="/redirect" class="n-link-base">
|
||||
Redirect
|
||||
</NuxtLink>
|
||||
<NuxtLink custom to="/redirect">
|
||||
<template #default="{ href, navigate }">
|
||||
<button @click="navigate">
|
||||
Custom: {{ href }}
|
||||
</button>
|
||||
</template>
|
||||
</NuxtLink>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
|
@ -2,37 +2,38 @@ import { defineComponent, h, resolveComponent, PropType, computed, DefineCompone
|
||||
import { RouteLocationRaw, Router } from 'vue-router'
|
||||
import { hasProtocol } from 'ufo'
|
||||
|
||||
import { useRouter } from '#app'
|
||||
import { navigateTo, useRouter } from '#app'
|
||||
|
||||
const firstNonUndefined = <T>(...args: T[]): T => args.find(arg => arg !== undefined)
|
||||
|
||||
const DEFAULT_EXTERNAL_REL_ATTRIBUTE = 'noopener noreferrer'
|
||||
|
||||
export type NuxtLinkOptions = {
|
||||
componentName?: string;
|
||||
externalRelAttribute?: string | null;
|
||||
activeClass?: string;
|
||||
exactActiveClass?: string;
|
||||
componentName?: string
|
||||
externalRelAttribute?: string | null
|
||||
activeClass?: string
|
||||
exactActiveClass?: string
|
||||
}
|
||||
|
||||
export type NuxtLinkProps = {
|
||||
// Routing
|
||||
to?: string | RouteLocationRaw;
|
||||
href?: string | RouteLocationRaw;
|
||||
external?: boolean;
|
||||
to?: string | RouteLocationRaw
|
||||
href?: string | RouteLocationRaw
|
||||
external?: boolean
|
||||
replace?: boolean
|
||||
custom?: boolean
|
||||
|
||||
// Attributes
|
||||
target?: string;
|
||||
rel?: string;
|
||||
noRel?: boolean;
|
||||
target?: string
|
||||
rel?: string
|
||||
noRel?: boolean
|
||||
|
||||
// Styling
|
||||
activeClass?: string;
|
||||
exactActiveClass?: string;
|
||||
activeClass?: string
|
||||
exactActiveClass?: string
|
||||
|
||||
// Vue Router's `<RouterLink>` additional props
|
||||
replace?: boolean;
|
||||
ariaCurrentValue?: string;
|
||||
ariaCurrentValue?: string
|
||||
};
|
||||
|
||||
export function defineNuxtLink (options: NuxtLinkOptions) {
|
||||
@ -154,9 +155,9 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
||||
activeClass: props.activeClass || options.activeClass,
|
||||
exactActiveClass: props.exactActiveClass || options.exactActiveClass,
|
||||
replace: props.replace,
|
||||
ariaCurrentValue: props.ariaCurrentValue
|
||||
ariaCurrentValue: props.ariaCurrentValue,
|
||||
custom: props.custom
|
||||
},
|
||||
// TODO: Slot API
|
||||
slots.default
|
||||
)
|
||||
}
|
||||
@ -175,6 +176,22 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
||||
// converts `""` to `null` to prevent the attribute from being added as empty (`rel=""`)
|
||||
: firstNonUndefined<string | null>(props.rel, options.externalRelAttribute, href ? DEFAULT_EXTERNAL_REL_ATTRIBUTE : '') || null
|
||||
|
||||
const navigate = () => navigateTo(href, { replace: props.replace })
|
||||
|
||||
// https://router.vuejs.org/api/#custom
|
||||
if (props.custom) {
|
||||
if (!slots.default) { return null }
|
||||
return slots.default({
|
||||
href,
|
||||
navigate,
|
||||
route: router.resolve(href),
|
||||
rel,
|
||||
target,
|
||||
isActive: false,
|
||||
isExactActive: false
|
||||
})
|
||||
}
|
||||
|
||||
return h('a', { href, rel, target }, slots.default?.())
|
||||
}
|
||||
}
|
||||
|
@ -179,8 +179,24 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>((nuxtApp) => {
|
||||
|
||||
nuxtApp.vueApp.component('RouterLink', {
|
||||
functional: true,
|
||||
props: { to: String },
|
||||
setup: (props, { slots }) => () => h('a', { href: props.to, onClick: (e) => { e.preventDefault(); router.push(props.to) } }, slots)
|
||||
props: {
|
||||
to: String,
|
||||
custom: Boolean,
|
||||
replace: Boolean,
|
||||
// Not implemented
|
||||
activeClass: String,
|
||||
exactActiveClass: String,
|
||||
ariaCurrentValue: String
|
||||
},
|
||||
setup: (props, { slots }) => {
|
||||
const navigate = () => handleNavigation(props.to, props.replace)
|
||||
return () => {
|
||||
const route = router.resolve(props.to)
|
||||
return props.custom
|
||||
? slots.default?.({ href: props.to, navigate, route })
|
||||
: h('a', { href: props.to, onClick: (e) => { e.preventDefault(); return navigate() } }, slots)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (process.client) {
|
||||
|
@ -81,7 +81,10 @@ export default defineNuxtModule({
|
||||
}
|
||||
|
||||
nuxt.hook('autoImports:extend', (autoImports) => {
|
||||
autoImports.push({ name: 'definePageMeta', as: 'definePageMeta', from: resolve(runtimeDir, 'composables') })
|
||||
autoImports.push(
|
||||
{ name: 'definePageMeta', as: 'definePageMeta', from: resolve(runtimeDir, 'composables') },
|
||||
{ name: 'useLink', as: 'useLink', from: 'vue-router' }
|
||||
)
|
||||
})
|
||||
|
||||
// Extract macros from pages
|
||||
|
Loading…
Reference in New Issue
Block a user