mirror of
https://github.com/discourse/discourse.git
synced 2025-09-06 10:50:21 +08:00
FEATURE: Use smooth scrolling for J/K keyboard shortcuts. (#7084)
This commit is contained in:
parent
edffd0097f
commit
6bc8382555
1 changed files with 88 additions and 29 deletions
|
@ -417,53 +417,112 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
_moveSelection(direction) {
|
_moveSelection(direction) {
|
||||||
|
// Pressing a move key (J/K) very quick (i.e. keeping J or K pressed) will
|
||||||
|
// move fast by disabling smooth page scrolling.
|
||||||
|
const now = +new Date();
|
||||||
|
const fast = this._lastMoveTime && now - this._lastMoveTime < 150;
|
||||||
|
this._lastMoveTime = now;
|
||||||
|
|
||||||
const $articles = this._findArticles();
|
const $articles = this._findArticles();
|
||||||
|
if ($articles === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof $articles === "undefined") return;
|
let $selected = $articles.filter(".selected");
|
||||||
|
if ($selected.length === 0) {
|
||||||
|
$selected = $articles.filter("[data-islastviewedtopic=true]");
|
||||||
|
}
|
||||||
|
|
||||||
const $selected =
|
// If still nothing is selected, select the first post that is
|
||||||
$articles.filter(".selected").length !== 0
|
// visible and cancel move operation.
|
||||||
? $articles.filter(".selected")
|
if ($selected.length === 0) {
|
||||||
: $articles.filter("[data-islastviewedtopic=true]");
|
const offset = minimumOffset();
|
||||||
|
$selected = $articles
|
||||||
|
.toArray()
|
||||||
|
.find(article => article.getBoundingClientRect().top > offset);
|
||||||
|
direction = 0;
|
||||||
|
}
|
||||||
|
|
||||||
let index = $articles.index($selected);
|
const index = $articles.index($selected);
|
||||||
|
let $article = $articles.eq(index);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try doing a page scroll in the context of current post.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!fast && direction !== 0 && $article.length > 0) {
|
||||||
|
/** @var Begin and end offsets for current article
|
||||||
|
* The beginning of first article is the beginning of the page.
|
||||||
|
*/
|
||||||
|
const beginArticle =
|
||||||
|
$article.is(".topic-post") && $article.find("#post_1").length
|
||||||
|
? 0
|
||||||
|
: $article.offset().top;
|
||||||
|
const endArticle =
|
||||||
|
$article.offset().top + $article[0].getBoundingClientRect().height;
|
||||||
|
|
||||||
|
/** @var Begin and end offsets for screen */
|
||||||
|
const beginScreen = $(window).scrollTop();
|
||||||
|
const endScreen = beginScreen + window.innerHeight;
|
||||||
|
|
||||||
|
if (direction < 0 && beginScreen > beginArticle) {
|
||||||
|
return this._scrollTo(
|
||||||
|
Math.max(
|
||||||
|
beginScreen - window.innerHeight + 3 * minimumOffset(), // page up
|
||||||
|
beginArticle - minimumOffset() // beginning of article
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else if (direction > 0 && endScreen < endArticle - minimumOffset()) {
|
||||||
|
return this._scrollTo(
|
||||||
|
Math.min(
|
||||||
|
endScreen - 3 * minimumOffset(), // page down
|
||||||
|
endArticle - window.innerHeight // end of article
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try scrolling to post above or below.
|
||||||
|
*/
|
||||||
|
|
||||||
if ($selected.length !== 0) {
|
if ($selected.length !== 0) {
|
||||||
if (direction === -1 && index === 0) return;
|
if (direction === -1 && index === 0) return;
|
||||||
if (direction === 1 && index === $articles.length - 1) return;
|
if (direction === 1 && index === $articles.length - 1) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// when nothing is selected
|
$article = $articles.eq(index + direction);
|
||||||
if ($selected.length === 0) {
|
|
||||||
// select the first post with its top visible
|
|
||||||
const offset = minimumOffset();
|
|
||||||
index = $articles
|
|
||||||
.toArray()
|
|
||||||
.findIndex(article => article.getBoundingClientRect().top > offset);
|
|
||||||
direction = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const $article = $articles.eq(index + direction);
|
|
||||||
|
|
||||||
if ($article.length > 0) {
|
if ($article.length > 0) {
|
||||||
$articles.removeClass("selected");
|
$articles.removeClass("selected");
|
||||||
$article.addClass("selected");
|
$article.addClass("selected");
|
||||||
|
|
||||||
if ($article.is(".topic-post")) {
|
const articleRect = $article[0].getBoundingClientRect();
|
||||||
$("a.tabLoc", $article).focus();
|
if (!fast && direction < 0 && articleRect.height > window.innerHeight) {
|
||||||
this._scrollToPost($article);
|
// Scrolling to the last "page" of the previous post if post has multiple
|
||||||
} else {
|
// "pages" (if its height does not fit in the screen).
|
||||||
this._scrollList($article, direction);
|
return this._scrollTo(
|
||||||
|
$article.offset().top + articleRect.height - window.innerHeight
|
||||||
|
);
|
||||||
|
} else if ($article.is(".topic-post")) {
|
||||||
|
return this._scrollTo(
|
||||||
|
$article.find("#post_1").length > 0
|
||||||
|
? 0
|
||||||
|
: $article.offset().top - minimumOffset(),
|
||||||
|
() => $("a.tabLoc", $article).focus()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Otherwise scroll through the suggested topic list.
|
||||||
|
*/
|
||||||
|
this._scrollList($article, direction);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_scrollToPost($article) {
|
_scrollTo(scrollTop, complete) {
|
||||||
if ($article.find("#post_1").length > 0) {
|
$("html, body")
|
||||||
$(window).scrollTop(0);
|
.stop(true, true)
|
||||||
} else {
|
.animate({ scrollTop }, { duration: 100, complete });
|
||||||
$(window).scrollTop($article.offset().top - minimumOffset());
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_scrollList($article) {
|
_scrollList($article) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue