mirror of
https://github.com/we-promise/sure.git
synced 2026-04-07 14:31:25 +00:00
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
This commit is contained in:
@@ -63,7 +63,7 @@ module SettingsHelper
|
|||||||
previous_setting = adjacent_setting(request.path, -1)
|
previous_setting = adjacent_setting(request.path, -1)
|
||||||
next_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(previous_setting)
|
||||||
concat(next_setting)
|
concat(next_setting)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -349,7 +349,7 @@ export default class extends Controller {
|
|||||||
const dialog = this.element.closest("dialog");
|
const dialog = this.element.closest("dialog");
|
||||||
this.tooltip = d3.select(dialog || document.body)
|
this.tooltip = d3.select(dialog || document.body)
|
||||||
.append("div")
|
.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("opacity", 0)
|
||||||
.style("pointer-events", "none");
|
.style("pointer-events", "none");
|
||||||
}
|
}
|
||||||
|
|||||||
46
app/javascript/controllers/viewport_controller.js
Normal file
46
app/javascript/controllers/viewport_controller.js
Normal file
@@ -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`
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,7 +44,7 @@ end %>
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%# MOBILE - Top nav %>
|
<%# MOBILE - Top nav %>
|
||||||
<nav class="lg:hidden flex justify-between items-center p-3">
|
<nav class="lg:hidden flex justify-between items-center p-3 pt-[calc(env(safe-area-inset-top)+0.75rem)]">
|
||||||
<% if intro_mode %>
|
<% if intro_mode %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= icon("panel-left", as_button: true, data: { action: "app-layout#openMobileSidebar"}) %>
|
<%= icon("panel-left", as_button: true, data: { action: "app-layout#openMobileSidebar"}) %>
|
||||||
@@ -126,7 +126,7 @@ end %>
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%# SHARED - Main content %>
|
<%# SHARED - Main content %>
|
||||||
<%= tag.main class: class_names("grow overflow-y-auto px-3 lg:px-10 pt-0 pb-4 w-full mx-auto max-w-5xl"), data: { app_layout_target: "content" } do %>
|
<%= tag.main class: class_names("grow overflow-y-auto px-3 lg:px-10 w-full mx-auto max-w-5xl"), data: { app_layout_target: "content", viewport_target: "content" } do %>
|
||||||
<% unless intro_mode %>
|
<% unless intro_mode %>
|
||||||
<div class="hidden lg:flex gap-2 items-center justify-between mb-6 sticky top-0 z-10 -mx-3 lg:-mx-10 px-3 lg:px-10 py-4 bg-surface border-b border-tertiary">
|
<div class="hidden lg:flex gap-2 items-center justify-between mb-6 sticky top-0 z-10 -mx-3 lg:-mx-10 px-3 lg:px-10 py-4 bg-surface border-b border-tertiary">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
@@ -173,7 +173,8 @@ end %>
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%# MOBILE - Bottom Nav %>
|
<%# MOBILE - Bottom Nav %>
|
||||||
<%= tag.nav class: "lg:hidden sticky bottom-0 left-0 right-0 bg-surface z-10 border-t border-tertiary flex justify-around" do %>
|
<%= tag.nav class: "lg:hidden fixed bottom-0 left-0 right-0 bg-surface z-10 border-t border-tertiary flex justify-around pb-[env(safe-area-inset-bottom)]",
|
||||||
|
data: { viewport_target: "bottomNav" } do %>
|
||||||
<% mobile_nav_items.each do |nav_item| %>
|
<% mobile_nav_items.each do |nav_item| %>
|
||||||
<%= render "layouts/shared/nav_item", **nav_item %>
|
<%= render "layouts/shared/nav_item", **nav_item %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<%= render "layouts/shared/htmldoc" do %>
|
<%= render "layouts/shared/htmldoc" do %>
|
||||||
<div class="flex flex-col md:flex-row h-full bg-surface">
|
<div class="flex flex-col md:flex-row h-full bg-surface pt-[env(safe-area-inset-top)]">
|
||||||
<div class="p-4 w-full md:w-96 shrink-0 md:h-full md:overflow-y-auto">
|
<div class="p-4 w-full md:w-96 shrink-0 md:h-full md:overflow-y-auto">
|
||||||
<%= render "settings/settings_nav" %>
|
<%= render "settings/settings_nav" %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,15 +5,15 @@
|
|||||||
<html
|
<html
|
||||||
lang="en"
|
lang="en"
|
||||||
data-theme="<%= theme %>"
|
data-theme="<%= theme %>"
|
||||||
data-controller="theme"
|
data-controller="theme viewport"
|
||||||
data-theme-user-preference-value="<%= Current.user&.theme || "system" %>"
|
data-theme-user-preference-value="<%= Current.user&.theme || "system" %>"
|
||||||
class="text-primary bg-surface overflow-hidden overscroll-none font-sans <%= @os %>">
|
class="h-[var(--app-height, 100dvh)] text-primary bg-surface overflow-hidden overscroll-none font-sans <%= @os %>">
|
||||||
<head>
|
<head>
|
||||||
<%= render "layouts/shared/head" %>
|
<%= render "layouts/shared/head" %>
|
||||||
<%= yield :head %>
|
<%= yield :head %>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="h-[100dvh] overflow-hidden antialiased pt-[env(safe-area-inset-top)] pb-[env(safe-area-inset-bottom)]">
|
<body class="h-[var(--app-height)] overflow-hidden antialiased">
|
||||||
<% if Rails.env.development? %>
|
<% if Rails.env.development? %>
|
||||||
<button hidden data-controller="hotkey" data-hotkey="t t /" data-action="theme#toggle"></button>
|
<button hidden data-controller="hotkey" data-hotkey="t t /" data-action="theme#toggle"></button>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
Reference in New Issue
Block a user