Epic 4: Chemins Narratifs, Challenge & Contact Stories implementees: - 4.1: Composant ChoiceCards pour choix narratifs binaires - 4.2: Sequence d'intro narrative avec Le Bug - 4.3: Chemins narratifs differencies avec useNarrativePath - 4.4: Table easter_eggs et systeme de detection (API + composable) - 4.5: Easter eggs UI (popup, notification, collection) - 4.6: Page challenge avec puzzle de code - 4.7: Page revelation "Monde de Code" - 4.8: Page contact avec formulaire et stats Fichiers crees: - Frontend: ChoiceCards, IntroSequence, ZoneEndChoice, EasterEggPopup, CodePuzzle, ChallengeSuccess, CodeWorld, et pages intro/challenge/revelation - API: EasterEggController, Model, Migration, Seeder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
89 lines
2.2 KiB
Vue
89 lines
2.2 KiB
Vue
<template>
|
|
<button
|
|
type="button"
|
|
class="choice-card group relative flex flex-col items-center p-6 rounded-xl border-2 transition-all duration-300 focus:outline-none"
|
|
:class="[
|
|
selected
|
|
? 'border-sky-accent bg-sky-accent/10 scale-105 shadow-lg shadow-sky-accent/20'
|
|
: 'border-sky-text/20 bg-sky-dark/50 hover:border-sky-accent/50 hover:bg-sky-dark/80',
|
|
disabled && 'opacity-50 cursor-not-allowed',
|
|
]"
|
|
:style="{ '--zone-color': choice.zoneColor }"
|
|
:disabled="disabled"
|
|
:aria-checked="selected"
|
|
role="radio"
|
|
@click="emit('select')"
|
|
>
|
|
<!-- Glow effect au hover -->
|
|
<div
|
|
class="absolute inset-0 rounded-xl opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none"
|
|
:style="{ boxShadow: `0 0 30px ${choice.zoneColor}40` }"
|
|
/>
|
|
|
|
<!-- Icône -->
|
|
<div
|
|
class="w-16 h-16 rounded-full flex items-center justify-center text-4xl mb-4"
|
|
:style="{ backgroundColor: `${choice.zoneColor}20` }"
|
|
>
|
|
{{ choice.icon }}
|
|
</div>
|
|
|
|
<!-- Texte narratif -->
|
|
<p class="font-narrative text-lg text-sky-text text-center leading-relaxed">
|
|
{{ text }}
|
|
</p>
|
|
|
|
<!-- Indicateur de sélection -->
|
|
<div
|
|
v-if="selected"
|
|
class="absolute -top-2 -right-2 w-6 h-6 rounded-full bg-sky-accent flex items-center justify-center"
|
|
>
|
|
<span class="text-white text-sm">✓</span>
|
|
</div>
|
|
</button>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { Choice } from '~/types/choice'
|
|
|
|
const props = defineProps<{
|
|
choice: Choice
|
|
selected: boolean
|
|
disabled: boolean
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
select: []
|
|
}>()
|
|
|
|
const { locale } = useI18n()
|
|
|
|
const text = computed(() => {
|
|
return locale.value === 'fr' ? props.choice.textFr : props.choice.textEn
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.choice-card:focus-visible {
|
|
outline: 2px solid var(--sky-accent, #38bdf8);
|
|
outline-offset: 4px;
|
|
}
|
|
|
|
.choice-card:not(:disabled):hover {
|
|
transform: translateY(-4px);
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.choice-card,
|
|
.choice-card * {
|
|
transition: none !important;
|
|
animation: none !important;
|
|
transform: none !important;
|
|
}
|
|
|
|
.choice-card:not(:disabled):hover {
|
|
transform: none !important;
|
|
}
|
|
}
|
|
</style>
|