Files
zammad/app/frontend/apps/desktop/pages/knowledge-base/components/KnowledgeBaseTopBarHeader/KnowledgeBaseBreadcrumb.vue
Benjamin Scharf 3a08058c1d Feature: Desktop view - View an answer in the knowledge base
Approved-by: Marcel Bialas <mb@zammad.com>
Approved-by: Benjamin Scharf <bs@zammad.com>
Approved-by: Dusan Vuckovic <dv@zammad.com>

Co-authored-by: Dominik Klein <dk@zammad.com>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Co-authored-by: Marcel Bialas <mb@zammad.com>
Co-authored-by: Rene Reimann <rr@zammad.com>
2026-08-17 08:01:05 +02:00

162 lines
4.7 KiB
Vue

<!-- Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/ -->
<script setup lang="ts">
import { computed } from 'vue'
import { i18n } from '#shared/i18n.ts'
import { useLocaleStore } from '#shared/stores/locale.ts'
import KnowledgeBaseIconStatus from '../KnowledgeBaseIconStatus.vue'
import type { KnowledgeBaseBreadcrumbItem } from '../../types.ts'
const props = withDefaults(
defineProps<{
items: KnowledgeBaseBreadcrumbItem[]
emphasizeLastItem?: boolean
size?: 'small' | 'large'
label?: string
}>(),
{
size: 'large',
label: __('Breadcrumb navigation'),
},
)
const locale = useLocaleStore()
const lastItemClasses = computed(() => {
return props.emphasizeLastItem ? ['last:dark:text-white last:text-black'] : []
})
const sizeClasses = computed(() => {
if (props.size === 'small') return ['text-xs']
return ['text-base'] // default -> 'large'
})
const getItemLabel = (item: KnowledgeBaseBreadcrumbItem) =>
item.noOptionLabelTranslation ? (item.label as string) : i18n.t(item.label as string)
const displayItems = computed(() =>
props.items.map((item) => Object.assign({}, item, { displayLabel: getItemLabel(item) })),
)
</script>
<template>
<nav :class="sizeClasses" :aria-label="$t(label)" class="max-w-full">
<ol class="flex">
<li
v-for="(item, idx) in displayItems"
:key="item.label as string"
class="flex items-center"
:class="[lastItemClasses, { 'print:hidden': idx === 0 }]"
>
<KnowledgeBaseIconStatus
v-if="!item.route && item.icon && item.iconSet"
:name="item.icon"
:set="item.iconSet"
:status="item.visibility"
size="xs"
class="me-2.25 shrink-0"
:class="item.iconClass"
/>
<CommonIcon
v-else-if="!item.route && item.icon"
:name="item.icon"
size="xs"
class="me-1 shrink-0"
:class="item.iconClass"
/>
<CommonLink
v-if="item.route && item.iconOnly"
v-tooltip="item.displayLabel"
class="inline-flex items-center text-gray-100 focus-visible-app-default hover:text-black dark:text-neutral-400 hover:dark:text-white"
:link="item.route"
internal
>
<KnowledgeBaseIconStatus
v-if="item.icon && item.iconSet"
:name="item.icon"
:set="item.iconSet"
:status="item.visibility"
size="xs"
class="me-1.25 shrink-0"
:class="item.iconClass"
/>
<CommonIcon
v-else-if="item.icon"
:name="item.icon"
size="xs"
class="shrink-0"
:class="item.iconClass"
/>
</CommonLink>
<CommonLink
v-else-if="item.route"
v-tooltip.supportive="item.displayLabel"
class="inline-flex items-center gap-1 focus-visible-app-default"
:link="item.route"
internal
>
<KnowledgeBaseIconStatus
v-if="item.icon && item.iconSet"
:name="item.icon"
:set="item.iconSet"
:status="item.visibility"
size="xs"
class="me-1.25 shrink-0"
:class="item.iconClass"
/>
<CommonIcon
v-else-if="item.icon"
:name="item.icon"
size="xs"
class="shrink-0"
:class="item.iconClass"
/>
<CommonLabel class="line-clamp-1! hover:text-black hover:dark:text-white" :size="size">
{{ item.displayLabel }}
</CommonLabel>
</CommonLink>
<component
:is="displayItems.at(-1) === item ? 'h1' : 'span'"
v-else
v-tooltip.supportive="item.displayLabel"
class="line-clamp-1"
:class="{
'text-black dark:text-white': item.isActive,
'break-all': displayItems.at(-1) === item,
}"
aria-current="page"
>
{{ item.displayLabel }}
</component>
<CommonBadge
v-if="item.count !== undefined"
class="leading-snug font-bold ltr:ml-1.5 rtl:mr-1.5"
size="xs"
rounded
>
{{ item.count }}
</CommonBadge>
<CommonIcon
v-if="idx !== displayItems.length - 1"
:name="locale.localeData?.dir === 'rtl' ? 'chevron-left' : 'chevron-right'"
size="xs"
class="mx-1 inline-flex shrink-0 text-stone-200 dark:text-neutral-500"
/>
<!-- Add a slot at the end of the last item. -->
<slot v-if="idx === displayItems.length - 1" name="trailing" />
</li>
</ol>
</nav>
</template>