sop-nuxt/components/CardView.vue
2025-07-13 18:13:22 +02:00

102 lines
2.9 KiB
Vue

<template>
<motion.img
v-if="currentCard"
:src="currentCard?.image"
draggable="false"
class="h-[60cqh] max-h-[800px] w-[400px] origin-bottom rounded-lg object-cover hover:cursor-grab active:cursor-grabbing md:w-[600px]"
:style="{
gridRow: 1,
gridColumn: 1,
transition: '0.125s transform',
x,
opacity,
rotate,
boxShadow:
'0 20px 25px -5px rgb(0 0 0 / 0.5), 0 8px 10px -6px rgb(0 0 0 / 0.5)',
}"
:alt="currentCard?.name"
drag="x"
:drag-constraints="{ left: 0, right: 0 }"
:on-drag-end="handleDragEnd"
/>
<h1 class="h-2 text-5xl">{{ currentCard?.name }}</h1>
<div v-if="currentCard" class="flex justify-between">
<!-- Pass Button -->
<button
class="mx-12 flex h-24 w-42 flex-col items-center justify-center rounded-xl bg-violet-500 text-4xl text-white shadow-lg transition-colors duration-150 hover:bg-violet-600 focus:ring-4 focus:ring-violet-300 focus:outline-none"
aria-label="Pass"
type="button"
@click="swipeLeft"
>
<span class="text-base font-semibold">Pass</span>
</button>
<!-- Smash Button -->
<button
class="mx-12 flex h-24 w-42 flex-col items-center justify-center rounded-xl bg-teal-500 text-4xl text-white shadow-lg transition-colors duration-150 hover:bg-teal-600 focus:ring-4 focus:ring-teal-300 focus:outline-none"
aria-label="Smash"
type="button"
@click="swipeRight"
>
<span class="text-base font-semibold">Smash</span>
</button>
</div>
</template>
<script setup lang="ts">
import { animate, motion, useMotionValue, useTransform } from "motion-v";
import type { PropType } from "vue";
import { useCardStore } from "~/store/Card";
import type { Card } from "~/types/Card";
const cardStore = useCardStore();
const props = defineProps({
cards: {
type: Object as PropType<Card[]>,
required: true,
},
});
const emit = defineEmits<{
(e: "empty-list", isEmpty: boolean): void;
}>();
const currentCard = computed(() => props.cards[0]);
const x = useMotionValue(0);
const opacity = useTransform(x, [-150, 0, 150], [0, 1, 0]);
const rotate = useTransform(x, [-150, 150], [-18, 18]);
watch(
() => props.cards.length,
(newLength, oldLength) => {
if (oldLength && oldLength > 0 && newLength === 0) {
emit("empty-list", true);
}
},
{ immediate: true },
);
const handleDragEnd = () => {
if (Math.abs(x.get()) > 50) {
cardStore.removeCard(currentCard?.value.id);
}
};
async function swipeRight() {
await animate(x, 150, { duration: 0.3 });
cardStore.smashList.push(currentCard.value);
cardStore.removeCard(currentCard.value.id);
await animate(x, 0, { duration: 0.6 });
}
async function swipeLeft() {
await animate(x, -150, { duration: 0.3 });
cardStore.passList.push(currentCard.value);
cardStore.removeCard(currentCard.value.id);
await animate(x, 0, { duration: 0.6 });
}
</script>