diff --git a/src/client/theme-default/components/NavBar.ts b/src/client/theme-default/components/NavBar.ts
index b0e961942cfc..e64fc5fce1e1 100644
--- a/src/client/theme-default/components/NavBar.ts
+++ b/src/client/theme-default/components/NavBar.ts
@@ -1,29 +1,14 @@
-// TODO dropdowns
import { computed } from 'vue'
-import { useSiteData, useRoute } from 'vitepress'
-import { withBase } from '../utils'
-
-const normalizePath = (path: string): string => {
- path = path
- .replace(/#.*$/, '')
- .replace(/\?.*$/, '')
- .replace(/\.html$/, '')
- if (path.endsWith('/')) {
- path += 'index'
- }
- return path
-}
+import { useSiteData } from 'vitepress'
+import NavBarLink from './NavBarLink.vue'
export default {
- setup() {
- const route = useRoute()
- const isActiveLink = (link: string): boolean => {
- return normalizePath(withBase(link)) === normalizePath(route.path)
- }
+ components: {
+ NavBarLink
+ },
+ setup() {
return {
- withBase,
- isActiveLink,
navData:
process.env.NODE_ENV === 'production'
? // navbar items do not change in production
diff --git a/src/client/theme-default/components/NavBar.vue b/src/client/theme-default/components/NavBar.vue
index e7af130b7ad5..5c34229d4422 100644
--- a/src/client/theme-default/components/NavBar.vue
+++ b/src/client/theme-default/components/NavBar.vue
@@ -13,16 +13,11 @@
{{ $site.title }}
@@ -44,18 +39,4 @@
.nav-links {
list-style-type: none;
}
-
-.nav-link {
- color: var(--text-color);
- margin-left: 1.5rem;
- font-weight: 600;
- display: inline-block;
- height: 1.75rem;
- line-height: 1.75rem;
-}
-
-.nav-link:hover,
-.nav-link.active {
- border-bottom: 2px solid var(--accent-color);
-}
diff --git a/src/client/theme-default/components/NavBarLink.ts b/src/client/theme-default/components/NavBarLink.ts
new file mode 100644
index 000000000000..973fd48044a6
--- /dev/null
+++ b/src/client/theme-default/components/NavBarLink.ts
@@ -0,0 +1,78 @@
+// TODO dropdowns
+import { defineComponent, computed, PropType } from 'vue'
+import { useRoute } from 'vitepress'
+import { withBase, isExternal } from '../utils'
+import { DefaultTheme } from '../config'
+import OutboundLink from './icons/OutboundLink.vue'
+
+const normalizePath = (path: string): string => {
+ path = path
+ .replace(/#.*$/, '')
+ .replace(/\?.*$/, '')
+ .replace(/\.html$/, '')
+ if (path.endsWith('/')) {
+ path += 'index'
+ }
+ return path
+}
+
+export default defineComponent({
+ components: {
+ OutboundLink
+ },
+
+ props: {
+ item: {
+ type: Object as PropType,
+ required: true
+ }
+ },
+
+ setup(props) {
+ const item = props.item
+
+ const route = useRoute()
+
+ const classes = computed(() => ({
+ active: isActiveLink.value,
+ external: isExternalLink.value
+ }))
+
+ const isActiveLink = computed(() => {
+ return normalizePath(withBase(item.link)) === normalizePath(route.path)
+ })
+
+ const isExternalLink = computed(() => {
+ return isExternal(item.link)
+ })
+
+ const href = computed(() => {
+ return isExternalLink.value ? item.link : withBase(item.link)
+ })
+
+ const target = computed(() => {
+ if (item.target) {
+ return item.target
+ }
+
+ return isExternalLink.value ? '_blank' : ''
+ })
+
+ const rel = computed(() => {
+ if (item.rel) {
+ return item.rel
+ }
+
+ return isExternalLink.value ? 'noopener noreferrer' : ''
+ })
+
+ return {
+ classes,
+ isActiveLink,
+ isExternalLink,
+ href,
+ target,
+ rel
+ }
+ }
+})
diff --git a/src/client/theme-default/components/NavBarLink.vue b/src/client/theme-default/components/NavBarLink.vue
new file mode 100644
index 000000000000..4a5f57caed4e
--- /dev/null
+++ b/src/client/theme-default/components/NavBarLink.vue
@@ -0,0 +1,35 @@
+
+
+ {{ item.text }}
+
+
+
+
+
+
+
diff --git a/src/client/theme-default/components/icons/OutboundLink.vue b/src/client/theme-default/components/icons/OutboundLink.vue
new file mode 100644
index 000000000000..06f8ba08b093
--- /dev/null
+++ b/src/client/theme-default/components/icons/OutboundLink.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
diff --git a/src/client/theme-default/utils.ts b/src/client/theme-default/utils.ts
index 4c6aee47dca2..a743905817cd 100644
--- a/src/client/theme-default/utils.ts
+++ b/src/client/theme-default/utils.ts
@@ -2,11 +2,16 @@ import { useSiteData, Route } from 'vitepress'
export const hashRE = /#.*$/
export const extRE = /\.(md|html)$/
+export const outboundRE = /^[a-z]+:/i
export function withBase(path: string) {
return (useSiteData().value.base + path).replace(/\/+/g, '/')
}
+export function isExternal(path: string): boolean {
+ return outboundRE.test(path)
+}
+
export function isActive(route: Route, path?: string): boolean {
if (path === undefined) {
return false