Improved back-to-top button behavior on anchor jump

This commit is contained in:
squidfunk 2022-01-16 16:09:51 +01:00
parent 0a08e6795c
commit 014ab602cb
7 changed files with 57 additions and 54 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -213,7 +213,7 @@
</script> </script>
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}
<script src="{{ 'assets/javascripts/bundle.1514a9a0.min.js' | url }}"></script> <script src="{{ 'assets/javascripts/bundle.01de222e.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

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

View File

@ -30,6 +30,8 @@ import {
endWith, endWith,
finalize, finalize,
map, map,
repeat,
skip,
takeLast, takeLast,
takeUntil, takeUntil,
tap tap
@ -61,8 +63,8 @@ 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 */
target$: Observable<HTMLElement> /* Location target observable */
} }
/** /**
@ -72,6 +74,7 @@ interface MountOptions {
viewport$: Observable<Viewport> /* Viewport observable */ viewport$: Observable<Viewport> /* Viewport observable */
header$: Observable<Header> /* Header observable */ header$: Observable<Header> /* Header observable */
main$: Observable<Main> /* Main area observable */ main$: Observable<Main> /* Main area observable */
target$: Observable<HTMLElement> /* Location target observable */
} }
/* ---------------------------------------------------------------------------- /* ----------------------------------------------------------------------------
@ -87,7 +90,7 @@ interface MountOptions {
* @returns Back-to-top observable * @returns Back-to-top observable
*/ */
export function watchBackToTop( export function watchBackToTop(
_el: HTMLElement, { viewport$, main$ }: WatchOptions _el: HTMLElement, { viewport$, main$, target$ }: WatchOptions
): Observable<BackToTop> { ): Observable<BackToTop> {
/* Compute direction */ /* Compute direction */
@ -95,25 +98,25 @@ export function watchBackToTop(
.pipe( .pipe(
map(({ offset: { y } }) => y), map(({ offset: { y } }) => y),
bufferCount(2, 1), bufferCount(2, 1),
map(([a, b]) => a > b && b), map(([a, b]) => a > b),
distinctUntilChanged() distinctUntilChanged()
) )
/* Compute whether button should be hidden */ /* Compute whether main area is active */
const hidden$ = main$ const active$ = main$
.pipe( .pipe(
distinctUntilKeyChanged("active") map(({ active }) => active)
) )
/* Compute threshold for hiding */ /* Compute threshold for hiding */
return combineLatest([hidden$, direction$]) return combineLatest([active$, direction$])
.pipe( .pipe(
map(([{ active }, direction]) => ({ map(([active, direction]) => !(active && direction)),
hidden: !(active && direction) distinctUntilChanged(),
})), takeUntil(target$.pipe(skip(1))),
distinctUntilChanged((a, b) => ( endWith(true),
a.hidden === b.hidden repeat({ delay: 250 }),
)) map(hidden => ({ hidden }))
) )
} }
@ -128,7 +131,7 @@ export function watchBackToTop(
* @returns Back-to-top component observable * @returns Back-to-top component observable
*/ */
export function mountBackToTop( export function mountBackToTop(
el: HTMLElement, { viewport$, header$, main$ }: MountOptions el: HTMLElement, { viewport$, header$, main$, target$ }: MountOptions
): Observable<Component<BackToTop>> { ): Observable<Component<BackToTop>> {
const push$ = new Subject<BackToTop>() const push$ = new Subject<BackToTop>()
push$.subscribe({ push$.subscribe({
@ -164,7 +167,7 @@ export function mountBackToTop(
}) })
/* Create and return component */ /* Create and return component */
return watchBackToTop(el, { viewport$, header$, main$ }) return watchBackToTop(el, { viewport$, main$, target$ })
.pipe( .pipe(
tap(state => push$.next(state)), tap(state => push$.next(state)),
finalize(() => push$.complete()), finalize(() => push$.complete()),