Navigation Menu

Skip to content

Commit

Permalink
feat(devtools): display components using useLink()
Browse files Browse the repository at this point in the history
Close #1003
  • Loading branch information
posva committed Jun 18, 2021
1 parent 57b1468 commit aab8c04
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 42 deletions.
11 changes: 10 additions & 1 deletion playground/App.vue
Expand Up @@ -66,6 +66,9 @@
<li>
<router-link to="/">Home</router-link>
</li>
<li>
<AppLink to="/">AppLink Home</AppLink>
</li>
<li>
<router-link to="/always-redirect">/always-redirect</router-link>
</li>
Expand Down Expand Up @@ -183,15 +186,21 @@
<script>
import { defineComponent, inject, computed, ref } from 'vue'
import { scrollWaiter } from './scrollWaiter'
import { useRoute } from '../src'
import { useLink, useRoute } from '../src'
import AppLink from './AppLink.vue'
export default defineComponent({
name: 'App',
components: { AppLink },
setup() {
const route = useRoute()
const state = inject('state')
const viewName = ref('default')
useLink({ to: '/' })
useLink({ to: '/documents/hello' })
useLink({ to: '/children' })
const currentLocation = computed(() => {
const { matched, ...rest } = route
return rest
Expand Down
64 changes: 64 additions & 0 deletions playground/AppLink.vue
@@ -0,0 +1,64 @@
<template>
<a
v-if="isExternalLink"
v-bind="attrs"
class="router-link"
:class="classes"
target="_blank"
rel="noopener noreferrer"
:href="to"
:tabindex="disabled ? -1 : undefined"
:aria-disabled="disabled"
>
<slot />
</a>
<a
v-else
v-bind="attrs"
class="router-link"
:class="classes"
:href="href"
:tabindex="disabled ? -1 : undefined"
:aria-disabled="disabled"
@click="navigate"
>
<slot />
</a>
</template>

<script>
import { RouterLinkImpl } from '../src/RouterLink'
import { computed, defineComponent, toRefs } from 'vue'
import { START_LOCATION, useLink, useRoute } from '../src'
export default defineComponent({
props: {
...RouterLinkImpl.props,
disabled: Boolean,
},
setup(props, { attrs }) {
const { replace, to, disabled } = toRefs(props)
const isExternalLink = computed(
() => typeof to.value === 'string' && to.value.startsWith('http')
)
const currentRoute = useRoute()
const { route, href, isActive, isExactActive, navigate } = useLink({
to: computed(() => (isExternalLink.value ? START_LOCATION : to.value)),
replace,
})
const classes = computed(() => ({
// allow link to be active for unrelated routes
'router-link-active':
isActive.value || currentRoute.path.startsWith(route.value.path),
'router-link-exact-active':
isExactActive.value || currentRoute.path === route.value.path,
}))
return { attrs, isExternalLink, href, navigate, classes, disabled }
},
})
</script>
5 changes: 5 additions & 0 deletions playground/index.html
Expand Up @@ -14,6 +14,11 @@
.router-link-exact-active {
color: red;
}
.router-link {
padding: 2px;
display: block;
border: 1px solid red;
}
.long {
background-color: lightgray;
height: 3000px;
Expand Down
57 changes: 36 additions & 21 deletions src/RouterLink.ts
Expand Up @@ -12,7 +12,12 @@ import {
getCurrentInstance,
watchEffect,
} from 'vue'
import { RouteLocationRaw, VueUseOptions, RouteLocation } from './types'
import {
RouteLocationRaw,
VueUseOptions,
RouteLocation,
RouteLocationNormalized,
} from './types'
import { isSameRouteLocationParams, isSameRouteRecord } from './location'
import { routerKey, routeLocationKey } from './injectionSymbols'
import { RouteRecord } from './matcher/types'
Expand Down Expand Up @@ -58,6 +63,12 @@ export interface RouterLinkProps extends RouterLinkOptions {
| 'false'
}

export interface UseLinkDevtoolsContext {
route: RouteLocationNormalized & { href: string }
isActive: boolean
isExactActive: boolean
}

export type UseLinkOptions = VueUseOptions<RouterLinkOptions>

// TODO: we could allow currentRoute as a prop to expose `isActive` and
Expand Down Expand Up @@ -122,6 +133,30 @@ export function useLink(props: UseLinkOptions) {
return Promise.resolve()
}

// devtools only
if ((__DEV__ || __FEATURE_PROD_DEVTOOLS__) && isBrowser) {
const instance = getCurrentInstance()
if (!instance) return
const linkContextDevtools: UseLinkDevtoolsContext = {
route: route.value,
isActive: isActive.value,
isExactActive: isExactActive.value,
}

// @ts-expect-error: this is internal
instance.__vrl_devtools = instance.__vrl_devtools || []
// @ts-expect-error: this is internal
instance.__vrl_devtools.push(linkContextDevtools)
watchEffect(
() => {
linkContextDevtools.route = route.value
linkContextDevtools.isActive = isActive.value
linkContextDevtools.isExactActive = isExactActive.value
},
{ flush: 'post' }
)
}

return {
route,
href: computed(() => route.value.href),
Expand Down Expand Up @@ -173,26 +208,6 @@ export const RouterLinkImpl = /*#__PURE__*/ defineComponent({
)]: link.isExactActive,
}))

// devtools only
if ((__DEV__ || __FEATURE_PROD_DEVTOOLS__) && isBrowser) {
const instance = getCurrentInstance()
watchEffect(
() => {
if (!instance) return
;(instance as any).__vrl_route = link.route
},
{ flush: 'post' }
)
watchEffect(
() => {
if (!instance) return
;(instance as any).__vrl_active = link.isActive
;(instance as any).__vrl_exactActive = link.isExactActive
},
{ flush: 'post' }
)
}

return () => {
const children = slots.default && slots.default(link)
return props.custom
Expand Down
42 changes: 22 additions & 20 deletions src/devtools.ts
Expand Up @@ -14,6 +14,7 @@ import { RouterMatcher } from './matcher'
import { RouteRecordMatcher } from './matcher/pathMatcher'
import { PathParser } from './matcher/pathParserRanker'
import { Router } from './router'
import { UseLinkDevtoolsContext } from './RouterLink'
import { RouteLocation, RouteLocationNormalized } from './types'
import { assign } from './utils'

Expand Down Expand Up @@ -87,30 +88,30 @@ export function addDevtools(app: App, router: Router, matcher: RouterMatcher) {

// mark router-link as active
api.on.visitComponentTree(({ treeNode: node, componentInstance }) => {
if (node.name === 'RouterLink') {
if (componentInstance.__vrl_route) {
node.tags.push({
label: (componentInstance.__vrl_route as RouteLocation).path,
textColor: 0,
backgroundColor: ORANGE_400,
})
}

if (componentInstance.__vrl_exactActive) {
node.tags.push({
label: 'exact',
textColor: 0,
backgroundColor: LIME_500,
})
}
// if multiple useLink are used
if (Array.isArray(componentInstance.__vrl_devtools)) {
componentInstance.__devtoolsApi = api
;(
componentInstance.__vrl_devtools as UseLinkDevtoolsContext[]
).forEach(devtoolsData => {
let backgroundColor = ORANGE_400
let tooltip: string = ''

if (devtoolsData.isExactActive) {
backgroundColor = LIME_500
tooltip = 'This is exactly active'
} else if (devtoolsData.isActive) {
backgroundColor = BLUE_600
tooltip = 'This link is active'
}

if (componentInstance.__vrl_active) {
node.tags.push({
label: 'active',
label: devtoolsData.route.path,
textColor: 0,
backgroundColor: BLUE_600,
tooltip,
backgroundColor,
})
}
})
}
})

Expand All @@ -119,6 +120,7 @@ export function addDevtools(app: App, router: Router, matcher: RouterMatcher) {
refreshRoutesView()
api.notifyComponentUpdate()
api.sendInspectorTree(routerInspectorId)
api.sendInspectorState(routerInspectorId)
})

const navigationsLayerId = 'router:navigations:' + id
Expand Down

0 comments on commit aab8c04

Please sign in to comment.