mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2024-06-14 11:52:32 +03:00
Added support for instant navigation progress indicator
This commit is contained in:
parent
015685f421
commit
f8fd537b97
18
material/overrides/assets/javascripts/custom.9458f965.min.js
vendored
Normal file
18
material/overrides/assets/javascripts/custom.9458f965.min.js
vendored
Normal file
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
@ -23,5 +23,5 @@
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
{{ super() }}
|
||||
<script src="{{ 'assets/javascripts/custom.a4bbca43.min.js' | url }}"></script>
|
||||
<script src="{{ 'assets/javascripts/custom.9458f965.min.js' | url }}"></script>
|
||||
{% endblock %}
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
29
material/templates/assets/javascripts/bundle.6eac0284.min.js
vendored
Normal file
29
material/templates/assets/javascripts/bundle.6eac0284.min.js
vendored
Normal file
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
1
material/templates/assets/stylesheets/main.79e020e9.min.css
vendored
Normal file
1
material/templates/assets/stylesheets/main.79e020e9.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -44,7 +44,7 @@
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.1dbc8ddf.min.css' | url }}">
|
||||
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.79e020e9.min.css' | url }}">
|
||||
{% if config.theme.palette %}
|
||||
{% set palette = config.theme.palette %}
|
||||
<link rel="stylesheet" href="{{ 'assets/stylesheets/palette.a5377069.min.css' | url }}">
|
||||
@ -206,6 +206,9 @@
|
||||
<div class="md-dialog" data-md-component="dialog">
|
||||
<div class="md-dialog__inner md-typeset"></div>
|
||||
</div>
|
||||
{% if "navigation.instant.progress" in features %}
|
||||
{% include "partials/progress.html" %}
|
||||
{% endif %}
|
||||
{% if config.extra.consent %}
|
||||
<div class="md-consent" data-md-component="consent" id="__consent" hidden>
|
||||
<div class="md-consent__overlay"></div>
|
||||
@ -250,7 +253,7 @@
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script src="{{ 'assets/javascripts/bundle.697ed5af.min.js' | url }}"></script>
|
||||
<script src="{{ 'assets/javascripts/bundle.6eac0284.min.js' | url }}"></script>
|
||||
{% for script in config.extra_javascript %}
|
||||
{{ script | script_tag }}
|
||||
{% endfor %}
|
||||
|
4
material/templates/partials/progress.html
Normal file
4
material/templates/partials/progress.html
Normal file
@ -0,0 +1,4 @@
|
||||
{#-
|
||||
This file was automatically generated - do not edit
|
||||
-#}
|
||||
<div class="md-progress" data-md-component="progress" role="progressbar"></div>
|
@ -51,6 +51,8 @@ theme:
|
||||
- navigation.footer
|
||||
- navigation.indexes
|
||||
# - navigation.instant
|
||||
# - navigation.instant.prefetch
|
||||
# - navigation.instant.progress
|
||||
# - navigation.prune
|
||||
- navigation.sections
|
||||
- navigation.tabs
|
||||
@ -253,7 +255,7 @@ nav:
|
||||
- Insiders:
|
||||
- insiders/index.md
|
||||
- Getting started: insiders/getting-started.md
|
||||
- Changelog:
|
||||
- Changelog:
|
||||
- insiders/changelog/index.md
|
||||
- How to upgrade: insiders/upgrade.md
|
||||
- Blog:
|
||||
|
@ -39,6 +39,7 @@ export type Flag =
|
||||
| "navigation.expand" /* Automatic expansion */
|
||||
| "navigation.indexes" /* Section pages */
|
||||
| "navigation.instant" /* Instant navigation */
|
||||
| "navigation.instant.progress" /* Instant navigation progress */
|
||||
| "navigation.sections" /* Section navigation */
|
||||
| "navigation.tabs" /* Tabs navigation */
|
||||
| "navigation.tabs.sticky" /* Tabs navigation (sticky) */
|
||||
|
@ -21,17 +21,24 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
EMPTY,
|
||||
Observable,
|
||||
catchError,
|
||||
from,
|
||||
Subject,
|
||||
map,
|
||||
of,
|
||||
shareReplay,
|
||||
switchMap,
|
||||
throwError
|
||||
switchMap
|
||||
} from "rxjs"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Helper types
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Options
|
||||
*/
|
||||
interface Options {
|
||||
progress$?: Subject<number> // Progress subject
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
@ -48,16 +55,46 @@ import {
|
||||
* @returns Response observable
|
||||
*/
|
||||
export function request(
|
||||
url: URL | string, options: RequestInit = { credentials: "same-origin" }
|
||||
): Observable<Response> {
|
||||
return from(fetch(`${url}`, options))
|
||||
.pipe(
|
||||
catchError(() => EMPTY),
|
||||
switchMap(res => res.status !== 200
|
||||
? throwError(() => new Error(res.statusText))
|
||||
: of(res)
|
||||
)
|
||||
)
|
||||
url: URL | string, options?: Options
|
||||
): Observable<Blob> {
|
||||
return new Observable<Blob>(observer => {
|
||||
const req = new XMLHttpRequest()
|
||||
req.open("GET", `${url}`)
|
||||
req.responseType = "blob"
|
||||
|
||||
// Handle response
|
||||
req.addEventListener("load", () => {
|
||||
if (req.status >= 200 && req.status < 300) {
|
||||
observer.next(req.response)
|
||||
observer.complete()
|
||||
} else {
|
||||
observer.error(new Error(req.statusText))
|
||||
}
|
||||
})
|
||||
|
||||
// Handle network errors
|
||||
req.addEventListener("error", () => {
|
||||
observer.error(new Error("Network Error"))
|
||||
})
|
||||
|
||||
// Handle aborted requests
|
||||
req.addEventListener("abort", () => {
|
||||
observer.error(new Error("Request aborted"))
|
||||
})
|
||||
|
||||
// Handle download progress
|
||||
if (typeof options?.progress$ !== "undefined") {
|
||||
req.addEventListener("progress", event => {
|
||||
options.progress$!.next((event.loaded / event.total) * 100)
|
||||
})
|
||||
|
||||
// Immediately set progress to 5% to indicate that we're loading
|
||||
options.progress$.next(5)
|
||||
}
|
||||
|
||||
// Send request
|
||||
req.send()
|
||||
})
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
@ -73,11 +110,12 @@ export function request(
|
||||
* @returns Data observable
|
||||
*/
|
||||
export function requestJSON<T>(
|
||||
url: URL | string, options?: RequestInit
|
||||
url: URL | string, options?: Options
|
||||
): Observable<T> {
|
||||
return request(url, options)
|
||||
.pipe(
|
||||
switchMap(res => res.json()),
|
||||
switchMap(res => res.text()),
|
||||
map(body => JSON.parse(body) as T),
|
||||
shareReplay(1)
|
||||
)
|
||||
}
|
||||
@ -91,7 +129,7 @@ export function requestJSON<T>(
|
||||
* @returns Data observable
|
||||
*/
|
||||
export function requestXML(
|
||||
url: URL | string, options?: RequestInit
|
||||
url: URL | string, options?: Options
|
||||
): Observable<Document> {
|
||||
const dom = new DOMParser()
|
||||
return request(url, options)
|
||||
|
@ -65,6 +65,7 @@ import {
|
||||
mountHeader,
|
||||
mountHeaderTitle,
|
||||
mountPalette,
|
||||
mountProgress,
|
||||
mountSearch,
|
||||
mountSearchHiglight,
|
||||
mountSidebar,
|
||||
@ -143,9 +144,12 @@ const index$ = document.forms.namedItem("search")
|
||||
const alert$ = new Subject<string>()
|
||||
setupClipboardJS({ alert$ })
|
||||
|
||||
/* Set up progress indicator */
|
||||
const progress$ = new Subject<number>()
|
||||
|
||||
/* Set up instant navigation, if enabled */
|
||||
if (feature("navigation.instant"))
|
||||
setupInstantNavigation({ location$, viewport$ })
|
||||
setupInstantNavigation({ location$, viewport$, progress$ })
|
||||
.subscribe(document$)
|
||||
|
||||
/* Set up version selector */
|
||||
@ -227,6 +231,10 @@ const control$ = merge(
|
||||
...getComponentElements("palette")
|
||||
.map(el => mountPalette(el)),
|
||||
|
||||
/* Progress bar */
|
||||
...getComponentElements("progress")
|
||||
.map(el => mountProgress(el, { progress$ })),
|
||||
|
||||
/* Search */
|
||||
...getComponentElements("search")
|
||||
.map(el => mountSearch(el, { index$, keyboard$ })),
|
||||
@ -304,4 +312,5 @@ window.tablet$ = tablet$ /* Media tablet observable */
|
||||
window.screen$ = screen$ /* Media screen observable */
|
||||
window.print$ = print$ /* Media print observable */
|
||||
window.alert$ = alert$ /* Alert subject */
|
||||
window.progress$ = progress$ /* Progress indicator subject */
|
||||
window.component$ = component$ /* Component observable */
|
||||
|
@ -41,6 +41,7 @@ export type ComponentType =
|
||||
| "main" /* Main area */
|
||||
| "outdated" /* Version warning */
|
||||
| "palette" /* Color palette */
|
||||
| "progress" /* Progress indicator */
|
||||
| "search" /* Search */
|
||||
| "search-query" /* Search input */
|
||||
| "search-result" /* Search results */
|
||||
@ -86,6 +87,7 @@ interface ComponentTypeMap {
|
||||
"main": HTMLElement /* Main area */
|
||||
"outdated": HTMLElement /* Version warning */
|
||||
"palette": HTMLElement /* Color palette */
|
||||
"progress": HTMLElement /* Progress indicator */
|
||||
"search": HTMLElement /* Search */
|
||||
"search-query": HTMLInputElement /* Search input */
|
||||
"search-result": HTMLElement /* Search results */
|
||||
|
@ -28,6 +28,7 @@ export * from "./dialog"
|
||||
export * from "./header"
|
||||
export * from "./main"
|
||||
export * from "./palette"
|
||||
export * from "./progress"
|
||||
export * from "./search"
|
||||
export * from "./sidebar"
|
||||
export * from "./source"
|
||||
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2023 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import {
|
||||
Observable,
|
||||
Subject,
|
||||
defer,
|
||||
finalize,
|
||||
map,
|
||||
tap
|
||||
} from "rxjs"
|
||||
|
||||
import { Component } from "../_"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Types
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Progress indicator
|
||||
*/
|
||||
export interface Progress {
|
||||
value: number // Progress value
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Helper types
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Mount options
|
||||
*/
|
||||
interface MountOptions {
|
||||
progress$: Subject<number> // Progress subject
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Mount progress indicator
|
||||
*
|
||||
* @param el - Progress indicator element
|
||||
* @param options - Options
|
||||
*
|
||||
* @returns Progress indicator component observable
|
||||
*/
|
||||
export function mountProgress(
|
||||
el: HTMLElement, { progress$ }: MountOptions
|
||||
): Observable<Component<Progress>> {
|
||||
|
||||
// Mount component on subscription
|
||||
return defer(() => {
|
||||
const push$ = new Subject<Progress>()
|
||||
push$.subscribe(({ value }) => {
|
||||
el.style.setProperty("--md-progress-value", `${value}`)
|
||||
})
|
||||
|
||||
// Create and return component
|
||||
return progress$
|
||||
.pipe(
|
||||
tap(value => push$.next({ value })),
|
||||
finalize(() => push$.complete()),
|
||||
map(value => ({ ref: el, value }))
|
||||
)
|
||||
})
|
||||
}
|
@ -67,8 +67,9 @@ import { fetchSitemap } from "../sitemap"
|
||||
* Setup options
|
||||
*/
|
||||
interface SetupOptions {
|
||||
location$: Subject<URL> /* Location subject */
|
||||
viewport$: Observable<Viewport> /* Viewport observable */
|
||||
location$: Subject<URL> // Location subject
|
||||
viewport$: Observable<Viewport> // Viewport observable
|
||||
progress$: Subject<number> // Progress suject
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
@ -138,7 +139,7 @@ function lookup(head: HTMLHeadElement): Map<string, HTMLElement> {
|
||||
* @returns Document observable
|
||||
*/
|
||||
export function setupInstantNavigation(
|
||||
{ location$, viewport$ }: SetupOptions
|
||||
{ location$, viewport$, progress$ }: SetupOptions
|
||||
): Observable<Document> {
|
||||
const config = configuration()
|
||||
if (location.protocol === "file:")
|
||||
@ -260,7 +261,7 @@ export function setupInstantNavigation(
|
||||
startWith(getLocation()),
|
||||
distinctUntilKeyChanged("pathname"),
|
||||
skip(1),
|
||||
switchMap(url => request(url)
|
||||
switchMap(url => request(url, { progress$ })
|
||||
.pipe(
|
||||
catchError(() => {
|
||||
setLocation(url, true)
|
||||
|
@ -56,6 +56,7 @@
|
||||
@import "main/components/nav";
|
||||
@import "main/components/pagination";
|
||||
@import "main/components/post";
|
||||
@import "main/components/progress";
|
||||
@import "main/components/search";
|
||||
@import "main/components/select";
|
||||
@import "main/components/sidebar";
|
||||
|
@ -0,0 +1,53 @@
|
||||
////
|
||||
/// Copyright (c) 2016-2023 Martin Donath <martin.donath@squidfunk.com>
|
||||
///
|
||||
/// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
/// copy of this software and associated documentation files (the "Software"),
|
||||
/// to deal in the Software without restriction, including without limitation
|
||||
/// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
/// and/or sell copies of the Software, and to permit persons to whom the
|
||||
/// Software is furnished to do so, subject to the following conditions:
|
||||
///
|
||||
/// The above copyright notice and this permission notice shall be included in
|
||||
/// all copies or substantial portions of the Software.
|
||||
///
|
||||
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
/// DEALINGS
|
||||
////
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Rules
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Progress variables
|
||||
:root {
|
||||
--md-progress-value: 0;
|
||||
--md-progress-delay: 400ms;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Progress indicator
|
||||
.md-progress {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 4;
|
||||
width: 100%;
|
||||
height: px2rem(1.5px);
|
||||
background: var(--md-primary-bg-color);
|
||||
opacity:
|
||||
min(
|
||||
clamp(0, var(--md-progress-value), 1),
|
||||
clamp(0, 100 - var(--md-progress-value), 1)
|
||||
);
|
||||
transition:
|
||||
transform 500ms cubic-bezier(0.19, 1, 0.22, 1),
|
||||
opacity 250ms var(--md-progress-delay);
|
||||
transform: scaleX(calc(var(--md-progress-value) * 1%));
|
||||
transform-origin: left;
|
||||
}
|
@ -26,10 +26,8 @@
|
||||
|
||||
// Tasklist variables
|
||||
:root {
|
||||
--md-tasklist-icon:
|
||||
svg-load("octicons/check-circle-fill-24.svg");
|
||||
--md-tasklist-icon--checked:
|
||||
svg-load("octicons/check-circle-fill-24.svg");
|
||||
--md-tasklist-icon: svg-load("octicons/check-circle-fill-24.svg");
|
||||
--md-tasklist-icon--checked: svg-load("octicons/check-circle-fill-24.svg");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -379,6 +379,11 @@
|
||||
<div class="md-dialog__inner md-typeset"></div>
|
||||
</div>
|
||||
|
||||
<!-- Progress indicator -->
|
||||
{% if "navigation.instant.progress" in features %}
|
||||
{% include "partials/progress.html" %}
|
||||
{% endif %}
|
||||
|
||||
<!-- Consent -->
|
||||
{% if config.extra.consent %}
|
||||
<div class="md-consent" data-md-component="consent" id="__consent" hidden>
|
||||
|
24
src/templates/partials/progress.html
Normal file
24
src/templates/partials/progress.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!--
|
||||
Copyright (c) 2016-2023 Martin Donath <martin.donath@squidfunk.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
-->
|
||||
|
||||
<!-- Progress indicator -->
|
||||
<div class="md-progress" data-md-component="progress" role="progressbar"></div>
|
1
typings/_/index.d.ts
vendored
1
typings/_/index.d.ts
vendored
@ -99,5 +99,6 @@ declare global {
|
||||
var screen$: Observable<boolean> /* Media screen observable */
|
||||
var print$: Observable<boolean> /* Media print observable */
|
||||
var alert$: Subject<string> /* Alert subject */
|
||||
var progress$: Subject<number> /* Progress indicator subject */
|
||||
var component$: Observable<Component>/* Component observable */
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user