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 %} {% endif %}
{% endblock %} {% endblock %}
{% block styles %} {% 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 %} {% if config.theme.palette %}
{% set palette = config.theme.palette %} {% set palette = config.theme.palette %}
<link rel="stylesheet" href="{{ 'assets/stylesheets/palette.f1a3b89f.min.css' | url }}"> <link rel="stylesheet" href="{{ 'assets/stylesheets/palette.f1a3b89f.min.css' | url }}">
@ -180,6 +180,7 @@
{% if "navigation.top" in features %} {% 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"> <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" %} {% include ".icons/material/arrow-up.svg" %}
{{ lang.t('top.title') }}
</a> </a>
{% endif %} {% endif %}
</main> </main>
@ -222,7 +223,7 @@
</script> </script>
{% endblock %} {% endblock %}
{% block scripts %} {% 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"] %} {% for path in config["extra_javascript"] %}
<script src="{{ path | url }}"></script> <script src="{{ path | url }}"></script>
{% endfor %} {% endfor %}

View File

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

View File

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

View File

@ -46,3 +46,28 @@ export function resetBackToTopState(
): void { ): void {
el.removeAttribute("data-md-state") 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 */ /* Back-to-top button */
...getComponentElements("top") ...getComponentElements("top")
.map(el => mountBackToTop(el, { viewport$, main$ })) .map(el => mountBackToTop(el, { viewport$, header$, main$ }))
)) ))
/* Set up component observables */ /* Set up component observables */

View File

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

View File

@ -26,20 +26,20 @@
// Back-to-top button // Back-to-top button
.md-top { .md-top {
position: sticky; position: fixed;
bottom: px2rem(8px); top: px2rem(48px + 16px);
z-index: 1; z-index: 1;
float: right; margin-left: 50%;
margin: px2rem(-56px) px2rem(8px) px2rem(8px); padding: px2rem(8px) px2rem(16px);
padding: px2rem(8px); color: var(--md-default-fg-color--light);
color: var(--md-primary-bg-color); font-size: px2rem(14px);
background: var(--md-primary-fg-color); background: var(--md-default-bg-color);
border-radius: 100%; border-radius: px2rem(32px);
outline: none; outline: none;
box-shadow: box-shadow:
0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1), 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),
0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.1); 0 0 px2rem(1px) hsla(0, 0%, 0%, 0.25);
transform: translateY(0); transform: translate(-50%, 0);
transition: transition:
opacity 125ms, opacity 125ms,
transform 125ms cubic-bezier(0.4, 0, 0.2, 1), transform 125ms cubic-bezier(0.4, 0, 0.2, 1),
@ -52,7 +52,7 @@
// Back-to-top button in hidden state // Back-to-top button in hidden state
&[data-md-state="hidden"] { &[data-md-state="hidden"] {
transform: translateY(px2rem(-4px)); transform: translate(-50%, px2rem(+4px));
opacity: 0; opacity: 0;
pointer-events: none; pointer-events: none;
} }
@ -60,7 +60,13 @@
// Back-to-top button on focus/hover // Back-to-top button on focus/hover
&:focus, &:focus,
&:hover { &:hover {
color: var(--md-accent-bg-color);
background: var(--md-accent-fg-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" data-md-state="hidden"
> >
{% include ".icons/material/arrow-up.svg" %} {% include ".icons/material/arrow-up.svg" %}
{{ lang.t('top.title') }}
</a> </a>
{% endif %} {% endif %}
</main> </main>