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-09 18:10:05 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-06-09 18:10:05 +0300
commitc0f42c6d662b776777afbf79ba72d8e833b8de48 (patch)
treed94d38bccd5297f59522090fd3c814d9264a1cc9 /app/assets/javascripts/nav
parentf4d6d3ec77286fa64810bd6a25c58671e0deedaf (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/nav')
-rw-r--r--app/assets/javascripts/nav/components/responsive_app.vue79
-rw-r--r--app/assets/javascripts/nav/components/responsive_header.vue37
-rw-r--r--app/assets/javascripts/nav/components/responsive_home.vue62
-rw-r--r--app/assets/javascripts/nav/components/top_nav_container_view.vue11
-rw-r--r--app/assets/javascripts/nav/components/top_nav_menu_item.vue14
-rw-r--r--app/assets/javascripts/nav/components/top_nav_menu_sections.vue1
-rw-r--r--app/assets/javascripts/nav/components/top_nav_new_dropdown.vue55
-rw-r--r--app/assets/javascripts/nav/utils/reset_menu_items_active.js14
8 files changed, 265 insertions, 8 deletions
diff --git a/app/assets/javascripts/nav/components/responsive_app.vue b/app/assets/javascripts/nav/components/responsive_app.vue
index 01b0fa370b5..cef1a14df19 100644
--- a/app/assets/javascripts/nav/components/responsive_app.vue
+++ b/app/assets/javascripts/nav/components/responsive_app.vue
@@ -1,30 +1,101 @@
<script>
+import { FREQUENT_ITEMS_PROJECTS, FREQUENT_ITEMS_GROUPS } from '~/frequent_items/constants';
+import { BV_DROPDOWN_SHOW, BV_DROPDOWN_HIDE } from '~/lib/utils/constants';
+import KeepAliveSlots from '~/vue_shared/components/keep_alive_slots.vue';
import eventHub, { EVENT_RESPONSIVE_TOGGLE } from '../event_hub';
-
-const TEMPORARY_PLACEHOLDER = 'Placeholder for responsive top nav';
+import { resetMenuItemsActive } from '../utils/reset_menu_items_active';
+import ResponsiveHeader from './responsive_header.vue';
+import ResponsiveHome from './responsive_home.vue';
+import TopNavContainerView from './top_nav_container_view.vue';
export default {
+ components: {
+ KeepAliveSlots,
+ ResponsiveHeader,
+ ResponsiveHome,
+ TopNavContainerView,
+ },
props: {
navData: {
type: Object,
required: true,
},
},
+ data() {
+ return {
+ activeView: 'home',
+ hasMobileOverlay: false,
+ };
+ },
+ computed: {
+ nav() {
+ return resetMenuItemsActive(this.navData);
+ },
+ },
created() {
eventHub.$on(EVENT_RESPONSIVE_TOGGLE, this.onToggle);
+ this.$root.$on(BV_DROPDOWN_SHOW, this.showMobileOverlay);
+ this.$root.$on(BV_DROPDOWN_HIDE, this.hideMobileOverlay);
},
beforeDestroy() {
eventHub.$off(EVENT_RESPONSIVE_TOGGLE, this.onToggle);
+ this.$root.$off(BV_DROPDOWN_SHOW, this.showMobileOverlay);
+ this.$root.$off(BV_DROPDOWN_HIDE, this.hideMobileOverlay);
},
methods: {
onToggle() {
document.body.classList.toggle('top-nav-responsive-open');
},
+ onMenuItemClick({ view }) {
+ if (view) {
+ this.activeView = view;
+ }
+ },
+ showMobileOverlay() {
+ this.hasMobileOverlay = true;
+ },
+ hideMobileOverlay() {
+ this.hasMobileOverlay = false;
+ },
},
- TEMPORARY_PLACEHOLDER,
+ FREQUENT_ITEMS_PROJECTS,
+ FREQUENT_ITEMS_GROUPS,
};
</script>
<template>
- <p>{{ $options.TEMPORARY_PLACEHOLDER }}</p>
+ <div>
+ <div
+ class="mobile-overlay"
+ :class="{ 'mobile-nav-open': hasMobileOverlay }"
+ data-testid="mobile-overlay"
+ ></div>
+ <keep-alive-slots :slot-key="activeView">
+ <template #home>
+ <responsive-home :nav-data="nav" @menu-item-click="onMenuItemClick" />
+ </template>
+ <template #projects>
+ <responsive-header @menu-item-click="onMenuItemClick">
+ {{ __('Projects') }}
+ </responsive-header>
+ <top-nav-container-view
+ :frequent-items-dropdown-type="$options.FREQUENT_ITEMS_PROJECTS.namespace"
+ :frequent-items-vuex-module="$options.FREQUENT_ITEMS_PROJECTS.vuexModule"
+ container-class="gl-px-3"
+ v-bind="nav.views.projects"
+ />
+ </template>
+ <template #groups>
+ <responsive-header @menu-item-click="onMenuItemClick">
+ {{ __('Groups') }}
+ </responsive-header>
+ <top-nav-container-view
+ :frequent-items-dropdown-type="$options.FREQUENT_ITEMS_GROUPS.namespace"
+ :frequent-items-vuex-module="$options.FREQUENT_ITEMS_GROUPS.vuexModule"
+ container-class="gl-px-3"
+ v-bind="nav.views.groups"
+ />
+ </template>
+ </keep-alive-slots>
+ </div>
</template>
diff --git a/app/assets/javascripts/nav/components/responsive_header.vue b/app/assets/javascripts/nav/components/responsive_header.vue
new file mode 100644
index 00000000000..8a1d21993b7
--- /dev/null
+++ b/app/assets/javascripts/nav/components/responsive_header.vue
@@ -0,0 +1,37 @@
+<script>
+import { GlTooltipDirective } from '@gitlab/ui';
+import TopNavMenuItem from './top_nav_menu_item.vue';
+
+export default {
+ components: {
+ TopNavMenuItem,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ computed: {
+ menuItem() {
+ return {
+ id: 'home',
+ view: 'home',
+ icon: 'angle-left',
+ };
+ },
+ },
+};
+</script>
+
+<template>
+ <header class="gl-py-4 gl-display-flex gl-align-items-center">
+ <top-nav-menu-item
+ v-gl-tooltip="{ title: s__('TopNav|Go back') }"
+ class="gl-p-3!"
+ :menu-item="menuItem"
+ icon-only
+ @click="$emit('menu-item-click', menuItem)"
+ />
+ <span class="gl-font-size-h2 gl-font-weight-bold gl-ml-2">
+ <slot></slot>
+ </span>
+ </header>
+</template>
diff --git a/app/assets/javascripts/nav/components/responsive_home.vue b/app/assets/javascripts/nav/components/responsive_home.vue
new file mode 100644
index 00000000000..c8f2f0bfb10
--- /dev/null
+++ b/app/assets/javascripts/nav/components/responsive_home.vue
@@ -0,0 +1,62 @@
+<script>
+import { GlTooltipDirective } from '@gitlab/ui';
+import TopNavMenuItem from './top_nav_menu_item.vue';
+import TopNavMenuSections from './top_nav_menu_sections.vue';
+import TopNavNewDropdown from './top_nav_new_dropdown.vue';
+
+const NEW_VIEW = 'new';
+const SEARCH_VIEW = 'search';
+
+export default {
+ components: {
+ TopNavMenuItem,
+ TopNavMenuSections,
+ TopNavNewDropdown,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ navData: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ menuSections() {
+ return [
+ { id: 'primary', menuItems: this.navData.primary },
+ { id: 'secondary', menuItems: this.navData.secondary },
+ ].filter((x) => x.menuItems?.length);
+ },
+ newDropdownViewModel() {
+ return this.navData.views[NEW_VIEW];
+ },
+ searchMenuItem() {
+ return this.navData.views[SEARCH_VIEW];
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <header class="gl-display-flex gl-align-items-center gl-py-4 gl-pl-4">
+ <h1 class="gl-m-0 gl-font-size-h2 gl-reset-color gl-mr-auto">{{ __('Menu') }}</h1>
+ <top-nav-menu-item
+ v-if="searchMenuItem"
+ v-gl-tooltip="{ title: searchMenuItem.title }"
+ class="gl-ml-3"
+ :menu-item="searchMenuItem"
+ icon-only
+ />
+ <top-nav-new-dropdown
+ v-if="newDropdownViewModel"
+ v-gl-tooltip="{ title: newDropdownViewModel.title }"
+ :view-model="newDropdownViewModel"
+ class="gl-ml-3"
+ />
+ </header>
+ <top-nav-menu-sections class="gl-h-full" :sections="menuSections" v-on="$listeners" />
+ </div>
+</template>
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 4043ef1ae5b..6f98f85ff90 100644
--- a/app/assets/javascripts/nav/components/top_nav_container_view.vue
+++ b/app/assets/javascripts/nav/components/top_nav_container_view.vue
@@ -20,6 +20,11 @@ export default {
type: String,
required: true,
},
+ containerClass: {
+ type: String,
+ required: false,
+ default: '',
+ },
linksPrimary: {
type: Array,
required: false,
@@ -50,7 +55,11 @@ export default {
<template>
<div class="top-nav-container-view gl-display-flex gl-flex-direction-column">
- <div class="frequent-items-dropdown-container gl-w-auto">
+ <div
+ class="frequent-items-dropdown-container gl-w-auto"
+ :class="containerClass"
+ data-testid="frequent-items-container"
+ >
<div class="frequent-items-dropdown-content gl-w-full! gl-pt-0!">
<vuex-module-provider :vuex-module="frequentItemsVuexModule">
<frequent-items-app v-bind="$attrs" />
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 3675ee50fdf..08b2fbf2ed1 100644
--- a/app/assets/javascripts/nav/components/top_nav_menu_item.vue
+++ b/app/assets/javascripts/nav/components/top_nav_menu_item.vue
@@ -16,6 +16,11 @@ export default {
type: Object,
required: true,
},
+ iconOnly: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
computed: {
dataAttrs() {
@@ -32,13 +37,16 @@ export default {
:href="menuItem.href"
class="top-nav-menu-item gl-display-block"
:class="[menuItem.css_class, { [$options.ACTIVE_CLASS]: menuItem.active }]"
+ :aria-label="menuItem.title"
v-bind="dataAttrs"
v-on="$listeners"
>
<span class="gl-display-flex">
- <gl-icon v-if="menuItem.icon" :name="menuItem.icon" class="gl-mr-2!" />
- {{ menuItem.title }}
- <gl-icon v-if="menuItem.view" name="chevron-right" class="gl-ml-auto" />
+ <gl-icon v-if="menuItem.icon" :name="menuItem.icon" :class="{ 'gl-mr-2!': !iconOnly }" />
+ <template v-if="!iconOnly">
+ {{ menuItem.title }}
+ <gl-icon v-if="menuItem.view" name="chevron-right" class="gl-ml-auto" />
+ </template>
</span>
</gl-button>
</template>
diff --git a/app/assets/javascripts/nav/components/top_nav_menu_sections.vue b/app/assets/javascripts/nav/components/top_nav_menu_sections.vue
index 407c79b1f42..442af512350 100644
--- a/app/assets/javascripts/nav/components/top_nav_menu_sections.vue
+++ b/app/assets/javascripts/nav/components/top_nav_menu_sections.vue
@@ -54,6 +54,7 @@ export default {
:key="menuItem.id"
:menu-item="menuItem"
data-testid="menu-item"
+ class="gl-w-full"
:class="{ 'gl-mt-1': menuItemIndex > 0 }"
@click="onClick(menuItem)"
/>
diff --git a/app/assets/javascripts/nav/components/top_nav_new_dropdown.vue b/app/assets/javascripts/nav/components/top_nav_new_dropdown.vue
new file mode 100644
index 00000000000..154bed81854
--- /dev/null
+++ b/app/assets/javascripts/nav/components/top_nav_new_dropdown.vue
@@ -0,0 +1,55 @@
+<script>
+import { GlDropdown, GlDropdownDivider, GlDropdownItem, GlDropdownSectionHeader } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownItem,
+ GlDropdownSectionHeader,
+ },
+ props: {
+ viewModel: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ sections() {
+ return this.viewModel.menu_sections || [];
+ },
+ showHeaders() {
+ return this.sections.length > 1;
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-dropdown
+ toggle-class="top-nav-menu-item"
+ icon="plus"
+ :text="viewModel.title"
+ category="tertiary"
+ text-sr-only
+ no-caret
+ right
+ >
+ <template v-for="({ title, menu_items }, index) in sections">
+ <gl-dropdown-divider v-if="index > 0" :key="`${index}_divider`" data-testid="divider" />
+ <gl-dropdown-section-header v-if="showHeaders" :key="`${index}_header`" data-testid="header">
+ {{ title }}
+ </gl-dropdown-section-header>
+ <template v-for="menuItem in menu_items">
+ <gl-dropdown-item
+ :key="`${index}_item_${menuItem.id}`"
+ link-class="top-nav-menu-item"
+ :href="menuItem.href"
+ data-testid="item"
+ >
+ {{ menuItem.title }}
+ </gl-dropdown-item>
+ </template>
+ </template>
+ </gl-dropdown>
+</template>
diff --git a/app/assets/javascripts/nav/utils/reset_menu_items_active.js b/app/assets/javascripts/nav/utils/reset_menu_items_active.js
new file mode 100644
index 00000000000..9b5d8e97c9c
--- /dev/null
+++ b/app/assets/javascripts/nav/utils/reset_menu_items_active.js
@@ -0,0 +1,14 @@
+const resetActiveInArray = (arr) => arr?.map((menuItem) => ({ ...menuItem, active: false }));
+
+/**
+ * This method sets `active: false` for the menu items within the given nav data.
+ *
+ * @returns navData with the menu items updated with `active: false`
+ */
+export const resetMenuItemsActive = ({ primary, secondary, ...navData }) => {
+ return {
+ ...navData,
+ primary: resetActiveInArray(primary),
+ secondary: resetActiveInArray(secondary),
+ };
+};