swipe game with overlay
This commit is contained in:
parent
f74eb98414
commit
6a818e9da5
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<motion.img
|
||||
:src="props.card.url"
|
||||
:src="props.card.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="{
|
||||
zIndex: props.isFront ? 10 : props.z,
|
||||
zIndex: props.isFront ? 500 : props.z,
|
||||
gridRow: 1,
|
||||
gridColumn: 1,
|
||||
transition: '0.125s transform',
|
||||
@ -75,11 +75,7 @@ const dragValue = computed(() => (props.isFront ? "x" : false));
|
||||
|
||||
const x = useMotionValue(0);
|
||||
const opacity = useTransform(x, [-150, 0, 150], [0, 1, 0]);
|
||||
const rotateRaw = useTransform(x, [-150, 150], [-18, 18]);
|
||||
const rotate = useTransform(() => {
|
||||
const offset = props.isFront ? 0 : props.card.id % 2 ? 4 : -4;
|
||||
return `${rotateRaw.get() + offset}deg`;
|
||||
});
|
||||
const rotate = useTransform(x, [-150, 150], [-18, 18]);
|
||||
|
||||
const handleDragEnd = () => {
|
||||
if (Math.abs(x.get()) > 50) {
|
||||
@ -88,6 +84,7 @@ const handleDragEnd = () => {
|
||||
};
|
||||
|
||||
async function swipeRight() {
|
||||
console.log(props.card.id);
|
||||
await animate(x, 300, { duration: 0.3 });
|
||||
cardStore.removeCard(props.card.id);
|
||||
}
|
||||
|
||||
62
components/UploadView.vue
Normal file
62
components/UploadView.vue
Normal file
@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="mt-20 flex flex-col items-center gap-5">
|
||||
<FloatLabel variant="in">
|
||||
<InputText id="collection" v-model="collectionName" variant="filled" />
|
||||
<label for="collection">Collection Name</label>
|
||||
</FloatLabel>
|
||||
<FileUpload
|
||||
name="files[]"
|
||||
:auto="false"
|
||||
:multiple="true"
|
||||
accept="image/*"
|
||||
custom-upload
|
||||
@uploader="onUpload"
|
||||
>
|
||||
<template #empty>
|
||||
<p>Drag and drop files here to upload.</p>
|
||||
</template>
|
||||
</FileUpload>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FileUploadUploaderEvent } from "primevue";
|
||||
|
||||
const toast = useToast();
|
||||
const collectionName = ref("");
|
||||
|
||||
const onUpload = async (event: FileUploadUploaderEvent) => {
|
||||
const files = Array.isArray(event.files) ? event.files : [event.files];
|
||||
if (collectionName.value.trim().length === 0) {
|
||||
toast.error({
|
||||
title: "Collection name missing",
|
||||
message: "Please enter a valid collection name.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
for (const file of files) {
|
||||
const formData = new FormData();
|
||||
formData.append("image", file);
|
||||
formData.append("name", toTitleCaseWithSpaces(parseImageName(file.name)));
|
||||
formData.append("collection_name", collectionName.value);
|
||||
|
||||
try {
|
||||
await $fetch("/api/cards/", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
baseURL: useApiBase(),
|
||||
});
|
||||
toast.success({
|
||||
title: "Success!",
|
||||
message: "Files uploaded successfully",
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
toast.error({
|
||||
title: "Upload failed!",
|
||||
message: "Something went wrong.",
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
1
composables/useApiBase.ts
Normal file
1
composables/useApiBase.ts
Normal file
@ -0,0 +1 @@
|
||||
export const useApiBase = () => useRuntimeConfig().public.apiBase;
|
||||
@ -15,8 +15,10 @@ export default defineNuxtConfig({
|
||||
],
|
||||
css: ["~/assets/css/main.css"],
|
||||
vite: { plugins: [tailwindcss()] },
|
||||
app: {
|
||||
baseURL: process.env.BASE_URL || "/",
|
||||
runtimeConfig: {
|
||||
public: {
|
||||
apiBase: process.env.API_BASE_URL || "http://localhost:8000",
|
||||
},
|
||||
},
|
||||
primevue: {
|
||||
components: {
|
||||
@ -35,4 +37,4 @@ export default defineNuxtConfig({
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
v-for="(card, index) in cardStore.cards"
|
||||
:key="card.id"
|
||||
:card="card"
|
||||
:is-front="index === cardStore.cards.length - 1"
|
||||
:is-front="index === 0"
|
||||
:z="index"
|
||||
/>
|
||||
</div>
|
||||
@ -15,6 +15,8 @@ import CardView from "~/components/CardView.vue";
|
||||
import { useCardStore } from "~/store/Card";
|
||||
|
||||
const route = useRoute();
|
||||
const collection = route.query.collection;
|
||||
const collection: string = route.query.collection as string;
|
||||
const cardStore = useCardStore();
|
||||
|
||||
cardStore.fetchCardsByCollection(collection);
|
||||
</script>
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div class="flex min-h-screen flex-col items-center justify-center">
|
||||
<div class="flex min-h-screen flex-col items-center justify-center gap-5">
|
||||
<h1 class="mb-8 text-3xl font-bold">Select a collection to begin</h1>
|
||||
<ul class="w-full max-w-md space-y-4">
|
||||
<li
|
||||
v-for="collection in cardStore.collections"
|
||||
:key="collection"
|
||||
:key="collection.name"
|
||||
class="flex items-center justify-between rounded p-4 shadow"
|
||||
>
|
||||
<span class="text-lg font-medium">{{ collection }}</span>
|
||||
<span class="text-lg font-bold">{{ collection.name }}</span>
|
||||
<!-- Large Play Button -->
|
||||
<Button
|
||||
icon="pi pi-play"
|
||||
@ -22,10 +22,11 @@
|
||||
width 0.3s,
|
||||
height 0.3s;
|
||||
"
|
||||
@click="play(collection)"
|
||||
@click="play(collection.name)"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<UploadView />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -34,8 +35,8 @@ import { useCardStore } from "~/store/Card";
|
||||
|
||||
const cardStore = useCardStore();
|
||||
|
||||
cardStore.collections = ["Test", "Test1"];
|
||||
|
||||
cardStore.fetchCollections();
|
||||
/*
|
||||
cardStore.cards = [
|
||||
{
|
||||
id: 1,
|
||||
@ -84,7 +85,7 @@ cardStore.cards = [
|
||||
name: "Pipi",
|
||||
url: "https://images.unsplash.com/photo-1505784045224-1247b2b29cf3?q=80&w=2340&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
},
|
||||
];
|
||||
]; */
|
||||
|
||||
const activeItem = ref();
|
||||
|
||||
|
||||
@ -1,20 +1,35 @@
|
||||
import { defineStore } from "pinia";
|
||||
import type { Card } from "~/types/Card";
|
||||
import type { Collection } from "~/types/Collection";
|
||||
|
||||
export const useCardStore = defineStore("cardStore", {
|
||||
state: () => ({
|
||||
collections: [] as string[],
|
||||
collections: [] as Collection[],
|
||||
cards: [] as Card[],
|
||||
smashList: [] as Card[],
|
||||
passList: [] as Card[],
|
||||
}),
|
||||
actions: {
|
||||
async fetchCollections() {
|
||||
const { data: collections } =
|
||||
await useFetch<string[]>("/api/collections");
|
||||
this.collections = collections.value ?? [];
|
||||
const data = await $fetch<Collection[]>("/api/collections/", {
|
||||
baseURL: useApiBase(),
|
||||
});
|
||||
this.collections = data ?? [];
|
||||
},
|
||||
async fetchCardsByCollection(collectionName: string) {
|
||||
const response = useFetch<Card[]>(`/api/cards/${collectionName}`);
|
||||
this.cards = response.data.value ?? [];
|
||||
const data = await $fetch<Card[]>(
|
||||
`/api/cards/?collection__name=${collectionName}`,
|
||||
{
|
||||
baseURL: useApiBase(),
|
||||
},
|
||||
);
|
||||
|
||||
this.cards =
|
||||
data.map((card, index) => ({
|
||||
...card,
|
||||
id: index,
|
||||
})) ?? [];
|
||||
console.log(this.cards);
|
||||
},
|
||||
removeCard(id: number) {
|
||||
const index = this.cards.findIndex((card) => card.id === id);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export type Card = {
|
||||
id: number;
|
||||
url: string;
|
||||
image: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
3
types/Collection.ts
Normal file
3
types/Collection.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export type Collection = {
|
||||
name: string;
|
||||
};
|
||||
13
utils/fileNameParser.ts
Normal file
13
utils/fileNameParser.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export const toTitleCaseWithSpaces = (input: string): string => {
|
||||
return input
|
||||
.replace(/[-_]/g, " ")
|
||||
.split(" ")
|
||||
.filter(Boolean)
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
||||
.join(" ");
|
||||
};
|
||||
|
||||
export const parseImageName = (fileName: string): string => {
|
||||
// Extract the name without the extension
|
||||
return fileName.split(".").slice(0, -1).join(".");
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user