Bug and animations fixes
This commit is contained in:
parent
cc86729dbc
commit
d31e17e3be
|
@ -21,7 +21,7 @@
|
|||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue'
|
||||
|
||||
import { darkMode, isDarkModeDefault } from '@/composables/settings'
|
||||
import { darkMode } from '@/composables/settings'
|
||||
|
||||
import AppHeader from './components/AppHeader.vue'
|
||||
import SideMenu from './components/SideMenu.vue'
|
||||
|
@ -29,6 +29,6 @@ import SideMenu from './components/SideMenu.vue'
|
|||
onMounted(() => {
|
||||
darkMode.value = localStorage.getItem('n0_dark')
|
||||
? localStorage.getItem('n0_dark') === 'true'
|
||||
: isDarkModeDefault()
|
||||
: true
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -141,7 +141,7 @@ export function isSolvable(result: number, plaquettes: number[]): boolean {
|
|||
}
|
||||
|
||||
if (histories.length) {
|
||||
// printHistory(histories[0])
|
||||
printHistory(histories[0])
|
||||
}
|
||||
return found
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</h1>
|
||||
<button @click="isSideMenuVisible = true">
|
||||
<IconMenu
|
||||
class="text-xl hover:text-cyan-500 dark:text-stone-400 transition-opacity" />
|
||||
class="text-xl btn" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -2,63 +2,61 @@
|
|||
<div class="relative font-mono text-xl text-center">
|
||||
<TransitionGroup name="slide_up">
|
||||
<div
|
||||
class=""
|
||||
class="inline-block"
|
||||
v-for="(op, i) in operations"
|
||||
:style="{
|
||||
transitionDelay: `${transDelay * (operations.length - i)}ms`,
|
||||
}"
|
||||
:key="i">
|
||||
<div class="inline-block">
|
||||
<!-- OPERATION LINE -->
|
||||
<div
|
||||
class="flex items-center pb-1 border-b border-stone-600 border-dashed md:py-2"
|
||||
:class="{ 'text-red-500': isOperationInvalid(op) }">
|
||||
<!-- LEFT OPERAND -->
|
||||
<div class="w-[2.5em] transition-all">
|
||||
{{ op.left?.value ?? ' ' }}
|
||||
</div>
|
||||
|
||||
<!-- OPERATOR -->
|
||||
<div class="flex justify-center w-[2.5em] transition-all">
|
||||
<Component
|
||||
class="text-lg text-stone-400"
|
||||
v-if="op.operator"
|
||||
:is="operatorIcons[op.operator]" />
|
||||
<span v-else> </span>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT OPERAND -->
|
||||
<div class="w-[2.5em] transition-all">
|
||||
{{ op.right?.value ?? ' ' }}
|
||||
</div>
|
||||
|
||||
<!-- EQUALS -->
|
||||
<div class="mx-4 text-stone-400 border-none">
|
||||
<IconEquals />
|
||||
</div>
|
||||
|
||||
<!-- RESULT -->
|
||||
<div class="w-[3.2em] transition-all">
|
||||
<IconSad v-if="isOperationInvalid(op)" />
|
||||
<span
|
||||
:class="{ 'text-cyan-500': op.result.value === result }"
|
||||
v-else-if="op.result">
|
||||
{{ op.result?.value }}
|
||||
</span>
|
||||
<span v-else>?</span>
|
||||
</div>
|
||||
|
||||
<!-- UNDO -->
|
||||
<button
|
||||
class="ml-4"
|
||||
@click="undoOperation(i)">
|
||||
<IconUndo
|
||||
class="text-stone-600 hover:text-cyan-500"
|
||||
:class="{
|
||||
invisible: !canOperationBeDeleted(op),
|
||||
}" />
|
||||
</button>
|
||||
<!-- OPERATION LINE -->
|
||||
<div
|
||||
class="flex items-center pb-1 border-b border-stone-600 border-dashed md:py-2"
|
||||
:class="{ 'text-red-500': isOperationInvalid(op) }">
|
||||
<!-- LEFT OPERAND -->
|
||||
<div class="w-[2.5em] transition-all">
|
||||
{{ op.left?.value ?? ' ' }}
|
||||
</div>
|
||||
|
||||
<!-- OPERATOR -->
|
||||
<div class="flex justify-center w-[2.5em] transition-all">
|
||||
<Component
|
||||
class="text-lg text-stone-400"
|
||||
v-if="op.operator"
|
||||
:is="operatorIcons[op.operator]" />
|
||||
<span v-else> </span>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT OPERAND -->
|
||||
<div class="w-[2.5em] transition-all">
|
||||
{{ op.right?.value ?? ' ' }}
|
||||
</div>
|
||||
|
||||
<!-- EQUALS -->
|
||||
<div class="mx-4 text-stone-400 border-none">
|
||||
<IconEquals />
|
||||
</div>
|
||||
|
||||
<!-- RESULT -->
|
||||
<div class="w-[3.2em] transition-all">
|
||||
<IconSad v-if="isOperationInvalid(op)" />
|
||||
<span
|
||||
:class="{ 'text-cyan-500': op.result.value === result }"
|
||||
v-else-if="op.result">
|
||||
{{ op.result?.value }}
|
||||
</span>
|
||||
<span v-else>?</span>
|
||||
</div>
|
||||
|
||||
<!-- UNDO -->
|
||||
<button
|
||||
class="ml-4"
|
||||
@click="undoOperation(i)">
|
||||
<IconUndo
|
||||
class="text-stone-600 hover:text-cyan-500"
|
||||
:class="{
|
||||
invisible: !canOperationBeDeleted(op),
|
||||
}" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
|
@ -93,9 +91,7 @@ function undoOperation(index: number): void {
|
|||
let popped: Operation
|
||||
if (i === index) {
|
||||
popped = operations[index]
|
||||
setTimeout(() => {
|
||||
operations[index] = getEmptyOperation()
|
||||
}, transDelay * l)
|
||||
operations[index] = getEmptyOperation()
|
||||
}
|
||||
else {
|
||||
popped = operations.pop()!
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class="flex gap-2 justify-center my-4">
|
||||
<PlaquetteBox
|
||||
is="button"
|
||||
class="aspect-square w-[1.5em] text-2xl border btn"
|
||||
class="aspect-square w-[1.5em] text-2xl border btn-border"
|
||||
@click="emit('click', item)"
|
||||
v-for="(item, i) in operators"
|
||||
:key="i">
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
@click="click(item)"
|
||||
class="h-11 border"
|
||||
:class="{
|
||||
'dark:text-stone-600 dark:border-stone-600 text-stone-500 border-stone-400': !item.free,
|
||||
'dark:text-stone-600 dark:border-stone-600 text-stone-500 border-stone-400':
|
||||
!item.free,
|
||||
'hover:border-cyan-500': item.free,
|
||||
}"
|
||||
:style="{ transitionDelay: `${initDelay * i}ms` }"
|
||||
|
@ -40,4 +41,6 @@ function click(item: Plaquette): void {
|
|||
initDelay.value = 0
|
||||
emit('clickNumber', item)
|
||||
}
|
||||
|
||||
defineExpose({ initDelay })
|
||||
</script>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<div class="h-12">
|
||||
<button @click="isSideMenuVisible = false">
|
||||
<IconClose
|
||||
class="absolute top-0 right-1 h-12 text-xl hover:text-cyan-500" />
|
||||
class="absolute top-0 right-1 h-12 text-xl btn" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
|
|
|
@ -36,3 +36,8 @@ export function numberOfGamesSinceStart(): number {
|
|||
)
|
||||
return differenceInDays(startDate, getCurrentDate())
|
||||
}
|
||||
|
||||
export function clearOperationsList(): void {
|
||||
operations.splice(0)
|
||||
operations.push(getEmptyOperation())
|
||||
}
|
||||
|
|
|
@ -59,17 +59,17 @@
|
|||
.route-move,
|
||||
.route-enter-active,
|
||||
.route-leave-active {
|
||||
transition: all .8s ease;
|
||||
transition: all 1s ease;
|
||||
}
|
||||
|
||||
.route-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(100vw);
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.route-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(-100vw);
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.route-leave-active {
|
||||
|
@ -82,17 +82,17 @@
|
|||
.route_back-move,
|
||||
.route_back-enter-active,
|
||||
.route_back-leave-active {
|
||||
transition: all .8s ease;
|
||||
transition: all 1s ease;
|
||||
}
|
||||
|
||||
.route_back-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(-100vw);
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.route_back-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(100vw);
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.route_back-leave-active {
|
||||
|
@ -128,9 +128,11 @@
|
|||
.slide_up-enter-from,
|
||||
.slide_up-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
transform: translateY(60px);
|
||||
}
|
||||
|
||||
/* ZERO HEIGHT */
|
||||
|
||||
.zero_height-enter-active,
|
||||
.zero_height-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
|
@ -144,10 +146,20 @@
|
|||
|
||||
@layer utils {
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply hover:text-cyan-500 dark:text-stone-400 dark:hover:text-cyan-500;
|
||||
@apply transition-colors duration-200;
|
||||
}
|
||||
.btn-border {
|
||||
@apply w-fit p-2;
|
||||
@apply rounded border border-stone-600 transition-opacity;
|
||||
@apply hover:border-cyan-500 hover:text-cyan-500;
|
||||
@apply transition-colors duration-200;
|
||||
}
|
||||
.btn-disabled {
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
|
@ -2,12 +2,13 @@
|
|||
"playAgain": "New number",
|
||||
"easy": "easy",
|
||||
"dailyGame": "Daily challenge\n",
|
||||
"endGame.failureLabel": "Too bad...<br>Another game?",
|
||||
"endGame.failureLabel": "Almost there...",
|
||||
"endGame.victoryLabel": "Well done!",
|
||||
"hard": "hard",
|
||||
"impossible": "☠",
|
||||
"medium": "medium",
|
||||
"randomGame": "Random number",
|
||||
"startGame": "Start",
|
||||
"dailyDescription": "The daily challenge changes every day at midnight, and is common to all players."
|
||||
"dailyDescription": "The daily challenge changes every day at midnight, and is common to all players.",
|
||||
"share": "Share"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"playAgain": "Nouveau nombre",
|
||||
"endGame.victoryLabel": "Bien joué !",
|
||||
"endGame.failureLabel": "Dommage...<br>Une autre partie ?",
|
||||
"endGame.failureLabel": "Presque...",
|
||||
"startGame": "Démarrer",
|
||||
"easy": "facile",
|
||||
"medium": "moyen",
|
||||
|
@ -11,5 +11,6 @@
|
|||
"randomGame": "Nombre au hasard",
|
||||
"gameDescription": "Combinez les nombres imposés afin d'atteindre le résultat, ou de vous en approcher le plus possible.",
|
||||
"dailyDescription": "Le défi quotidien change chaque jour à minuit, et est commun à tous les joueurs.",
|
||||
"randomDescription": "Une partie au hasard, pour le plaisir."
|
||||
"randomDescription": "Une partie au hasard, pour le plaisir.",
|
||||
"share": "Partager"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
<button
|
||||
@click="startGame"
|
||||
class="py-2 px-4 mt-4 btn">
|
||||
class="py-2 px-4 mt-4 btn-border">
|
||||
{{ t('startGame') }}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -25,6 +25,7 @@
|
|||
|
||||
<!-- PLAQUETTES -->
|
||||
<PlaquettesList
|
||||
ref="cmpPlaquettes"
|
||||
:plaquettes="shownPlaquettes"
|
||||
@click-number="selectNumber" />
|
||||
|
||||
|
@ -34,8 +35,7 @@
|
|||
<OperatorsList @click="selectOperator" />
|
||||
|
||||
<!-- Divider -->
|
||||
<div
|
||||
class="my-4 mx-auto max-w-sm border-b border-cyan-500/50" />
|
||||
<div class="my-4 mx-auto max-w-sm border-b border-cyan-500/50" />
|
||||
|
||||
<!-- List of Operations -->
|
||||
<OperationsList v-show="gameIsRunning" />
|
||||
|
@ -53,10 +53,16 @@
|
|||
v-else
|
||||
v-html="t('endGame.failureLabel')" />
|
||||
<button
|
||||
class="btn"
|
||||
v-if="!isDaily"
|
||||
class="btn-border"
|
||||
@click="reboot">
|
||||
{{ t('playAgain') }}
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="btn-border">
|
||||
{{ t('share') }}
|
||||
</button>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
|
@ -78,6 +84,7 @@ import OperationsList from '@/components/OperationsList.vue'
|
|||
import OperatorsList from '@/components/OperatorsList.vue'
|
||||
import PlaquettesList from '@/components/PlaquettesList.vue'
|
||||
import {
|
||||
clearOperationsList,
|
||||
currentOperation,
|
||||
gameIsRunning,
|
||||
gameState,
|
||||
|
@ -98,6 +105,7 @@ const { t } = useI18n() // call `useI18n`, and spread `t` from `useI18n` return
|
|||
*/
|
||||
|
||||
const difficultyLevel = ref<1 | 2>(1)
|
||||
const cmpPlaquettes = ref<InstanceType<typeof PlaquettesList>>()
|
||||
|
||||
const isDaily = computed(() => useRoute()?.name === 'daily')
|
||||
const shownPlaquettes = computed(() =>
|
||||
|
@ -128,6 +136,13 @@ watch(
|
|||
function startGame(): void {
|
||||
gameState.value = GameState.Loading
|
||||
setTimeout(() => (gameState.value = GameState.Playing), 500)
|
||||
setTimeout(() => {
|
||||
if (cmpPlaquettes.value) {
|
||||
// Remove the animation delay for the plaquettes,
|
||||
// or else the mouse hover effect is delayed until the first click
|
||||
cmpPlaquettes.value.initDelay = 0
|
||||
}
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
function selectNumber(p: Plaquette): void {
|
||||
|
@ -175,8 +190,7 @@ function reboot(): void {
|
|||
})()
|
||||
// result.value = 29
|
||||
// Reset Operations list
|
||||
operations.splice(0)
|
||||
operations.push(getEmptyOperation())
|
||||
clearOperationsList()
|
||||
|
||||
// Generate result and plaquettes
|
||||
plaquettes.value = []
|
||||
|
@ -221,6 +235,8 @@ onMounted(() => {
|
|||
reboot()
|
||||
gameState.value = GameState.Waiting
|
||||
}, 800)
|
||||
// But make sure the operations list is empty asap
|
||||
clearOperationsList()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<div class="flex flex-col gap-4 items-center">
|
||||
<RouterLink
|
||||
to="/daily"
|
||||
class="text-2xl btn">
|
||||
class="text-2xl btn-border">
|
||||
{{ t('dailyGame') }}
|
||||
</RouterLink>
|
||||
<span v-html="t('dailyDescription')" />
|
||||
|
@ -19,7 +19,7 @@
|
|||
<div class="flex flex-col gap-4 items-center">
|
||||
<RouterLink
|
||||
to="/random"
|
||||
class="text-2xl btn">
|
||||
class="text-2xl btn-border">
|
||||
{{ t('randomGame') }}
|
||||
</RouterLink>
|
||||
{{ t('randomDescription') }}
|
||||
|
|
Loading…
Reference in New Issue
Block a user