refactor: simplify basic layout (#1039)
A dynamically expanding/collapsing topbar is difficult to maintain and not very useful.
This commit is contained in:
parent
52f5ee9cd3
commit
d81f836b06
11 changed files with 26 additions and 294 deletions
|
@ -1,6 +1,6 @@
|
||||||
<!-- The paginator for post list on HomgPage. -->
|
<!-- The paginator for post list on HomgPage. -->
|
||||||
|
|
||||||
<ul class="pagination align-items-center mt-4 ps-lg-2">
|
<ul class="pagination align-items-center my-4 ps-lg-2">
|
||||||
<!-- left arrow -->
|
<!-- left arrow -->
|
||||||
{% if paginator.previous_page %}
|
{% if paginator.previous_page %}
|
||||||
{% assign prev_url = paginator.previous_page_path | relative_url %}
|
{% assign prev_url = paginator.previous_page_path | relative_url %}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<div id="topbar-wrapper">
|
<div id="topbar-wrapper">
|
||||||
<div
|
<div
|
||||||
id="topbar"
|
id="topbar"
|
||||||
class="container d-flex align-items-center justify-content-between h-100 px-3 px-md-4 px-xxl-5"
|
class="container d-flex align-items-center justify-content-between h-100"
|
||||||
>
|
>
|
||||||
<span id="breadcrumb">
|
<span id="breadcrumb">
|
||||||
{% assign paths = page.url | split: '/' %}
|
{% assign paths = page.url | split: '/' %}
|
||||||
|
|
|
@ -4,10 +4,7 @@
|
||||||
|
|
||||||
export function back2top() {
|
export function back2top() {
|
||||||
$(window).on('scroll', () => {
|
$(window).on('scroll', () => {
|
||||||
if (
|
if ($(window).scrollTop() > 50) {
|
||||||
$(window).scrollTop() > 50 &&
|
|
||||||
$('#sidebar-trigger').css('display') === 'none'
|
|
||||||
) {
|
|
||||||
$('#back-to-top').fadeIn();
|
$('#back-to-top').fadeIn();
|
||||||
} else {
|
} else {
|
||||||
$('#back-to-top').fadeOut();
|
$('#back-to-top').fadeOut();
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
/**
|
|
||||||
* Top bar title auto change while scrolling up/down in mobile screens.
|
|
||||||
*/
|
|
||||||
const titleSelector = 'div.post>h1:first-of-type';
|
|
||||||
const $pageTitle = $(titleSelector);
|
|
||||||
const $topbarTitle = $('#topbar-title');
|
|
||||||
const defaultTitleText = $topbarTitle.text().trim();
|
|
||||||
|
|
||||||
export function convertTitle() {
|
|
||||||
if (
|
|
||||||
$pageTitle.length === 0 /* on Home page */ ||
|
|
||||||
$pageTitle.hasClass('dynamic-title') ||
|
|
||||||
$topbarTitle.is(':hidden')
|
|
||||||
) {
|
|
||||||
/* not in mobile views */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let pageTitleText = $pageTitle.text().trim();
|
|
||||||
let hasScrolled = false;
|
|
||||||
let lastScrollTop = 0;
|
|
||||||
|
|
||||||
if ($('#page-category').length || $('#page-tag').length) {
|
|
||||||
/* The title in Category or Tag page will be "<title> <count_of_posts>" */
|
|
||||||
if (/\s/.test(pageTitleText)) {
|
|
||||||
pageTitleText = pageTitleText.replace(/[0-9]/g, '').trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When the page is scrolled down and then refreshed, the topbar title needs to be initialized
|
|
||||||
if ($pageTitle.offset().top < $(window).scrollTop()) {
|
|
||||||
$topbarTitle.text(pageTitleText);
|
|
||||||
}
|
|
||||||
|
|
||||||
let options = {
|
|
||||||
rootMargin: '-48px 0px 0px 0px', // 48px equals to the topbar height (3rem)
|
|
||||||
threshold: [0, 1]
|
|
||||||
};
|
|
||||||
|
|
||||||
let observer = new IntersectionObserver((entries) => {
|
|
||||||
if (!hasScrolled) {
|
|
||||||
hasScrolled = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let curScrollTop = $(window).scrollTop();
|
|
||||||
let isScrollDown = lastScrollTop < curScrollTop;
|
|
||||||
lastScrollTop = curScrollTop;
|
|
||||||
let heading = entries[0];
|
|
||||||
|
|
||||||
if (isScrollDown) {
|
|
||||||
if (heading.intersectionRatio === 0) {
|
|
||||||
$topbarTitle.text(pageTitleText);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (heading.intersectionRatio === 1) {
|
|
||||||
$topbarTitle.text(defaultTitleText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, options);
|
|
||||||
|
|
||||||
observer.observe(document.querySelector(titleSelector));
|
|
||||||
|
|
||||||
/* Click title will scroll to top */
|
|
||||||
$topbarTitle.on('click', () => window.scrollTo(0, 0));
|
|
||||||
}
|
|
|
@ -4,7 +4,7 @@
|
||||||
const $btnSbTrigger = $('#sidebar-trigger');
|
const $btnSbTrigger = $('#sidebar-trigger');
|
||||||
const $btnSearchTrigger = $('#search-trigger');
|
const $btnSearchTrigger = $('#search-trigger');
|
||||||
const $btnCancel = $('#search-cancel');
|
const $btnCancel = $('#search-cancel');
|
||||||
const $main = $('#main');
|
const $content = $('#main>.row');
|
||||||
const $topbarTitle = $('#topbar-title');
|
const $topbarTitle = $('#topbar-title');
|
||||||
const $searchWrapper = $('#search-wrapper');
|
const $searchWrapper = $('#search-wrapper');
|
||||||
const $resultWrapper = $('#search-result-wrapper');
|
const $resultWrapper = $('#search-result-wrapper');
|
||||||
|
@ -58,7 +58,7 @@ class ResultSwitch {
|
||||||
// the block method must be called before $(#main) unloaded.
|
// the block method must be called before $(#main) unloaded.
|
||||||
ScrollBlocker.on();
|
ScrollBlocker.on();
|
||||||
$resultWrapper.removeClass(C_UNLOADED);
|
$resultWrapper.removeClass(C_UNLOADED);
|
||||||
$main.addClass(C_UNLOADED);
|
$content.addClass(C_UNLOADED);
|
||||||
ScrollBlocker.resultVisible = true;
|
ScrollBlocker.resultVisible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ class ResultSwitch {
|
||||||
$hints.removeClass(C_UNLOADED);
|
$hints.removeClass(C_UNLOADED);
|
||||||
}
|
}
|
||||||
$resultWrapper.addClass(C_UNLOADED);
|
$resultWrapper.addClass(C_UNLOADED);
|
||||||
$main.removeClass(C_UNLOADED);
|
$content.removeClass(C_UNLOADED);
|
||||||
|
|
||||||
// now the release method must be called after $(#main) display
|
// now the release method must be called after $(#main) display
|
||||||
ScrollBlocker.off();
|
ScrollBlocker.off();
|
||||||
|
|
|
@ -1,99 +0,0 @@
|
||||||
/**
|
|
||||||
* Hide Header on scroll down
|
|
||||||
*/
|
|
||||||
import ScrollHelper from './utils/scroll-helper';
|
|
||||||
|
|
||||||
const $searchInput = $('#search-input');
|
|
||||||
const delta = ScrollHelper.getTopbarHeight();
|
|
||||||
|
|
||||||
let lastScrollTop = 0;
|
|
||||||
|
|
||||||
function hasScrolled() {
|
|
||||||
let st = $(window).scrollTop();
|
|
||||||
|
|
||||||
/* Make sure they scroll more than delta */
|
|
||||||
if (Math.abs(lastScrollTop - st) <= delta) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (st > lastScrollTop) {
|
|
||||||
/* Scroll down */
|
|
||||||
ScrollHelper.hideTopbar();
|
|
||||||
|
|
||||||
if ($searchInput.is(':focus')) {
|
|
||||||
$searchInput.trigger('blur'); /* remove focus */
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Scroll up */
|
|
||||||
|
|
||||||
// has not yet scrolled to the bottom of the screen, that is, there is still space for scrolling
|
|
||||||
if (st + $(window).height() < $(document).height()) {
|
|
||||||
if (ScrollHelper.hasScrollUpTask()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ScrollHelper.topbarLocked()) {
|
|
||||||
// avoid redundant scroll up event from smooth scrolling
|
|
||||||
ScrollHelper.unlockTopbar();
|
|
||||||
} else {
|
|
||||||
if (ScrollHelper.orientationLocked()) {
|
|
||||||
// avoid device auto scroll up on orientation change
|
|
||||||
ScrollHelper.unLockOrientation();
|
|
||||||
} else {
|
|
||||||
ScrollHelper.showTopbar();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lastScrollTop = st;
|
|
||||||
} // hasScrolled()
|
|
||||||
|
|
||||||
function handleLandscape() {
|
|
||||||
if ($(window).scrollTop() === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ScrollHelper.lockOrientation();
|
|
||||||
ScrollHelper.hideTopbar();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function switchTopbar() {
|
|
||||||
const orientation = screen.orientation;
|
|
||||||
let didScroll = false;
|
|
||||||
|
|
||||||
const handleOrientationChange = () => {
|
|
||||||
const type = orientation.type;
|
|
||||||
if (type === 'landscape-primary' || type === 'landscape-secondary') {
|
|
||||||
handleLandscape();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleWindowChange = () => {
|
|
||||||
if ($(window).width() < $(window).height()) {
|
|
||||||
// before rotating, it is still in portrait mode.
|
|
||||||
handleLandscape();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleScroll = () => {
|
|
||||||
didScroll = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkScroll = () => {
|
|
||||||
if (didScroll) {
|
|
||||||
hasScrolled();
|
|
||||||
didScroll = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (orientation) {
|
|
||||||
orientation.addEventListener('change', handleOrientationChange);
|
|
||||||
} else {
|
|
||||||
// for the browsers that not support `window.screen.orientation` API
|
|
||||||
$(window).on('orientationchange', handleWindowChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
$(window).on('scroll', handleScroll);
|
|
||||||
|
|
||||||
setInterval(checkScroll, 250);
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
/**
|
|
||||||
* A tool for smooth scrolling and topbar switcher
|
|
||||||
*/
|
|
||||||
|
|
||||||
const ATTR_TOPBAR_VISIBLE = 'data-topbar-visible';
|
|
||||||
const $body = $('body');
|
|
||||||
const $topbarWrapper = $('#topbar-wrapper');
|
|
||||||
|
|
||||||
export default class ScrollHelper {
|
|
||||||
static scrollUpCount = 0; // the number of times the scroll up was triggered by ToC or anchor
|
|
||||||
static topbarIsLocked = false;
|
|
||||||
static orientationIsLocked = false;
|
|
||||||
|
|
||||||
static hideTopbar() {
|
|
||||||
$body.attr(ATTR_TOPBAR_VISIBLE, 'false');
|
|
||||||
}
|
|
||||||
|
|
||||||
static showTopbar() {
|
|
||||||
$body.attr(ATTR_TOPBAR_VISIBLE, 'true');
|
|
||||||
}
|
|
||||||
|
|
||||||
// scroll up
|
|
||||||
|
|
||||||
static addScrollUpTask() {
|
|
||||||
ScrollHelper.scrollUpCount += 1;
|
|
||||||
if (!ScrollHelper.topbarIsLocked) {
|
|
||||||
ScrollHelper.topbarIsLocked = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static popScrollUpTask() {
|
|
||||||
ScrollHelper.scrollUpCount -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static hasScrollUpTask() {
|
|
||||||
return ScrollHelper.scrollUpCount > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static topbarLocked() {
|
|
||||||
return ScrollHelper.topbarIsLocked === true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unlockTopbar() {
|
|
||||||
ScrollHelper.topbarIsLocked = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static getTopbarHeight() {
|
|
||||||
return $topbarWrapper.outerHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
// orientation change
|
|
||||||
|
|
||||||
static orientationLocked() {
|
|
||||||
return ScrollHelper.orientationIsLocked === true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static lockOrientation() {
|
|
||||||
ScrollHelper.orientationIsLocked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unLockOrientation() {
|
|
||||||
ScrollHelper.orientationIsLocked = false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +1,5 @@
|
||||||
import { convertTitle } from '../components/convert-title';
|
|
||||||
import { displaySearch } from '../components/search-display';
|
import { displaySearch } from '../components/search-display';
|
||||||
import { switchTopbar } from '../components/topbar-switcher';
|
|
||||||
|
|
||||||
export function initTopbar() {
|
export function initTopbar() {
|
||||||
convertTitle();
|
|
||||||
displaySearch();
|
displaySearch();
|
||||||
switchTopbar();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,17 +19,16 @@ layout: compress
|
||||||
<html lang="{{ site.alt_lang | default: site.lang }}" {{ prefer_mode }}>
|
<html lang="{{ site.alt_lang | default: site.lang }}" {{ prefer_mode }}>
|
||||||
{% include head.html %}
|
{% include head.html %}
|
||||||
|
|
||||||
<body data-topbar-visible="true">
|
<body>
|
||||||
{% include sidebar.html lang=lang %}
|
{% include sidebar.html lang=lang %}
|
||||||
{% include topbar.html lang=lang %}
|
|
||||||
|
|
||||||
<div id="main-wrapper" class="d-flex justify-content-center">
|
<div id="main-wrapper" class="d-flex justify-content-center">
|
||||||
<div id="main" class="container px-xxl-5">
|
<div id="main" class="container px-xxl-5">
|
||||||
|
{% include topbar.html lang=lang %}
|
||||||
{{ content }}
|
{{ content }}
|
||||||
</div>
|
|
||||||
|
|
||||||
{% include search-results.html lang=lang %}
|
{% include search-results.html lang=lang %}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% include footer.html lang=lang %}
|
{% include footer.html lang=lang %}
|
||||||
|
|
||||||
|
|
|
@ -252,10 +252,6 @@ i {
|
||||||
transition: all 0.3s ease-in;
|
transition: all 0.3s ease-in;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-topbar-visible='true'] & > div {
|
|
||||||
top: 5.5rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#access-lastmod {
|
#access-lastmod {
|
||||||
|
@ -938,29 +934,7 @@ $btn-mb: 0.5rem;
|
||||||
|
|
||||||
#topbar-wrapper {
|
#topbar-wrapper {
|
||||||
height: $topbar-height;
|
height: $topbar-height;
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: $sidebar-width; /* same as sidebar width */
|
|
||||||
right: 0;
|
|
||||||
transition: top 0.2s ease-in-out;
|
|
||||||
z-index: 50;
|
|
||||||
background-color: var(--topbar-bg);
|
background-color: var(--topbar-bg);
|
||||||
|
|
||||||
[data-topbar-visible='false'] & {
|
|
||||||
top: -$topbar-height; /* same as topbar height. */
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
$blur: 12px;
|
|
||||||
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: $topbar-height;
|
|
||||||
z-index: -1;
|
|
||||||
-webkit-backdrop-filter: blur($blur);
|
|
||||||
backdrop-filter: blur($blur);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#topbar {
|
#topbar {
|
||||||
|
@ -1168,11 +1142,6 @@ $btn-mb: 0.5rem;
|
||||||
@include pl-pr(0);
|
@include pl-pr(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#core-wrapper,
|
|
||||||
#panel-wrapper {
|
|
||||||
margin-top: $topbar-height; /* same as the height of topbar */
|
|
||||||
}
|
|
||||||
|
|
||||||
#topbar-wrapper.row,
|
#topbar-wrapper.row,
|
||||||
#main > .row,
|
#main > .row,
|
||||||
#search-result-wrapper > .row {
|
#search-result-wrapper > .row {
|
||||||
|
@ -1182,12 +1151,14 @@ $btn-mb: 0.5rem;
|
||||||
/* --- button back-to-top --- */
|
/* --- button back-to-top --- */
|
||||||
|
|
||||||
#back-to-top {
|
#back-to-top {
|
||||||
$size: 2.7em;
|
$size: 3rem;
|
||||||
|
|
||||||
display: none;
|
display: none;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
right: 1rem;
|
||||||
|
bottom: 2rem;
|
||||||
background: var(--button-bg);
|
background: var(--button-bg);
|
||||||
color: var(--btn-backtotop-color);
|
color: var(--btn-backtotop-color);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -1198,6 +1169,11 @@ $btn-mb: 0.5rem;
|
||||||
transition: transform 0.2s ease-out;
|
transition: transform 0.2s ease-out;
|
||||||
-webkit-transition: transform 0.2s ease-out;
|
-webkit-transition: transform 0.2s ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translate3d(0, -5px, 0);
|
||||||
|
-webkit-transform: translate3d(0, -5px, 0);
|
||||||
|
}
|
||||||
|
|
||||||
i {
|
i {
|
||||||
line-height: $size;
|
line-height: $size;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -1205,11 +1181,6 @@ $btn-mb: 0.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#back-to-top:hover {
|
|
||||||
transform: translate3d(0, -5px, 0);
|
|
||||||
-webkit-transform: translate3d(0, -5px, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#notification {
|
#notification {
|
||||||
@-webkit-keyframes popup {
|
@-webkit-keyframes popup {
|
||||||
from {
|
from {
|
||||||
|
@ -1344,11 +1315,14 @@ $btn-mb: 0.5rem;
|
||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#topbar-wrapper,
|
|
||||||
#main-wrapper,
|
#main-wrapper,
|
||||||
footer {
|
footer {
|
||||||
transform: translateX(#{$sidebar-width});
|
transform: translateX(#{$sidebar-width});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#back-to-top {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar {
|
#sidebar {
|
||||||
|
@ -1360,8 +1334,6 @@ $btn-mb: 0.5rem;
|
||||||
|
|
||||||
#main-wrapper {
|
#main-wrapper {
|
||||||
@include slide;
|
@include slide;
|
||||||
|
|
||||||
padding-top: $topbar-height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#topbar,
|
#topbar,
|
||||||
|
@ -1413,12 +1385,6 @@ $btn-mb: 0.5rem;
|
||||||
}
|
}
|
||||||
} /* max-width: 849px */
|
} /* max-width: 849px */
|
||||||
|
|
||||||
@media all and (max-width: 849px) and (orientation: portrait) {
|
|
||||||
[data-topbar-visible='false'] #topbar-wrapper {
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Phone & Pad */
|
/* Phone & Pad */
|
||||||
@media all and (min-width: 577px) and (max-width: 1199px) {
|
@media all and (min-width: 577px) and (max-width: 1199px) {
|
||||||
footer .d-flex > div {
|
footer .d-flex > div {
|
||||||
|
@ -1470,8 +1436,8 @@ $btn-mb: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#search-result-wrapper {
|
#search-result-wrapper {
|
||||||
margin-top: 3rem;
|
|
||||||
max-width: $main-content-max-width;
|
max-width: $main-content-max-width;
|
||||||
|
justify-content: start !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post {
|
.post {
|
||||||
|
@ -1596,7 +1562,6 @@ $btn-mb: 0.5rem;
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#topbar,
|
|
||||||
#main,
|
#main,
|
||||||
footer > .container {
|
footer > .container {
|
||||||
max-width: $main-content-max-width;
|
max-width: $main-content-max-width;
|
||||||
|
|
|
@ -5,6 +5,10 @@
|
||||||
#post-list {
|
#post-list {
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
|
|
||||||
|
&:only-child {
|
||||||
|
margin-bottom: 3.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
a.card-wrapper {
|
a.card-wrapper {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue