components/menu_item.vue (134 lines of code) (raw):

<script> import uniqueId from 'lodash/uniqueId'; import { GlIcon } from '../helpers/gitlab_ui'; import { slugify } from '../helpers/slugify'; export default { name: 'MenuItem', components: { GlIcon, }, props: { item: { type: Object, required: true, }, basePath: { type: String, required: false, default: '', }, depth: { type: Number, required: false, default: 0, }, navTree: { type: Object, required: true, }, }, computed: { depthClass() { switch (this.depth) { case 2: return 'gl-pl-6'; case 3: return 'gl-pl-8'; default: return null; } }, hasChildren() { return this.item.children?.length; }, isExpanded() { return this.item.isActive; }, isExternalLink() { return this.item.path && this.item.path.startsWith('http'); }, itemId() { return uniqueId(`item-${slugify(this.item.title)}-`); }, nextBasePath() { let { basePath: nextBasePath } = this; if (this.item.path) { nextBasePath += `/${this.item.path}`; } return nextBasePath; }, path() { if (!this.item.path) { return ''; } if (this.isExternalLink) { return this.item.path; } return `${this.basePath}/${this.item.path}`; }, }, }; </script> <template> <li v-if="depth === 0" class="gl-m-0 gl-list-none gl-p-0"> <div aria-hidden="true" class="gl-py-2 gl-pl-4 gl-pr-3 gl-text-sm gl-font-bold"> {{ item.title }} </div> <ul :aria-label="item.title" class="gl-m-0 gl-pl-0"> <menu-item v-for="child in item.children" :key="child.id" :item="child" :depth="depth + 1" :base-path="nextBasePath" :nav-tree="navTree" /> </ul> </li> <li v-else-if="hasChildren" class="gl-m-0 gl-list-none gl-p-0"> <button class="sidebar__nav-toggle gl-block gl-w-full gl-cursor-pointer gl-appearance-none gl-rounded-base gl-border-none gl-bg-transparent gl-p-3 gl-pl-4 gl-text-left gl-font-regular gl-text-base" :class="depthClass" :aria-expanded="isExpanded" :aria-controls="itemId" @click.prevent="item.toggle()" > <span class="gl-flex gl-items-center gl-gap-2"> <span class="gl-shrink gl-grow">{{ item.title }}</span> <gl-icon :class="{ 'gl-rotate-90': isExpanded }" name="chevron-right" /> </span> </button> <ul v-show="isExpanded" :id="itemId" :aria-label="item.title" class="gl-m-0 gl-pl-0"> <menu-item v-for="child in item.children" :key="child.id" :item="child" :depth="depth + 1" :base-path="nextBasePath" :nav-tree="navTree" /> </ul> </li> <li v-else class="sidebar__nav-option gl-m-0 gl-list-none gl-p-0"> <a v-if="isExternalLink" :href="path" target="_blank" rel="noopener" class="sidebar__nav-anchor gl-flex gl-gap-2 gl-rounded-base gl-p-3 gl-pl-4 !gl-no-underline" :class="depthClass" > <span>{{ item.title }}</span> <gl-icon name="external-link" /> </a> <nuxt-link v-else :to="path" class="sidebar__nav-anchor gl-flex gl-rounded-base gl-p-3 gl-pl-4 !gl-no-underline" :class="depthClass" @click.prevent="navTree.activateNode(item)" > {{ item.title }} </nuxt-link> </li> </template>