Skip to content

Commit

Permalink
feat(Link)!: rename from LinkCustom and add exact-query / `exact-…
Browse files Browse the repository at this point in the history
…hash` props
  • Loading branch information
benjamincanac committed Jul 30, 2023
1 parent a9300db commit cefe5a7
Show file tree
Hide file tree
Showing 10 changed files with 70 additions and 27 deletions.
24 changes: 24 additions & 0 deletions docs/content/2.elements/9.link.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
title: 'Link'
description: Render a NuxtLink but with superpowers.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/elements/Link.vue
navigation:
badge: Edge
---

## Usage

The Link component is a wrapper around [`<NuxtLink>`](https://nuxt.com/docs/api/components/nuxt-link) through the [custom](https://router.vuejs.org/api/interfaces/RouterLinkProps.html#Properties-custom) prop that provides a few extra props:

- `inactive-class` prop to set a class when the link is inactive, `active-class` is used when active.
- `exact` prop to style with `active-class` when the link is active and the route is exactly the same as the current route.
- `exact-query` and `exact-hash` props to style with `active-class` when the link is active and the query or hash is exactly the same as the current query or hash.

The incentive behind this is to provide the same API as NuxtLink back in Nuxt 2 / Vue 2. You can read more about it in the Vue Router [migration from Vue 2](https://router.vuejs.org/guide/migration/#removal-of-the-exact-prop-in-router-link) guide.

It also renders an `<a>` tag when a `to` prop is provided, otherwise it renders a `<button>` tag.

It is used underneath by the [Button](/elements/button), [Dropdown](/elements/dropdown) and [VerticalNavigation](/navigation/vertical-navigation) components.
8 changes: 4 additions & 4 deletions src/runtime/components/elements/Button.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<ULinkCustom :type="type" :disabled="disabled || loading" :class="buttonClass">
<ULink :type="type" :disabled="disabled || loading" :class="buttonClass">
<slot name="leading" :disabled="disabled" :loading="loading">
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="leadingIconClass" aria-hidden="true" />
</slot>
Expand All @@ -13,15 +13,15 @@
<slot name="trailing" :disabled="disabled" :loading="loading">
<UIcon v-if="isTrailing && trailingIconName" :name="trailingIconName" :class="trailingIconClass" aria-hidden="true" />
</slot>
</ULinkCustom>
</ULink>
</template>

<script lang="ts">
import { computed, defineComponent, useSlots } from 'vue'
import type { PropType } from 'vue'
import { defu } from 'defu'
import UIcon from '../elements/Icon.vue'
import ULinkCustom from '../elements/LinkCustom.vue'
import ULink from '../elements/Link.vue'
import { classNames } from '../../utils'
import { useAppConfig } from '#imports'
// TODO: Remove
Expand All @@ -33,7 +33,7 @@ import appConfig from '#build/app.config'
export default defineComponent({
components: {
UIcon,
ULinkCustom
ULink
},
props: {
type: {
Expand Down
8 changes: 4 additions & 4 deletions src/runtime/components/elements/Dropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<HMenuItems :class="[ui.base, ui.divide, ui.ring, ui.rounded, ui.shadow, ui.background, ui.height]" static>
<div v-for="(subItems, index) of items" :key="index" :class="ui.padding">
<HMenuItem v-for="(item, subIndex) of subItems" :key="subIndex" v-slot="{ active, disabled: itemDisabled }" :disabled="item.disabled">
<ULinkCustom
<ULink
v-bind="omit(item, ['label', 'slot', 'icon', 'iconClass', 'avatar', 'shortcuts', 'disabled', 'click'])"
:class="[ui.item.base, ui.item.padding, ui.item.size, ui.item.rounded, active ? ui.item.active : ui.item.inactive, itemDisabled && ui.item.disabled]"
@click="item.click"
Expand All @@ -35,7 +35,7 @@
<UKbd v-for="shortcut of item.shortcuts" :key="shortcut">{{ shortcut }}</UKbd>
</span>
</slot>
</ULinkCustom>
</ULink>
</HMenuItem>
</div>
</HMenuItems>
Expand All @@ -53,7 +53,7 @@ import { omit } from 'lodash-es'
import UIcon from '../elements/Icon.vue'
import UAvatar from '../elements/Avatar.vue'
import UKbd from '../elements/Kbd.vue'
import ULinkCustom from '../elements/LinkCustom.vue'
import ULink from '../elements/Link.vue'
import { usePopper } from '../../composables/usePopper'
import type { DropdownItem } from '../../types/dropdown'
import type { PopperOptions } from '../../types'
Expand All @@ -73,7 +73,7 @@ export default defineComponent({
UIcon,
UAvatar,
UKbd,
ULinkCustom
ULink
},
props: {
items: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<template>
<button v-if="!to" :type="type" :disabled="disabled" v-bind="$attrs" :class="inactiveClass">
<button v-if="!to" v-bind="$attrs" :class="inactiveClass">
<slot />
</button>
<NuxtLink
v-else
v-slot="{ href, target, rel, navigate, isActive, isExactActive, isExternal }"
v-slot="{ route, href, target, rel, navigate, isActive, isExactActive, isExternal }"
v-bind="$props"
custom
>
Expand All @@ -13,7 +13,7 @@
:href="href"
:rel="rel"
:target="target"
:class="resolveLinkClass({ isActive, isExactActive })"
:class="resolveLinkClass(route, { isActive, isExactActive })"
@click="(e) => !isExternal && navigate(e)"
>
<slot v-bind="{ isActive: exact ? isExactActive : isActive }" />
Expand All @@ -22,22 +22,23 @@
</template>

<script lang="ts">
import { isEqual } from 'lodash-es'
import { defineComponent } from 'vue'
import { NuxtLink } from '#components'
export default defineComponent({
inheritAttrs: false,
props: {
...NuxtLink.props,
type: {
type: String,
default: null
exact: {
type: Boolean,
default: false
},
disabled: {
exactQuery: {
type: Boolean,
default: null
default: false
},
exact: {
exactHash: {
type: Boolean,
default: false
},
Expand All @@ -47,7 +48,14 @@ export default defineComponent({
}
},
setup (props) {
function resolveLinkClass ({ isActive, isExactActive }: { isActive: boolean, isExactActive: boolean }) {
function resolveLinkClass (route, { isActive, isExactActive }: { isActive: boolean, isExactActive: boolean }) {
if (props.exactQuery && !isEqual(route.query, useRoute().query)) {
return props.inactiveClass
}
if (props.exactHash && route.hash !== useRoute().hash) {
return props.inactiveClass
}
if (props.exact && isExactActive) {
return props.activeClass
}
Expand Down
8 changes: 4 additions & 4 deletions src/runtime/components/navigation/VerticalNavigation.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<nav :class="ui.wrapper">
<ULinkCustom
<ULink
v-for="(link, index) of links"
v-slot="{ isActive }"
:key="index"
Expand Down Expand Up @@ -33,7 +33,7 @@
{{ link.badge }}
</span>
</slot>
</ULinkCustom>
</ULink>
</nav>
</template>

Expand All @@ -44,7 +44,7 @@ import { defu } from 'defu'
import { omit } from 'lodash-es'
import UIcon from '../elements/Icon.vue'
import UAvatar from '../elements/Avatar.vue'
import ULinkCustom from '../elements/LinkCustom.vue'
import ULink from '../elements/Link.vue'
import type { VerticalNavigationLink } from '../../types/vertical-navigation'
import { useAppConfig } from '#imports'
// TODO: Remove
Expand All @@ -57,7 +57,7 @@ export default defineComponent({
components: {
UIcon,
UAvatar,
ULinkCustom
ULink
},
props: {
links: {
Expand Down
4 changes: 3 additions & 1 deletion src/runtime/types/button.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export interface Button {
import type { Link } from './link'

export interface Button extends Link {
type?: string
block?: boolean
label?: string
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/types/dropdown.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { NuxtLinkProps } from '#app'
import type { Link } from './link'
import type { Avatar } from './avatar'

export interface DropdownItem extends NuxtLinkProps {
export interface DropdownItem extends Link {
label: string
slot?: string
icon?: string
Expand Down
1 change: 1 addition & 0 deletions src/runtime/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './button'
export * from './clipboard'
export * from './command-palette'
export * from './dropdown'
export * from './link'
export * from './notification'
export * from './popper'
export * from './tabs'
Expand Down
8 changes: 8 additions & 0 deletions src/runtime/types/link.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { NuxtLinkProps } from '#app'

export interface Link extends NuxtLinkProps {
exact?: boolean
exactQuery?: boolean
exactMatch?: boolean
inactiveClass?: string
}
4 changes: 2 additions & 2 deletions src/runtime/types/vertical-navigation.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { NuxtLinkProps } from '#app'
import type { Link } from './link'
import type { Avatar } from './avatar'

export interface VerticalNavigationLink extends NuxtLinkProps {
export interface VerticalNavigationLink extends Link {
label: string
icon?: string
iconClass?: string
Expand Down

0 comments on commit cefe5a7

Please sign in to comment.