mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2024-11-24 15:40:15 +01:00
Improved browser history navigation in conjunction with instant loading
This commit is contained in:
parent
1020953fa5
commit
674e3456f8
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
24
material/assets/javascripts/bundle.95ab87dc.min.js
vendored
Normal file
24
material/assets/javascripts/bundle.95ab87dc.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
material/assets/javascripts/bundle.95ab87dc.min.js.map
Normal file
1
material/assets/javascripts/bundle.95ab87dc.min.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -1,8 +1,8 @@
|
||||
{
|
||||
"assets/javascripts/bundle.js": "assets/javascripts/bundle.71def461.min.js",
|
||||
"assets/javascripts/bundle.js.map": "assets/javascripts/bundle.71def461.min.js.map",
|
||||
"assets/javascripts/bundle.js": "assets/javascripts/bundle.95ab87dc.min.js",
|
||||
"assets/javascripts/bundle.js.map": "assets/javascripts/bundle.95ab87dc.min.js.map",
|
||||
"assets/javascripts/worker/search.js": "assets/javascripts/worker/search.926ffd9e.min.js",
|
||||
"assets/javascripts/worker/search.js.map": "assets/javascripts/worker/search.926ffd9e.min.js.map",
|
||||
"assets/stylesheets/app-palette.scss": "assets/stylesheets/app-palette.3f90c815.min.css",
|
||||
"assets/stylesheets/app.scss": "assets/stylesheets/app.0f079138.min.css"
|
||||
"assets/stylesheets/app.scss": "assets/stylesheets/app.68c05372.min.css"
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -43,7 +43,7 @@
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" href="{{ 'assets/stylesheets/app.0f079138.min.css' | url }}">
|
||||
<link rel="stylesheet" href="{{ 'assets/stylesheets/app.68c05372.min.css' | url }}">
|
||||
{% if palette.primary or palette.accent %}
|
||||
<link rel="stylesheet" href="{{ 'assets/stylesheets/app-palette.3f90c815.min.css' | url }}">
|
||||
{% endif %}
|
||||
@ -190,7 +190,7 @@
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% block scripts %}
|
||||
<script src="{{ 'assets/javascripts/bundle.71def461.min.js' | url }}"></script>
|
||||
<script src="{{ 'assets/javascripts/bundle.95ab87dc.min.js' | url }}"></script>
|
||||
{%- set translations = {} -%}
|
||||
{%- for key in [
|
||||
"clipboard.copy",
|
||||
|
@ -45,14 +45,14 @@ import {
|
||||
take,
|
||||
mapTo,
|
||||
shareReplay,
|
||||
switchMapTo,
|
||||
skip
|
||||
sample
|
||||
} from "rxjs/operators"
|
||||
|
||||
import {
|
||||
watchToggle,
|
||||
setToggle,
|
||||
getElements,
|
||||
getLocation,
|
||||
watchMedia,
|
||||
watchDocument,
|
||||
watchLocation,
|
||||
@ -271,7 +271,7 @@ export function initialize(config: unknown) {
|
||||
)
|
||||
)
|
||||
.subscribe(hash => {
|
||||
getElement(hash)!.scrollIntoView()
|
||||
getElement(`[id="${hash}"]`)!.scrollIntoView()
|
||||
})
|
||||
|
||||
// Scroll lock // document -> document$ => { body } !?
|
||||
@ -305,17 +305,20 @@ export function initialize(config: unknown) {
|
||||
.pipe(
|
||||
take(1), // only initial load
|
||||
switchMap(({ body }) => fromEvent(body, "click")),
|
||||
switchMap(ev => {
|
||||
withLatestFrom(viewport$),
|
||||
switchMap(([ev, { offset }]) => {
|
||||
if (ev.target && ev.target instanceof HTMLElement) {
|
||||
const anchor = ev.target.closest("a")
|
||||
if (anchor) {
|
||||
if (/(:\/\/|^#)/.test(anchor.getAttribute("href")!) === false) {
|
||||
const link = ev.target.closest("a")
|
||||
if (link) {
|
||||
if (/(:\/\/|^#)/.test(link.getAttribute("href")!) === false) {
|
||||
ev.preventDefault()
|
||||
|
||||
// we must copy the value, or weird stuff will happen
|
||||
const href = anchor.href
|
||||
history.pushState(true, "", href)
|
||||
return of(href)
|
||||
// remember scroll position!
|
||||
const href = link.href
|
||||
history.replaceState(offset, document.title)
|
||||
history.pushState({}, "", href)
|
||||
return of(href) // anchor.href
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -325,20 +328,110 @@ export function initialize(config: unknown) {
|
||||
)
|
||||
: NEVER
|
||||
|
||||
// the location might change, but popstate might not be triggered which is
|
||||
// the case when we hit the back button on the same page. scroll to top.
|
||||
// location$
|
||||
// .pipe(
|
||||
// bufferCount(2, 1)
|
||||
// )
|
||||
// .subscribe(x => {
|
||||
// console.log(x)
|
||||
// })
|
||||
|
||||
// deploy new location - can be written as instant$.subscribe(location$)
|
||||
instant$.subscribe(url => {
|
||||
console.log(`Load ${url}`)
|
||||
location$.next(url)
|
||||
})
|
||||
|
||||
if ("scrollRestoration" in history)
|
||||
history.scrollRestoration = "manual"
|
||||
|
||||
const pop$ = fromEvent<PopStateEvent>(window, "popstate")
|
||||
.pipe(
|
||||
shareReplay(1) // TODO: share() should be enough
|
||||
)
|
||||
|
||||
pop$
|
||||
.subscribe(() => location$.next(getLocation()))
|
||||
|
||||
pop$
|
||||
.pipe(
|
||||
sample(document$),
|
||||
withLatestFrom(document$),
|
||||
)
|
||||
.subscribe(([ev, { title, head }]) => {
|
||||
|
||||
document.title = title
|
||||
|
||||
// replace meta tags
|
||||
for (const selector of [
|
||||
"link[rel=canonical]",
|
||||
"meta[name=author]",
|
||||
"meta[name=description]"
|
||||
]) {
|
||||
const next = getElement(selector, head)
|
||||
const prev = getElement(selector, document.head)
|
||||
if (
|
||||
typeof next !== "undefined" &&
|
||||
typeof prev !== "undefined"
|
||||
) {
|
||||
prev.replaceWith(next)
|
||||
}
|
||||
}
|
||||
|
||||
console.log(ev)
|
||||
if (ev.state)
|
||||
setViewportOffset(ev.state)
|
||||
})
|
||||
|
||||
// make links absolute, so they remain stable
|
||||
for (const selector of [
|
||||
"link[rel='shortcut icon']",
|
||||
"link[rel='stylesheet']"
|
||||
]) {
|
||||
for (const el of getElements<HTMLLinkElement>(selector))
|
||||
el.href = el.href
|
||||
}
|
||||
|
||||
// if a new url is deployed via instant loading, switch to document observable
|
||||
// to exactly know when the content was loaded. then go to top.
|
||||
instant$
|
||||
.pipe(
|
||||
switchMapTo(document$.pipe(skip(1))), // TODO: just use document$ and skip(1)
|
||||
sample(document$),
|
||||
withLatestFrom(document$),
|
||||
)
|
||||
.subscribe(() => {
|
||||
setViewportOffset({ y: 0 })
|
||||
.subscribe(([url, { title, head }]) => {
|
||||
document.title = title
|
||||
|
||||
// replace meta tags
|
||||
for (const selector of [
|
||||
"link[rel=canonical]",
|
||||
"meta[name=author]",
|
||||
"meta[name=description]"
|
||||
]) {
|
||||
const next = getElement(selector, head)
|
||||
const prev = getElement(selector, document.head)
|
||||
if (
|
||||
typeof next !== "undefined" &&
|
||||
typeof prev !== "undefined"
|
||||
) {
|
||||
prev.replaceWith(next)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this doesnt work as expected
|
||||
const { hash } = new URL(url)
|
||||
if (hash) {
|
||||
const el = getElement(hash)
|
||||
if (typeof el !== "undefined") {
|
||||
el.scrollIntoView()
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// console.log("scroll to top")
|
||||
setViewportOffset({ y: 0 })
|
||||
}
|
||||
})
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
import * as ClipboardJS from "clipboard"
|
||||
import { NEVER, Observable, Subject, fromEventPattern } from "rxjs"
|
||||
import { mapTo, shareReplay, tap } from "rxjs/operators"
|
||||
import { mapTo, share, tap } from "rxjs/operators"
|
||||
|
||||
import { getElements } from "observables"
|
||||
import { renderClipboard } from "templates"
|
||||
@ -76,7 +76,7 @@ export function setupClipboard(
|
||||
new ClipboardJS(".md-clipboard").on("success", next)
|
||||
})
|
||||
.pipe(
|
||||
shareReplay(1)
|
||||
share()
|
||||
)
|
||||
|
||||
/* Display notification for clipboard event */
|
||||
|
@ -54,11 +54,11 @@ export function setLocation(value: string): void {
|
||||
*/
|
||||
export function watchLocation(): Subject<string> {
|
||||
const location$ = new Subject<string>()
|
||||
fromEvent<PopStateEvent>(window, "popstate")
|
||||
.pipe(
|
||||
map(getLocation)
|
||||
)
|
||||
.subscribe(location$)
|
||||
// fromEvent<PopStateEvent>(window, "popstate")
|
||||
// .pipe(
|
||||
// map(getLocation)
|
||||
// )
|
||||
// .subscribe(location$)
|
||||
|
||||
/* Return location subject */
|
||||
return location$
|
||||
|
@ -33,7 +33,7 @@ import { filter, map, share, startWith } from "rxjs/operators"
|
||||
* @return Location hash
|
||||
*/
|
||||
export function getLocationHash(): string {
|
||||
return location.hash
|
||||
return location.hash.substring(1)
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
@ -42,6 +42,9 @@ export interface ViewportOffset {
|
||||
/**
|
||||
* Retrieve viewport offset
|
||||
*
|
||||
* On iOS Safari, viewport offset can be negative due to overflow scrolling.
|
||||
* As this may induce strange behaviors downstream, we'll just limit it to 0.
|
||||
*
|
||||
* @return Viewport offset
|
||||
*/
|
||||
export function getViewportOffset(): ViewportOffset {
|
||||
|
Loading…
Reference in New Issue
Block a user