diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-06-04 00:10:02 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-06-04 00:10:02 +0300 |
commit | 8e2f50b44d51768c38d300a2ba2f9208107933b2 (patch) | |
tree | ea5c7ae41c2b9d4130dd37fcdd072dee7e39915f /app/assets/javascripts/nav | |
parent | 524639c7063131c40b848789ff541758b68c1cca (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/nav')
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> |