From eb64db828e116edbacdf10266f8102ed7028a2f2 Mon Sep 17 00:00:00 2001 From: Renato Atilio Date: Tue, 14 Jan 2025 10:28:46 -0300 Subject: [PATCH] DEV: move d-composer-position to setupEditor (#30717) Small refactor to move the `DComposerPosition` component to be a function used on the `setupEditor` editor lifecycle, so it's recycled/re-added whenever the editor component is mounted. Additionally, we reference the passed `editor` instead of the `event.target`, allowing a `contentEditable` editor (which is a DOM tree) to still work with the positioning hack. --- .../app/components/composer-container.hbs | 1 - .../app/components/composer-editor.js | 12 +-- .../app/components/d-composer-position.gjs | 74 ------------------- .../app/lib/composer/composer-position.js | 61 +++++++++++++++ 4 files changed, 68 insertions(+), 80 deletions(-) delete mode 100644 app/assets/javascripts/discourse/app/components/d-composer-position.gjs create mode 100644 app/assets/javascripts/discourse/app/lib/composer/composer-position.js diff --git a/app/assets/javascripts/discourse/app/components/composer-container.hbs b/app/assets/javascripts/discourse/app/components/composer-container.hbs index ad0a829ca84..21782830dca 100644 --- a/app/assets/javascripts/discourse/app/components/composer-container.hbs +++ b/app/assets/javascripts/discourse/app/components/composer-container.hbs @@ -211,7 +211,6 @@ {{/unless}} - { - input?.removeEventListener( + destroyComposerPosition(); + + input.removeEventListener( "scroll", this._throttledSyncEditorAndPreviewScroll ); diff --git a/app/assets/javascripts/discourse/app/components/d-composer-position.gjs b/app/assets/javascripts/discourse/app/components/d-composer-position.gjs deleted file mode 100644 index 2c0a5ffc783..00000000000 --- a/app/assets/javascripts/discourse/app/components/d-composer-position.gjs +++ /dev/null @@ -1,74 +0,0 @@ -import Component from "@glimmer/component"; -import { later } from "@ember/runloop"; - -export default class DComposerPosition extends Component { - // This component contains two composer positioning adjustments - // for Safari iOS/iPad and Firefox on Android - // The fixes here go together with styling in base/compose.css - constructor() { - super(...arguments); - - const html = document.documentElement; - - if ( - html.classList.contains("mobile-device") || - html.classList.contains("ipados-device") - ) { - window.addEventListener("scroll", this._correctScrollPosition); - this._correctScrollPosition(); - const editor = document.querySelector(".d-editor-input"); - editor?.addEventListener("touchmove", this._textareaTouchMove); - } - } - - willDestroy() { - super.willDestroy(...arguments); - - const html = document.documentElement; - - if ( - html.classList.contains("mobile-device") || - html.classList.contains("ipados-device") - ) { - window.removeEventListener("scroll", this._correctScrollPosition); - const editor = document.querySelector(".d-editor-input"); - editor?.removeEventListener("touchmove", this._textareaTouchMove); - } - } - - _correctScrollPosition() { - // In some rare cases, when quoting a large text or - // when editing a long topic, Safari/Firefox will scroll - // the body so that the input/textarea is centered - // This pushes the fixed element offscreen - // Here we detect when the composer's top position is above the window's - // current scroll offset and correct it - later(() => { - const el = document.querySelector("#reply-control"); - const rect = el.getBoundingClientRect(); - - if (rect.top < -1) { - const scrollAmount = window.scrollY + rect.top; - - window.scrollTo({ - top: scrollAmount, - behavior: "instant", - }); - } - }, 150); - } - - _textareaTouchMove(event) { - // This is an alternative to locking up the body - // It stops scrolling in the given element from bubbling up to the body - // when the textarea does not have any content to scroll - if (event.target) { - const notScrollable = - event.target.scrollHeight <= event.target.clientHeight; - if (notScrollable) { - event.preventDefault(); - event.stopPropagation(); - } - } - } -} diff --git a/app/assets/javascripts/discourse/app/lib/composer/composer-position.js b/app/assets/javascripts/discourse/app/lib/composer/composer-position.js new file mode 100644 index 00000000000..4c56657e25d --- /dev/null +++ b/app/assets/javascripts/discourse/app/lib/composer/composer-position.js @@ -0,0 +1,61 @@ +import { later } from "@ember/runloop"; + +export function setupComposerPosition(editor) { + // This component contains two composer positioning adjustments + // for Safari iOS/iPad and Firefox on Android + // The fixes here go together with styling in base/compose.css + const html = document.documentElement; + + function editorTouchMove(event) { + // This is an alternative to locking up the body + // It stops scrolling in the given element from bubbling up to the body + // when the editor does not have any content to scroll + const notScrollable = editor.scrollHeight <= editor.clientHeight; + if (notScrollable) { + event.preventDefault(); + event.stopPropagation(); + } + } + + if ( + html.classList.contains("mobile-device") || + html.classList.contains("ipados-device") + ) { + window.addEventListener("scroll", correctScrollPosition); + correctScrollPosition(); + editor.addEventListener("touchmove", editorTouchMove); + } + + // destructor + return () => { + if ( + html.classList.contains("mobile-device") || + html.classList.contains("ipados-device") + ) { + window.removeEventListener("scroll", correctScrollPosition); + editor.removeEventListener("touchmove", editorTouchMove); + } + }; +} + +function correctScrollPosition() { + // In some rare cases, when quoting a large text or + // when editing a long topic, Safari/Firefox will scroll + // the body so that the editor is centered + // This pushes the fixed element offscreen + // Here we detect when the composer's top position is above the window's + // current scroll offset and correct it + later(() => { + const el = document.querySelector("#reply-control"); + const rect = el.getBoundingClientRect(); + + if (rect.top < -1) { + const scrollAmount = window.scrollY + rect.top; + + window.scrollTo({ + top: scrollAmount, + behavior: "instant", + }); + } + }, 150); +}