All files / src/components/organisms/common AppNavDrawer.vue

100% Statements 54/54
100% Branches 12/12
66.66% Functions 4/6
100% Lines 54/54

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125  1x 1x 1x 1x 1x 1x 1x     1x 1x 1x 1x 1x 1x   1x 3x 3x 3x 3x 3x 3x 3x 1x   1x 1x 1x 1x   1x 1x 1x 1x 1x 1x   1x 1x   1x 1x 1x 1x         1x   1x 1x 1x                             1x 1x       1x                       1x 3x 1x         1x                 1x 1x 1x 1x   1x                                  
<template>
  <!-- 親から v-model で開閉を制御 -->
  <v-navigation-drawer
    v-model="localOpen"
    location="left"
    temporary
    :width="drawerWidth"
    class="drawer-overlay"
  >
 
    <!-- ヘッダーと高さを合わせたドロワ用ツールバー -->
    <v-toolbar
      density="comfortable"
      color="transparent"
      flat
      class="px-2"
    >
      <IconButton
        role="close"
        icon="mdi-close"
        size="default"
        rounded="pill"
        @click="closeOnNavigate && close()"
      />
      <v-spacer />
    </v-toolbar>
 
    <v-list
      density="compact"
      nav
      class="nav-tight"
    >
      <v-list-item
        v-for="(link, i) in links"
        :key="i"
        :to="link.to"
        exact
        @click="closeOnNavigate && close()"
      >
        <template #prepend>
          <v-icon size="18" class="me-0">{{ link.icon || 'mdi-circle-small' }}</v-icon>
        </template>
        <v-list-item-title>{{ t(link.titleKey) }}</v-list-item-title>
      </v-list-item>
    </v-list>
  </v-navigation-drawer>
</template>
 
<script setup lang="ts">
// region Dependency Injection
import {computed} from 'vue'
import type {RouteLocationRaw} from 'vue-router'
import {useDisplay} from 'vuetify'
import { useI18n } from 'vue-i18n'
import IconButton from '@/components/atoms/IconButton.vue'
// endregion Dependency Injection
 
// region Component Injection
type MenuLink = {
  to: RouteLocationRaw
  titleKey: string
  icon?: string
}
// endregion Component Injection
 
// region interface
// endregion interface
 
// region constants
const display = useDisplay()
const { t } = useI18n()
// endregion constants
 
// region props
const props = defineProps<{
  modelValue: boolean
  links: MenuLink[]
  closeOnNavigate?: boolean
}>()
// endregion props
 
// region variable
// endregion variable
 
// region properties
// スマホ (smAndDown) のときは画面幅いっぱい、それ以外は固定幅
const drawerWidth = computed(() =>
  display.smAndDown.value ? '100%' : 280
)
// endregion properties
 
// region emits
 
const emit = defineEmits<{
  (e: 'update:modelValue', v: boolean): void
}>()
// endregion emits
 
// region validator
// endregion validator
 
// region methods
const localOpen = computed({
  get: () => props.modelValue,
  set: (v: boolean) => emit('update:modelValue', v),
})
 
const close = () => emit('update:modelValue', false)
// endregion methods
 
// region export
// endregion export
</script>
 
<style scoped>
/* v-app-bar より前面に(必要な場合だけ) */
.nav-on-top { z-index: 2500; } /* app-barが~2000前後なので十分上 */
.drawer-overlay {
  z-index: 2501 !important;   /* app-bar(≈2000) より前面へ */
  --v-layout-top: 0 !important; /* ヘッダぶんの top オフセットを無効化 */
  top: 0 !important;            /* 念のため固定 */
  height: 100vh;                /* 全高(スマホ時も隙間なし) */
}
</style>