mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2024-06-14 11:52:32 +03: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": "assets/javascripts/bundle.95ab87dc.min.js",
|
||||||
"assets/javascripts/bundle.js.map": "assets/javascripts/bundle.71def461.min.js.map",
|
"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": "assets/javascripts/worker/search.926ffd9e.min.js",
|
||||||
"assets/javascripts/worker/search.js.map": "assets/javascripts/worker/search.926ffd9e.min.js.map",
|
"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-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 %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block styles %}
|
{% 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 %}
|
{% if palette.primary or palette.accent %}
|
||||||
<link rel="stylesheet" href="{{ 'assets/stylesheets/app-palette.3f90c815.min.css' | url }}">
|
<link rel="stylesheet" href="{{ 'assets/stylesheets/app-palette.3f90c815.min.css' | url }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -190,7 +190,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script src="{{ 'assets/javascripts/bundle.71def461.min.js' | url }}"></script>
|
<script src="{{ 'assets/javascripts/bundle.95ab87dc.min.js' | url }}"></script>
|
||||||
{%- set translations = {} -%}
|
{%- set translations = {} -%}
|
||||||
{%- for key in [
|
{%- for key in [
|
||||||
"clipboard.copy",
|
"clipboard.copy",
|
||||||
|
@ -45,14 +45,14 @@ import {
|
|||||||
take,
|
take,
|
||||||
mapTo,
|
mapTo,
|
||||||
shareReplay,
|
shareReplay,
|
||||||
switchMapTo,
|
sample
|
||||||
skip
|
|
||||||
} from "rxjs/operators"
|
} from "rxjs/operators"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
watchToggle,
|
watchToggle,
|
||||||
setToggle,
|
setToggle,
|
||||||
getElements,
|
getElements,
|
||||||
|
getLocation,
|
||||||
watchMedia,
|
watchMedia,
|
||||||
watchDocument,
|
watchDocument,
|
||||||
watchLocation,
|
watchLocation,
|
||||||
@ -271,7 +271,7 @@ export function initialize(config: unknown) {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
.subscribe(hash => {
|
.subscribe(hash => {
|
||||||
getElement(hash)!.scrollIntoView()
|
getElement(`[id="${hash}"]`)!.scrollIntoView()
|
||||||
})
|
})
|
||||||
|
|
||||||
// Scroll lock // document -> document$ => { body } !?
|
// Scroll lock // document -> document$ => { body } !?
|
||||||
@ -305,17 +305,20 @@ export function initialize(config: unknown) {
|
|||||||
.pipe(
|
.pipe(
|
||||||
take(1), // only initial load
|
take(1), // only initial load
|
||||||
switchMap(({ body }) => fromEvent(body, "click")),
|
switchMap(({ body }) => fromEvent(body, "click")),
|
||||||
switchMap(ev => {
|
withLatestFrom(viewport$),
|
||||||
|
switchMap(([ev, { offset }]) => {
|
||||||
if (ev.target && ev.target instanceof HTMLElement) {
|
if (ev.target && ev.target instanceof HTMLElement) {
|
||||||
const anchor = ev.target.closest("a")
|
const link = ev.target.closest("a")
|
||||||
if (anchor) {
|
if (link) {
|
||||||
if (/(:\/\/|^#)/.test(anchor.getAttribute("href")!) === false) {
|
if (/(:\/\/|^#)/.test(link.getAttribute("href")!) === false) {
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
|
|
||||||
// we must copy the value, or weird stuff will happen
|
// we must copy the value, or weird stuff will happen
|
||||||
const href = anchor.href
|
// remember scroll position!
|
||||||
history.pushState(true, "", href)
|
const href = link.href
|
||||||
return of(href)
|
history.replaceState(offset, document.title)
|
||||||
|
history.pushState({}, "", href)
|
||||||
|
return of(href) // anchor.href
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -325,20 +328,110 @@ export function initialize(config: unknown) {
|
|||||||
)
|
)
|
||||||
: NEVER
|
: 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$)
|
// deploy new location - can be written as instant$.subscribe(location$)
|
||||||
instant$.subscribe(url => {
|
instant$.subscribe(url => {
|
||||||
console.log(`Load ${url}`)
|
console.log(`Load ${url}`)
|
||||||
location$.next(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
|
// 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.
|
// to exactly know when the content was loaded. then go to top.
|
||||||
instant$
|
instant$
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMapTo(document$.pipe(skip(1))), // TODO: just use document$ and skip(1)
|
sample(document$),
|
||||||
|
withLatestFrom(document$),
|
||||||
)
|
)
|
||||||
.subscribe(() => {
|
.subscribe(([url, { title, head }]) => {
|
||||||
setViewportOffset({ y: 0 })
|
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 * as ClipboardJS from "clipboard"
|
||||||
import { NEVER, Observable, Subject, fromEventPattern } from "rxjs"
|
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 { getElements } from "observables"
|
||||||
import { renderClipboard } from "templates"
|
import { renderClipboard } from "templates"
|
||||||
@ -76,7 +76,7 @@ export function setupClipboard(
|
|||||||
new ClipboardJS(".md-clipboard").on("success", next)
|
new ClipboardJS(".md-clipboard").on("success", next)
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
shareReplay(1)
|
share()
|
||||||
)
|
)
|
||||||
|
|
||||||
/* Display notification for clipboard event */
|
/* Display notification for clipboard event */
|
||||||
|
@ -54,11 +54,11 @@ export function setLocation(value: string): void {
|
|||||||
*/
|
*/
|
||||||
export function watchLocation(): Subject<string> {
|
export function watchLocation(): Subject<string> {
|
||||||
const location$ = new Subject<string>()
|
const location$ = new Subject<string>()
|
||||||
fromEvent<PopStateEvent>(window, "popstate")
|
// fromEvent<PopStateEvent>(window, "popstate")
|
||||||
.pipe(
|
// .pipe(
|
||||||
map(getLocation)
|
// map(getLocation)
|
||||||
)
|
// )
|
||||||
.subscribe(location$)
|
// .subscribe(location$)
|
||||||
|
|
||||||
/* Return location subject */
|
/* Return location subject */
|
||||||
return location$
|
return location$
|
||||||
|
@ -33,7 +33,7 @@ import { filter, map, share, startWith } from "rxjs/operators"
|
|||||||
* @return Location hash
|
* @return Location hash
|
||||||
*/
|
*/
|
||||||
export function getLocationHash(): string {
|
export function getLocationHash(): string {
|
||||||
return location.hash
|
return location.hash.substring(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
@ -42,6 +42,9 @@ export interface ViewportOffset {
|
|||||||
/**
|
/**
|
||||||
* Retrieve viewport offset
|
* 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
|
* @return Viewport offset
|
||||||
*/
|
*/
|
||||||
export function getViewportOffset(): ViewportOffset {
|
export function getViewportOffset(): ViewportOffset {
|
||||||
|
Loading…
Reference in New Issue
Block a user