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>2021-06-04 00:10:02 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-06-04 00:10:02 +0300
commit8e2f50b44d51768c38d300a2ba2f9208107933b2 (patch)
treeea5c7ae41c2b9d4130dd37fcdd072dee7e39915f /app/assets/javascripts/nav
parent524639c7063131c40b848789ff541758b68c1cca (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/nav')
-rw-r--r--app/assets/javascripts/nav/components/top_nav_container_view.vue27
-rw-r--r--app/assets/javascripts/nav/components/top_nav_dropdown_menu.vue83
-rw-r--r--app/assets/javascripts/nav/components/top_nav_menu_item.vue5
-rw-r--r--app/assets/javascripts/nav/components/top_nav_menu_sections.vue62
4 files changed, 94 insertions, 83 deletions
diff --git a/app/assets/javascripts/nav/components/top_nav_container_view.vue b/app/assets/javascripts/nav/components/top_nav_container_view.vue
index c52c5d1a6e5..4043ef1ae5b 100644
--- a/app/assets/javascripts/nav/components/top_nav_container_view.vue
+++ b/app/assets/javascripts/nav/components/top_nav_container_view.vue
@@ -2,12 +2,12 @@
import FrequentItemsApp from '~/frequent_items/components/app.vue';
import eventHub from '~/frequent_items/event_hub';
import VuexModuleProvider from '~/vue_shared/components/vuex_module_provider.vue';
-import TopNavMenuItem from './top_nav_menu_item.vue';
+import TopNavMenuSections from './top_nav_menu_sections.vue';
export default {
components: {
FrequentItemsApp,
- TopNavMenuItem,
+ TopNavMenuSections,
VuexModuleProvider,
},
inheritAttrs: false,
@@ -32,11 +32,11 @@ export default {
},
},
computed: {
- linkGroups() {
+ menuSections() {
return [
- { key: 'primary', links: this.linksPrimary },
- { key: 'secondary', links: this.linksSecondary },
- ].filter((x) => x.links?.length);
+ { id: 'primary', menuItems: this.linksPrimary },
+ { id: 'secondary', menuItems: this.linksSecondary },
+ ].filter((x) => x.menuItems?.length);
},
},
mounted() {
@@ -57,19 +57,6 @@ export default {
</vuex-module-provider>
</div>
</div>
- <div
- v-for="({ key, links }, groupIndex) in linkGroups"
- :key="key"
- :class="{ 'gl-mt-3': groupIndex !== 0 }"
- class="gl-mt-auto gl-pt-3 gl-border-1 gl-border-t-solid gl-border-gray-100"
- data-testid="menu-item-group"
- >
- <top-nav-menu-item
- v-for="(link, linkIndex) in links"
- :key="link.title"
- :menu-item="link"
- :class="{ 'gl-mt-1': linkIndex !== 0 }"
- />
- </div>
+ <top-nav-menu-sections class="gl-mt-auto" :sections="menuSections" with-top-border />
</div>
</template>
diff --git a/app/assets/javascripts/nav/components/top_nav_dropdown_menu.vue b/app/assets/javascripts/nav/components/top_nav_dropdown_menu.vue
index 1cbd64b501d..22e1ed00045 100644
--- a/app/assets/javascripts/nav/components/top_nav_dropdown_menu.vue
+++ b/app/assets/javascripts/nav/components/top_nav_dropdown_menu.vue
@@ -1,17 +1,15 @@
<script>
+import { cloneDeep } from 'lodash';
import { FREQUENT_ITEMS_PROJECTS, FREQUENT_ITEMS_GROUPS } from '~/frequent_items/constants';
import KeepAliveSlots from '~/vue_shared/components/keep_alive_slots.vue';
import TopNavContainerView from './top_nav_container_view.vue';
-import TopNavMenuItem from './top_nav_menu_item.vue';
-
-const ACTIVE_CLASS = 'gl-shadow-none! gl-font-weight-bold! active';
-const SECONDARY_GROUP_CLASS = 'gl-pt-3 gl-mt-3 gl-border-1 gl-border-t-solid gl-border-gray-100';
+import TopNavMenuSections from './top_nav_menu_sections.vue';
export default {
components: {
KeepAliveSlots,
TopNavContainerView,
- TopNavMenuItem,
+ TopNavMenuSections,
},
props: {
primary: {
@@ -31,29 +29,25 @@ export default {
},
},
data() {
+ // It's expected that primary & secondary never change, so these are treated as "init" props.
+ // We need to clone so that we can mutate the data without mutating the props
+ const menuSections = [
+ { id: 'primary', menuItems: cloneDeep(this.primary) },
+ { id: 'secondary', menuItems: cloneDeep(this.secondary) },
+ ].filter((x) => x.menuItems?.length);
+
return {
- activeId: '',
+ menuSections,
};
},
computed: {
- menuItemGroups() {
- return [
- { key: 'primary', items: this.primary, classes: '' },
- {
- key: 'secondary',
- items: this.secondary,
- classes: SECONDARY_GROUP_CLASS,
- },
- ].filter((x) => x.items?.length);
- },
allMenuItems() {
- return this.menuItemGroups.flatMap((x) => x.items);
- },
- activeMenuItem() {
- return this.allMenuItems.find((x) => x.id === this.activeId);
+ return this.menuSections.flatMap((x) => x.menuItems);
},
activeView() {
- return this.activeMenuItem?.view;
+ const active = this.allMenuItems.find((x) => x.active);
+
+ return active?.view;
},
menuClass() {
if (!this.activeView) {
@@ -63,61 +57,26 @@ export default {
return '';
},
},
- created() {
- // Initialize activeId based on initialization prop
- this.activeId = this.allMenuItems.find((x) => x.active)?.id;
- },
methods: {
- onClick({ id, href }) {
- // If we're a link, let's just do the default behavior so the view won't change
- if (href) {
- return;
- }
-
- this.activeId = id;
- },
- menuItemClasses(menuItem) {
- if (menuItem.id === this.activeId) {
- return ACTIVE_CLASS;
- }
-
- return '';
+ onMenuItemClick({ id }) {
+ this.allMenuItems.forEach((menuItem) => {
+ this.$set(menuItem, 'active', id === menuItem.id);
+ });
},
},
FREQUENT_ITEMS_PROJECTS,
FREQUENT_ITEMS_GROUPS,
- // expose for unit tests
- ACTIVE_CLASS,
- SECONDARY_GROUP_CLASS,
};
</script>
<template>
<div class="gl-display-flex gl-align-items-stretch">
<div
- class="gl-w-grid-size-30 gl-flex-shrink-0 gl-bg-gray-10"
+ class="gl-w-grid-size-30 gl-flex-shrink-0 gl-bg-gray-10 gl-py-3 gl-px-5"
:class="menuClass"
data-testid="menu-sidebar"
>
- <div
- class="gl-py-3 gl-px-5 gl-h-full gl-display-flex gl-align-items-stretch gl-flex-direction-column"
- >
- <div
- v-for="group in menuItemGroups"
- :key="group.key"
- :class="group.classes"
- data-testid="menu-item-group"
- >
- <top-nav-menu-item
- v-for="(menu, index) in group.items"
- :key="menu.id"
- data-testid="menu-item"
- :class="[{ 'gl-mt-1': index !== 0 }, menuItemClasses(menu)]"
- :menu-item="menu"
- @click="onClick(menu)"
- />
- </div>
- </div>
+ <top-nav-menu-sections :sections="menuSections" @menu-item-click="onMenuItemClick" />
</div>
<keep-alive-slots
v-show="activeView"
diff --git a/app/assets/javascripts/nav/components/top_nav_menu_item.vue b/app/assets/javascripts/nav/components/top_nav_menu_item.vue
index 067180abd08..3675ee50fdf 100644
--- a/app/assets/javascripts/nav/components/top_nav_menu_item.vue
+++ b/app/assets/javascripts/nav/components/top_nav_menu_item.vue
@@ -4,6 +4,8 @@ import { kebabCase, mapKeys } from 'lodash';
const getDataKey = (key) => `data-${kebabCase(key)}`;
+const ACTIVE_CLASS = 'gl-shadow-none! gl-font-weight-bold! active';
+
export default {
components: {
GlButton,
@@ -20,6 +22,7 @@ export default {
return mapKeys(this.menuItem.data || {}, (value, key) => getDataKey(key));
},
},
+ ACTIVE_CLASS,
};
</script>
@@ -28,7 +31,7 @@ export default {
category="tertiary"
:href="menuItem.href"
class="top-nav-menu-item gl-display-block"
- :class="menuItem.css_class"
+ :class="[menuItem.css_class, { [$options.ACTIVE_CLASS]: menuItem.active }]"
v-bind="dataAttrs"
v-on="$listeners"
>
diff --git a/app/assets/javascripts/nav/components/top_nav_menu_sections.vue b/app/assets/javascripts/nav/components/top_nav_menu_sections.vue
new file mode 100644
index 00000000000..407c79b1f42
--- /dev/null
+++ b/app/assets/javascripts/nav/components/top_nav_menu_sections.vue
@@ -0,0 +1,62 @@
+<script>
+import TopNavMenuItem from './top_nav_menu_item.vue';
+
+const BORDER_CLASSES = 'gl-pt-3 gl-border-1 gl-border-t-solid gl-border-gray-100';
+
+export default {
+ components: {
+ TopNavMenuItem,
+ },
+ props: {
+ sections: {
+ type: Array,
+ required: true,
+ },
+ withTopBorder: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ methods: {
+ onClick(menuItem) {
+ // If we're a link, let's just do the default behavior so the view won't change
+ if (menuItem.href) {
+ return;
+ }
+
+ this.$emit('menu-item-click', menuItem);
+ },
+ getMenuSectionClasses(index) {
+ // This is a method instead of a computed so we don't have to incur the cost of
+ // creating a whole new array/object.
+ return {
+ [BORDER_CLASSES]: this.withTopBorder || index > 0,
+ 'gl-mt-3': index > 0,
+ };
+ },
+ },
+ // Expose for unit tests
+ BORDER_CLASSES,
+};
+</script>
+
+<template>
+ <div class="gl-display-flex gl-align-items-stretch gl-flex-direction-column">
+ <div
+ v-for="({ id, menuItems }, sectionIndex) in sections"
+ :key="id"
+ :class="getMenuSectionClasses(sectionIndex)"
+ data-testid="menu-section"
+ >
+ <top-nav-menu-item
+ v-for="(menuItem, menuItemIndex) in menuItems"
+ :key="menuItem.id"
+ :menu-item="menuItem"
+ data-testid="menu-item"
+ :class="{ 'gl-mt-1': menuItemIndex > 0 }"
+ @click="onClick(menuItem)"
+ />
+ </div>
+ </div>
+</template>