feat(10-03): implement BreadcrumbBar.vue — shared breadcrumb component (GREEN)
- Options API component with segments/{id?,label}, rootLabel, showRoot props
- Emits navigate(segment.id) for intermediate clicks, navigate(null) for root
- Computed visibleSegments collapses >4 segments to first+ellipsis+last two
- Segments without id render as plain non-clickable spans (admin static segments)
- Uses AppIcon for chevronRight separator (no inline SVG)
- All 10 Vitest tests pass
- Also creates AppIcon.vue (Rule 3: BreadcrumbBar imports it)
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<nav aria-label="Navigation">
|
||||
<ol class="flex items-center gap-1 text-sm flex-wrap">
|
||||
<li v-if="showRoot" class="flex items-center gap-1">
|
||||
<button
|
||||
@click="$emit('navigate', null)"
|
||||
class="text-indigo-600 hover:underline font-medium"
|
||||
>
|
||||
{{ rootLabel }}
|
||||
</button>
|
||||
</li>
|
||||
|
||||
<template v-for="(segment, idx) in visibleSegments" :key="segment.id ?? 'static-' + idx">
|
||||
<li
|
||||
v-if="showRoot || idx > 0"
|
||||
class="shrink-0"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<AppIcon name="chevronRight" class="w-3 h-3 text-gray-400" />
|
||||
</li>
|
||||
|
||||
<li v-if="segment.id === 'ellipsis'" class="flex items-center">
|
||||
<span class="px-2 py-1 text-gray-400">…</span>
|
||||
</li>
|
||||
|
||||
<li v-else-if="idx === visibleSegments.length - 1" class="flex items-center">
|
||||
<span class="text-gray-900 font-medium">{{ segment.label }}</span>
|
||||
</li>
|
||||
|
||||
<li v-else-if="segment.id" class="flex items-center">
|
||||
<button
|
||||
@click="$emit('navigate', segment.id)"
|
||||
class="text-indigo-600 hover:underline font-medium"
|
||||
>
|
||||
{{ segment.label }}
|
||||
</button>
|
||||
</li>
|
||||
|
||||
<li v-else class="flex items-center">
|
||||
<span class="text-gray-500">{{ segment.label }}</span>
|
||||
</li>
|
||||
</template>
|
||||
</ol>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AppIcon from './AppIcon.vue'
|
||||
|
||||
export default {
|
||||
name: 'BreadcrumbBar',
|
||||
components: { AppIcon },
|
||||
props: {
|
||||
segments: { type: Array, default: () => [] },
|
||||
rootLabel: { type: String, default: 'Home' },
|
||||
showRoot: { type: Boolean, default: true },
|
||||
},
|
||||
emits: ['navigate'],
|
||||
computed: {
|
||||
visibleSegments() {
|
||||
if (this.segments.length > 4) {
|
||||
return [
|
||||
this.segments[0],
|
||||
{ id: 'ellipsis', label: '…' },
|
||||
...this.segments.slice(-2),
|
||||
]
|
||||
}
|
||||
return this.segments
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user