Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 39 additions & 95 deletions resources/js/components/navigation/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
defineComponent,
nextTick,
onBeforeUnmount,
onMounted,
ref,
toRef,
watch,
Expand Down Expand Up @@ -861,104 +860,49 @@ export const SiteBottomNav = defineComponent({
const resolvedHref = (item: { href: string }) =>
item.href === '/' ? homeHref.value : item.href;

const isKeyboardOpen = ref(false);

const checkFocus = () => {
const activeEl = document.activeElement;
const isInputFocused =
activeEl instanceof HTMLInputElement ||
activeEl instanceof HTMLTextAreaElement ||
activeEl?.getAttribute('contenteditable') === 'true';

let isViewportShrunk = false;

if (typeof window !== 'undefined' && window.visualViewport) {
isViewportShrunk =
window.innerHeight - window.visualViewport.height > 150;
}

isKeyboardOpen.value = isInputFocused || isViewportShrunk;
};

onMounted(() => {
if (typeof window === 'undefined') {
return;
}

window.addEventListener('focusin', checkFocus);
window.addEventListener('focusout', () => {
setTimeout(checkFocus, 100);
});

if (window.visualViewport) {
window.visualViewport.addEventListener('resize', checkFocus);
window.visualViewport.addEventListener('scroll', checkFocus);
}
});

onBeforeUnmount(() => {
if (typeof window === 'undefined') {
return;
}

window.removeEventListener('focusin', checkFocus);
window.removeEventListener('focusout', checkFocus);

if (window.visualViewport) {
window.visualViewport.removeEventListener('resize', checkFocus);
window.visualViewport.removeEventListener('scroll', checkFocus);
}
});

return () => {
if (isKeyboardOpen.value) {
return null;
}

return (
<nav
class="fixed inset-x-0 bottom-0 z-40 w-full border-t border-slate-200/70 bg-white/95 pb-[env(safe-area-inset-bottom)] backdrop-blur-xl dark:border-slate-800 dark:bg-slate-900/95"
aria-label="Bottom navigation"
>
<div class="mx-auto flex w-full max-w-md items-center justify-around px-1 py-2">
{bottomNavItems.value.map((item) => (
<Link
key={item.href}
href={resolvedHref(item)}
return () => (
<nav
class="fixed inset-x-0 bottom-0 z-40 w-full border-t border-slate-200/70 bg-white/95 pb-[env(safe-area-inset-bottom)] backdrop-blur-xl dark:border-slate-800 dark:bg-slate-900/95"
aria-label="Bottom navigation"
>
<div class="mx-auto flex w-full max-w-md items-center justify-around px-1 py-2">
{bottomNavItems.value.map((item) => (
<Link
key={item.href}
href={resolvedHref(item)}
class={[
'flex min-w-0 flex-1 flex-col items-center gap-1 rounded-xl px-2 py-2 transition-all duration-150 ease-out',
isActive(item.href, item.match)
? 'text-slate-900 dark:text-white'
: 'text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200',
]}
>
<MaterialIcon
name={item.icon}
size={26}
filled={isActive(item.href, item.match)}
weight={400}
class={`shrink-0 transition-transform duration-150 ${
isActive(item.href, item.match)
? 'scale-[1.02] text-slate-900 dark:text-white'
: 'text-slate-500 dark:text-slate-400'
}`}
/>
<span
class={[
'flex min-w-0 flex-1 flex-col items-center gap-1 rounded-xl px-2 py-2 transition-all duration-150 ease-out',
'text-[10px] leading-none tracking-wide antialiased',
isActive(item.href, item.match)
? 'text-slate-900 dark:text-white'
: 'text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200',
? 'font-bold'
: 'font-medium',
]}
>
<MaterialIcon
name={item.icon}
size={26}
filled={isActive(item.href, item.match)}
weight={400}
class={`shrink-0 transition-transform duration-150 ${
isActive(item.href, item.match)
? 'scale-[1.02] text-slate-900 dark:text-white'
: 'text-slate-500 dark:text-slate-400'
}`}
/>
<span
class={[
'text-[10px] leading-none tracking-wide antialiased',
isActive(item.href, item.match)
? 'font-bold'
: 'font-medium',
]}
>
{item.label}
</span>
</Link>
))}
</div>
</nav>
);
};
{item.label}
</span>
</Link>
))}
</div>
</nav>
);
},
});

Expand Down
15 changes: 0 additions & 15 deletions resources/js/pages/Chat/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -476,20 +476,6 @@ const scrollToBottom = (smooth = true) => {
});
};

const handleInputFocus = () => {
scrollToBottom(true);

if (typeof window !== 'undefined' && window.visualViewport) {
setTimeout(() => {
messageInputRef.value?.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
});
scrollToBottom(true);
}, 150);
}
};

let lastFetchTimestamp = 0;
const fetchLatestMessages = async (force = false) => {
const now = Date.now();
Expand Down Expand Up @@ -2144,7 +2130,6 @@ onUnmounted(() => {
"
:maxlength="maxLengthLimit"
@input="handleInput"
@focus="handleInputFocus"
@click="checkMentionTrigger"
@keyup="checkMentionTrigger"
@keydown="handleInputKeydown"
Expand Down
2 changes: 1 addition & 1 deletion resources/views/app.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
} catch (_) {}
})();
</script>
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, interactive-widget=overlays-content">
<meta name="csrf-token" content="{{ csrf_token() }}">
<meta name="color-scheme" content="light dark">
{{-- Browser chrome: top status bar + bottom nav bar (Chrome / Edge / Samsung Internet) --}}
Expand Down