From 092350f1f8d21e88a2e9c4ee73c5b68eee029591 Mon Sep 17 00:00:00 2001 From: Luan Estradioto Date: Thu, 22 May 2025 12:46:57 -0300 Subject: [PATCH] Feat: Mobile Settings menu with preserve scroll + scroll on connect (#2278) * feat: preserve scroll and scroll on connect, better responsive mobile settings menu * Update app/javascript/controllers/scroll_on_connect_controller.js Signed-off-by: Zach Gollwitzer * Update app/javascript/controllers/scroll_on_connect_controller.js Signed-off-by: Zach Gollwitzer --------- Signed-off-by: Zach Gollwitzer Co-authored-by: Zach Gollwitzer --- .../controllers/preserve_scroll_controller.js | 44 +++++++++++++++++++ .../scroll_on_connect_controller.js | 41 +++++++++++++++++ app/views/settings/_settings_nav.html.erb | 2 +- 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 app/javascript/controllers/preserve_scroll_controller.js create mode 100644 app/javascript/controllers/scroll_on_connect_controller.js diff --git a/app/javascript/controllers/preserve_scroll_controller.js b/app/javascript/controllers/preserve_scroll_controller.js new file mode 100644 index 00000000..8bf20e52 --- /dev/null +++ b/app/javascript/controllers/preserve_scroll_controller.js @@ -0,0 +1,44 @@ +import { Controller } from "@hotwired/stimulus" + +/* + https://dev.to/konnorrogers/maintain-scroll-position-in-turbo-without-data-turbo-permanent-2b1i + modified to add support for horizontal scrolling + + only requirement is that the element has an id + */ +export default class extends Controller { + static scrollPositions = {} + + connect() { + this.preserveScrollBound = this.preserveScroll.bind(this) + this.restoreScrollBound = this.restoreScroll.bind(this) + + window.addEventListener("turbo:before-cache", this.preserveScrollBound) + window.addEventListener("turbo:before-render", this.restoreScrollBound) + window.addEventListener("turbo:render", this.restoreScrollBound) + } + + disconnect() { + window.removeEventListener("turbo:before-cache", this.preserveScrollBound) + window.removeEventListener("turbo:before-render", this.restoreScrollBound) + window.removeEventListener("turbo:render", this.restoreScrollBound) + } + + preserveScroll() { + if (!this.element.id) return + + this.constructor.scrollPositions[this.element.id] = { + top: this.element.scrollTop, + left: this.element.scrollLeft + } + } + + restoreScroll(event) { + if (!this.element.id) return + + if (this.constructor.scrollPositions[this.element.id]) { + this.element.scrollTop = this.constructor.scrollPositions[this.element.id].top + this.element.scrollLeft = this.constructor.scrollPositions[this.element.id].left + } + } +} \ No newline at end of file diff --git a/app/javascript/controllers/scroll_on_connect_controller.js b/app/javascript/controllers/scroll_on_connect_controller.js new file mode 100644 index 00000000..c03cdbcb --- /dev/null +++ b/app/javascript/controllers/scroll_on_connect_controller.js @@ -0,0 +1,41 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static values = { + selector: { type: String, default: "[aria-current=\"page\"]" }, + delay: { type: Number, default: 500 } + } + + connect() { + setTimeout(() => { + this.scrollToActiveItem() + }, this.delayValue) + } + + scrollToActiveItem() { + const activeItem = this.element?.querySelector(this.selectorValue) + + + if (!activeItem) return + + const scrollContainer = this.element + const containerRect = scrollContainer.getBoundingClientRect() + const activeItemRect = activeItem.getBoundingClientRect() + + const scrollPositionX = (activeItemRect.left + scrollContainer.scrollLeft) - + (containerRect.width / 2) + + (activeItemRect.width / 2) + + const scrollPositionY = (activeItemRect.top + scrollContainer.scrollTop) - + (containerRect.height / 2) + + (activeItemRect.height / 2) + + + // Smooth scroll to position + scrollContainer.scrollTo({ + top: Math.max(0, scrollPositionY), + left: Math.max(0, scrollPositionX), + behavior: 'smooth' + }) + } +} \ No newline at end of file diff --git a/app/views/settings/_settings_nav.html.erb b/app/views/settings/_settings_nav.html.erb index 83bceea4..e63f5ea2 100644 --- a/app/views/settings/_settings_nav.html.erb +++ b/app/views/settings/_settings_nav.html.erb @@ -67,7 +67,7 @@ nav_sections = [ <% end %> -