DEV: Fewer jQuery calls in offset calculation ()

This commit is contained in:
Penar Musaraj 2022-01-07 16:02:03 -05:00 committed by GitHub
parent 0a0e06fcf8
commit 1ed2520589
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 92 additions and 84 deletions
app/assets/javascripts/discourse/app
plugins/discourse-narrative-bot/assets/javascripts/initializers

@ -8,7 +8,7 @@ import Composer from "discourse/models/composer";
import KeyEnterEscape from "discourse/mixins/key-enter-escape"; import KeyEnterEscape from "discourse/mixins/key-enter-escape";
import afterTransition from "discourse/lib/after-transition"; import afterTransition from "discourse/lib/after-transition";
import discourseDebounce from "discourse-common/lib/debounce"; import discourseDebounce from "discourse-common/lib/debounce";
import { headerOffset } from "discourse/components/site-header"; import { headerOffset } from "discourse/lib/offset-calculator";
import positioningWorkaround from "discourse/lib/safari-hacks"; import positioningWorkaround from "discourse/lib/safari-hacks";
const START_DRAG_EVENTS = ["touchstart", "mousedown"]; const START_DRAG_EVENTS = ["touchstart", "mousedown"];

@ -7,6 +7,7 @@ import Docking from "discourse/mixins/docking";
import MountWidget from "discourse/components/mount-widget"; import MountWidget from "discourse/components/mount-widget";
import ItsATrap from "@discourse/itsatrap"; import ItsATrap from "@discourse/itsatrap";
import RerenderOnDoNotDisturbChange from "discourse/mixins/rerender-on-do-not-disturb-change"; import RerenderOnDoNotDisturbChange from "discourse/mixins/rerender-on-do-not-disturb-change";
import { headerOffset } from "discourse/lib/offset-calculator";
import { observes } from "discourse-common/utils/decorators"; import { observes } from "discourse-common/utils/decorators";
import { topicTitleDecorators } from "discourse/components/topic-title"; import { topicTitleDecorators } from "discourse/components/topic-title";
@ -438,15 +439,6 @@ export default SiteHeaderComponent.extend({
classNames: ["d-header-wrap"], classNames: ["d-header-wrap"],
}); });
export function headerOffset() {
return (
parseInt(
document.documentElement.style.getPropertyValue("--header-offset"),
10
) || 0
);
}
export function headerTop() { export function headerTop() {
const header = document.querySelector("header.d-header"); const header = document.querySelector("header.d-header");
const headerOffsetTop = header.offsetTop ? header.offsetTop : 0; const headerOffsetTop = header.offsetTop ? header.offsetTop : 0;

@ -5,7 +5,7 @@ import PanEvents, {
import Component from "@ember/component"; import Component from "@ember/component";
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
import discourseDebounce from "discourse-common/lib/debounce"; import discourseDebounce from "discourse-common/lib/debounce";
import { headerOffset } from "discourse/components/site-header"; import { headerOffset } from "discourse/lib/offset-calculator";
import { later, next } from "@ember/runloop"; import { later, next } from "@ember/runloop";
import { observes } from "discourse-common/utils/decorators"; import { observes } from "discourse-common/utils/decorators";
import showModal from "discourse/lib/show-modal"; import showModal from "discourse/lib/show-modal";

@ -1,6 +1,6 @@
import Docking from "discourse/mixins/docking"; import Docking from "discourse/mixins/docking";
import MountWidget from "discourse/components/mount-widget"; import MountWidget from "discourse/components/mount-widget";
import { headerOffset } from "discourse/components/site-header"; import { headerOffset } from "discourse/lib/offset-calculator";
import { next } from "@ember/runloop"; import { next } from "@ember/runloop";
import { observes } from "discourse-common/utils/decorators"; import { observes } from "discourse-common/utils/decorators";
import optionalService from "discourse/lib/optional-service"; import optionalService from "discourse/lib/optional-service";

@ -1,3 +1,5 @@
import { bind } from "discourse-common/utils/decorators";
import discourseDebounce from "discourse-common/lib/debounce";
import { isAppWebview } from "discourse/lib/utilities"; import { isAppWebview } from "discourse/lib/utilities";
import { later, run, schedule, throttle } from "@ember/runloop"; import { later, run, schedule, throttle } from "@ember/runloop";
import { import {
@ -6,9 +8,10 @@ import {
} from "discourse/lib/topic-list-tracker"; } from "discourse/lib/topic-list-tracker";
import Composer from "discourse/models/composer"; import Composer from "discourse/models/composer";
import DiscourseURL from "discourse/lib/url"; import DiscourseURL from "discourse/lib/url";
import domUtils from "discourse-common/utils/dom-utils";
import { INPUT_DELAY } from "discourse-common/config/environment"; import { INPUT_DELAY } from "discourse-common/config/environment";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import { minimumOffset } from "discourse/lib/offset-calculator"; import { headerOffset } from "discourse/lib/offset-calculator";
const DEFAULT_BINDINGS = { const DEFAULT_BINDINGS = {
"!": { postAction: "showFlags" }, "!": { postAction: "showFlags" },
@ -608,7 +611,7 @@ export default {
// Discard selection if it is not in viewport, so users can combine // Discard selection if it is not in viewport, so users can combine
// keyboard shortcuts with mouse scrolling. // keyboard shortcuts with mouse scrolling.
if ($selected.length !== 0 && !fast) { if ($selected.length !== 0 && !fast) {
const offset = minimumOffset(); const offset = headerOffset();
const beginScreen = $(window).scrollTop() - offset; const beginScreen = $(window).scrollTop() - offset;
const endScreen = beginScreen + window.innerHeight + offset; const endScreen = beginScreen + window.innerHeight + offset;
const beginArticle = $selected.offset().top; const beginArticle = $selected.offset().top;
@ -621,7 +624,7 @@ export default {
// If still nothing is selected, select the first post that is // If still nothing is selected, select the first post that is
// visible and cancel move operation. // visible and cancel move operation.
if (!$selected || $selected.length === 0) { if (!$selected || $selected.length === 0) {
const offset = minimumOffset(); const offset = headerOffset();
$selected = $articles $selected = $articles
.toArray() .toArray()
.find((article) => .find((article) =>
@ -636,32 +639,32 @@ export default {
} }
const index = $articles.index($selected); const index = $articles.index($selected);
let $article = $articles.eq(index); let article = $articles.eq(index)[0];
// Try doing a page scroll in the context of current post. // Try doing a page scroll in the context of current post.
if (!fast && direction !== 0 && $article.length > 0) { if (!fast && direction !== 0 && article) {
// The beginning of first article is the beginning of the page. // The beginning of first article is the beginning of the page.
const beginArticle = const beginArticle =
$article.is(".topic-post") && $article.find("#post_1").length article.classList.contains("topic-post") &&
article.querySelector("#post_1")
? 0 ? 0
: $article.offset().top; : domUtils.offset(article).top;
const endArticle = const endArticle = domUtils.offset(article).top + article.offsetHeight;
$article.offset().top + $article[0].getBoundingClientRect().height;
const beginScreen = $(window).scrollTop(); const beginScreen = window.scrollY;
const endScreen = beginScreen + window.innerHeight; const endScreen = beginScreen + window.innerHeight;
if (direction < 0 && beginScreen > beginArticle) { if (direction < 0 && beginScreen > beginArticle) {
return this._scrollTo( return this._scrollTo(
Math.max( Math.max(
beginScreen - window.innerHeight + 3 * minimumOffset(), // page up beginScreen - window.innerHeight + 3 * headerOffset(), // page up
beginArticle - minimumOffset() // beginning of article beginArticle - headerOffset() // beginning of article
) )
); );
} else if (direction > 0 && endScreen < endArticle - minimumOffset()) { } else if (direction > 0 && endScreen < endArticle - headerOffset()) {
return this._scrollTo( return this._scrollTo(
Math.min( Math.min(
endScreen - 3 * minimumOffset(), // page down endScreen - 3 * headerOffset(), // page down
endArticle - window.innerHeight // end of article endArticle - window.innerHeight // end of article
) )
); );
@ -678,68 +681,59 @@ export default {
} }
} }
$article = $articles.eq(index + direction); article = $articles.eq(index + direction)[0];
if ($article.length > 0) { if (!article) {
return;
}
$articles.removeClass("selected"); $articles.removeClass("selected");
$article.addClass("selected"); article.classList.add("selected");
this.appEvents.trigger("keyboard:move-selection", { this.appEvents.trigger("keyboard:move-selection", {
articles: $articles.get(), articles: $articles.get(),
selectedArticle: $article.get(0), selectedArticle: article,
}); });
const articleRect = $article[0].getBoundingClientRect(); const articleTop = domUtils.offset(article).top;
if (!fast && direction < 0 && articleRect.height > window.innerHeight) { if (!fast && direction < 0 && article.offsetHeight > window.innerHeight) {
// Scrolling to the last "page" of the previous post if post has multiple // Scrolling to the last "page" of the previous post if post has multiple
// "pages" (if its height does not fit in the screen). // "pages" (if its height does not fit in the screen).
return this._scrollTo( return this._scrollTo(
$article.offset().top + articleRect.height - window.innerHeight articleTop + article.offsetHeight - window.innerHeight
); );
} else if ($article.is(".topic-post")) { } else if (article.classList.contains("topic-post")) {
return this._scrollTo( return this._scrollTo(
$article.find("#post_1").length > 0 article.querySelector("#post_1") ? 0 : articleTop - headerOffset(),
? 0 { focusTabLoc: true }
: $article.offset().top - minimumOffset(),
() => $("a.tabLoc", $article).focus()
); );
} }
// Otherwise scroll through the suggested topic list. // Otherwise scroll through the suggested topic list.
this._scrollList($article, direction); article.scrollIntoView({
} behavior: "smooth",
block: "center",
});
}, },
_scrollTo(scrollTop) { _scrollTo(scrollTop, opts = {}) {
window.scrollTo({ window.scrollTo({
top: scrollTop, top: scrollTop,
behavior: "smooth", behavior: "smooth",
}); });
if (opts.focusTabLoc) {
window.addEventListener("scroll", this._onScrollEnds, { passive: true });
}
}, },
_scrollList($article) { @bind
// Try to keep the article on screen _onScrollEnds() {
const pos = $article.offset(); window.removeEventListener("scroll", this._onScrollEnds, { passive: true });
const height = $article.height(); discourseDebounce(this, this._onScrollEndsCallback, animationDuration);
const headerHeight = $("header.d-header").height(); },
const scrollTop = $(window).scrollTop();
const windowHeight = $(window).height();
// skip if completely on screen _onScrollEndsCallback() {
if ( document.querySelector(".topic-post.selected a.tabLoc")?.focus();
pos.top - headerHeight > scrollTop &&
pos.top + height < scrollTop + windowHeight
) {
return;
}
let scrollPos = pos.top + height / 2 - windowHeight * 0.5;
if (height > windowHeight - headerHeight) {
scrollPos = pos.top - headerHeight;
}
if (scrollPos < 0) {
scrollPos = 0;
}
this._scrollTo(scrollPos);
}, },
categoriesTopicsList() { categoriesTopicsList() {

@ -1,5 +1,5 @@
import { bind } from "discourse-common/utils/decorators"; import { bind } from "discourse-common/utils/decorators";
import { minimumOffset } from "discourse/lib/offset-calculator"; import { headerOffset } from "discourse/lib/offset-calculator";
// Dear traveller, you are entering a zone where we are at war with the browser. // Dear traveller, you are entering a zone where we are at war with the browser.
// The browser is insisting on positioning scrollTop per the location it was in // The browser is insisting on positioning scrollTop per the location it was in
@ -50,7 +50,7 @@ export default class LockOn {
} }
} }
return offset - minimumOffset(); return offset - headerOffset();
} }
clearLock() { clearLock() {

@ -1,8 +1,18 @@
import deprecated from "discourse-common/lib/deprecated";
export function scrollTopFor(y) { export function scrollTopFor(y) {
return y - offsetCalculator(); return y - offsetCalculator();
} }
export function minimumOffset() { export function minimumOffset() {
deprecated(
"The minimumOffset() helper is deprecated, please use headerOffset() instead.",
{
since: "2.8.0.beta10",
dropFrom: "2.9.0.beta2",
}
);
const header = document.querySelector("header.d-header"), const header = document.querySelector("header.d-header"),
iPadNav = document.querySelector(".footer-nav-ipad .footer-nav"), iPadNav = document.querySelector(".footer-nav-ipad .footer-nav"),
iPadNavHeight = iPadNav ? iPadNav.offsetHeight : 0; iPadNavHeight = iPadNav ? iPadNav.offsetHeight : 0;
@ -17,8 +27,17 @@ export function minimumOffset() {
: 0; : 0;
} }
export function headerOffset() {
return (
parseInt(
document.documentElement.style.getPropertyValue("--header-offset"),
10
) || 0
);
}
export default function offsetCalculator() { export default function offsetCalculator() {
const min = minimumOffset(); const min = headerOffset();
// on mobile, just use the header // on mobile, just use the header
if (document.querySelector("html").classList.contains("mobile-view")) { if (document.querySelector("html").classList.contains("mobile-view")) {

@ -1,7 +1,7 @@
import { addWidgetCleanCallback } from "discourse/components/mount-widget"; import { addWidgetCleanCallback } from "discourse/components/mount-widget";
import Site from "discourse/models/site"; import Site from "discourse/models/site";
import { bind } from "discourse-common/utils/decorators"; import { bind } from "discourse-common/utils/decorators";
import { headerOffset } from "discourse/components/site-header"; import { headerOffset } from "discourse/lib/offset-calculator";
import { schedule } from "@ember/runloop"; import { schedule } from "@ember/runloop";
export default class StickyAvatars { export default class StickyAvatars {
@ -79,6 +79,9 @@ export default class StickyAvatars {
@bind @bind
_initIntersectionObserver() { _initIntersectionObserver() {
schedule("afterRender", () => { schedule("afterRender", () => {
const headerOffsetInPx =
headerOffset() <= 0 ? "0px" : `-${headerOffset()}px`;
this.intersectionObserver = new IntersectionObserver( this.intersectionObserver = new IntersectionObserver(
(entries) => { (entries) => {
entries.forEach((entry) => { entries.forEach((entry) => {
@ -99,7 +102,7 @@ export default class StickyAvatars {
}, },
{ {
threshold: [0.0, 1.0], threshold: [0.0, 1.0],
rootMargin: `-${headerOffset()}px 0px 0px 0px`, rootMargin: `${headerOffsetInPx} 0px 0px 0px`,
} }
); );
}); });

@ -1,6 +1,6 @@
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import discourseDebounce from "discourse-common/lib/debounce"; import discourseDebounce from "discourse-common/lib/debounce";
import { headerOffset } from "discourse/components/site-header"; import { headerOffset } from "discourse/lib/offset-calculator";
import isElementInViewport from "discourse/lib/is-element-in-viewport"; import isElementInViewport from "discourse/lib/is-element-in-viewport";
import { withPluginApi } from "discourse/lib/plugin-api"; import { withPluginApi } from "discourse/lib/plugin-api";