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.
This commit is contained in:
Renato Atilio 2025-01-14 10:28:46 -03:00 committed by GitHub
parent 7330cfa76a
commit eb64db828e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 68 additions and 80 deletions

View File

@ -211,7 +211,6 @@
{{/unless}}
</div>
</ComposerEditor>
<DComposerPosition />
<span>
<PluginOutlet

View File

@ -10,6 +10,7 @@ import $ from "jquery";
import { resolveAllShortUrls } from "pretty-text/upload-short-url";
import { ajax } from "discourse/lib/ajax";
import { tinyAvatar } from "discourse/lib/avatar-utils";
import { setupComposerPosition } from "discourse/lib/composer/composer-position";
import discourseComputed, { bind, debounce } from "discourse/lib/decorators";
import {
fetchUnseenHashtagsInContext,
@ -200,18 +201,19 @@ export default class ComposerEditor extends Component {
const input = this.element.querySelector(".d-editor-input");
input?.addEventListener(
"scroll",
this._throttledSyncEditorAndPreviewScroll
);
input.addEventListener("scroll", this._throttledSyncEditorAndPreviewScroll);
// Focus on the body unless we have a title
if (!this.get("composer.model.canEditTitle")) {
this.textManipulation.putCursorAtEnd();
}
const destroyComposerPosition = setupComposerPosition(input);
return () => {
input?.removeEventListener(
destroyComposerPosition();
input.removeEventListener(
"scroll",
this._throttledSyncEditorAndPreviewScroll
);

View File

@ -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();
}
}
}
}

View File

@ -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);
}