mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2024-06-14 11:52:32 +03:00
Added component map observables
This commit is contained in:
parent
526c663af9
commit
aeded5b844
127
src/assets/javascripts/component/_/index.ts
Normal file
127
src/assets/javascripts/component/_/index.ts
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2019 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 { MonoTypeOperatorFunction, Observable, of, pipe } from "rxjs"
|
||||
import { scan, shareReplay, tap } from "rxjs/operators"
|
||||
|
||||
import { getElement } from "../../ui"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Types
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Components
|
||||
*/
|
||||
export type Component =
|
||||
| "header" /* Header */
|
||||
| "title" /* Header title */
|
||||
| "search" /* Search */
|
||||
| "query" /* Search input */
|
||||
| "reset" /* Search reset */
|
||||
| "result" /* Search results */
|
||||
| "container" /* Container */
|
||||
| "tabs" /* Tabs */
|
||||
| "navigation" /* Navigation */
|
||||
| "toc" /* Table of contents */
|
||||
| "footer" /* Footer */
|
||||
|
||||
/**
|
||||
* Component map
|
||||
*/
|
||||
export type ComponentMap = {
|
||||
[P in Component]?: HTMLElement
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Retrieve the component to element mapping
|
||||
*
|
||||
* The document must be passed as a parameter to support retrieving elements
|
||||
* from the document object returned through asynchronous loading.
|
||||
*
|
||||
* @param document - Document of reference
|
||||
*
|
||||
* @return Component map observable
|
||||
*/
|
||||
export function watchComponentMap(
|
||||
document: Document
|
||||
): Observable<ComponentMap> {
|
||||
|
||||
/* Build component map */
|
||||
const map$ = of([
|
||||
"header", /* Header */
|
||||
"title", /* Header title */
|
||||
"search", /* Search */
|
||||
"query", /* Search input */
|
||||
"reset", /* Search reset */
|
||||
"result", /* Search results */
|
||||
"container", /* Container */
|
||||
"tabs", /* Tabs */
|
||||
"navigation", /* Navigation */
|
||||
"toc", /* Table of contents */
|
||||
"footer" /* Footer */
|
||||
].reduce<ComponentMap>((map, name) => {
|
||||
const el = getElement(`[data-md-component=${name}]`, document)
|
||||
return {
|
||||
...map,
|
||||
...typeof el !== "undefined" ? { [name]: el } : {}
|
||||
}
|
||||
}, {}))
|
||||
|
||||
/* Return component map as hot observable */
|
||||
return map$
|
||||
.pipe(
|
||||
shareReplay({ bufferSize: 1, refCount: true })
|
||||
)
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Paint component map from source observable
|
||||
*
|
||||
* This operator function will swap the components in the previous component
|
||||
* map with the new components identified by the given names.
|
||||
*
|
||||
* @param names - Components to paint
|
||||
*
|
||||
* @return Operator function
|
||||
*/
|
||||
export function paintComponentMap(
|
||||
names: Component[] = ["title", "tabs", "container", "footer"]
|
||||
): MonoTypeOperatorFunction<ComponentMap> {
|
||||
return pipe(
|
||||
scan<ComponentMap>((prev, next) => {
|
||||
for (const name of names) {
|
||||
if (name in prev && typeof prev[name] !== "undefined") {
|
||||
prev[name]!.replaceWith(next[name]!)
|
||||
prev[name] = next[name]
|
||||
}
|
||||
}
|
||||
return prev
|
||||
})
|
||||
)
|
||||
}
|
@ -100,7 +100,8 @@ export function watchAnchorList(
|
||||
): Observable<AnchorList> {
|
||||
const table = new Map<HTMLAnchorElement, HTMLElement>()
|
||||
for (const el of els) {
|
||||
const target = getElement(decodeURIComponent(el.hash))
|
||||
const id = decodeURIComponent(el.hash.substring(1))
|
||||
const target = getElement(`[id="${id}"]`)
|
||||
if (typeof target !== "undefined")
|
||||
table.set(el, target)
|
||||
}
|
||||
|
@ -25,8 +25,7 @@ import {
|
||||
distinctUntilChanged,
|
||||
map,
|
||||
pluck,
|
||||
shareReplay,
|
||||
tap
|
||||
shareReplay
|
||||
} from "rxjs/operators"
|
||||
|
||||
import { ViewportOffset, ViewportSize } from "../../ui"
|
||||
|
@ -20,6 +20,7 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
export * from "./_"
|
||||
export * from "./anchor"
|
||||
export * from "./container"
|
||||
export * from "./header"
|
||||
|
@ -69,7 +69,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
/* Retrieve all summaries and polyfill open/close functionality */
|
||||
const summaries = document.querySelectorAll("details > summary")
|
||||
summaries.forEach(summary => {
|
||||
summary.addEventListener("click", ev => {
|
||||
summary.addEventListener("click", () => {
|
||||
const details = summary.parentNode as HTMLElement
|
||||
if (details.hasAttribute("open")) {
|
||||
details.removeAttribute("open")
|
||||
|
@ -20,7 +20,7 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { Observable, fromEvent } from "rxjs"
|
||||
import { Observable, Subject, fromEvent } from "rxjs"
|
||||
import { filter, map, share, startWith } from "rxjs/operators"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
@ -28,24 +28,46 @@ import { filter, map, share, startWith } from "rxjs/operators"
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Observable for window hash changes
|
||||
* Observable for window hash change events
|
||||
*/
|
||||
const hash$ = fromEvent<HashChangeEvent>(window, "hashchange")
|
||||
const hashchange$ = fromEvent<HashChangeEvent>(window, "hashchange")
|
||||
|
||||
/**
|
||||
* Observable for window pop state events
|
||||
*/
|
||||
const popstate$ = fromEvent<PopStateEvent>(window, "popstate")
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Create a subject to watch or alter the location
|
||||
*
|
||||
* @return Location subject
|
||||
*/
|
||||
export function watchLocation(): Subject<string> {
|
||||
const location$ = new Subject<string>()
|
||||
popstate$
|
||||
.pipe(
|
||||
map(() => location.href)
|
||||
)
|
||||
.subscribe(location$)
|
||||
|
||||
/* Return subject */
|
||||
return location$
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an observable to watch the location hash
|
||||
*
|
||||
* @return Location hash observable
|
||||
*/
|
||||
export function watchLocationHash(): Observable<string> {
|
||||
return hash$
|
||||
return hashchange$
|
||||
.pipe(
|
||||
map(() => document.location.hash),
|
||||
startWith(document.location.hash),
|
||||
map(() => location.hash),
|
||||
startWith(location.hash),
|
||||
filter(hash => hash.length > 0),
|
||||
share()
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user