Hopefully more robust header autoclose logic to avoid OSD keyboard causing a just-focused search bar getting hidden.
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
<template>
|
||||
<div class="header-controls-wrapper">
|
||||
<Transition name="header-controls" appear>
|
||||
<div v-if="isVisible" class="header-controls">
|
||||
<div
|
||||
v-if="isVisible"
|
||||
ref="headerControlsRef"
|
||||
class="header-controls"
|
||||
@focusin="handleFocusIn"
|
||||
@focusout="handleFocusOut"
|
||||
>
|
||||
<div class="search-with-spacer">
|
||||
<!-- Shrinkable spacer to align search with week label column; smoothly shrinks as needed -->
|
||||
<div class="pre-search-spacer" aria-hidden="true"></div>
|
||||
@@ -69,7 +75,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref, onMounted, onBeforeUnmount, defineExpose, nextTick } from 'vue'
|
||||
import { computed, ref, onMounted, onBeforeUnmount, defineExpose, nextTick, watch } from 'vue'
|
||||
import { useCalendarStore } from '@/stores/CalendarStore'
|
||||
import { formatTodayString } from '@/utils/date'
|
||||
import EventSearch from '@/components/Search.vue'
|
||||
@@ -122,18 +128,19 @@ function goToToday() {
|
||||
|
||||
// Screen size detection and visibility toggle
|
||||
const isVisible = ref(false)
|
||||
// Track if we auto-opened due to a find (Ctrl/Cmd+F)
|
||||
const autoOpenedForSearch = ref(false)
|
||||
const headerControlsRef = ref(null)
|
||||
const hasFocusWithin = ref(false)
|
||||
|
||||
function checkScreenSize() {
|
||||
const isSmallScreen = window.innerHeight < 600
|
||||
// Default to open on large screens, closed on small screens
|
||||
isVisible.value = !isSmallScreen
|
||||
isVisible.value = !isSmallScreen || hasFocusWithin.value
|
||||
}
|
||||
|
||||
function toggleVisibility() {
|
||||
isVisible.value = !isVisible.value
|
||||
if (!isVisible.value) autoOpenedForSearch.value = false
|
||||
if (!isVisible.value) {
|
||||
hasFocusWithin.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Settings dialog integration
|
||||
@@ -150,6 +157,25 @@ function openSettings() {
|
||||
|
||||
// Search component ref exposure
|
||||
const eventSearchRef = ref(null)
|
||||
|
||||
function handleFocusIn() {
|
||||
hasFocusWithin.value = true
|
||||
if (!isVisible.value) isVisible.value = true
|
||||
}
|
||||
|
||||
function handleFocusOut(event) {
|
||||
const container = headerControlsRef.value
|
||||
if (!container) {
|
||||
hasFocusWithin.value = false
|
||||
return
|
||||
}
|
||||
const nextTarget = event.relatedTarget ?? document.activeElement
|
||||
if (nextTarget && container.contains(nextTarget)) return
|
||||
hasFocusWithin.value = false
|
||||
if (window.innerHeight < 600) {
|
||||
checkScreenSize()
|
||||
}
|
||||
}
|
||||
function focusSearch(selectAll = true) {
|
||||
eventSearchRef.value?.focusSearch(selectAll)
|
||||
}
|
||||
@@ -167,24 +193,21 @@ function handleGlobalFind(e) {
|
||||
e.preventDefault()
|
||||
if (!isVisible.value) {
|
||||
isVisible.value = true
|
||||
autoOpenedForSearch.value = true
|
||||
} else {
|
||||
autoOpenedForSearch.value = false
|
||||
}
|
||||
// Defer focus until after transition renders input
|
||||
nextTick(() => requestAnimationFrame(() => focusSearch(true)))
|
||||
nextTick(() => focusSearch(true))
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearchActivate(r) {
|
||||
emit('search-activate', r)
|
||||
// Auto close only if we auto-opened for search shortcut
|
||||
if (autoOpenedForSearch.value) {
|
||||
isVisible.value = false
|
||||
}
|
||||
autoOpenedForSearch.value = false
|
||||
}
|
||||
|
||||
watch(isVisible, (visible) => {
|
||||
if (visible) nextTick(() => focusSearch(true))
|
||||
else hasFocusWithin.value = false
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
checkScreenSize()
|
||||
window.addEventListener('resize', checkScreenSize)
|
||||
|
||||
Reference in New Issue
Block a user