fix: hoist prompt toast to App.vue so it survives ChoicePanel unmount after selection
This commit is contained in:
35
src/App.vue
35
src/App.vue
@@ -26,6 +26,8 @@ const showChapterSelect = ref(false)
|
|||||||
const hasAutoSave = ref(false)
|
const hasAutoSave = ref(false)
|
||||||
const currentSpeed = ref(1)
|
const currentSpeed = ref(1)
|
||||||
const canSkip = ref(false)
|
const canSkip = ref(false)
|
||||||
|
const promptToast = ref('')
|
||||||
|
const showPromptToast = ref(false)
|
||||||
|
|
||||||
const { loadGame, start, resumeAutoSave, makeChoice, clickHotspot, startChapter,
|
const { loadGame, start, resumeAutoSave, makeChoice, clickHotspot, startChapter,
|
||||||
skipScene, setSpeed, getSpeed, isSceneWatched,
|
skipScene, setSpeed, getSpeed, isSceneWatched,
|
||||||
@@ -57,6 +59,12 @@ function onChoose(index: number) {
|
|||||||
makeChoice(index)
|
makeChoice(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onPrompt(text: string) {
|
||||||
|
promptToast.value = text
|
||||||
|
showPromptToast.value = true
|
||||||
|
setTimeout(() => { showPromptToast.value = false }, 2500)
|
||||||
|
}
|
||||||
|
|
||||||
function toggleMenu() {
|
function toggleMenu() {
|
||||||
showMenu.value = !showMenu.value
|
showMenu.value = !showMenu.value
|
||||||
if (showMenu.value) {
|
if (showMenu.value) {
|
||||||
@@ -167,7 +175,11 @@ init()
|
|||||||
:timer-total="store.timerTotal"
|
:timer-total="store.timerTotal"
|
||||||
:timer-remaining="store.timerRemaining"
|
:timer-remaining="store.timerRemaining"
|
||||||
@choose="onChoose"
|
@choose="onChoose"
|
||||||
|
@prompt="onPrompt"
|
||||||
/>
|
/>
|
||||||
|
<Transition name="prompt-toast">
|
||||||
|
<div v-if="showPromptToast" class="prompt-toast">{{ promptToast }}</div>
|
||||||
|
</Transition>
|
||||||
<div v-if="started && !store.gameEnded" class="top-bar">
|
<div v-if="started && !store.gameEnded" class="top-bar">
|
||||||
<LangSwitch />
|
<LangSwitch />
|
||||||
<PlaybackBar
|
<PlaybackBar
|
||||||
@@ -356,4 +368,27 @@ html, body {
|
|||||||
.end-btn:hover {
|
.end-btn:hover {
|
||||||
background: rgba(255, 200, 100, 0.15);
|
background: rgba(255, 200, 100, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prompt-toast {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
padding: 16px 36px;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #ffc107;
|
||||||
|
background: rgba(0, 0, 0, 0.85);
|
||||||
|
border: 1px solid rgba(255, 193, 7, 0.4);
|
||||||
|
border-radius: 6px;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-toast-enter-active { transition: opacity 0.3s ease; }
|
||||||
|
.prompt-toast-leave-active { transition: opacity 0.8s ease; }
|
||||||
|
.prompt-toast-enter-from,
|
||||||
|
.prompt-toast-leave-to { opacity: 0; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -11,13 +11,12 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
choose: [index: number]
|
choose: [index: number]
|
||||||
|
prompt: [text: string]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const focusIndex = ref(0)
|
const focusIndex = ref(0)
|
||||||
const btnRefs = ref<(HTMLButtonElement | null)[]>([])
|
const btnRefs = ref<(HTMLButtonElement | null)[]>([])
|
||||||
const toastText = ref('')
|
|
||||||
const toastVisible = ref(false)
|
|
||||||
|
|
||||||
function timerPercent(): number {
|
function timerPercent(): number {
|
||||||
if (props.timerTotal <= 0) return 0
|
if (props.timerTotal <= 0) return 0
|
||||||
@@ -58,9 +57,7 @@ function onKeydown(e: KeyboardEvent, index: number) {
|
|||||||
function handleChoose(index: number) {
|
function handleChoose(index: number) {
|
||||||
const choice = props.choices[index]
|
const choice = props.choices[index]
|
||||||
if (choice?.prompt) {
|
if (choice?.prompt) {
|
||||||
toastText.value = choice.prompt
|
emit('prompt', choice.prompt)
|
||||||
toastVisible.value = true
|
|
||||||
setTimeout(() => { toastVisible.value = false }, 2200)
|
|
||||||
}
|
}
|
||||||
emit('choose', index)
|
emit('choose', index)
|
||||||
}
|
}
|
||||||
@@ -90,10 +87,6 @@ function handleChoose(index: number) {
|
|||||||
{{ t(choice.textKey || choice.text) }}
|
{{ t(choice.textKey || choice.text) }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Transition name="toast-fade">
|
|
||||||
<div class="prompt-toast" v-if="toastVisible">{{ toastText }}</div>
|
|
||||||
</Transition>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -191,26 +184,4 @@ function handleChoose(index: number) {
|
|||||||
border-color: rgba(255, 193, 7, 0.7);
|
border-color: rgba(255, 193, 7, 0.7);
|
||||||
box-shadow: 0 0 12px rgba(255, 193, 7, 0.3);
|
box-shadow: 0 0 12px rgba(255, 193, 7, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.prompt-toast {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
padding: 14px 32px;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #ffc107;
|
|
||||||
background: rgba(0, 0, 0, 0.85);
|
|
||||||
border: 1px solid rgba(255, 193, 7, 0.4);
|
|
||||||
border-radius: 6px;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast-fade-enter-active { transition: opacity 0.3s ease; }
|
|
||||||
.toast-fade-leave-active { transition: opacity 0.6s ease; }
|
|
||||||
.toast-fade-enter-from,
|
|
||||||
.toast-fade-leave-to { opacity: 0; }
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user