Improved appearance of back-to-top button

This commit is contained in:
squidfunk 2021-07-01 19:12:40 +02:00
parent 31aab182b7
commit 9f01704a73
15 changed files with 88 additions and 38 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -39,7 +39,7 @@
{% endif %}
{% endblock %}
{% block styles %}
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.ca7ac06f.min.css' | url }}">
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.48b295f8.min.css' | url }}">
{% if config.theme.palette %}
{% set palette = config.theme.palette %}
<link rel="stylesheet" href="{{ 'assets/stylesheets/palette.f1a3b89f.min.css' | url }}">
@ -180,6 +180,7 @@
{% if "navigation.top" in features %}
<a href="#" class="md-top md-icon" title="{{ lang.t('top.title') }}" data-md-component="top" data-md-state="hidden">
{% include ".icons/material/arrow-up.svg" %}
{{ lang.t('top.title') }}
</a>
{% endif %}
</main>
@ -222,7 +223,7 @@
</script>
{% endblock %}
{% block scripts %}
<script src="{{ 'assets/javascripts/bundle.5d86c58f.min.js' | url }}"></script>
<script src="{{ 'assets/javascripts/bundle.70097392.min.js' | url }}"></script>
{% for path in config["extra_javascript"] %}
<script src="{{ path | url }}"></script>
{% endfor %}

View File

@ -35,5 +35,5 @@
{% endblock %}
{% block scripts %}
{{ super() }}
<script src="{{ 'overrides/assets/javascripts/bundle.9f7670a0.min.js' | url }}"></script>
<script src="{{ 'overrides/assets/javascripts/bundle.cdc3d82f.min.js' | url }}"></script>
{% endblock %}

View File

@ -55,6 +55,7 @@ theme:
# - navigation.instant
- navigation.sections
- navigation.tabs
- navigation.top
- navigation.tracking
- search.highlight
- search.share

View File

@ -46,3 +46,28 @@ export function resetBackToTopState(
): void {
el.removeAttribute("data-md-state")
}
/* ------------------------------------------------------------------------- */
/**
* Set back-to-top offset
*
* @param el - Back-to-top element
* @param value - Back-to-top offset
*/
export function setBackToTopOffset(
el: HTMLElement, value: number
): void {
el.style.top = `${value}px`
}
/**
* Reset back-to-top offset
*
* @param el - Back-to-top element
*/
export function resetBackToTopOffset(
el: HTMLElement
): void {
el.style.top = ""
}

View File

@ -216,7 +216,7 @@ const content$ = defer(() => merge(
/* Back-to-top button */
...getComponentElements("top")
.map(el => mountBackToTop(el, { viewport$, main$ }))
.map(el => mountBackToTop(el, { viewport$, header$, main$ }))
))
/* Set up component observables */

View File

@ -33,13 +33,20 @@ import {
finalize,
map,
observeOn,
tap
tap,
withLatestFrom
} from "rxjs/operators"
import { resetBackToTopState, setBackToTopState } from "~/actions"
import {
resetBackToTopOffset,
resetBackToTopState,
setBackToTopOffset,
setBackToTopState
} from "~/actions"
import { Viewport } from "~/browser"
import { Component } from "../_"
import { Header } from "../header"
import { Main } from "../main"
/* ----------------------------------------------------------------------------
@ -62,6 +69,7 @@ export interface BackToTop {
*/
interface WatchOptions {
viewport$: Observable<Viewport> /* Viewport observable */
header$: Observable<Header> /* Header observable */
main$: Observable<Main> /* Main area observable */
}
@ -70,6 +78,7 @@ interface WatchOptions {
*/
interface MountOptions {
viewport$: Observable<Viewport> /* Viewport observable */
header$: Observable<Header> /* Header observable */
main$: Observable<Main> /* Main area observable */
}
@ -127,17 +136,23 @@ export function watchBackToTop(
* @returns Back-to-top component observable
*/
export function mountBackToTop(
el: HTMLElement, options: MountOptions
el: HTMLElement, { viewport$, header$, main$ }: MountOptions
): Observable<Component<BackToTop>> {
const internal$ = new Subject<BackToTop>()
internal$
.pipe(
observeOn(animationFrameScheduler)
observeOn(animationFrameScheduler),
withLatestFrom(header$
.pipe(
distinctUntilKeyChanged("height")
)
)
)
.subscribe({
/* Update state */
next({ hidden }) {
next([{ hidden }, { height }]) {
setBackToTopOffset(el, height + 16)
if (hidden)
setBackToTopState(el, "hidden")
else
@ -146,12 +161,13 @@ export function mountBackToTop(
/* Reset on complete */
complete() {
resetBackToTopOffset(el)
resetBackToTopState(el)
}
})
/* Create and return component */
return watchBackToTop(el, options)
return watchBackToTop(el, { viewport$, header$, main$ })
.pipe(
tap(internal$),
finalize(() => internal$.complete()),

View File

@ -26,20 +26,20 @@
// Back-to-top button
.md-top {
position: sticky;
bottom: px2rem(8px);
position: fixed;
top: px2rem(48px + 16px);
z-index: 1;
float: right;
margin: px2rem(-56px) px2rem(8px) px2rem(8px);
padding: px2rem(8px);
color: var(--md-primary-bg-color);
background: var(--md-primary-fg-color);
border-radius: 100%;
margin-left: 50%;
padding: px2rem(8px) px2rem(16px);
color: var(--md-default-fg-color--light);
font-size: px2rem(14px);
background: var(--md-default-bg-color);
border-radius: px2rem(32px);
outline: none;
box-shadow:
0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),
0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.1);
transform: translateY(0);
0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),
0 0 px2rem(1px) hsla(0, 0%, 0%, 0.25);
transform: translate(-50%, 0);
transition:
opacity 125ms,
transform 125ms cubic-bezier(0.4, 0, 0.2, 1),
@ -52,7 +52,7 @@
// Back-to-top button in hidden state
&[data-md-state="hidden"] {
transform: translateY(px2rem(-4px));
transform: translate(-50%, px2rem(+4px));
opacity: 0;
pointer-events: none;
}
@ -60,7 +60,13 @@
// Back-to-top button on focus/hover
&:focus,
&:hover {
color: var(--md-accent-bg-color);
background: var(--md-accent-fg-color);
transform: scale(1.1);
}
// Inline icon
svg {
display: inline-block;
vertical-align: -0.5em;
}
}

View File

@ -344,6 +344,7 @@
data-md-state="hidden"
>
{% include ".icons/material/arrow-up.svg" %}
{{ lang.t('top.title') }}
</a>
{% endif %}
</main>