Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-09-20 14:18:08 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-09-20 14:18:08 +0300
commit5afcbe03ead9ada87621888a31a62652b10a7e4f (patch)
tree9918b67a0d0f0bafa6542e839a8be37adf73102d /app/assets/javascripts/super_sidebar/components/nav_item.vue
parentc97c0201564848c1f53226fe19d71fdcc472f7d0 (diff)
Add latest changes from gitlab-org/gitlab@16-4-stable-eev16.4.0-rc42
Diffstat (limited to 'app/assets/javascripts/super_sidebar/components/nav_item.vue')
-rw-r--r--app/assets/javascripts/super_sidebar/components/nav_item.vue148
1 files changed, 119 insertions, 29 deletions
diff --git a/app/assets/javascripts/super_sidebar/components/nav_item.vue b/app/assets/javascripts/super_sidebar/components/nav_item.vue
index 36803a885e7..5e0f8fffb0e 100644
--- a/app/assets/javascripts/super_sidebar/components/nav_item.vue
+++ b/app/assets/javascripts/super_sidebar/components/nav_item.vue
@@ -1,6 +1,6 @@
<script>
-import { GlButton, GlIcon, GlBadge, GlTooltipDirective } from '@gitlab/ui';
-import { s__ } from '~/locale';
+import { GlAvatar, GlButton, GlIcon, GlBadge, GlTooltipDirective } from '@gitlab/ui';
+import { s__, sprintf } from '~/locale';
import {
CLICK_MENU_ITEM_ACTION,
CLICK_PINNED_MENU_ITEM_ACTION,
@@ -12,11 +12,14 @@ import NavItemRouterLink from './nav_item_router_link.vue';
export default {
i18n: {
+ pin: s__('Navigation|Pin %{title}'),
pinItem: s__('Navigation|Pin item'),
+ unpin: s__('Navigation|Unpin %{title}'),
unpinItem: s__('Navigation|Unpin item'),
},
name: 'NavItem',
components: {
+ GlAvatar,
GlButton,
GlIcon,
GlBadge,
@@ -62,6 +65,12 @@ export default {
default: false,
},
},
+ data() {
+ return {
+ isMouseIn: false,
+ canClickPinButton: false,
+ };
+ },
computed: {
pillData() {
return this.item.pill_count;
@@ -96,12 +105,27 @@ export default {
...extraData,
};
},
+ /**
+ * Some QA specs rely on a stable "Project overview"/"Group overview" nav
+ * item data-qa-submenu-item attribute value.
+ *
+ * This computed ensures that those particular nav items use the `id` of
+ * the item rather than its title for that QA attribute.
+ *
+ * In future, probably all nav items should do this, for consistency.
+ * See https://gitlab.com/gitlab-org/gitlab/-/issues/422925.
+ */
+ qaSubMenuItem() {
+ const { id } = this.item;
+ if (id === 'project_overview' || id === 'group_overview') return id.replace(/_/g, '-');
+ return this.item.title;
+ },
linkProps() {
return {
...this.$attrs,
...this.trackingProps,
item: this.item,
- 'data-qa-submenu-item': this.item.title,
+ 'data-qa-submenu-item': this.qaSubMenuItem,
'data-method': this.item.data_method ?? null,
};
},
@@ -118,26 +142,73 @@ export default {
navItemLinkComponent() {
return this.item.to ? NavItemRouterLink : NavItemLink;
},
+ hasAvatar() {
+ return Boolean(this.item.entity_id);
+ },
+ avatarShape() {
+ return this.item.avatar_shape || 'rect';
+ },
+ pinAriaLabel() {
+ return sprintf(this.$options.i18n.pin, {
+ title: this.item.title,
+ });
+ },
+ unpinAriaLabel() {
+ return sprintf(this.$options.i18n.unpin, {
+ title: this.item.title,
+ });
+ },
+ activeIndicatorStyle() {
+ const style = {
+ width: '3px',
+ borderRadius: '3px',
+ marginRight: '1px',
+ };
+
+ // The active indicator is too close to the avatar for items with one, so shift
+ // it left by 1px.
+ //
+ // The indicator is absolutely positioned using rem units. This tweak for this
+ // edge case is in pixel units, so that it does not scale with root font size.
+ if (this.hasAvatar) style.transform = 'translateX(-1px)';
+
+ return style;
+ },
+ },
+ mounted() {
+ if (this.item.is_active) {
+ this.$el.scrollIntoView(false);
+ }
+ },
+ methods: {
+ togglePointerEvents() {
+ this.canClickPinButton = this.isMouseIn;
+ },
},
};
</script>
<template>
- <li>
+ <li
+ class="gl-relative show-on-focus-or-hover--context hide-on-focus-or-hover--context transition-opacity-on-hover--context"
+ data-testid="nav-item"
+ @mouseenter="isMouseIn = true"
+ @mouseleave="isMouseIn = false"
+ >
<component
:is="navItemLinkComponent"
#default="{ isActive }"
v-bind="linkProps"
- class="nav-item-link gl-relative gl-display-flex gl-align-items-center gl-min-h-7 gl-gap-3 gl-mb-1 gl-py-2 gl-text-black-normal! gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-text-decoration-none! gl-focus--focus show-on-focus-or-hover--context"
+ class="gl-relative gl-display-flex gl-align-items-center gl-min-h-7 gl-gap-3 gl-mb-1 gl-py-2 gl-text-black-normal! gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-text-decoration-none! gl-focus--focus show-on-focus-or-hover--control hide-on-focus-or-hover--control"
:class="computedLinkClasses"
- data-qa-selector="nav_item_link"
data-testid="nav-item-link"
+ data-qa-selector="nav_item_link"
>
<div
:class="[isActive ? 'gl-opacity-10' : 'gl-opacity-0']"
class="active-indicator gl-bg-blue-500 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"
+ :style="activeIndicatorStyle"
data-testid="active-indicator"
></div>
<div v-if="!isFlyout" class="gl-flex-shrink-0 gl-w-6 gl-display-flex">
@@ -148,6 +219,14 @@ export default {
name="grip"
class="gl-m-auto gl-text-gray-400 js-draggable-icon gl-cursor-grab show-on-focus-or-hover--target"
/>
+ <gl-avatar
+ v-else-if="hasAvatar"
+ :size="24"
+ :shape="avatarShape"
+ :entity-name="item.title"
+ :entity-id="item.entity_id"
+ :src="item.avatar"
+ />
</slot>
</div>
<div class="gl-flex-grow-1 gl-text-gray-900 gl-truncate-end">
@@ -157,36 +236,47 @@ export default {
</div>
</div>
<slot name="actions"></slot>
- <span v-if="hasPill || isPinnable" class="gl-text-right gl-relative">
+ <span v-if="hasPill || isPinnable" class="gl-text-right gl-relative gl-min-w-8">
<gl-badge
v-if="hasPill"
size="sm"
variant="neutral"
- :class="{ 'nav-item-badge gl-absolute gl-right-0 gl-top-2': isPinnable }"
+ class="gl-bg-t-gray-a-08!"
+ :class="{
+ 'hide-on-focus-or-hover--target transition-opacity-on-hover--target': isPinnable,
+ }"
>
{{ pillData }}
</gl-badge>
- <gl-button
- v-if="isPinnable && !isPinned"
- v-gl-tooltip.noninteractive.ds500.right.viewport="$options.i18n.pinItem"
- size="small"
- category="tertiary"
- icon="thumbtack"
- class="show-on-focus-or-hover--target"
- :aria-label="$options.i18n.pinItem"
- @click.prevent="$emit('pin-add', item.id)"
- />
- <gl-button
- v-else-if="isPinnable && isPinned"
- v-gl-tooltip.noninteractive.ds500.right.viewport="$options.i18n.unpinItem"
- size="small"
- category="tertiary"
- :aria-label="$options.i18n.unpinItem"
- icon="thumbtack-solid"
- class="show-on-focus-or-hover--target"
- @click.prevent="$emit('pin-remove', item.id)"
- />
</span>
</component>
+ <template v-if="isPinnable">
+ <gl-button
+ v-if="isPinned"
+ v-gl-tooltip.noninteractive.right.viewport="$options.i18n.unpinItem"
+ :aria-label="unpinAriaLabel"
+ category="tertiary"
+ class="show-on-focus-or-hover--target transition-opacity-on-hover--target always-animate gl-absolute gl-right-3 gl-top-2"
+ :class="{ 'gl-pointer-events-none': !canClickPinButton }"
+ data-testid="nav-item-unpin"
+ icon="thumbtack-solid"
+ size="small"
+ @click="$emit('pin-remove', item.id)"
+ @transitionend="togglePointerEvents"
+ />
+ <gl-button
+ v-else
+ v-gl-tooltip.noninteractive.right.viewport="$options.i18n.pinItem"
+ :aria-label="pinAriaLabel"
+ category="tertiary"
+ class="show-on-focus-or-hover--target transition-opacity-on-hover--target always-animate gl-absolute gl-right-3 gl-top-2"
+ :class="{ 'gl-pointer-events-none': !canClickPinButton }"
+ data-testid="nav-item-pin"
+ icon="thumbtack"
+ size="small"
+ @click="$emit('pin-add', item.id)"
+ @transitionend="togglePointerEvents"
+ />
+ </template>
</li>
</template>