123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- <!-- Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ -->
- <script setup lang="ts">
- import { transform, deburr } from 'lodash-es'
- import { computed, ref } from 'vue'
- import { i18n } from '#shared/i18n.ts'
- import { useSessionStore } from '#shared/stores/session.ts'
- import NavigationMenuFilter from '#desktop/components/NavigationMenu/NavigationMenuFilter.vue'
- import NavigationMenuHeader from '#desktop/components/NavigationMenu/NavigationMenuHeader.vue'
- import NavigationMenuList from '#desktop/components/NavigationMenu/NavigationMenuList.vue'
- import { useTransitionCollapse } from '#desktop/composables/useTransitionCollapse.ts'
- import type { NavigationMenuCategory, NavigationMenuEntry } from './types'
- interface Props {
- categories: NavigationMenuCategory[]
- entries: Record<string, NavigationMenuEntry[]>
- hasNoFiltering?: boolean
- }
- const props = defineProps<Props>()
- const session = useSessionStore()
- const permittedEntries = computed(() => {
- return transform(
- props.entries,
- (memo, entries, category) => {
- memo[category] = entries.filter((entry) => {
- if (
- entry.route.meta?.requiredPermission &&
- !session.hasPermission(entry.route.meta.requiredPermission)
- )
- return false
- if (typeof entry.show === 'function') return entry.show()
- return true
- })
- },
- {} as Record<string, NavigationMenuEntry[]>,
- )
- })
- const searchText = ref('')
- const collapsedCategories = ref<Set<string>>(new Set([]))
- const toggleCategory = (categoryLabel: string) => {
- return collapsedCategories.value.has(categoryLabel)
- ? collapsedCategories.value.delete(categoryLabel)
- : collapsedCategories.value.add(categoryLabel)
- }
- const normalizeString = (input: string) => deburr(input).toLocaleLowerCase()
- const searchTextMatcher = computed(() => {
- if (!searchText.value) return ''
- return normalizeString(searchText.value)
- })
- const isMatchingFilterLabel = (entry: NavigationMenuEntry) => {
- return normalizeString(i18n.t(entry.label)).includes(searchTextMatcher.value)
- }
- const isMatchingFilterKeywords = (entry: NavigationMenuEntry) => {
- if (!entry.keywords) return false
- return i18n
- .t(entry.keywords)
- .split(',')
- .some((elem) => normalizeString(elem).includes(searchTextMatcher.value))
- }
- const isMatchingFilter = (entry: NavigationMenuEntry) => {
- return isMatchingFilterLabel(entry) || isMatchingFilterKeywords(entry)
- }
- const allFilteredEntries = computed<NavigationMenuEntry[]>(() => {
- return Object.values(permittedEntries.value)
- .flat()
- .filter((entry) => isMatchingFilter(entry))
- })
- const { collapseDuration, collapseEnter, collapseAfterEnter, collapseLeave } =
- useTransitionCollapse()
- </script>
- <template>
- <NavigationMenuFilter v-model.trim="searchText" />
- <NavigationMenuList
- v-if="searchText && allFilteredEntries.length > 0"
- :items="allFilteredEntries"
- />
- <CommonLabel
- v-else-if="allFilteredEntries.length == 0"
- class="px-2 py-1 text-stone-200 dark:text-neutral-500"
- >{{ __('No results found') }}
- </CommonLabel>
- <ul v-else>
- <li
- v-for="category in categories"
- :key="category.label"
- class="bg-neutral-00 relative z-0 mb-4"
- :class="{ 'overflow-clip': collapsedCategories.has(category.label) }"
- >
- <template v-if="permittedEntries[category.label].length > 0">
- <NavigationMenuHeader
- :id="category.id"
- class="mb-1 px-2"
- :collapsed="collapsedCategories.has(category.label)"
- :title="category.label"
- :icon="category.icon"
- collapsible
- @toggle-collapsed="toggleCategory"
- />
- <Transition
- name="collapse"
- :duration="collapseDuration"
- @enter="collapseEnter"
- @after-enter="collapseAfterEnter"
- @leave="collapseLeave"
- >
- <NavigationMenuList
- v-show="
- permittedEntries[category.label] &&
- !collapsedCategories.has(category.label)
- "
- :id="category.id"
- :aria-label="$t(category.label)"
- :items="permittedEntries[category.label]"
- />
- </Transition>
- </template>
- </li>
- </ul>
- </template>
|