Skip to content

Commit

Permalink
chore: revamp UI
Browse files Browse the repository at this point in the history
  • Loading branch information
darlanalves committed Apr 2, 2024
1 parent c726505 commit 330b96e
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 108 deletions.
2 changes: 2 additions & 0 deletions assets/next.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions assets/previous.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions assets/shuffle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
230 changes: 122 additions & 108 deletions components/App.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div class="flex flex-col h-screen w-screen justify-center app px-4">
<div>
<nav
v-if="!lang"
class="flex flex-col items-center justify-center gap-4 p-2 text-xl text-white"
Expand All @@ -10,126 +10,128 @@
<a href="/?lang=it" class="p-2">Italian</a>
<a href="/?lang=tr" class="p-2">Turkish</a>
</nav>

<!-- bg -->
<div
class="flex justify-center items-center h-screen bg-primary p-6"
v-if="lang"
@click="flip()"
class="cards relative w-full max-w-sm mx-auto bg-white shadow-lg rounded-lg overflow-hidden text-center"
>
<div
class="text-gray-900 font-bold card absolute z-1 inset-0 justify-center items-center flex text-center"
ref="card1"
v-if="cards.length"
class="bg-white w-full max-w-lg rounded-lg shadow-lg"
>
<span>{{ flashcards.length && flashcards[currentCard].front }}</span>
</div>
<div
class="text-gray-900 font-bold card absolute z-1 inset-0 justify-center items-center flex text-center"
ref="card2"
>
<span>{{ flashcards.length && flashcards[currentCard].back }}</span>
<div
class="relative overflow-hidden card h-48 text-center rounded-t-lg"
@click="showTranslation = !showTranslation"
>
<div
class="text-3xl absolute h-48 flex items-center justify-center"
:class="[
showTranslation ? 'out' : 'in',
animated ? 'animated' : '',
]"
>
{{ cards[index].front }}
</div>
<div
class="text-3xl absolute h-48 flex items-center justify-center bg-secondary"
:class="[
showTranslation ? 'in' : 'out',
animated ? 'animated' : '',
]"
>
{{ cards[index].back }}
</div>
</div>
<div
class="p-3 bg-gray-100 border-t border-gray-200 flex justify-between rounded-b-lg overflow-hidden"
>
<button
:disabled="index <= 0"
@click="prevCard"
class="py-1 px-2 border border-blue-400 text-blue-500 font-semibold border rounded"
>
<img src="/assets/previous.svg" class="w-12 h-12" alt="Previous" />
</button>

<button
:disabled="index <= 0"
@click="shuffle"
class="py-1 px-2 border border-blue-400 text-blue-500 font-semibold border rounded"
>
<img src="/assets/shuffle.svg" class="w-12 h-12" alt="Shuffle" />
</button>

<button
:disabled="index >= cards.length - 1"
@click="nextCard"
class="py-1 px-2 border border-blue-400 text-blue-500 font-semibold rounded"
>
<img src="/assets/next.svg" class="w-12 h-12" alt="Next" />
</button>
</div>
</div>
</div>

<div v-if="lang" class="pt-8 flex justify-center items-center gap-4">
<button
class="bg-white shadow-lg rounded-full p-4 leading-4"
@click="resetList()"
>
<span class="material-icons" :class="loading && 'animate-spin'">{{
loading ? "refresh" : "shuffle"
}}</span>
</button>
<button
class="bg-white shadow-lg rounded-full p-4 leading-4"
@click="prevCard()"
>
<span class="material-icons">undo</span>
</button>
<button
class="bg-white shadow-lg rounded-full p-4 leading-4"
@click="nextCard()"
>
<span class="material-icons">redo</span>
</button>

<button
class="bg-white shadow-lg rounded-full p-4 leading-4"
v-if="canSpeak"
@click="speak()"
>
<span class="material-icons">volume_mute</span>
</button>
</div>
<!-- end -->
</div>
</template>

