Skip to content

Commit

Permalink
feat(client): add RouteLink component
Browse files Browse the repository at this point in the history
  • Loading branch information
meteorlxy committed Feb 5, 2024
1 parent 6edc2d2 commit d4dbcc6
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,55 @@ const guardEvent = (event: MouseEvent): boolean | void => {
return true
}

export interface VPLinkProps extends HTMLAttributes {
export interface RouteLinkProps extends HTMLAttributes {
/**
* Whether the link is active to have an active class
*
* Notice that the active status is not automatically determined according to the current route.
*
* @default false
*/
active?: boolean

/**
* The class to add when the link is active
*
* @default 'route-link-active'
*/
activeClass?: string

/**
* The route path to link to
*/
to: string
}

export const VPLink: FunctionalComponent<
VPLinkProps,
/**
* Component to render a link to another route.
*
* It's similar to `RouterLink` in `vue-router`, but more lightweight.
*
* It's recommended to use `RouteLink` in VuePress.
*/
export const RouteLink: FunctionalComponent<
RouteLinkProps,
Record<never, never>,
{
default: () => string | VNode | (string | VNode)[]
}
> = ({ to = '', ...attrs }, { slots }) => {
> = (
{ active = false, activeClass = 'route-link-active', to, ...attrs },
{ slots },
) => {
const router = useRouter()
const path = withBase(resolveRoutePath(to))

return h(
'a',
{
class: 'vp-link',
href: path,
...attrs,
class: ['route-link', { [activeClass]: active }],
href: path,
onClick: (event: MouseEvent = {} as MouseEvent) => {
guardEvent(event) ? router.push(to).catch() : Promise.resolve()
},
Expand All @@ -51,4 +80,4 @@ export const VPLink: FunctionalComponent<
)
}

VPLink.displayName = 'VPLink'
RouteLink.displayName = 'RouteLink'
2 changes: 1 addition & 1 deletion packages/client/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from './ClientOnly.js'
export * from './Content.js'
export * from './VPLink.js'
export * from './RouteLink.js'
4 changes: 2 additions & 2 deletions packages/client/src/setupGlobalComponents.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { App } from 'vue'
import { ClientOnly, Content, VPLink } from './components/index.js'
import { ClientOnly, Content, RouteLink } from './components/index.js'

/**
* Register global built-in components
Expand All @@ -8,6 +8,6 @@ export const setupGlobalComponents = (app: App): void => {
/* eslint-disable vue/match-component-file-name, vue/no-reserved-component-names */
app.component('ClientOnly', ClientOnly)
app.component('Content', Content)
app.component('VPLink', VPLink)
app.component('RouteLink', RouteLink)
/* eslint-enable vue/match-component-file-name, vue/no-reserved-component-names */
}
12 changes: 6 additions & 6 deletions packages/markdown/src/plugins/linksPlugin/linksPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ export interface LinksPluginOptions {
/**
* Tag for internal links
*
* @default 'VPLink'
* @default 'RouteLink'
*/
internalTag?: 'a' | 'VPLink' | 'RouterLink'
internalTag?: 'a' | 'RouteLink' | 'RouterLink'

/**
* Additional attributes for external links
Expand All @@ -37,7 +37,7 @@ export const linksPlugin: PluginWithOptions<LinksPluginOptions> = (
options: LinksPluginOptions = {},
): void => {
// tag of internal links
const internalTag = options.internalTag || 'VPLink'
const internalTag = options.internalTag || 'RouteLink'

// attrs that going to be added to external links
const externalAttrs = {
Expand Down Expand Up @@ -93,7 +93,7 @@ export const linksPlugin: PluginWithOptions<LinksPluginOptions> = (
// convert
// <a href="hrefLink">
// to
// <VPLink to="toProp">
// <RouteLink to="toProp">

// notice that the path and hash are encoded by markdown-it
const rawPath = internalLinkMatch[1]
Expand All @@ -106,14 +106,14 @@ export const linksPlugin: PluginWithOptions<LinksPluginOptions> = (
filePathRelative,
)

if (['RouterLink', 'VPLink'].includes(internalTag)) {
if (['RouterLink', 'RouteLink'].includes(internalTag)) {
// convert starting tag of internal link to `internalTag`
token.tag = internalTag
// replace the original `href` attr with `to` attr
hrefAttr[0] = 'to'
// normalize markdown file path to route path
// we are removing the `base` from absolute path because it should not be
// passed to `<VPLink>` or `<RouterLink>`
// passed to `<RouteLink>` or `<RouterLink>`
const normalizedPath = normalizeRoutePath(
absolutePath.replace(new RegExp(`^${base}`), '/'),
)
Expand Down
150 changes: 75 additions & 75 deletions packages/markdown/tests/plugins/linksPlugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,24 +293,24 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => {

expect(rendered).toEqual(
[
'<VPLink to="foo.html">foo1</VPLink>',
'<VPLink to="foo.html#hash">foo2</VPLink>',
'<VPLink to="foo.html">foo3</VPLink>',
'<VPLink to="../bar.html">bar1</VPLink>',
'<VPLink to="../bar.html#hash">bar2</VPLink>',
'<VPLink to="../bar.html">bar3</VPLink>',
'<VPLink to="foo/bar.html">foobar1</VPLink>',
'<VPLink to="foo/bar.html#hash">foobar2</VPLink>',
'<VPLink to="../foo/bar.html">foobar3</VPLink>',
'<VPLink to="../foo/bar.html#hash">foobar4</VPLink>',
'<VPLink to="index.html">index1</VPLink>',
'<VPLink to="index.html#hash">index2</VPLink>',
'<VPLink to="index.html">index3</VPLink>',
'<VPLink to="../">index4</VPLink>',
'<VPLink to="../foo/bar/">index5</VPLink>',
'<VPLink to="index.html">readme1</VPLink>',
'<VPLink to="../#hash">readme2</VPLink>',
'<VPLink to="../foo/bar/">readme3</VPLink>',
'<RouteLink to="foo.html">foo1</RouteLink>',
'<RouteLink to="foo.html#hash">foo2</RouteLink>',
'<RouteLink to="foo.html">foo3</RouteLink>',
'<RouteLink to="../bar.html">bar1</RouteLink>',
'<RouteLink to="../bar.html#hash">bar2</RouteLink>',
'<RouteLink to="../bar.html">bar3</RouteLink>',
'<RouteLink to="foo/bar.html">foobar1</RouteLink>',
'<RouteLink to="foo/bar.html#hash">foobar2</RouteLink>',
'<RouteLink to="../foo/bar.html">foobar3</RouteLink>',
'<RouteLink to="../foo/bar.html#hash">foobar4</RouteLink>',
'<RouteLink to="index.html">index1</RouteLink>',
'<RouteLink to="index.html#hash">index2</RouteLink>',
'<RouteLink to="index.html">index3</RouteLink>',
'<RouteLink to="../">index4</RouteLink>',
'<RouteLink to="../foo/bar/">index5</RouteLink>',
'<RouteLink to="index.html">readme1</RouteLink>',
'<RouteLink to="../#hash">readme2</RouteLink>',
'<RouteLink to="../foo/bar/">readme3</RouteLink>',
]
.map((a) => `<p>${a}</p>`)
.join('\n') + '\n',
Expand Down Expand Up @@ -420,24 +420,24 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => {

expect(rendered).toEqual(
[
'<VPLink to="/path/to/foo.html">foo1</VPLink>',
'<VPLink to="/path/to/foo.html#hash">foo2</VPLink>',
'<VPLink to="/path/to/foo.html">foo3</VPLink>',
'<VPLink to="/path/bar.html">bar1</VPLink>',
'<VPLink to="/path/bar.html#hash">bar2</VPLink>',
'<VPLink to="/path/bar.html">bar3</VPLink>',
'<VPLink to="/path/to/foo/bar.html">foobar1</VPLink>',
'<VPLink to="/path/to/foo/bar.html#hash">foobar2</VPLink>',
'<VPLink to="/path/foo/bar.html">foobar3</VPLink>',
'<VPLink to="/path/foo/bar.html#hash">foobar4</VPLink>',
'<VPLink to="/path/to/">index1</VPLink>',
'<VPLink to="/path/to/#hash">index2</VPLink>',
'<VPLink to="/path/to/">index3</VPLink>',
'<VPLink to="/path/">index4</VPLink>',
'<VPLink to="/path/foo/bar/">index5</VPLink>',
'<VPLink to="/path/to/">readme1</VPLink>',
'<VPLink to="/path/#hash">readme2</VPLink>',
'<VPLink to="/path/foo/bar/">readme3</VPLink>',
'<RouteLink to="/path/to/foo.html">foo1</RouteLink>',
'<RouteLink to="/path/to/foo.html#hash">foo2</RouteLink>',
'<RouteLink to="/path/to/foo.html">foo3</RouteLink>',
'<RouteLink to="/path/bar.html">bar1</RouteLink>',
'<RouteLink to="/path/bar.html#hash">bar2</RouteLink>',
'<RouteLink to="/path/bar.html">bar3</RouteLink>',
'<RouteLink to="/path/to/foo/bar.html">foobar1</RouteLink>',
'<RouteLink to="/path/to/foo/bar.html#hash">foobar2</RouteLink>',
'<RouteLink to="/path/foo/bar.html">foobar3</RouteLink>',
'<RouteLink to="/path/foo/bar.html#hash">foobar4</RouteLink>',
'<RouteLink to="/path/to/">index1</RouteLink>',
'<RouteLink to="/path/to/#hash">index2</RouteLink>',
'<RouteLink to="/path/to/">index3</RouteLink>',
'<RouteLink to="/path/">index4</RouteLink>',
'<RouteLink to="/path/foo/bar/">index5</RouteLink>',
'<RouteLink to="/path/to/">readme1</RouteLink>',
'<RouteLink to="/path/#hash">readme2</RouteLink>',
'<RouteLink to="/path/foo/bar/">readme3</RouteLink>',
]
.map((a) => `<p>${a}</p>`)
.join('\n') + '\n',
Expand Down Expand Up @@ -549,24 +549,24 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => {

expect(rendered).toEqual(
[
`<VPLink to="/${encoded中}/${encoded文}/foo.html">foo1</VPLink>`,
`<VPLink to="/${encoded中}/${encoded文}/foo.html#hash">foo2</VPLink>`,
`<VPLink to="/${encoded中}/${encoded文}/foo.html">foo3</VPLink>`,
`<VPLink to="/${encoded中}/bar.html">bar1</VPLink>`,
`<VPLink to="/${encoded中}/bar.html#hash">bar2</VPLink>`,
`<VPLink to="/${encoded中}/bar.html">bar3</VPLink>`,
`<VPLink to="/${encoded中}/${encoded文}/foo/bar.html">foobar1</VPLink>`,
`<VPLink to="/${encoded中}/${encoded文}/foo/bar.html#hash">foobar2</VPLink>`,
`<VPLink to="/${encoded中}/foo/bar.html">foobar3</VPLink>`,
`<VPLink to="/${encoded中}/foo/bar.html#hash">foobar4</VPLink>`,
`<VPLink to="/${encoded中}/${encoded文}/">index1</VPLink>`,
`<VPLink to="/${encoded中}/${encoded文}/#hash">index2</VPLink>`,
`<VPLink to="/${encoded中}/${encoded文}/">index3</VPLink>`,
`<VPLink to="/${encoded中}/">index4</VPLink>`,
`<VPLink to="/${encoded中}/foo/bar/">index5</VPLink>`,
`<VPLink to="/${encoded中}/${encoded文}/">readme1</VPLink>`,
`<VPLink to="/${encoded中}/#hash">readme2</VPLink>`,
`<VPLink to="/${encoded中}/foo/bar/">readme3</VPLink>`,
`<RouteLink to="/${encoded中}/${encoded文}/foo.html">foo1</RouteLink>`,
`<RouteLink to="/${encoded中}/${encoded文}/foo.html#hash">foo2</RouteLink>`,
`<RouteLink to="/${encoded中}/${encoded文}/foo.html">foo3</RouteLink>`,
`<RouteLink to="/${encoded中}/bar.html">bar1</RouteLink>`,
`<RouteLink to="/${encoded中}/bar.html#hash">bar2</RouteLink>`,
`<RouteLink to="/${encoded中}/bar.html">bar3</RouteLink>`,
`<RouteLink to="/${encoded中}/${encoded文}/foo/bar.html">foobar1</RouteLink>`,
`<RouteLink to="/${encoded中}/${encoded文}/foo/bar.html#hash">foobar2</RouteLink>`,
`<RouteLink to="/${encoded中}/foo/bar.html">foobar3</RouteLink>`,
`<RouteLink to="/${encoded中}/foo/bar.html#hash">foobar4</RouteLink>`,
`<RouteLink to="/${encoded中}/${encoded文}/">index1</RouteLink>`,
`<RouteLink to="/${encoded中}/${encoded文}/#hash">index2</RouteLink>`,
`<RouteLink to="/${encoded中}/${encoded文}/">index3</RouteLink>`,
`<RouteLink to="/${encoded中}/">index4</RouteLink>`,
`<RouteLink to="/${encoded中}/foo/bar/">index5</RouteLink>`,
`<RouteLink to="/${encoded中}/${encoded文}/">readme1</RouteLink>`,
`<RouteLink to="/${encoded中}/#hash">readme2</RouteLink>`,
`<RouteLink to="/${encoded中}/foo/bar/">readme3</RouteLink>`,
]
.map((a) => `<p>${a}</p>`)
.join('\n') + '\n',
Expand Down Expand Up @@ -677,24 +677,24 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => {

expect(rendered).toEqual(
[
'<VPLink to="/path/to/foo.html">foo1</VPLink>',
'<VPLink to="/path/to/foo.html#hash">foo2</VPLink>',
'<VPLink to="/path/to/foo.html">foo3</VPLink>',
'<VPLink to="/path/bar.html">bar1</VPLink>',
'<VPLink to="/path/bar.html#hash">bar2</VPLink>',
'<VPLink to="/path/bar.html">bar3</VPLink>',
'<VPLink to="/path/to/foo/bar.html">foobar1</VPLink>',
'<VPLink to="/path/to/foo/bar.html#hash">foobar2</VPLink>',
'<VPLink to="/path/foo/bar.html">foobar3</VPLink>',
'<VPLink to="/path/foo/bar.html#hash">foobar4</VPLink>',
'<VPLink to="/path/to/">index1</VPLink>',
'<VPLink to="/path/to/#hash">index2</VPLink>',
'<VPLink to="/path/to/">index3</VPLink>',
'<VPLink to="/path/">index4</VPLink>',
'<VPLink to="/path/foo/bar/">index5</VPLink>',
'<VPLink to="/path/to/">readme1</VPLink>',
'<VPLink to="/path/#hash">readme2</VPLink>',
'<VPLink to="/path/foo/bar/">readme3</VPLink>',
'<RouteLink to="/path/to/foo.html">foo1</RouteLink>',
'<RouteLink to="/path/to/foo.html#hash">foo2</RouteLink>',
'<RouteLink to="/path/to/foo.html">foo3</RouteLink>',
'<RouteLink to="/path/bar.html">bar1</RouteLink>',
'<RouteLink to="/path/bar.html#hash">bar2</RouteLink>',
'<RouteLink to="/path/bar.html">bar3</RouteLink>',
'<RouteLink to="/path/to/foo/bar.html">foobar1</RouteLink>',
'<RouteLink to="/path/to/foo/bar.html#hash">foobar2</RouteLink>',
'<RouteLink to="/path/foo/bar.html">foobar3</RouteLink>',
'<RouteLink to="/path/foo/bar.html#hash">foobar4</RouteLink>',
'<RouteLink to="/path/to/">index1</RouteLink>',
'<RouteLink to="/path/to/#hash">index2</RouteLink>',
'<RouteLink to="/path/to/">index3</RouteLink>',
'<RouteLink to="/path/">index4</RouteLink>',
'<RouteLink to="/path/foo/bar/">index5</RouteLink>',
'<RouteLink to="/path/to/">readme1</RouteLink>',
'<RouteLink to="/path/#hash">readme2</RouteLink>',
'<RouteLink to="/path/foo/bar/">readme3</RouteLink>',
]
.map((a) => `<p>${a}</p>`)
.join('\n') + '\n',
Expand Down Expand Up @@ -812,9 +812,9 @@ describe('@vuepress/markdown > plugins > linksPlugin', () => {

expect(rendered).toEqual(
[
'<VPLink to="/path/to/">md</VPLink>',
'<VPLink to="/base/path/to/">md-with-redundant-base</VPLink>',
'<VPLink to="/path/to/">html</VPLink>',
'<RouteLink to="/path/to/">md</RouteLink>',
'<RouteLink to="/base/path/to/">md-with-redundant-base</RouteLink>',
'<RouteLink to="/path/to/">html</RouteLink>',
]
.map((a) => `<p>${a}</p>`)
.join('\n') + '\n',
Expand Down

0 comments on commit d4dbcc6

Please sign in to comment.