From e0ae71f33a69e5b8422fe09afc4a562db54077a2 Mon Sep 17 00:00:00 2001 From: Alessio Cappa <104093777+alessiocappa@users.noreply.github.com> Date: Sun, 15 Feb 2026 13:23:19 +0100 Subject: [PATCH] fix: Viewport issue in PWA (#995) * fix: move safe-area padding from body/HTML to navbars. Add script to compute app height dynamically. * fix: Initialize sankey tooltip with top-0 to avoid overflow * fix: add fallback to HTML height * fix: properly set bottom spacing and use position fixed for bottom navbar * fix: move viewport controller initialization --- app/helpers/settings_helper.rb | 2 +- .../controllers/sankey_chart_controller.js | 2 +- .../controllers/viewport_controller.js | 46 +++++++++++++++++++ app/views/layouts/application.html.erb | 7 +-- app/views/layouts/settings.html.erb | 2 +- app/views/layouts/shared/_htmldoc.html.erb | 6 +-- 6 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 app/javascript/controllers/viewport_controller.js diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb index f4de696c2..78364ab7b 100644 --- a/app/helpers/settings_helper.rb +++ b/app/helpers/settings_helper.rb @@ -63,7 +63,7 @@ module SettingsHelper previous_setting = adjacent_setting(request.path, -1) next_setting = adjacent_setting(request.path, 1) - content_tag :div, class: "md:hidden flex flex-col gap-4" do + content_tag :div, class: "md:hidden flex flex-col gap-4 pb-[env(safe-area-inset-bottom)]" do concat(previous_setting) concat(next_setting) end diff --git a/app/javascript/controllers/sankey_chart_controller.js b/app/javascript/controllers/sankey_chart_controller.js index 5a1b9bb44..15cbf374b 100644 --- a/app/javascript/controllers/sankey_chart_controller.js +++ b/app/javascript/controllers/sankey_chart_controller.js @@ -349,7 +349,7 @@ export default class extends Controller { const dialog = this.element.closest("dialog"); this.tooltip = d3.select(dialog || document.body) .append("div") - .attr("class", "bg-gray-700 text-white text-sm p-2 rounded pointer-events-none absolute z-50") + .attr("class", "bg-gray-700 text-white text-sm p-2 rounded pointer-events-none absolute z-50 top-0") .style("opacity", 0) .style("pointer-events", "none"); } diff --git a/app/javascript/controllers/viewport_controller.js b/app/javascript/controllers/viewport_controller.js new file mode 100644 index 000000000..c8b9100f4 --- /dev/null +++ b/app/javascript/controllers/viewport_controller.js @@ -0,0 +1,46 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static targets = ["content", "bottomNav"] + + connect() { + this.updateViewport() + this.updateBottomSpacing() + + window.addEventListener("resize", this.handleResize) + window.addEventListener("orientationchange", this.handleResize) + + if (this.hasBottomNavTarget) { + this.resizeObserver = new ResizeObserver(() => { + this.updateBottomSpacing() + }) + this.resizeObserver.observe(this.bottomNavTarget) + } + } + + disconnect() { + window.removeEventListener("resize", this.handleResize) + window.removeEventListener("orientationchange", this.handleResize) + + if (this.resizeObserver) { + this.resizeObserver.disconnect() + } + } + + handleResize = () => { + this.updateViewport() + this.updateBottomSpacing() + } + + updateViewport() { + const height = window.innerHeight + document.documentElement.style.setProperty("--app-height", `${height}px`) + } + + updateBottomSpacing() { + if (!this.hasBottomNavTarget || !this.hasContentTarget) return + + const navHeight = this.bottomNavTarget.offsetHeight + this.contentTarget.style.paddingBottom = `${navHeight}px` + } +} diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index cd79700e3..32b76a0ae 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -44,7 +44,7 @@ end %> <%# MOBILE - Top nav %> -