<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref, unref } from "vue";
import { fetchPairs, getRandomPairs, Pair } from "./word-pairs";
import { onBeforeUnmount, onMounted, ref } from "vue";
import { fetchPairs, getRandomPairs } from "./word-pairs";
const card1 = ref<HTMLDivElement | null>(null);
const card2 = ref<HTMLDivElement | null>(null);
const pairs = ref<Pair[]>([]);
const loading = ref(false);
const canSpeak = ref(!!window.speechSynthesis);
const lang = ref("");
const canSpeak = ref(!!window.speechSynthesis);
const index = ref(0);
const animated = ref(true);
const showTranslation = ref(false);
const cards = ref([]);
const pairs = ref([]);
function shuffle() {
cards.value = getRandomPairs(pairs.value);
}
let flipped = false;
const useState = <T = any>(value: T) => {
const r = ref<T>(value);
const s = (v: T) => (r.value = v as any);
return [r, s] as const;
};
async function flip() {
if (flipped) {
return unflip();
}
await Promise.all([
card1.value?.animate({ opacity: "0" }, { duration: 200, fill: "forwards" })
.finished,
card2.value?.animate({ opacity: "1" }, { duration: 200, fill: "forwards" })
.finished,
]);
function flip() {
showTranslation.value = true;
}
flipped = true;
function unflip() {
showTranslation.value = false;
}
async function unflip() {
await Promise.all([
card1.value?.animate({ opacity: "1" }, { duration: 10, fill: "forwards" })
.finished,
card2.value?.animate({ opacity: "0" }, { duration: 10, fill: "forwards" })
.finished,
]);
function prevCard() {
animated.value = false;
unflip();
if (index.value > 0) {
index.value--;
} else {
index.value = cards.length - 1;
}
flipped = false;
setTimeout(() => (animated.value = true));
}
const [flashcards, setFlashCards] = useState<Pair[]>([]);
const [currentCard, setCurrentCard] = useState(0);
function nextCard() {
animated.value = false;
unflip();
const resetList = async () => {
setFlashCards(getRandomPairs(pairs.value));
await unflip();
};
if (index.value < cards.length - 1) {
index.value++;
} else {
index.value = 0;
}
const speak = async() => {
speechSynthesis.speak(Object.assign(new SpeechSynthesisUtterance(flashcards.value[currentCard.value].front), { lang: lang.value }))
setTimeout(() => (animated.value = true));
}
const nextCard = async () => {
await unflip();
setCurrentCard((unref(currentCard) + 1) % unref(flashcards).length);
};
const prevCard = async () => {
await unflip();
setCurrentCard(
(unref(currentCard) > 0
? unref(currentCard) - 1
: unref(flashcards).length) % unref(flashcards).length
const speak = async () => {
speechSynthesis.speak(
Object.assign(
new SpeechSynthesisUtterance(flashcards.value[currentCard.value].front),
{ lang: lang.value }
)
);
};
Expand All @@ -149,17 +151,20 @@ const handleKeyDown = (event) => {
if (event.key === "a" || event.key === "ArrowUp") {
unflip();
}
if (event.key === "r") {
shuffle();
}
};
onMounted(async () => {
window.addEventListener("keydown", handleKeyDown);
lang.value = new URL(location.href).searchParams.get("lang") || "";
if (lang.value) {
loading.value = true;
pairs.value = await fetchPairs(lang.value);
await resetList();
loading.value = false;
await shuffle();
}
});
Expand All @@ -169,18 +174,27 @@ onBeforeUnmount(() => {
</script>

<style scoped>
.card, .cards {
min-height: 15rem;
.in {
width: 100%;
top: 0%;
}
.card {
font-size: 2rem;
opacity: 1;
transition: opacity 0.25s linear;
backface-visibility: hidden;
.out {
width: 100%;
top: 100%;
}
.app {
.animated {
transition: top 0.1s ease-in-out 0.05s;
}
.bg-primary {
background-color: #e91e63;
}
.bg-secondary {
background-color: #3f51b5;
color: white;
}
</style>

0 comments on commit 330b96e

Please sign in to comment.