Skip to content

Commit

Permalink
Add badge support to navigation and sidebar (#110)
Browse files Browse the repository at this point in the history
  • Loading branch information
29avet1 authored Sep 17, 2024
1 parent 6d88c52 commit dcfb246
Show file tree
Hide file tree
Showing 19 changed files with 92 additions and 13 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ dist
TODOs.md
.DS_Store
demo/.vitepress/cache
.idea
5 changes: 4 additions & 1 deletion src/core/components/VTFlyout.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { MenuItem, MenuItemChild } from '../types/menu'
import { MenuBadgeItem, MenuItem, MenuItemChild } from '../types/menu'
import { useFocusContainer } from '../composables/FocusContainer'
import VTIconChevronDown from './icons/VTIconChevronDown.vue'
import VTIconMoreHorizontal from './icons/VTIconMoreHorizontal.vue'
import VTMenu from './VTMenu.vue'
import VTMenuBadge from './VTMenuBadge.vue'
const props = defineProps<{
button?: string
items?: (MenuItem | MenuItemChild)[]
label?: string
badge?: MenuBadgeItem
}>()
const open = ref(false)
Expand Down Expand Up @@ -40,6 +42,7 @@ useFocusContainer({
<slot name="btn-slot">
<span v-if="props.button" class="vt-flyout-button-text">
{{ props.button }}
<VTMenuBadge v-if="badge" :item="badge" />
<VTIconChevronDown class="vt-flyout-button-text-icon" />
</span>

Expand Down
5 changes: 4 additions & 1 deletion src/core/components/VTLink.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
<script lang="ts" setup>
import { computed } from 'vue'
import { MenuBadgeItem } from '../types/menu'
import VTIconExternalLink from './icons/VTIconExternalLink.vue'
import VTMenuBadge from './VTMenuBadge.vue'
const props = defineProps<{
href?: string
noIcon?: boolean
badge?: MenuBadgeItem
}>()
const isExternal = computed(() => props.href && /^[a-z]+:/i.test(props.href))
</script>

Expand All @@ -20,6 +22,7 @@ const isExternal = computed(() => props.href && /^[a-z]+:/i.test(props.href))
:rel="isExternal ? 'noopener noreferrer' : undefined"
>
<slot />
<VTMenuBadge v-if="badge" :item="badge" />
<VTIconExternalLink v-if="isExternal && !noIcon" class="vt-link-icon" />
</component>
</template>
9 changes: 9 additions & 0 deletions src/core/components/VTMenuBadge.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script setup lang="ts">
import { MenuBadgeItem } from '../../core'
defineProps<{ item: MenuBadgeItem }>()
</script>

<template>
<span class="vt-menu-badge" :class="item.type">{{ item.text }}</span>
</template>
2 changes: 1 addition & 1 deletion src/core/components/VTMenuLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defineProps<{ item: MenuItemWithLink }>()
</script>

<template>
<VTLink class="vt-menu-link" :href="item.link">
<VTLink class="vt-menu-link" :href="item.link" :badge="item.badge">
{{ item.text }}
</VTLink>
</template>
1 change: 1 addition & 0 deletions src/core/styles/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
@import './vt-hamburger.css';
@import './vt-link.css';
@import './vt-menu.css';
@import './vt-menu-badge.css';
@import './vt-menu-group.css';
@import './vt-menu-link.css';
@import './vt-locales.css';
Expand Down
35 changes: 35 additions & 0 deletions src/core/styles/vt-menu-badge.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.vt-menu-badge {
display: inline-block;
padding: 3.5px 4px;
margin-left: 6px;
font-size: 10px;
font-style: normal;
font-weight: 600;
line-height: 1;
letter-spacing: .2px;
border-radius: 6px;
background: var(--vt-c-blue);
color: var(--vt-c-white-soft);
}

.vt-menu-badge.secondary {
background: var(--vt-c-gray-light-5);
color: var(--vt-c-blue-dark);
}

.vt-menu-badge.success {
background: var(--vt-c-green);
}

.vt-menu-badge.info {
background: var(--vt-c-indigo-soft);
}

.vt-menu-badge.warning {
background: var(--vt-c-yellow-light);
color: var(--vt-c-black-soft);
}

.vt-menu-badge.danger {
background: var(--vt-c-red);
}
6 changes: 6 additions & 0 deletions src/core/types/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export type MenuItem = MenuItemWithLink | MenuItemWithChildren
export interface MenuItemWithLink {
text: string
link: string
badge?: MenuBadgeItem
}

export interface MenuItemWithChildren {
Expand All @@ -17,6 +18,11 @@ export interface MenuItemChildWithChildren {
items: MenuItemWithLink[]
}

export interface MenuBadgeItem {
text: string
type?: 'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'danger'
}

export type LocaleLinkItem = MenuItemWithLink & {
repo?: string
isTranslationsDesc?: boolean
Expand Down
1 change: 1 addition & 0 deletions src/vitepress/components/VPNavBarMenuGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const { page } = useData()
active: isActive(page.relativePath, item.activeMatch, true)
}"
:button="item.text"
:badge="item.badge"
:items="item.items"
/>
</template>
Expand Down
1 change: 1 addition & 0 deletions src/vitepress/components/VPNavBarMenuLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const { page } = useData()
)
}"
:href="item.link"
:badge="item.badge"
:noIcon="true"
>
{{ item.text }}
Expand Down
2 changes: 2 additions & 0 deletions src/vitepress/components/VPNavScreenMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ const { config } = useConfig()
<VPNavScreenMenuLink
v-if="'link' in item"
:text="item.text"
:badge="item.badge"
:link="item.link"
/>
<VPNavScreenMenuGroup
v-else
:text="item.text || ''"
:badge="item.badge"
:items="item.items"
/>
</template>
Expand Down
10 changes: 8 additions & 2 deletions src/vitepress/components/VPNavScreenMenuGroup.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<script lang="ts" setup>
import { computed, ref } from 'vue'
import { VTIconPlus, MenuItemChild } from '../../core'
import { VTIconPlus, MenuItemChild, MenuBadgeItem } from '../../core'
import VPNavScreenMenuGroupLink from './VPNavScreenMenuGroupLink.vue'
import VPNavScreenMenuGroupSection from './VPNavScreenMenuGroupSection.vue'
import VTMenuBadge from '../../core/components/VTMenuBadge.vue'
const props = defineProps<{
text: string
badge?: MenuBadgeItem
items: MenuItemChild[]
}>()
Expand All @@ -28,7 +30,10 @@ function toggle() {
:aria-expanded="isOpen"
@click="toggle"
>
<span class="button-text">{{ text }}</span>
<div>
<span class="button-text">{{ text }}</span>
<VTMenuBadge v-if="badge" :item="badge" />
</div>
<VTIconPlus class="button-icon" />
</button>

Expand All @@ -38,6 +43,7 @@ function toggle() {
<VPNavScreenMenuGroupLink
:text="item.text"
:link="item.link"
:badge="item.badge"
/>
</div>

Expand Down
5 changes: 3 additions & 2 deletions src/vitepress/components/VPNavScreenMenuGroupLink.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
<script lang="ts" setup>
import { VTLink } from '../../core'
import { VTLink, MenuBadgeItem } from '../../core'
import { inject } from 'vue'
defineProps<{
text: string
link: string
badge?: MenuBadgeItem
}>()
const closeScreen = inject('close-screen') as () => void
</script>

<template>
<VTLink class="VPNavScreenMenuGroupLink" :href="link" @click="closeScreen">
<VTLink class="VPNavScreenMenuGroupLink" :href="link" :badge="badge" @click="closeScreen">
{{ text }}
</VTLink>
</template>
Expand Down
1 change: 1 addition & 0 deletions src/vitepress/components/VPNavScreenMenuGroupSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ defineProps<{
:key="item.text"
:text="item.text"
:link="item.link"
:badge="item.badge"
/>
</div>
</template>
Expand Down
5 changes: 3 additions & 2 deletions src/vitepress/components/VPNavScreenMenuLink.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
<script lang="ts" setup>
import { VTLink } from '../../core'
import { MenuBadgeItem, VTLink } from '../../core'
import { inject } from 'vue'
defineProps<{
text: string
link: string
badge?: MenuBadgeItem
}>()
const closeScreen = inject('close-screen') as () => void
</script>

<template>
<VTLink class="VPNavScreenMenuLink" :href="link" @click="closeScreen">
<VTLink class="VPNavScreenMenuLink" :href="link" :badge="badge" @click="closeScreen">
{{ text }}
</VTLink>
</template>
Expand Down
2 changes: 1 addition & 1 deletion src/vitepress/components/VPSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ watchPostEffect(async () => {
config.i18n?.ariaSidebarNav ?? 'Sidebar Navigation'
}}</span>
<div v-for="group in sidebar" :key="group.text" class="group">
<VPSidebarGroup :text="group.text" :items="group.items" />
<VPSidebarGroup :text="group.text" :badge="group.badge" :items="group.items"/>
</div>
<slot name="bottom" />
</nav>
Expand Down
6 changes: 4 additions & 2 deletions src/vitepress/components/VPSidebarGroup.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<script lang="ts" setup>
import { MenuItemWithLink } from '../../core'
import { MenuItemWithLink, MenuBadgeItem } from '../../core'
import VPSidebarLink from './VPSidebarLink.vue'
import VTMenuBadge from '../../core/components/VTMenuBadge.vue'
import { isActive } from '../support/utils'
import { useData } from 'vitepress'
const props = defineProps<{
text: string
badge?: MenuBadgeItem
items: MenuItemWithLink[]
}>()
Expand All @@ -20,7 +22,7 @@ function hasActiveLink() {
<section class="VPSidebarGroup">
<div class="title">
<h2 class="title-text" :class="{ active: hasActiveLink() }">
{{ text }}
{{ text }}<VTMenuBadge v-if="badge" :item="badge" />
</h2>
</div>

Expand Down
5 changes: 4 additions & 1 deletion src/vitepress/components/VPSidebarLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { useData } from 'vitepress'
import { ref, inject, onMounted, watchPostEffect } from 'vue'
import { MenuItemWithLink } from '../../core'
import VTMenuBadge from '../../core/components/VTMenuBadge.vue'
import { isActive } from '../support/utils'
const props = defineProps<{
Expand All @@ -26,7 +27,9 @@ watchPostEffect(updateActive)
:href="item.link"
@click="closeSideBar"
>
<p class="link-text">{{ item.text }}</p>
<p class="link-text">
{{ item.text }}<VTMenuBadge v-if="item.badge" :item="item.badge" />
</p>
</a>
</template>

Expand Down
3 changes: 3 additions & 0 deletions src/vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
LocaleLinkItem,
MenuItemChildWithChildren,
MenuItemWithLink,
MenuBadgeItem,
SocialLink
} from '../core'

Expand Down Expand Up @@ -157,6 +158,7 @@ export type NavItemWithLink = MenuItemWithLink & {
export interface NavItemWithChildren {
text?: string
activeMatch?: string
badge?: MenuBadgeItem
items: (NavItemWithLink | MenuItemChildWithChildren)[]
}

Expand All @@ -168,6 +170,7 @@ export interface MultiSidebarConfig {

export interface SidebarGroup {
text: string
badge?: MenuBadgeItem
items: MenuItemWithLink[]
}

Expand Down

0 comments on commit dcfb246

Please sign in to comment.