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:
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> {
|
): Observable<AnchorList> {
|
||||||
const table = new Map<HTMLAnchorElement, HTMLElement>()
|
const table = new Map<HTMLAnchorElement, HTMLElement>()
|
||||||
for (const el of els) {
|
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")
|
if (typeof target !== "undefined")
|
||||||
table.set(el, target)
|
table.set(el, target)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ import {
|
|||||||
distinctUntilChanged,
|
distinctUntilChanged,
|
||||||
map,
|
map,
|
||||||
pluck,
|
pluck,
|
||||||
shareReplay,
|
shareReplay
|
||||||
tap
|
|
||||||
} from "rxjs/operators"
|
} from "rxjs/operators"
|
||||||
|
|
||||||
import { ViewportOffset, ViewportSize } from "../../ui"
|
import { ViewportOffset, ViewportSize } from "../../ui"
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
* IN THE SOFTWARE.
|
* IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export * from "./_"
|
||||||
export * from "./anchor"
|
export * from "./anchor"
|
||||||
export * from "./container"
|
export * from "./container"
|
||||||
export * from "./header"
|
export * from "./header"
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
/* Retrieve all summaries and polyfill open/close functionality */
|
/* Retrieve all summaries and polyfill open/close functionality */
|
||||||
const summaries = document.querySelectorAll("details > summary")
|
const summaries = document.querySelectorAll("details > summary")
|
||||||
summaries.forEach(summary => {
|
summaries.forEach(summary => {
|
||||||
summary.addEventListener("click", ev => {
|
summary.addEventListener("click", () => {
|
||||||
const details = summary.parentNode as HTMLElement
|
const details = summary.parentNode as HTMLElement
|
||||||
if (details.hasAttribute("open")) {
|
if (details.hasAttribute("open")) {
|
||||||
details.removeAttribute("open")
|
details.removeAttribute("open")
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
* IN THE SOFTWARE.
|
* IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Observable, fromEvent } from "rxjs"
|
import { Observable, Subject, fromEvent } from "rxjs"
|
||||||
import { filter, map, share, startWith } from "rxjs/operators"
|
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
|
* 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
|
* Create an observable to watch the location hash
|
||||||
*
|
*
|
||||||
* @return Location hash observable
|
* @return Location hash observable
|
||||||
*/
|
*/
|
||||||
export function watchLocationHash(): Observable<string> {
|
export function watchLocationHash(): Observable<string> {
|
||||||
return hash$
|
return hashchange$
|
||||||
.pipe(
|
.pipe(
|
||||||
map(() => document.location.hash),
|
map(() => location.hash),
|
||||||
startWith(document.location.hash),
|
startWith(location.hash),
|
||||||
filter(hash => hash.length > 0),
|
filter(hash => hash.length > 0),
|
||||||
share()
|
share()
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user