feat: UI polish, chapter select improvements, save system enhancements, roadmap update
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, nextTick, computed } from 'vue'
|
||||
import type { Choice } from '@engine/types'
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -11,6 +12,9 @@ const emit = defineEmits<{
|
||||
choose: [index: number]
|
||||
}>()
|
||||
|
||||
const focusIndex = ref(0)
|
||||
const btnRefs = ref<(HTMLButtonElement | null)[]>([])
|
||||
|
||||
function timerPercent(): number {
|
||||
if (props.timerTotal <= 0) return 0
|
||||
return (props.timerRemaining / props.timerTotal) * 100
|
||||
@@ -20,6 +24,32 @@ function timerClass(): string {
|
||||
if (props.timerRemaining <= 3) return 'danger'
|
||||
return ''
|
||||
}
|
||||
|
||||
function setRef(el: HTMLButtonElement | null, index: number) {
|
||||
btnRefs.value[index] = el
|
||||
}
|
||||
|
||||
watch(() => props.choices.length, async (len) => {
|
||||
if (len > 0) {
|
||||
focusIndex.value = 0
|
||||
await nextTick()
|
||||
btnRefs.value[0]?.focus()
|
||||
}
|
||||
})
|
||||
|
||||
function onKeydown(e: KeyboardEvent, index: number) {
|
||||
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
|
||||
e.preventDefault()
|
||||
const dir = e.key === 'ArrowDown' ? 1 : -1
|
||||
const next = Math.max(0, Math.min(props.choices.length - 1, index + dir))
|
||||
focusIndex.value = next
|
||||
btnRefs.value[next]?.focus()
|
||||
}
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault()
|
||||
emit('choose', index)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -37,8 +67,11 @@ function timerClass(): string {
|
||||
<button
|
||||
v-for="(choice, index) in choices"
|
||||
:key="index"
|
||||
:ref="(el: any) => setRef(el, index)"
|
||||
class="choice-btn"
|
||||
tabindex="0"
|
||||
@click="emit('choose', index)"
|
||||
@keydown="onKeydown($event, index)"
|
||||
>
|
||||
{{ choice.text }}
|
||||
</button>
|
||||
@@ -110,10 +143,17 @@ function timerClass(): string {
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s, border-color 0.2s;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.choice-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
border-color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.choice-btn:focus-visible {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
border-color: rgba(255, 255, 255, 0.6);
|
||||
box-shadow: 0 0 8px rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user