ptitlutins/app/components/molecules/EtapeCard.vue
2026-06-15 23:34:49 +02:00

88 lines
2.1 KiB
Vue

<script setup lang="ts">
import type { Node } from "~/types"
const props = defineProps<{
node: Node
}>()
defineEmits<{
routeTime: [node: Node]
routePlace: [node: Node]
}>()
// Drilling is navigation (changes ?n=) → a real link, so open-in-new-tab and
// the back button work. The pills sit above the stretched link as siblings,
// so there is no interactive element nested inside the <a>.
const { currentPath, linkTo } = useNodeNav()
const drillTo = computed(() => linkTo([...currentPath.value, props.node.id]))
const kids = computed(() => childrenOf(props.node).length)
</script>
<template>
<Card class="etape-card" :style="{ padding: '0.65rem 0.8rem' }">
<NuxtLink :to="drillTo" class="etape-card__drill">
<span class="etape-card__title">{{ node.event.title }}</span>
<Icon name="carbon:chevron-right" size="1.1rem" class="etape-card__chevron" />
</NuxtLink>
<div class="etape-card__pills">
<TimePill :node="node" @route="$emit('routeTime', node)" />
<PlacePill :node="node" @route="$emit('routePlace', node)" />
<ChildPill v-if="kids" :n="kids" />
</div>
</Card>
</template>
<style scoped>
.etape-card {
position: relative;
transition:
box-shadow 140ms ease,
transform 140ms ease;
}
.etape-card:hover {
box-shadow: var(--shadow-3);
transform: translateY(-2px);
}
.etape-card__drill {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: var(--space-2);
text-decoration: none;
color: inherit;
}
/* Stretched link: the whole card navigates without nesting the pills in the <a>. */
.etape-card__drill::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
}
.etape-card__title {
font-family: var(--font-body);
font-size: 0.98rem;
font-weight: var(--weight-semibold);
color: var(--text-body);
line-height: 1.25;
}
.etape-card__chevron {
color: var(--neutral-40);
margin-top: 2px;
flex-shrink: 0;
}
.etape-card__pills {
position: relative;
z-index: 1;
width: fit-content;
margin-top: 7px;
display: flex;
gap: 6px;
flex-wrap: wrap;
}
</style>