diff --git a/src/lib/components/layout.svelte b/src/lib/components/layout.svelte index 7beea24f..1e78bd6e 100644 --- a/src/lib/components/layout.svelte +++ b/src/lib/components/layout.svelte @@ -4,6 +4,7 @@
+
{#if $$slots.header}
@@ -24,8 +25,8 @@ display: flex; flex-direction: column; position: relative; - height: 100vh; - height: 100dvh; + height: 100vvh; + height: var(--100vvh); } .base { @@ -54,4 +55,8 @@ position: sticky; inset: auto 0 0 0; } + .keyboard-fix { + height: var(--offset-h, 0); + width: 100%; + } diff --git a/src/lib/utils/viewport-fix.js b/src/lib/utils/viewport-fix.js new file mode 100644 index 00000000..14e54cc2 --- /dev/null +++ b/src/lib/utils/viewport-fix.js @@ -0,0 +1,61 @@ +export class VVP { + constructor() { + console.debug('VVP fix') + this.enabled = typeof window === 'object' && typeof window.visualViewport === 'object' + if (!this.enabled) { + console.error('Visual Viewport is not available in this browser.') + } + this.vvp = { w: 0, h: 0 } + this.vp = { w: 0, h: 0 } + this.create_style_element() + this.refresh() + + window.visualViewport?.addEventListener('resize', this.refresh.bind(this)) + } + + get style_element() { + return document.getElementById('viewport_fix_variables') + } + + get calculate_viewport() { + return new Promise((resolve, reject) => { + if (!this.enabled) { + return reject('Could not calculate window.visualViewport') + } + if (!window.visualViewport) { + return reject('Could not calculate window.visualViewport') + } + this.vvp.w = window.visualViewport.width + this.vvp.h = window.visualViewport.height + + this.vp.w = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0) + this.vp.h = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0) + return resolve(undefined) + }) + } + + refresh() { + return this.calculate_viewport.then(() => this.set_viewport()).catch((e) => console.error(e)) + } + + create_style_element() { + const style_tag = document.createElement('style') + style_tag.id = 'viewport_fix_variables' + return document.head.prepend(style_tag) + } + + set_viewport() { + if (!this.style_element) { + return + } + return (this.style_element.innerHTML = ` + :root { + --100vvw: ${this.vvp.w}px; + --100vvh: ${this.vvp.h}px; + + --offset-w: ${this.vp.w - this.vvp.w}px; + --offset-h: ${this.vp.h - this.vvp.h}px; + } + `) + } +} diff --git a/src/routes/chat/[id]/+page.svelte b/src/routes/chat/[id]/+page.svelte index 4b6c20fb..1c64a71d 100644 --- a/src/routes/chat/[id]/+page.svelte +++ b/src/routes/chat/[id]/+page.svelte @@ -33,6 +33,7 @@ } from '$lib/utils/format' import ChatDateBadge from '$lib/components/chat-date-badge.svelte' import { userDisplayName } from '$lib/utils/user' + import { VVP } from '$lib/utils/viewport-fix' let div: HTMLElement let autoscroll = true @@ -52,6 +53,8 @@ }) onMount(() => { + new VVP() + if (browser && div) { div.scrollTo({ top: div.scrollHeight,