feat: UI polish, chapter select improvements, save system enhancements, roadmap update

This commit is contained in:
2026-06-09 15:19:53 +08:00
parent 2748b2c16f
commit 72e442f2c3
7 changed files with 206 additions and 18 deletions

View File

@@ -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>