2023-03-17 16:20:25 +05:30
|
|
|
<script>
|
2023-05-27 22:25:52 +05:30
|
|
|
import { kebabCase } from 'lodash';
|
2023-06-20 00:43:36 +05:30
|
|
|
import { GlButton, GlCollapse, GlIcon, GlBadge } from '@gitlab/ui';
|
|
|
|
import { s__ } from '~/locale';
|
|
|
|
import {
|
|
|
|
CLICK_MENU_ITEM_ACTION,
|
|
|
|
TRACKING_UNKNOWN_ID,
|
|
|
|
TRACKING_UNKNOWN_PANEL,
|
|
|
|
} from '~/super_sidebar/constants';
|
2023-03-17 16:20:25 +05:30
|
|
|
|
|
|
|
export default {
|
2023-06-20 00:43:36 +05:30
|
|
|
i18n: {
|
|
|
|
pinItem: s__('Navigation|Pin item'),
|
|
|
|
unpinItem: s__('Navigation|Unpin item'),
|
|
|
|
},
|
2023-03-17 16:20:25 +05:30
|
|
|
name: 'NavItem',
|
|
|
|
components: {
|
2023-06-20 00:43:36 +05:30
|
|
|
GlButton,
|
2023-05-27 22:25:52 +05:30
|
|
|
GlCollapse,
|
2023-03-17 16:20:25 +05:30
|
|
|
GlIcon,
|
2023-05-27 22:25:52 +05:30
|
|
|
GlBadge,
|
2023-03-17 16:20:25 +05:30
|
|
|
},
|
2023-06-20 00:43:36 +05:30
|
|
|
inject: {
|
|
|
|
pinnedItemIds: { default: { ids: [] } },
|
|
|
|
panelSupportsPins: { default: false },
|
|
|
|
panelType: { default: '' },
|
|
|
|
},
|
2023-03-17 16:20:25 +05:30
|
|
|
props: {
|
2023-06-20 00:43:36 +05:30
|
|
|
draggable: {
|
|
|
|
type: Boolean,
|
|
|
|
required: false,
|
|
|
|
default: false,
|
|
|
|
},
|
|
|
|
isStatic: {
|
|
|
|
type: Boolean,
|
|
|
|
required: false,
|
|
|
|
default: false,
|
|
|
|
},
|
2023-03-17 16:20:25 +05:30
|
|
|
item: {
|
|
|
|
type: Object,
|
|
|
|
required: true,
|
|
|
|
},
|
2023-05-27 22:25:52 +05:30
|
|
|
linkClasses: {
|
|
|
|
type: Object,
|
|
|
|
required: false,
|
|
|
|
default: () => ({}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
expanded: this.item.is_active,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
elem() {
|
|
|
|
return this.isSection ? 'button' : 'a';
|
|
|
|
},
|
|
|
|
collapseIcon() {
|
|
|
|
return this.expanded ? 'chevron-up' : 'chevron-down';
|
|
|
|
},
|
|
|
|
isSection() {
|
|
|
|
return Boolean(this.item?.items?.length);
|
|
|
|
},
|
|
|
|
itemId() {
|
|
|
|
return kebabCase(this.item.title);
|
|
|
|
},
|
|
|
|
pillData() {
|
|
|
|
return this.item.pill_count;
|
|
|
|
},
|
|
|
|
hasPill() {
|
|
|
|
return (
|
|
|
|
Number.isFinite(this.pillData) ||
|
|
|
|
(typeof this.pillData === 'string' && this.pillData !== '')
|
|
|
|
);
|
|
|
|
},
|
|
|
|
isActive() {
|
|
|
|
if (this.isSection) {
|
|
|
|
return !this.expanded && this.item.is_active;
|
|
|
|
}
|
|
|
|
return this.item.is_active;
|
|
|
|
},
|
2023-06-20 00:43:36 +05:30
|
|
|
isPinnable() {
|
|
|
|
return this.panelSupportsPins && !this.isSection && !this.isStatic;
|
|
|
|
},
|
|
|
|
isPinned() {
|
|
|
|
return this.pinnedItemIds.ids.includes(this.item.id);
|
|
|
|
},
|
|
|
|
trackingProps() {
|
|
|
|
// Set extra event data to debug missing IDs / Panel Types
|
|
|
|
const extraData =
|
|
|
|
!this.item.id || !this.panelType
|
|
|
|
? { 'data-track-extra': JSON.stringify({ title: this.item.title }) }
|
|
|
|
: {};
|
|
|
|
|
|
|
|
return {
|
|
|
|
'data-track-action': CLICK_MENU_ITEM_ACTION,
|
|
|
|
'data-track-label': this.item.id ?? TRACKING_UNKNOWN_ID,
|
|
|
|
'data-track-property': this.panelType
|
|
|
|
? `nav_panel_${this.panelType}`
|
|
|
|
: TRACKING_UNKNOWN_PANEL,
|
|
|
|
...extraData,
|
|
|
|
};
|
|
|
|
},
|
2023-05-27 22:25:52 +05:30
|
|
|
linkProps() {
|
|
|
|
if (this.isSection) {
|
|
|
|
return {
|
|
|
|
'aria-controls': this.itemId,
|
|
|
|
'aria-expanded': String(this.expanded),
|
2023-06-20 00:43:36 +05:30
|
|
|
'data-qa-menu-item': this.item.title,
|
2023-05-27 22:25:52 +05:30
|
|
|
};
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
...this.$attrs,
|
2023-06-20 00:43:36 +05:30
|
|
|
...this.trackingProps,
|
2023-05-27 22:25:52 +05:30
|
|
|
href: this.item.link,
|
|
|
|
'aria-current': this.isActive ? 'page' : null,
|
2023-06-20 00:43:36 +05:30
|
|
|
'data-qa-submenu-item': this.item.title,
|
2023-05-27 22:25:52 +05:30
|
|
|
};
|
|
|
|
},
|
|
|
|
computedLinkClasses() {
|
|
|
|
return {
|
|
|
|
// Reset user agent styles on <button>
|
|
|
|
'gl-appearance-none gl-border-0 gl-bg-transparent gl-text-left': this.isSection,
|
2023-06-20 00:43:36 +05:30
|
|
|
'gl-w-full gl-focus--focus': this.isSection,
|
|
|
|
'nav-item-link': !this.isSection,
|
2023-05-27 22:25:52 +05:30
|
|
|
'gl-bg-t-gray-a-08': this.isActive,
|
2023-06-20 00:43:36 +05:30
|
|
|
'gl-py-2': this.isPinnable,
|
|
|
|
'gl-py-3': !this.isPinnable,
|
2023-05-27 22:25:52 +05:30
|
|
|
...this.linkClasses,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
click(event) {
|
|
|
|
if (this.isSection) {
|
|
|
|
event.preventDefault();
|
|
|
|
this.expanded = !this.expanded;
|
|
|
|
}
|
|
|
|
},
|
2023-03-17 16:20:25 +05:30
|
|
|
},
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
|
|
|
<li>
|
2023-05-27 22:25:52 +05:30
|
|
|
<component
|
|
|
|
:is="elem"
|
|
|
|
v-bind="linkProps"
|
2023-06-20 00:43:36 +05:30
|
|
|
class="gl-rounded-base gl-relative gl-display-flex gl-align-items-center gl-mb-1 gl-px-0 gl-line-height-normal gl-text-black-normal! gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-text-decoration-none!"
|
2023-05-27 22:25:52 +05:30
|
|
|
:class="computedLinkClasses"
|
2023-06-20 00:43:36 +05:30
|
|
|
data-qa-selector="nav_item_link"
|
2023-05-27 22:25:52 +05:30
|
|
|
data-testid="nav-item-link"
|
|
|
|
@click="click"
|
2023-03-17 16:20:25 +05:30
|
|
|
>
|
2023-05-27 22:25:52 +05:30
|
|
|
<div
|
|
|
|
:class="[isActive ? 'gl-bg-blue-500' : 'gl-bg-transparent']"
|
|
|
|
class="gl-absolute gl-left-2 gl-top-2 gl-bottom-2 gl-transition-slow"
|
|
|
|
aria-hidden="true"
|
|
|
|
style="width: 3px; border-radius: 3px; margin-right: 1px"
|
|
|
|
></div>
|
|
|
|
<div class="gl-flex-shrink-0 gl-w-6 gl-mx-3">
|
2023-03-17 16:20:25 +05:30
|
|
|
<slot name="icon">
|
2023-06-20 00:43:36 +05:30
|
|
|
<gl-icon v-if="item.icon" :name="item.icon" class="gl-ml-2 item-icon" />
|
|
|
|
<gl-icon
|
|
|
|
v-else-if="draggable"
|
|
|
|
name="grip"
|
|
|
|
class="gl-text-gray-400 gl-ml-2 draggable-icon"
|
|
|
|
/>
|
2023-03-17 16:20:25 +05:30
|
|
|
</slot>
|
|
|
|
</div>
|
2023-06-20 00:43:36 +05:30
|
|
|
<div class="gl-pr-3 gl-text-gray-900 gl-truncate-end">
|
2023-03-17 16:20:25 +05:30
|
|
|
{{ item.title }}
|
2023-06-20 00:43:36 +05:30
|
|
|
<div v-if="item.subtitle" class="gl-font-sm gl-text-gray-500 gl-truncate-end">
|
2023-03-17 16:20:25 +05:30
|
|
|
{{ item.subtitle }}
|
|
|
|
</div>
|
|
|
|
</div>
|
2023-06-20 00:43:36 +05:30
|
|
|
<slot name="actions"></slot>
|
|
|
|
<span v-if="isSection || hasPill || isPinnable" class="gl-flex-grow-1 gl-text-right gl-mr-3">
|
2023-05-27 22:25:52 +05:30
|
|
|
<gl-badge v-if="hasPill" size="sm" variant="info">
|
|
|
|
{{ pillData }}
|
|
|
|
</gl-badge>
|
|
|
|
<gl-icon v-else-if="isSection" :name="collapseIcon" />
|
2023-06-20 00:43:36 +05:30
|
|
|
<gl-button
|
|
|
|
v-else-if="isPinnable && !isPinned"
|
|
|
|
size="small"
|
|
|
|
category="tertiary"
|
|
|
|
icon="thumbtack"
|
|
|
|
:aria-label="$options.i18n.pinItem"
|
|
|
|
@click.prevent="$emit('pin-add', item.id)"
|
|
|
|
/>
|
|
|
|
<gl-button
|
|
|
|
v-else-if="isPinnable && isPinned"
|
|
|
|
size="small"
|
|
|
|
category="tertiary"
|
|
|
|
:aria-label="$options.i18n.unpinItem"
|
|
|
|
icon="thumbtack-solid"
|
|
|
|
@click.prevent="$emit('pin-remove', item.id)"
|
|
|
|
/>
|
2023-05-27 22:25:52 +05:30
|
|
|
</span>
|
|
|
|
</component>
|
|
|
|
<gl-collapse
|
|
|
|
v-if="isSection"
|
|
|
|
:id="itemId"
|
|
|
|
v-model="expanded"
|
2023-06-20 00:43:36 +05:30
|
|
|
data-qa-selector="menu_section"
|
|
|
|
:data-qa-section="item.title"
|
2023-05-27 22:25:52 +05:30
|
|
|
:aria-label="item.title"
|
|
|
|
class="gl-list-style-none gl-p-0"
|
|
|
|
tag="ul"
|
|
|
|
>
|
|
|
|
<nav-item
|
|
|
|
v-for="subItem of item.items"
|
|
|
|
:key="`${item.title}-${subItem.title}`"
|
|
|
|
:item="subItem"
|
2023-06-20 00:43:36 +05:30
|
|
|
@pin-add="(itemId) => $emit('pin-add', itemId)"
|
|
|
|
@pin-remove="(itemId) => $emit('pin-remove', itemId)"
|
2023-05-27 22:25:52 +05:30
|
|
|
/>
|
|
|
|
</gl-collapse>
|
2023-03-17 16:20:25 +05:30
|
|
|
</li>
|
|
|
|
</template>
|