xref: /openbmc/webui-vue/src/components/AppNavigation/AppNavigation.vue (revision 24b377db47a05f742b1f3db77606f6bae845f637)
1a2988f40SDerick Montague<template>
274f8687dSYoshie Muranaka  <div>
374f8687dSYoshie Muranaka    <div class="nav-container" :class="{ open: isNavigationOpen }">
46859203cSDerick Montague      <nav ref="nav" :aria-label="$t('appNavigation.primaryNavigation')">
57d044352SYoshie Muranaka        <b-nav vertical class="mb-4">
67d6b44cbSEd Tanous          <template v-for="navItem in navigationItems">
7d329ec84SYoshie Muranaka            <!-- Navigation items with no children -->
8d329ec84SYoshie Muranaka            <b-nav-item
9d329ec84SYoshie Muranaka              v-if="!navItem.children"
107d6b44cbSEd Tanous              :key="navItem.index"
11d329ec84SYoshie Muranaka              :to="navItem.route"
12d329ec84SYoshie Muranaka              :data-test-id="`nav-item-${navItem.id}`"
13d329ec84SYoshie Muranaka            >
14d329ec84SYoshie Muranaka              <component :is="navItem.icon" />
15d329ec84SYoshie Muranaka              {{ navItem.label }}
16e0b76c33SYoshie Muranaka            </b-nav-item>
1742c19893SDerick Montague
18d329ec84SYoshie Muranaka            <!-- Navigation items with children -->
197d6b44cbSEd Tanous            <li v-else :key="navItem.index" class="nav-item">
202d589a74SDerick Montague              <b-button
21d329ec84SYoshie Muranaka                v-b-toggle="`${navItem.id}`"
222d589a74SDerick Montague                variant="link"
23d329ec84SYoshie Muranaka                :data-test-id="`nav-button-${navItem.id}`"
242d589a74SDerick Montague              >
25d329ec84SYoshie Muranaka                <component :is="navItem.icon" />
26d329ec84SYoshie Muranaka                {{ navItem.label }}
2771ac230aSYoshie Muranaka                <icon-expand class="icon-expand" />
2842c19893SDerick Montague              </b-button>
29d329ec84SYoshie Muranaka              <b-collapse :id="navItem.id" tag="ul" class="nav-item__nav">
30fba4d625SSukanya Pandey                <li class="nav-item">
31fba4d625SSukanya Pandey                  <router-link
32aeb19816SDamian Celico                    v-for="(subNavItem, i) of filteredNavItem(navItem.children)"
33d329ec84SYoshie Muranaka                    :key="i"
34d329ec84SYoshie Muranaka                    :to="subNavItem.route"
35d329ec84SYoshie Muranaka                    :data-test-id="`nav-item-${subNavItem.id}`"
36fba4d625SSukanya Pandey                    class="nav-link"
372d589a74SDerick Montague                  >
38d329ec84SYoshie Muranaka                    {{ subNavItem.label }}
39fba4d625SSukanya Pandey                  </router-link>
40fba4d625SSukanya Pandey                </li>
4142c19893SDerick Montague              </b-collapse>
42a2988f40SDerick Montague            </li>
43d329ec84SYoshie Muranaka          </template>
4442c19893SDerick Montague        </b-nav>
4574f8687dSYoshie Muranaka      </nav>
4674f8687dSYoshie Muranaka    </div>
4774f8687dSYoshie Muranaka    <transition name="fade">
4874f8687dSYoshie Muranaka      <div
4974f8687dSYoshie Muranaka        v-if="isNavigationOpen"
50ad2ceb6dSDerick Montague        id="nav-overlay"
5174f8687dSYoshie Muranaka        class="nav-overlay"
5274f8687dSYoshie Muranaka        @click="toggleIsOpen"
5374f8687dSYoshie Muranaka      ></div>
5474f8687dSYoshie Muranaka    </transition>
5574f8687dSYoshie Muranaka  </div>
56a2988f40SDerick Montague</template>
57a2988f40SDerick Montague
58a2988f40SDerick Montague<script>
598263d85cSYoshie Muranaka//Do not change Mixin import.
608263d85cSYoshie Muranaka//Exact match alias set to support
618263d85cSYoshie Muranaka//dotenv customizations.
62d329ec84SYoshie Muranakaimport AppNavigationMixin from './AppNavigationMixin';
63883a0d59SEd Tanousimport { useI18n } from 'vue-i18n';
64a2988f40SDerick Montague
65a2988f40SDerick Montagueexport default {
66e2fd1567SDerick Montague  name: 'AppNavigation',
67d329ec84SYoshie Muranaka  mixins: [AppNavigationMixin],
6874f8687dSYoshie Muranaka  data() {
6974f8687dSYoshie Muranaka    return {
70883a0d59SEd Tanous      $t: useI18n().t,
71602e98aaSDerick Montague      isNavigationOpen: false,
72aeb19816SDamian Celico      currentUserRole: null,
7374f8687dSYoshie Muranaka    };
7474f8687dSYoshie Muranaka  },
7574f8687dSYoshie Muranaka  watch: {
7674f8687dSYoshie Muranaka    $route: function () {
7774f8687dSYoshie Muranaka      this.isNavigationOpen = false;
7874f8687dSYoshie Muranaka    },
7974f8687dSYoshie Muranaka    isNavigationOpen: function (isNavigationOpen) {
80edb8a774SSukanya Pandey      this.$root.$emit('change-is-navigation-open', isNavigationOpen);
81602e98aaSDerick Montague    },
8274f8687dSYoshie Muranaka  },
8374f8687dSYoshie Muranaka  mounted() {
84aeb19816SDamian Celico    this.getPrivilege();
85edb8a774SSukanya Pandey    this.$root.$on('toggle-navigation', () => this.toggleIsOpen());
8674f8687dSYoshie Muranaka  },
8774f8687dSYoshie Muranaka  methods: {
8874f8687dSYoshie Muranaka    toggleIsOpen() {
8974f8687dSYoshie Muranaka      this.isNavigationOpen = !this.isNavigationOpen;
90602e98aaSDerick Montague    },
91aeb19816SDamian Celico    getPrivilege() {
92aeb19816SDamian Celico      this.currentUserRole = this.$store?.getters['global/userPrivilege'];
93aeb19816SDamian Celico    },
94aeb19816SDamian Celico    filteredNavItem(navItem) {
95aeb19816SDamian Celico      if (this.currentUserRole) {
96aeb19816SDamian Celico        return navItem.filter(({ exclusiveToRoles }) => {
97aeb19816SDamian Celico          if (!exclusiveToRoles?.length) return true;
98aeb19816SDamian Celico          return exclusiveToRoles.includes(this.currentUserRole);
99aeb19816SDamian Celico        });
100aeb19816SDamian Celico      } else return navItem;
101aeb19816SDamian Celico    },
102602e98aaSDerick Montague  },
103a2988f40SDerick Montague};
104a2988f40SDerick Montague</script>
10542c19893SDerick Montague
106*24b377dbSsuryav9724<style scoped lang="scss">
10771ac230aSYoshie Muranakasvg {
10866f903fbSDerick Montague  fill: currentColor;
10966f903fbSDerick Montague  height: 1.2rem;
11066f903fbSDerick Montague  width: 1.2rem;
11171ac230aSYoshie Muranaka  margin-left: 0 !important; //!important overriding button specificity
11271ac230aSYoshie Muranaka  vertical-align: text-bottom;
11371ac230aSYoshie Muranaka  &:not(.icon-expand) {
11471ac230aSYoshie Muranaka    margin-right: $spacer;
11571ac230aSYoshie Muranaka  }
11671ac230aSYoshie Muranaka}
11771ac230aSYoshie Muranaka
11871ac230aSYoshie Muranaka.nav {
119057232b8SSurenNeware  padding-top: $spacer / 4;
120057232b8SSurenNeware  @include media-breakpoint-up($responsive-layout-bp) {
12171ac230aSYoshie Muranaka    padding-top: $spacer;
12271ac230aSYoshie Muranaka  }
123057232b8SSurenNeware}
12471ac230aSYoshie Muranaka
12571ac230aSYoshie Muranaka.nav-item__nav {
12642c19893SDerick Montague  list-style: none;
12742c19893SDerick Montague  padding-left: 0;
12871ac230aSYoshie Muranaka  margin-left: 0;
12971ac230aSYoshie Muranaka
13085358ed1SYoshie Muranaka  .nav-item {
13185358ed1SYoshie Muranaka    outline: none;
13285358ed1SYoshie Muranaka  }
13385358ed1SYoshie Muranaka
13471ac230aSYoshie Muranaka  .nav-link {
13571ac230aSYoshie Muranaka    padding-left: $spacer * 4;
13685358ed1SYoshie Muranaka    outline: none;
13771ac230aSYoshie Muranaka
13871ac230aSYoshie Muranaka    &:not(.nav-link--current) {
13971ac230aSYoshie Muranaka      font-weight: normal;
14071ac230aSYoshie Muranaka    }
14171ac230aSYoshie Muranaka  }
14271ac230aSYoshie Muranaka}
14371ac230aSYoshie Muranaka
14471ac230aSYoshie Muranaka.btn-link {
14530f11f81SDixsie Wolmers  display: inline-block;
14671ac230aSYoshie Muranaka  width: 100%;
14771ac230aSYoshie Muranaka  text-align: left;
14871ac230aSYoshie Muranaka  text-decoration: none !important;
14971ac230aSYoshie Muranaka  border-radius: 0;
15071ac230aSYoshie Muranaka
15171ac230aSYoshie Muranaka  &.collapsed {
15271ac230aSYoshie Muranaka    .icon-expand {
15371ac230aSYoshie Muranaka      transform: rotate(180deg);
15471ac230aSYoshie Muranaka    }
15571ac230aSYoshie Muranaka  }
15671ac230aSYoshie Muranaka}
15771ac230aSYoshie Muranaka
15871ac230aSYoshie Muranaka.icon-expand {
15971ac230aSYoshie Muranaka  float: right;
16071ac230aSYoshie Muranaka  margin-top: $spacer / 4;
16171ac230aSYoshie Muranaka}
16271ac230aSYoshie Muranaka
16371ac230aSYoshie Muranaka.btn-link,
16471ac230aSYoshie Muranaka.nav-link {
16571ac230aSYoshie Muranaka  position: relative;
16671ac230aSYoshie Muranaka  font-weight: $headings-font-weight;
16771ac230aSYoshie Muranaka  padding-left: $spacer; // defining consistent padding for links and buttons
16871ac230aSYoshie Muranaka  padding-right: $spacer;
16901da8187SYoshie Muranaka  color: theme-color('secondary');
17071ac230aSYoshie Muranaka
17171ac230aSYoshie Muranaka  &:hover {
1721f8117f8SSurenNeware    background-color: theme-color-level(dark, -10.5);
17301da8187SYoshie Muranaka    color: theme-color('dark');
17471ac230aSYoshie Muranaka  }
1759f5cea8bSYoshie Muranaka
1769f5cea8bSYoshie Muranaka  &:focus {
1771f8117f8SSurenNeware    background-color: theme-color-level(light, 0);
1781f8117f8SSurenNeware    box-shadow: inset 0 0 0 2px theme-color('primary');
17901da8187SYoshie Muranaka    color: theme-color('dark');
18059569d8fSDerick Montague    outline: 0;
1819f5cea8bSYoshie Muranaka  }
1821f8117f8SSurenNeware
1831f8117f8SSurenNeware  &:active {
18459569d8fSDerick Montague    background-color: theme-color('secondary');
1851f8117f8SSurenNeware    color: $white;
1861f8117f8SSurenNeware  }
18771ac230aSYoshie Muranaka}
18871ac230aSYoshie Muranaka
1891f8117f8SSurenNeware.nav-link--current {
19071ac230aSYoshie Muranaka  font-weight: $headings-font-weight;
19101da8187SYoshie Muranaka  background-color: theme-color('secondary');
19201da8187SYoshie Muranaka  color: theme-color('light');
19366f903fbSDerick Montague  cursor: default;
1941f8117f8SSurenNeware  box-shadow: none;
19571ac230aSYoshie Muranaka
19671ac230aSYoshie Muranaka  &::before {
19771ac230aSYoshie Muranaka    content: '';
19871ac230aSYoshie Muranaka    position: absolute;
19971ac230aSYoshie Muranaka    top: 0;
20071ac230aSYoshie Muranaka    bottom: 0;
20171ac230aSYoshie Muranaka    left: 0;
20271ac230aSYoshie Muranaka    width: 4px;
20301da8187SYoshie Muranaka    background-color: theme-color('primary');
20471ac230aSYoshie Muranaka  }
20559569d8fSDerick Montague
20659569d8fSDerick Montague  &:hover,
2071f8117f8SSurenNeware  &:focus {
20859569d8fSDerick Montague    background-color: theme-color('secondary');
20959569d8fSDerick Montague    color: theme-color('light');
2101f8117f8SSurenNeware  }
2111f8117f8SSurenNeware}
21274f8687dSYoshie Muranaka
21374f8687dSYoshie Muranaka.nav-container {
21474f8687dSYoshie Muranaka  position: fixed;
21574f8687dSYoshie Muranaka  width: $navigation-width;
21674f8687dSYoshie Muranaka  top: $header-height;
21774f8687dSYoshie Muranaka  bottom: 0;
21874f8687dSYoshie Muranaka  left: 0;
21974f8687dSYoshie Muranaka  z-index: $zindex-fixed;
22074f8687dSYoshie Muranaka  overflow-y: auto;
22159569d8fSDerick Montague  background-color: theme-color('light');
22274f8687dSYoshie Muranaka  transform: translateX(-$navigation-width);
22374f8687dSYoshie Muranaka  transition: transform $exit-easing--productive $duration--moderate-02;
22459569d8fSDerick Montague  border-right: 1px solid theme-color-level('light', 2.85);
22559569d8fSDerick Montague
226057232b8SSurenNeware  @include media-breakpoint-down(md) {
227057232b8SSurenNeware    z-index: $zindex-fixed + 2;
228057232b8SSurenNeware  }
22974f8687dSYoshie Muranaka
23074466b85SDerick Montague  &.open,
23174466b85SDerick Montague  &:focus-within {
23274f8687dSYoshie Muranaka    transform: translateX(0);
23374f8687dSYoshie Muranaka    transition-timing-function: $entrance-easing--productive;
23474f8687dSYoshie Muranaka  }
23574f8687dSYoshie Muranaka
23674f8687dSYoshie Muranaka  @include media-breakpoint-up($responsive-layout-bp) {
23774f8687dSYoshie Muranaka    transition-duration: $duration--fast-01;
23874f8687dSYoshie Muranaka    transform: translateX(0);
23974f8687dSYoshie Muranaka  }
24074f8687dSYoshie Muranaka}
24174f8687dSYoshie Muranaka
24274f8687dSYoshie Muranaka.nav-overlay {
24374f8687dSYoshie Muranaka  position: fixed;
24474f8687dSYoshie Muranaka  top: $header-height;
24574f8687dSYoshie Muranaka  bottom: 0;
24674f8687dSYoshie Muranaka  left: 0;
24774f8687dSYoshie Muranaka  right: 0;
248057232b8SSurenNeware  z-index: $zindex-fixed + 1;
24974f8687dSYoshie Muranaka  background-color: $black;
25074f8687dSYoshie Muranaka  opacity: 0.5;
25174f8687dSYoshie Muranaka
25274f8687dSYoshie Muranaka  &.fade-enter-active {
25374f8687dSYoshie Muranaka    transition: opacity $duration--moderate-02 $entrance-easing--productive;
25474f8687dSYoshie Muranaka  }
25574f8687dSYoshie Muranaka
25674f8687dSYoshie Muranaka  &.fade-leave-active {
25774f8687dSYoshie Muranaka    transition: opacity $duration--fast-02 $exit-easing--productive;
25874f8687dSYoshie Muranaka  }
25974f8687dSYoshie Muranaka
260151dd249SSurenNeware  &.fade-enter, // Remove this vue2 based only class when switching to vue3
261151dd249SSurenNeware  &.fade-enter-from, // This is vue3 based only class modified from 'fade-enter'
26274f8687dSYoshie Muranaka  &.fade-leave-to {
26374f8687dSYoshie Muranaka    opacity: 0;
26474f8687dSYoshie Muranaka  }
26574f8687dSYoshie Muranaka
26674f8687dSYoshie Muranaka  @include media-breakpoint-up($responsive-layout-bp) {
26774f8687dSYoshie Muranaka    display: none;
26874f8687dSYoshie Muranaka  }
26974f8687dSYoshie Muranaka}
27042c19893SDerick Montague</style>
271