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

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()),