search/app/composables/useDetailHistory.ts

57 lines
1.6 KiB
TypeScript

import { watch, onMounted, onBeforeUnmount, toValue } from 'vue'
import type { Ref, WritableComputedRef } from 'vue'
/**
* Intercepts the mobile hardware back button when a detail panel is open,
* closing the panel instead of navigating away from the page.
*
* Strategy: when the panel opens on mobile, push a history entry with the
* same URL. When the user presses back, popstate fires and we close the
* panel. When the user closes via the X button, we programmatically pop
* the entry we pushed so the stack stays clean.
*/
export function useDetailHistory(
isOpen: WritableComputedRef<boolean> | Ref<boolean>,
isMobile: Ref<boolean>
) {
let pushed = false
let fromPopState = false
let suppressNext = false
function handlePopState() {
if (suppressNext) {
suppressNext = false
return
}
if (pushed && toValue(isMobile)) {
fromPopState = true
pushed = false
isOpen.value = false
}
}
watch(isOpen, (open, wasOpen) => {
if (!toValue(isMobile)) return
if (open && !wasOpen) {
history.pushState({ detailPanel: true }, '')
pushed = true
} else if (!open && wasOpen) {
if (fromPopState) {
// Back button closed the panel — state already popped by the browser.
fromPopState = false
} else if (pushed) {
// X button (or any programmatic close) — pop the entry we pushed.
pushed = false
suppressNext = true
history.go(-1)
}
}
})
if (import.meta.client) {
onMounted(() => window.addEventListener('popstate', handlePopState))
onBeforeUnmount(() => window.removeEventListener('popstate', handlePopState))
}
}