57 lines
1.6 KiB
TypeScript
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))
|
|
}
|
|
}
|