mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2024-06-14 11:52:32 +03:00
Refactored project structure
This commit is contained in:
parent
ad1b964aaa
commit
88ba609ee1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
29
material/assets/javascripts/bundle.4fa4ff07.min.js
vendored
Normal file
29
material/assets/javascripts/bundle.4fa4ff07.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
material/assets/javascripts/bundle.4fa4ff07.min.js.map
Normal file
7
material/assets/javascripts/bundle.4fa4ff07.min.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -34,7 +34,7 @@
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.b5f74394.min.css' | url }}">
|
||||
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.b29cf17d.min.css' | url }}">
|
||||
{% if config.theme.palette %}
|
||||
{% set palette = config.theme.palette %}
|
||||
<link rel="stylesheet" href="{{ 'assets/stylesheets/palette.9204c3b2.min.css' | url }}">
|
||||
@ -211,7 +211,7 @@
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script src="{{ 'assets/javascripts/bundle.0d86bc28.min.js' | url }}"></script>
|
||||
<script src="{{ 'assets/javascripts/bundle.4fa4ff07.min.js' | url }}"></script>
|
||||
{% for path in config["extra_javascript"] %}
|
||||
<script src="{{ path | url }}"></script>
|
||||
{% endfor %}
|
||||
|
File diff suppressed because one or more lines are too long
18
material/overrides/assets/javascripts/bundle.5ee92cf1.min.js
vendored
Normal file
18
material/overrides/assets/javascripts/bundle.5ee92cf1.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
@ -16,5 +16,5 @@
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
{{ super() }}
|
||||
<script src="{{ 'overrides/assets/javascripts/bundle.2a83b894.min.js' | url }}"></script>
|
||||
<script src="{{ 'overrides/assets/javascripts/bundle.5ee92cf1.min.js' | url }}"></script>
|
||||
{% endblock %}
|
||||
|
@ -20,7 +20,7 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { getElementOrThrow, getLocation } from "~/browser"
|
||||
import { getElement, getLocation } from "~/browser"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Types
|
||||
@ -99,7 +99,7 @@ export interface Config {
|
||||
/**
|
||||
* Retrieve global configuration and make base URL absolute
|
||||
*/
|
||||
const script = getElementOrThrow("#__config")
|
||||
const script = getElement("#__config")
|
||||
const config: Config = JSON.parse(script.textContent!)
|
||||
config.base = `${new URL(config.base, getLocation())}`
|
||||
|
||||
|
@ -23,8 +23,7 @@
|
||||
import {
|
||||
ReplaySubject,
|
||||
Subject,
|
||||
fromEvent,
|
||||
mapTo
|
||||
fromEvent
|
||||
} from "rxjs"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
@ -40,12 +39,9 @@ import {
|
||||
* @returns Document subject
|
||||
*/
|
||||
export function watchDocument(): Subject<Document> {
|
||||
const document$ = new ReplaySubject<Document>()
|
||||
fromEvent(document, "DOMContentLoaded")
|
||||
.pipe(
|
||||
mapTo(document)
|
||||
)
|
||||
.subscribe(document$)
|
||||
const document$ = new ReplaySubject<Document>(1)
|
||||
fromEvent(document, "DOMContentLoaded", { once: true })
|
||||
.subscribe(() => document$.next(document))
|
||||
|
||||
/* Return document */
|
||||
return document$
|
||||
|
@ -24,72 +24,6 @@
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Retrieve an element matching the query selector
|
||||
*
|
||||
* @template T - Element type
|
||||
*
|
||||
* @param selector - Query selector
|
||||
* @param node - Node of reference
|
||||
*
|
||||
* @returns Element or nothing
|
||||
*/
|
||||
export function getElement<T extends keyof HTMLElementTagNameMap>(
|
||||
selector: T, node?: ParentNode
|
||||
): HTMLElementTagNameMap[T] | undefined
|
||||
|
||||
export function getElement<T extends HTMLElement>(
|
||||
selector: string, node?: ParentNode
|
||||
): T | undefined
|
||||
|
||||
export function getElement<T extends HTMLElement>(
|
||||
selector: string, node: ParentNode = document
|
||||
): T | undefined {
|
||||
return node.querySelector<T>(selector) || undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an element matching a query selector or throw a reference error
|
||||
*
|
||||
* @template T - Element type
|
||||
*
|
||||
* @param selector - Query selector
|
||||
* @param node - Node of reference
|
||||
*
|
||||
* @returns Element
|
||||
*/
|
||||
export function getElementOrThrow<T extends keyof HTMLElementTagNameMap>(
|
||||
selector: T, node?: ParentNode
|
||||
): HTMLElementTagNameMap[T]
|
||||
|
||||
export function getElementOrThrow<T extends HTMLElement>(
|
||||
selector: string, node?: ParentNode
|
||||
): T
|
||||
|
||||
export function getElementOrThrow<T extends HTMLElement>(
|
||||
selector: string, node: ParentNode = document
|
||||
): T {
|
||||
const el = getElement<T>(selector, node)
|
||||
if (typeof el === "undefined")
|
||||
throw new ReferenceError(
|
||||
`Missing element: expected "${selector}" to be present`
|
||||
)
|
||||
|
||||
/* Return element */
|
||||
return el
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the currently active element
|
||||
*
|
||||
* @returns Element or nothing
|
||||
*/
|
||||
export function getActiveElement(): HTMLElement | undefined {
|
||||
return document.activeElement instanceof HTMLElement
|
||||
? document.activeElement
|
||||
: undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all elements matching the query selector
|
||||
*
|
||||
@ -114,16 +48,73 @@ export function getElements<T extends HTMLElement>(
|
||||
return Array.from(node.querySelectorAll<T>(selector))
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an element matching a query selector or throw a reference error
|
||||
*
|
||||
* Note that this function assumes that the element is present. If unsure if an
|
||||
* element is existent, use the `getOptionalElement` function instead.
|
||||
*
|
||||
* @template T - Element type
|
||||
*
|
||||
* @param selector - Query selector
|
||||
* @param node - Node of reference
|
||||
*
|
||||
* @returns Element
|
||||
*/
|
||||
export function getElement<T extends keyof HTMLElementTagNameMap>(
|
||||
selector: T, node?: ParentNode
|
||||
): HTMLElementTagNameMap[T]
|
||||
|
||||
export function getElement<T extends HTMLElement>(
|
||||
selector: string, node?: ParentNode
|
||||
): T
|
||||
|
||||
export function getElement<T extends HTMLElement>(
|
||||
selector: string, node: ParentNode = document
|
||||
): T {
|
||||
const el = getOptionalElement<T>(selector, node)
|
||||
if (typeof el === "undefined")
|
||||
throw new ReferenceError(
|
||||
`Missing element: expected "${selector}" to be present`
|
||||
)
|
||||
|
||||
/* Return element */
|
||||
return el
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Replace an element with the given list of nodes
|
||||
* Retrieve an optional element matching the query selector
|
||||
*
|
||||
* @param el - Element
|
||||
* @param nodes - Replacement nodes
|
||||
* @template T - Element type
|
||||
*
|
||||
* @param selector - Query selector
|
||||
* @param node - Node of reference
|
||||
*
|
||||
* @returns Element or nothing
|
||||
*/
|
||||
export function replaceElement(
|
||||
el: HTMLElement, ...nodes: Node[]
|
||||
): void {
|
||||
el.replaceWith(...nodes)
|
||||
export function getOptionalElement<T extends keyof HTMLElementTagNameMap>(
|
||||
selector: T, node?: ParentNode
|
||||
): HTMLElementTagNameMap[T] | undefined
|
||||
|
||||
export function getOptionalElement<T extends HTMLElement>(
|
||||
selector: string, node?: ParentNode
|
||||
): T | undefined
|
||||
|
||||
export function getOptionalElement<T extends HTMLElement>(
|
||||
selector: string, node: ParentNode = document
|
||||
): T | undefined {
|
||||
return node.querySelector<T>(selector) || undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the currently active element
|
||||
*
|
||||
* @returns Element or nothing
|
||||
*/
|
||||
export function getActiveElement(): HTMLElement | undefined {
|
||||
return document.activeElement instanceof HTMLElement
|
||||
? document.activeElement || undefined
|
||||
: undefined
|
||||
}
|
||||
|
@ -25,3 +25,4 @@ export * from "./focus"
|
||||
export * from "./offset"
|
||||
export * from "./selection"
|
||||
export * from "./size"
|
||||
export * from "./visibility"
|
||||
|
77
src/assets/javascripts/browser/element/offset/_/index.ts
Normal file
77
src/assets/javascripts/browser/element/offset/_/index.ts
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2021 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,
|
||||
fromEvent,
|
||||
map,
|
||||
startWith
|
||||
} from "rxjs"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Types
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Element offset
|
||||
*/
|
||||
export interface ElementOffset {
|
||||
x: number /* Horizontal offset */
|
||||
y: number /* Vertical offset */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Retrieve element offset
|
||||
*
|
||||
* @param el - Element
|
||||
*
|
||||
* @returns Element offset
|
||||
*/
|
||||
export function getElementOffset(el: HTMLElement): ElementOffset {
|
||||
return {
|
||||
x: el.offsetLeft,
|
||||
y: el.offsetTop
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Watch element offset
|
||||
*
|
||||
* @param el - Element
|
||||
*
|
||||
* @returns Element offset observable
|
||||
*/
|
||||
export function watchElementOffset(
|
||||
el: HTMLElement
|
||||
): Observable<ElementOffset> {
|
||||
return fromEvent(window, "resize")
|
||||
.pipe(
|
||||
map(() => getElementOffset(el)),
|
||||
startWith(getElementOffset(el))
|
||||
)
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2021 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,
|
||||
fromEvent,
|
||||
map,
|
||||
merge,
|
||||
startWith
|
||||
} from "rxjs"
|
||||
|
||||
import { ElementOffset } from "../_"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Retrieve element content offset (= scroll offset)
|
||||
*
|
||||
* @param el - Element
|
||||
*
|
||||
* @returns Element content offset
|
||||
*/
|
||||
export function getElementContentOffset(el: HTMLElement): ElementOffset {
|
||||
return {
|
||||
x: el.scrollLeft,
|
||||
y: el.scrollTop
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Watch element content offset
|
||||
*
|
||||
* @param el - Element
|
||||
*
|
||||
* @returns Element content offset observable
|
||||
*/
|
||||
export function watchElementContentOffset(
|
||||
el: HTMLElement
|
||||
): Observable<ElementOffset> {
|
||||
return merge(
|
||||
fromEvent(el, "scroll"),
|
||||
fromEvent(window, "resize")
|
||||
)
|
||||
.pipe(
|
||||
map(() => getElementContentOffset(el)),
|
||||
startWith(getElementContentOffset(el))
|
||||
)
|
||||
}
|
@ -20,95 +20,5 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import {
|
||||
Observable,
|
||||
distinctUntilChanged,
|
||||
fromEvent,
|
||||
map,
|
||||
merge,
|
||||
startWith
|
||||
} from "rxjs"
|
||||
|
||||
import {
|
||||
getElementContentSize,
|
||||
getElementSize
|
||||
} from "../size"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Types
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Element offset
|
||||
*/
|
||||
export interface ElementOffset {
|
||||
x: number /* Horizontal offset */
|
||||
y: number /* Vertical offset */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Retrieve element offset
|
||||
*
|
||||
* @param el - Element
|
||||
*
|
||||
* @returns Element offset
|
||||
*/
|
||||
export function getElementOffset(el: HTMLElement): ElementOffset {
|
||||
return {
|
||||
x: el.scrollLeft,
|
||||
y: el.scrollTop
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Watch element offset
|
||||
*
|
||||
* @param el - Element
|
||||
*
|
||||
* @returns Element offset observable
|
||||
*/
|
||||
export function watchElementOffset(
|
||||
el: HTMLElement
|
||||
): Observable<ElementOffset> {
|
||||
return merge(
|
||||
fromEvent(el, "scroll"),
|
||||
fromEvent(window, "resize")
|
||||
)
|
||||
.pipe(
|
||||
map(() => getElementOffset(el)),
|
||||
startWith(getElementOffset(el))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch element threshold
|
||||
*
|
||||
* This function returns an observable which emits whether the bottom scroll
|
||||
* offset of an elements is within a certain threshold.
|
||||
*
|
||||
* @param el - Element
|
||||
* @param threshold - Threshold
|
||||
*
|
||||
* @returns Element threshold observable
|
||||
*/
|
||||
export function watchElementThreshold(
|
||||
el: HTMLElement, threshold = 16
|
||||
): Observable<boolean> {
|
||||
return watchElementOffset(el)
|
||||
.pipe(
|
||||
map(({ y }) => {
|
||||
const visible = getElementSize(el)
|
||||
const content = getElementContentSize(el)
|
||||
return y >= (
|
||||
content.height - visible.height - threshold
|
||||
)
|
||||
}),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
}
|
||||
export * from "./_"
|
||||
export * from "./content"
|
||||
|
138
src/assets/javascripts/browser/element/size/_/index.ts
Normal file
138
src/assets/javascripts/browser/element/size/_/index.ts
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2021 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 {
|
||||
NEVER,
|
||||
Observable,
|
||||
Subject,
|
||||
defer,
|
||||
filter,
|
||||
finalize,
|
||||
map,
|
||||
of,
|
||||
shareReplay,
|
||||
startWith,
|
||||
switchMap,
|
||||
tap
|
||||
} from "rxjs"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Types
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Element offset
|
||||
*/
|
||||
export interface ElementSize {
|
||||
width: number /* Element width */
|
||||
height: number /* Element height */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Data
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Resize observer entry subject
|
||||
*/
|
||||
const entry$ = new Subject<ResizeObserverEntry>()
|
||||
|
||||
/**
|
||||
* Resize observer observable
|
||||
*
|
||||
* This observable will create a `ResizeObserver` on the first subscription
|
||||
* and will automatically terminate it when there are no more subscribers.
|
||||
* It's quite important to centralize observation in a single `ResizeObserver`,
|
||||
* as the performance difference can be quite dramatic, as the link shows.
|
||||
*
|
||||
* @see https://bit.ly/3iIYfEm - Google Groups on performance
|
||||
*/
|
||||
const observer$ = defer(() => of(
|
||||
new ResizeObserver(entries => {
|
||||
for (const entry of entries)
|
||||
entry$.next(entry)
|
||||
})
|
||||
))
|
||||
.pipe(
|
||||
switchMap(observer => NEVER.pipe(startWith(observer))
|
||||
.pipe(
|
||||
finalize(() => observer.disconnect())
|
||||
)
|
||||
),
|
||||
shareReplay(1)
|
||||
)
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Retrieve element size
|
||||
*
|
||||
* @param el - Element
|
||||
*
|
||||
* @returns Element size
|
||||
*/
|
||||
export function getElementSize(el: HTMLElement): ElementSize {
|
||||
return {
|
||||
width: el.offsetWidth,
|
||||
height: el.offsetHeight
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Watch element size
|
||||
*
|
||||
* This function returns an observable that subscribes to a single internal
|
||||
* instance of `ResizeObserver` upon subscription, and emit resize events until
|
||||
* termination. Note that this function should not be called with the same
|
||||
* element twice, as the first unsubscription will terminate observation.
|
||||
*
|
||||
* Sadly, we can't use the `DOMRect` objects returned by the observer, because
|
||||
* we need the emitted values to be consistent with `getElementSize`, which will
|
||||
* return the used values (rounded) and not actual values (unrounded). Thus, we
|
||||
* use the `offset*` properties. See the linked GitHub issue.
|
||||
*
|
||||
* @see https://bit.ly/3m0k3he - GitHub issue
|
||||
*
|
||||
* @param el - Element
|
||||
*
|
||||
* @returns Element size observable
|
||||
*/
|
||||
export function watchElementSize(
|
||||
el: HTMLElement
|
||||
): Observable<ElementSize> {
|
||||
return observer$
|
||||
.pipe(
|
||||
tap(observer => observer.observe(el)),
|
||||
switchMap(observer => entry$
|
||||
.pipe(
|
||||
filter(({ target }) => target === el),
|
||||
finalize(() => observer.unobserve(el)),
|
||||
map(() => getElementSize(el))
|
||||
)
|
||||
),
|
||||
startWith(getElementSize(el))
|
||||
)
|
||||
}
|
41
src/assets/javascripts/browser/element/size/content/index.ts
Normal file
41
src/assets/javascripts/browser/element/size/content/index.ts
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2021 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 { ElementSize } from "../_"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Retrieve element content size (= scroll width and height)
|
||||
*
|
||||
* @param el - Element
|
||||
*
|
||||
* @returns Element content size
|
||||
*/
|
||||
export function getElementContentSize(el: HTMLElement): ElementSize {
|
||||
return {
|
||||
width: el.scrollWidth,
|
||||
height: el.scrollHeight
|
||||
}
|
||||
}
|
@ -20,133 +20,5 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import {
|
||||
NEVER,
|
||||
Observable,
|
||||
Subject,
|
||||
defer,
|
||||
filter,
|
||||
finalize,
|
||||
map,
|
||||
of,
|
||||
shareReplay,
|
||||
startWith,
|
||||
switchMap,
|
||||
tap
|
||||
} from "rxjs"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Types
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Element offset
|
||||
*/
|
||||
export interface ElementSize {
|
||||
width: number /* Element width */
|
||||
height: number /* Element height */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Data
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Resize observer entry subject
|
||||
*/
|
||||
const entry$ = new Subject<ResizeObserverEntry>()
|
||||
|
||||
/**
|
||||
* Resize observer observable
|
||||
*
|
||||
* This observable will create a `ResizeObserver` on the first subscription
|
||||
* and will automatically terminate it when there are no more subscribers.
|
||||
* It's quite important to centralize observation in a single `ResizeObserver`,
|
||||
* as the performance difference can be quite dramatic, as the link shows.
|
||||
*
|
||||
* @see https://bit.ly/3iIYfEm - Google Groups on performance
|
||||
*/
|
||||
const observer$ = defer(() => of(
|
||||
new ResizeObserver(entries => {
|
||||
for (const entry of entries)
|
||||
entry$.next(entry)
|
||||
})
|
||||
))
|
||||
.pipe(
|
||||
switchMap(resize => NEVER.pipe(startWith(resize))
|
||||
.pipe(
|
||||
finalize(() => resize.disconnect())
|
||||
)
|
||||
),
|
||||
shareReplay(1)
|
||||
)
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Retrieve element size
|
||||
*
|
||||
* @param el - Element
|
||||
*
|
||||
* @returns Element size
|
||||
*/
|
||||
export function getElementSize(el: HTMLElement): ElementSize {
|
||||
return {
|
||||
width: el.offsetWidth,
|
||||
height: el.offsetHeight
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve element content size, i.e. including overflowing content
|
||||
*
|
||||
* @param el - Element
|
||||
*
|
||||
* @returns Element size
|
||||
*/
|
||||
export function getElementContentSize(el: HTMLElement): ElementSize {
|
||||
return {
|
||||
width: el.scrollWidth,
|
||||
height: el.scrollHeight
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Watch element size
|
||||
*
|
||||
* This function returns an observable that subscribes to a single internal
|
||||
* instance of `ResizeObserver` upon subscription, and emit resize events until
|
||||
* termination. Note that this function should not be called with the same
|
||||
* element twice, as the first unsubscription will terminate observation.
|
||||
*
|
||||
* Sadly, we can't use the `DOMRect` objects returned by the observer, because
|
||||
* we need the emitted values to be consistent with `getElementSize`, which will
|
||||
* return the used values (rounded) and not actual values (unrounded). Thus, we
|
||||
* use the `offset*` properties. See the linked GitHub issue.
|
||||
*
|
||||
* @see https://bit.ly/3m0k3he - GitHub issue
|
||||
*
|
||||
* @param el - Element
|
||||
*
|
||||
* @returns Element size observable
|
||||
*/
|
||||
export function watchElementSize(
|
||||
el: HTMLElement
|
||||
): Observable<ElementSize> {
|
||||
return observer$
|
||||
.pipe(
|
||||
tap(observer => observer.observe(el)),
|
||||
switchMap(observer => entry$
|
||||
.pipe(
|
||||
filter(({ target }) => target === el),
|
||||
finalize(() => observer.unobserve(el)),
|
||||
map(() => getElementSize(el))
|
||||
)
|
||||
),
|
||||
startWith(getElementSize(el))
|
||||
)
|
||||
}
|
||||
export * from "./_"
|
||||
export * from "./content"
|
||||
|
131
src/assets/javascripts/browser/element/visibility/index.ts
Normal file
131
src/assets/javascripts/browser/element/visibility/index.ts
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2021 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 {
|
||||
NEVER,
|
||||
Observable,
|
||||
Subject,
|
||||
defer,
|
||||
distinctUntilChanged,
|
||||
filter,
|
||||
finalize,
|
||||
map,
|
||||
of,
|
||||
shareReplay,
|
||||
startWith,
|
||||
switchMap,
|
||||
tap
|
||||
} from "rxjs"
|
||||
|
||||
import {
|
||||
getElementContentSize,
|
||||
getElementSize,
|
||||
watchElementContentOffset
|
||||
} from "~/browser"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Data
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Intersection observer entry subject
|
||||
*/
|
||||
const entry$ = new Subject<IntersectionObserverEntry>()
|
||||
|
||||
/**
|
||||
* Intersection observer observable
|
||||
*
|
||||
* This observable will create an `IntersectionObserver` on first subscription
|
||||
* and will automatically terminate it when there are no more subscribers.
|
||||
*
|
||||
* @see https://bit.ly/3iIYfEm - Google Groups on performance
|
||||
*/
|
||||
const observer$ = defer(() => of(
|
||||
new IntersectionObserver(entries => {
|
||||
for (const entry of entries)
|
||||
entry$.next(entry)
|
||||
}, {
|
||||
threshold: 1
|
||||
})
|
||||
))
|
||||
.pipe(
|
||||
switchMap(observer => NEVER.pipe(startWith(observer))
|
||||
.pipe(
|
||||
finalize(() => observer.disconnect())
|
||||
)
|
||||
),
|
||||
shareReplay(1)
|
||||
)
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Watch element visibility
|
||||
*
|
||||
* @param el - Element
|
||||
*
|
||||
* @returns Element visibility observable
|
||||
*/
|
||||
export function watchElementVisibility(
|
||||
el: HTMLElement
|
||||
): Observable<boolean> {
|
||||
return observer$
|
||||
.pipe(
|
||||
tap(observer => observer.observe(el)),
|
||||
switchMap(observer => entry$
|
||||
.pipe(
|
||||
filter(({ target }) => target === el),
|
||||
finalize(() => observer.unobserve(el)),
|
||||
map(({ isIntersecting }) => isIntersecting)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch element boundary
|
||||
*
|
||||
* This function returns an observable which emits whether the bottom content
|
||||
* boundary (= scroll offset) of an element is within a certain threshold.
|
||||
*
|
||||
* @param el - Element
|
||||
* @param threshold - Threshold
|
||||
*
|
||||
* @returns Element threshold observable
|
||||
*/
|
||||
export function watchElementBoundary(
|
||||
el: HTMLElement, threshold = 16
|
||||
): Observable<boolean> {
|
||||
return watchElementContentOffset(el)
|
||||
.pipe(
|
||||
map(({ y }) => {
|
||||
const visible = getElementSize(el)
|
||||
const content = getElementContentSize(el)
|
||||
return y >= (
|
||||
content.height - visible.height - threshold
|
||||
)
|
||||
}),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
}
|
@ -29,7 +29,7 @@ import {
|
||||
startWith
|
||||
} from "rxjs"
|
||||
|
||||
import { getElement } from "~/browser"
|
||||
import { getOptionalElement } from "~/browser"
|
||||
import { h } from "~/utilities"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
@ -86,7 +86,7 @@ export function watchLocationHash(): Observable<string> {
|
||||
export function watchLocationTarget(): Observable<HTMLElement> {
|
||||
return watchLocationHash()
|
||||
.pipe(
|
||||
map(id => getElement(`[id="${id}"]`)!),
|
||||
map(id => getOptionalElement(`[id="${id}"]`)!),
|
||||
filter(el => typeof el !== "undefined")
|
||||
)
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ import {
|
||||
startWith
|
||||
} from "rxjs"
|
||||
|
||||
import { getElementOrThrow } from "../element"
|
||||
import { getElement } from "../element"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Types
|
||||
@ -48,8 +48,8 @@ export type Toggle =
|
||||
* Toggle map
|
||||
*/
|
||||
const toggles: Record<Toggle, HTMLInputElement> = {
|
||||
drawer: getElementOrThrow("[data-md-toggle=drawer]"),
|
||||
search: getElementOrThrow("[data-md-toggle=search]")
|
||||
drawer: getElement("[data-md-toggle=drawer]"),
|
||||
search: getElement("[data-md-toggle=search]")
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
|
@ -30,6 +30,7 @@ import {
|
||||
|
||||
import { Header } from "~/components"
|
||||
|
||||
import { getElementOffset } from "../../element"
|
||||
import {
|
||||
ViewportOffset,
|
||||
watchViewportOffset
|
||||
@ -102,10 +103,7 @@ export function watchViewportAt(
|
||||
/* Compute element offset */
|
||||
const offset$ = combineLatest([size$, header$])
|
||||
.pipe(
|
||||
map((): ViewportOffset => ({
|
||||
x: el.offsetLeft,
|
||||
y: el.offsetTop
|
||||
}))
|
||||
map(() => getElementOffset(el))
|
||||
)
|
||||
|
||||
/* Compute relative viewport, return hot observable */
|
||||
|
@ -54,8 +54,8 @@ export interface ViewportOffset {
|
||||
*/
|
||||
export function getViewportOffset(): ViewportOffset {
|
||||
return {
|
||||
x: Math.max(0, pageXOffset),
|
||||
y: Math.max(0, pageYOffset)
|
||||
x: Math.max(0, scrollX),
|
||||
y: Math.max(0, scrollY)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ import {
|
||||
import { configuration, feature } from "./_"
|
||||
import {
|
||||
at,
|
||||
getElement,
|
||||
getOptionalElement,
|
||||
requestJSON,
|
||||
setToggle,
|
||||
watchDocument,
|
||||
@ -96,6 +96,7 @@ const keyboard$ = watchKeyboard()
|
||||
const viewport$ = watchViewport()
|
||||
const tablet$ = watchMedia("(min-width: 960px)")
|
||||
const screen$ = watchMedia("(min-width: 1220px)")
|
||||
const hover$ = watchMedia("(hover)")
|
||||
const print$ = watchPrint()
|
||||
|
||||
/* Retrieve search index, if search is enabled */
|
||||
@ -139,7 +140,7 @@ keyboard$
|
||||
/* Go to previous page */
|
||||
case "p":
|
||||
case ",":
|
||||
const prev = getElement("[href][rel=prev]")
|
||||
const prev = getOptionalElement("[href][rel=prev]")
|
||||
if (typeof prev !== "undefined")
|
||||
prev.click()
|
||||
break
|
||||
@ -147,7 +148,7 @@ keyboard$
|
||||
/* Go to next page */
|
||||
case "n":
|
||||
case ".":
|
||||
const next = getElement("[href][rel=next]")
|
||||
const next = getOptionalElement("[href][rel=next]")
|
||||
if (typeof next !== "undefined")
|
||||
next.click()
|
||||
break
|
||||
@ -197,7 +198,7 @@ const content$ = defer(() => merge(
|
||||
|
||||
/* Content */
|
||||
...getComponentElements("content")
|
||||
.map(el => mountContent(el, { target$, viewport$, print$ })),
|
||||
.map(el => mountContent(el, { target$, viewport$, hover$, print$ })),
|
||||
|
||||
/* Search highlighting */
|
||||
...getComponentElements("content")
|
||||
@ -250,8 +251,9 @@ window.location$ = location$ /* Location subject */
|
||||
window.target$ = target$ /* Location target observable */
|
||||
window.keyboard$ = keyboard$ /* Keyboard observable */
|
||||
window.viewport$ = viewport$ /* Viewport observable */
|
||||
window.tablet$ = tablet$ /* Tablet observable */
|
||||
window.screen$ = screen$ /* Screen observable */
|
||||
window.print$ = print$ /* Print observable */
|
||||
window.tablet$ = tablet$ /* Media tablet observable */
|
||||
window.screen$ = screen$ /* Media screen observable */
|
||||
window.hover$ = hover$ /* Media hover observable */
|
||||
window.print$ = print$ /* Media print observable */
|
||||
window.alert$ = alert$ /* Alert subject */
|
||||
window.component$ = component$ /* Component observable */
|
||||
|
@ -20,7 +20,7 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { getElementOrThrow, getElements } from "~/browser"
|
||||
import { getElement, getElements } from "~/browser"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Types
|
||||
@ -114,7 +114,7 @@ interface ComponentTypeMap {
|
||||
export function getComponentElement<T extends ComponentType>(
|
||||
type: T, node: ParentNode = document
|
||||
): ComponentTypeMap[T] {
|
||||
return getElementOrThrow(`[data-md-component=${type}]`, node)
|
||||
return getElement(`[data-md-component=${type}]`, node)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -53,7 +53,8 @@ export type Content =
|
||||
interface MountOptions {
|
||||
target$: Observable<HTMLElement> /* Location target observable */
|
||||
viewport$: Observable<Viewport> /* Viewport observable */
|
||||
print$: Observable<boolean> /* Print observable */
|
||||
hover$: Observable<boolean> /* Media hover observable */
|
||||
print$: Observable<boolean> /* Media print observable */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
@ -72,13 +73,13 @@ interface MountOptions {
|
||||
* @returns Content component observable
|
||||
*/
|
||||
export function mountContent(
|
||||
el: HTMLElement, { target$, viewport$, print$ }: MountOptions
|
||||
el: HTMLElement, { target$, viewport$, hover$, print$ }: MountOptions
|
||||
): Observable<Component<Content>> {
|
||||
return merge(
|
||||
|
||||
/* Code blocks */
|
||||
...getElements("pre > code", el)
|
||||
.map(child => mountCodeBlock(child, { viewport$, print$ })),
|
||||
.map(child => mountCodeBlock(child, { viewport$, hover$, print$ })),
|
||||
|
||||
/* Data tables */
|
||||
...getElements("table:not([class])", el)
|
||||
|
@ -41,14 +41,17 @@ import {
|
||||
} from "rxjs"
|
||||
|
||||
import { feature } from "~/_"
|
||||
import { resetFocusable, setFocusable } from "~/actions"
|
||||
import {
|
||||
resetFocusable,
|
||||
setFocusable
|
||||
} from "~/actions"
|
||||
import {
|
||||
Viewport,
|
||||
getElement,
|
||||
getElementContentSize,
|
||||
getElementOrThrow,
|
||||
getElementSize,
|
||||
getElements,
|
||||
getOptionalElement,
|
||||
watchMedia
|
||||
} from "~/browser"
|
||||
import {
|
||||
@ -174,7 +177,7 @@ export function watchCodeBlock(
|
||||
container.insertAdjacentElement("afterend", list)
|
||||
for (const annotation of annotations) {
|
||||
const id = parseInt(annotation.getAttribute("data-index")!, 10)
|
||||
const typeset = getElement(":scope .md-typeset", annotation)!
|
||||
const typeset = getOptionalElement(":scope .md-typeset", annotation)!
|
||||
items[id - 1].append(...Array.from(typeset.childNodes))
|
||||
}
|
||||
} else {
|
||||
@ -182,7 +185,7 @@ export function watchCodeBlock(
|
||||
for (const annotation of annotations) {
|
||||
const id = parseInt(annotation.getAttribute("data-index")!, 10)
|
||||
const nodes = items[id - 1].childNodes
|
||||
getElementOrThrow(":scope .md-typeset", annotation)
|
||||
getElement(":scope .md-typeset", annotation)
|
||||
.append(...Array.from(nodes))
|
||||
}
|
||||
}
|
||||
@ -239,7 +242,7 @@ export function mountCodeBlock(
|
||||
take(1),
|
||||
takeWhile(({ annotations }) => !!annotations?.length),
|
||||
map(({ annotations }) => annotations!
|
||||
.map(annotation => getElementOrThrow(".md-tooltip", annotation))
|
||||
.map(annotation => getElement(".md-tooltip", annotation))
|
||||
),
|
||||
combineLatestWith(viewport$
|
||||
.pipe(
|
||||
|
@ -54,7 +54,7 @@ export interface Details {
|
||||
*/
|
||||
interface WatchOptions {
|
||||
target$: Observable<HTMLElement> /* Location target observable */
|
||||
print$: Observable<boolean> /* Print observable */
|
||||
print$: Observable<boolean> /* Media print observable */
|
||||
}
|
||||
|
||||
/**
|
||||
@ -62,7 +62,7 @@ interface WatchOptions {
|
||||
*/
|
||||
interface MountOptions {
|
||||
target$: Observable<HTMLElement> /* Location target observable */
|
||||
print$: Observable<boolean> /* Print observable */
|
||||
print$: Observable<boolean> /* Media print observable */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
|
@ -22,7 +22,6 @@
|
||||
|
||||
import { Observable, of } from "rxjs"
|
||||
|
||||
import { replaceElement } from "~/browser"
|
||||
import { renderTable } from "~/templates"
|
||||
import { h } from "~/utilities"
|
||||
|
||||
@ -63,8 +62,8 @@ const sentinel = h("table")
|
||||
export function mountDataTable(
|
||||
el: HTMLElement
|
||||
): Observable<Component<DataTable>> {
|
||||
replaceElement(el, sentinel)
|
||||
replaceElement(sentinel, renderTable(el))
|
||||
el.replaceWith(sentinel)
|
||||
sentinel.replaceWith(renderTable(el))
|
||||
|
||||
/* Create and return component */
|
||||
return of({ ref: el })
|
||||
|
@ -21,7 +21,6 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
NEVER,
|
||||
Observable,
|
||||
Subject,
|
||||
finalize,
|
||||
@ -33,7 +32,7 @@ import {
|
||||
} from "rxjs"
|
||||
|
||||
import {
|
||||
getElementOrThrow,
|
||||
getElement,
|
||||
getElements
|
||||
} from "~/browser"
|
||||
|
||||
@ -64,15 +63,12 @@ export interface ContentTabs {
|
||||
export function watchContentTabs(
|
||||
el: HTMLElement
|
||||
): Observable<ContentTabs> {
|
||||
if (!el.classList.contains("tabbed-alternate"))
|
||||
return NEVER
|
||||
else
|
||||
return merge(...getElements(":scope > input", el)
|
||||
.map(input => fromEvent(input, "change").pipe(mapTo(input.id)))
|
||||
)
|
||||
.pipe(
|
||||
map(id => ({
|
||||
active: getElementOrThrow<HTMLLabelElement>(`label[for=${id}]`)
|
||||
active: getElement<HTMLLabelElement>(`label[for=${id}]`)
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
@ -40,7 +40,10 @@ import {
|
||||
} from "rxjs"
|
||||
|
||||
import { feature } from "~/_"
|
||||
import { resetHeaderState, setHeaderState } from "~/actions"
|
||||
import {
|
||||
resetHeaderState,
|
||||
setHeaderState
|
||||
} from "~/actions"
|
||||
import {
|
||||
Viewport,
|
||||
watchElementSize,
|
||||
|
@ -38,8 +38,8 @@ import {
|
||||
} from "~/actions"
|
||||
import {
|
||||
Viewport,
|
||||
getElement,
|
||||
getElementSize,
|
||||
getOptionalElement,
|
||||
watchViewportAt
|
||||
} from "~/browser"
|
||||
|
||||
@ -131,7 +131,7 @@ export function mountHeaderTitle(
|
||||
})
|
||||
|
||||
/* Obtain headline, if any */
|
||||
const headline = getElement<HTMLHeadingElement>("article h1")
|
||||
const headline = getOptionalElement<HTMLHeadingElement>("article h1")
|
||||
if (typeof headline === "undefined")
|
||||
return NEVER
|
||||
|
||||
|
@ -29,7 +29,10 @@ import {
|
||||
switchMap
|
||||
} from "rxjs"
|
||||
|
||||
import { Viewport, watchElementSize } from "~/browser"
|
||||
import {
|
||||
Viewport,
|
||||
watchElementSize
|
||||
} from "~/browser"
|
||||
|
||||
import { Header } from "../header"
|
||||
|
||||
|
@ -53,10 +53,19 @@ import {
|
||||
getComponentElement,
|
||||
getComponentElements
|
||||
} from "../../_"
|
||||
import { SearchQuery, mountSearchQuery } from "../query"
|
||||
import {
|
||||
SearchQuery,
|
||||
mountSearchQuery
|
||||
} from "../query"
|
||||
import { mountSearchResult } from "../result"
|
||||
import { SearchShare, mountSearchShare } from "../share"
|
||||
import { SearchSuggest, mountSearchSuggest } from "../suggest"
|
||||
import {
|
||||
SearchShare,
|
||||
mountSearchShare
|
||||
} from "../share"
|
||||
import {
|
||||
SearchSuggest,
|
||||
mountSearchSuggest
|
||||
} from "../suggest"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Types
|
||||
|
@ -46,8 +46,8 @@ import {
|
||||
setSearchResultMeta
|
||||
} from "~/actions"
|
||||
import {
|
||||
getElementOrThrow,
|
||||
watchElementThreshold
|
||||
getElement,
|
||||
watchElementBoundary
|
||||
} from "~/browser"
|
||||
import {
|
||||
SearchResult,
|
||||
@ -91,14 +91,14 @@ export function mountSearchResult(
|
||||
el: HTMLElement, { rx$ }: SearchWorker, { query$ }: MountOptions
|
||||
): Observable<Component<SearchResult>> {
|
||||
const internal$ = new Subject<SearchResult>()
|
||||
const boundary$ = watchElementThreshold(el.parentElement!)
|
||||
const boundary$ = watchElementBoundary(el.parentElement!)
|
||||
.pipe(
|
||||
filter(Boolean)
|
||||
)
|
||||
|
||||
/* Retrieve nested components */
|
||||
const meta = getElementOrThrow(":scope > :first-child", el)
|
||||
const list = getElementOrThrow(":scope > :last-child", el)
|
||||
const meta = getElement(":scope > :first-child", el)
|
||||
const list = getElement(":scope > :last-child", el)
|
||||
|
||||
/* Wait until search is ready */
|
||||
const ready$ = rx$
|
||||
|
@ -98,9 +98,10 @@ interface MountOptions {
|
||||
export function watchSidebar(
|
||||
el: HTMLElement, { viewport$, main$ }: WatchOptions
|
||||
): Observable<Sidebar> {
|
||||
const parent = el.parentElement!
|
||||
const adjust =
|
||||
el.parentElement!.offsetTop -
|
||||
el.parentElement!.parentElement!.offsetTop
|
||||
parent.offsetTop -
|
||||
parent.parentElement!.offsetTop
|
||||
|
||||
/* Compute the sidebar's available height and if it should be locked */
|
||||
return combineLatest([main$, viewport$])
|
||||
|
@ -34,11 +34,17 @@ import {
|
||||
tap
|
||||
} from "rxjs"
|
||||
|
||||
import { setSourceFacts, setSourceState } from "~/actions"
|
||||
import {
|
||||
setSourceFacts,
|
||||
setSourceState
|
||||
} from "~/actions"
|
||||
import { renderSourceFacts } from "~/templates"
|
||||
|
||||
import { Component } from "../../_"
|
||||
import { SourceFacts, fetchSourceFacts } from "../facts"
|
||||
import {
|
||||
SourceFacts,
|
||||
fetchSourceFacts
|
||||
} from "../facts"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Types
|
||||
|
@ -34,7 +34,10 @@ import {
|
||||
} from "rxjs"
|
||||
|
||||
import { feature } from "~/_"
|
||||
import { resetTabsState, setTabsState } from "~/actions"
|
||||
import {
|
||||
resetTabsState,
|
||||
setTabsState
|
||||
} from "~/actions"
|
||||
import {
|
||||
Viewport,
|
||||
watchElementSize,
|
||||
|
@ -48,9 +48,9 @@ import {
|
||||
} from "~/actions"
|
||||
import {
|
||||
Viewport,
|
||||
getElement,
|
||||
getElements,
|
||||
getLocation,
|
||||
getOptionalElement,
|
||||
watchElementSize
|
||||
} from "~/browser"
|
||||
|
||||
@ -122,7 +122,7 @@ export function watchTableOfContents(
|
||||
const anchors = getElements<HTMLAnchorElement>("[href^=\\#]", el)
|
||||
for (const anchor of anchors) {
|
||||
const id = decodeURIComponent(anchor.hash.substring(1))
|
||||
const target = getElement(`[id="${id}"]`)
|
||||
const target = getOptionalElement(`[id="${id}"]`)
|
||||
if (typeof target !== "undefined")
|
||||
table.set(anchor, target)
|
||||
}
|
||||
|
@ -43,7 +43,10 @@ import {
|
||||
setBackToTopState,
|
||||
setFocusable
|
||||
} from "~/actions"
|
||||
import { Viewport, setElementFocus } from "~/browser"
|
||||
import {
|
||||
Viewport,
|
||||
setElementFocus
|
||||
} from "~/browser"
|
||||
|
||||
import { Component } from "../_"
|
||||
import { Header } from "../header"
|
||||
|
@ -25,7 +25,7 @@ import { Observable, Subject } from "rxjs"
|
||||
|
||||
import { translation } from "~/_"
|
||||
import {
|
||||
getElementOrThrow,
|
||||
getElement,
|
||||
getElements
|
||||
} from "~/browser"
|
||||
|
||||
@ -85,7 +85,7 @@ export function setupClipboardJS(
|
||||
new ClipboardJS("[data-clipboard-target], [data-clipboard-text]", {
|
||||
text: el => (
|
||||
el.getAttribute("data-clipboard-text")! ||
|
||||
extract(getElementOrThrow(
|
||||
extract(getElement(
|
||||
el.getAttribute("data-clipboard-target")!
|
||||
))
|
||||
)
|
||||
|
@ -47,9 +47,8 @@ import { configuration, feature } from "~/_"
|
||||
import {
|
||||
Viewport,
|
||||
ViewportOffset,
|
||||
getElement,
|
||||
getElements,
|
||||
replaceElement,
|
||||
getOptionalElement,
|
||||
request,
|
||||
requestXML,
|
||||
setLocation,
|
||||
@ -166,7 +165,7 @@ export function setupInstantLoading(
|
||||
}
|
||||
|
||||
/* Hack: ensure absolute favicon link to omit 404s when switching */
|
||||
const favicon = getElement<HTMLLinkElement>("link[rel=icon]")
|
||||
const favicon = getOptionalElement<HTMLLinkElement>("link[rel=icon]")
|
||||
if (typeof favicon !== "undefined")
|
||||
favicon.href = favicon.href
|
||||
|
||||
@ -286,13 +285,13 @@ export function setupInstantLoading(
|
||||
? ["[data-md-component=tabs]"]
|
||||
: []
|
||||
]) {
|
||||
const source = getElement(selector)
|
||||
const target = getElement(selector, replacement)
|
||||
const source = getOptionalElement(selector)
|
||||
const target = getOptionalElement(selector, replacement)
|
||||
if (
|
||||
typeof source !== "undefined" &&
|
||||
typeof target !== "undefined"
|
||||
) {
|
||||
replaceElement(source, target)
|
||||
source.replaceWith(target)
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -308,7 +307,7 @@ export function setupInstantLoading(
|
||||
if (el.src) {
|
||||
for (const name of el.getAttributeNames())
|
||||
script.setAttribute(name, el.getAttribute(name)!)
|
||||
replaceElement(el, script)
|
||||
el.replaceWith(script)
|
||||
|
||||
/* Complete when script is loaded */
|
||||
return new Observable(observer => {
|
||||
@ -318,7 +317,7 @@ export function setupInstantLoading(
|
||||
/* Complete immediately */
|
||||
} else {
|
||||
script.textContent = el.textContent
|
||||
replaceElement(el, script)
|
||||
el.replaceWith(script)
|
||||
return EMPTY
|
||||
}
|
||||
})
|
||||
|
@ -24,7 +24,7 @@ import { combineLatest, map } from "rxjs"
|
||||
|
||||
import { configuration } from "~/_"
|
||||
import {
|
||||
getElementOrThrow,
|
||||
getElement,
|
||||
requestJSON
|
||||
} from "~/browser"
|
||||
import { getComponentElements } from "~/components"
|
||||
@ -60,7 +60,7 @@ export function setupVersionSelector(): void {
|
||||
/* Render version selector and warning */
|
||||
combineLatest([versions$, current$])
|
||||
.subscribe(([versions, current]) => {
|
||||
const topic = getElementOrThrow(".md-header__topic")
|
||||
const topic = getElement(".md-header__topic")
|
||||
topic.appendChild(renderVersionSelector(versions, current))
|
||||
|
||||
/* Check if version state was already determined */
|
||||
|
@ -43,7 +43,7 @@ import { getElements } from "~/browser"
|
||||
*/
|
||||
interface PatchOptions {
|
||||
document$: Observable<Document> /* Document observable */
|
||||
tablet$: Observable<boolean> /* Tablet breakpoint observable */
|
||||
tablet$: Observable<boolean> /* Media tablet observable */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
|
@ -32,8 +32,14 @@ import {
|
||||
withLatestFrom
|
||||
} from "rxjs"
|
||||
|
||||
import { resetScrollLock, setScrollLock } from "~/actions"
|
||||
import { Viewport, watchToggle } from "~/browser"
|
||||
import {
|
||||
resetScrollLock,
|
||||
setScrollLock
|
||||
} from "~/actions"
|
||||
import {
|
||||
Viewport,
|
||||
watchToggle
|
||||
} from "~/browser"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Helper types
|
||||
@ -44,7 +50,7 @@ import { Viewport, watchToggle } from "~/browser"
|
||||
*/
|
||||
interface PatchOptions {
|
||||
viewport$: Observable<Viewport> /* Viewport observable */
|
||||
tablet$: Observable<boolean> /* Tablet breakpoint observable */
|
||||
tablet$: Observable<boolean> /* Media tablet observable */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
|
@ -27,7 +27,7 @@ import { h } from "~/utilities"
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Render a 'copy-to-clipboard' button
|
||||
* Render a a code annotation
|
||||
*
|
||||
* @param id - Unique identifier
|
||||
* @param content - Annotation content
|
||||
|
@ -20,7 +20,7 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { getElementOrThrow, getElements } from "~/browser"
|
||||
import { getElement, getElements } from "~/browser"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Types
|
||||
@ -84,7 +84,7 @@ interface ComponentTypeMap {
|
||||
export function getComponentElement<T extends ComponentType>(
|
||||
type: T, node: ParentNode = document
|
||||
): ComponentTypeMap[T] {
|
||||
return getElementOrThrow(`[data-mdx-component=${type}]`, node)
|
||||
return getElement(`[data-mdx-component=${type}]`, node)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,8 +47,8 @@ import {
|
||||
setSearchResultMeta
|
||||
} from "~/actions"
|
||||
import {
|
||||
getElementOrThrow,
|
||||
watchElementThreshold
|
||||
getElement,
|
||||
watchElementBoundary
|
||||
} from "~/browser"
|
||||
|
||||
import { Icon, renderIconSearchResult } from "_/templates"
|
||||
@ -147,13 +147,13 @@ export function mountIconSearchResult(
|
||||
el: HTMLElement, { index$, query$ }: MountOptions
|
||||
): Observable<Component<IconSearchResult, HTMLElement>> {
|
||||
const internal$ = new Subject<IconSearchResult>()
|
||||
const boundary$ = watchElementThreshold(el)
|
||||
const boundary$ = watchElementBoundary(el)
|
||||
.pipe(
|
||||
filter(Boolean)
|
||||
)
|
||||
|
||||
/* Update search result metadata */
|
||||
const meta = getElementOrThrow(":scope > :first-child", el)
|
||||
const meta = getElement(":scope > :first-child", el)
|
||||
internal$
|
||||
.pipe(
|
||||
observeOn(animationFrameScheduler),
|
||||
@ -167,7 +167,7 @@ export function mountIconSearchResult(
|
||||
})
|
||||
|
||||
/* Update icon search result list */
|
||||
const list = getElementOrThrow(":scope > :last-child", el)
|
||||
const list = getElement(":scope > :last-child", el)
|
||||
internal$
|
||||
.pipe(
|
||||
observeOn(animationFrameScheduler),
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
import { Observable, map } from "rxjs"
|
||||
|
||||
import { getElementOrThrow, requestJSON } from "~/browser"
|
||||
import { getElement, requestJSON } from "~/browser"
|
||||
|
||||
import { renderPrivateSponsor, renderPublicSponsor } from "_/templates"
|
||||
|
||||
@ -121,7 +121,7 @@ export function mountSponsorship(
|
||||
el.removeAttribute("hidden")
|
||||
|
||||
/* Render public sponsors with avatar and links */
|
||||
const list = getElementOrThrow(":scope > :first-child", el)
|
||||
const list = getElement(":scope > :first-child", el)
|
||||
for (const sponsor of sponsorship.sponsors)
|
||||
if (sponsor.type === "public")
|
||||
list.appendChild(renderPublicSponsor(sponsor.user))
|
||||
|
@ -182,6 +182,7 @@ export function transformScript(
|
||||
map: Buffer.from(data, "base64")
|
||||
})
|
||||
}),
|
||||
catchError(() => NEVER),
|
||||
switchMap(({ js, map }) => {
|
||||
const file = digest(options.to, js)
|
||||
return concat(
|
||||
|
7
typings/_/index.d.ts
vendored
7
typings/_/index.d.ts
vendored
@ -106,9 +106,10 @@ declare global {
|
||||
var target$: Observable<HTMLElement> /* Location target observable */
|
||||
var keyboard$: Observable<Keyboard> /* Keyboard observable */
|
||||
var viewport$: Observable<Viewport> /* Viewport obsevable */
|
||||
var tablet$: Observable<boolean> /* Tablet breakpoint observable */
|
||||
var screen$: Observable<boolean> /* Screen breakpoint observable */
|
||||
var print$: Observable<boolean> /* Print observable */
|
||||
var tablet$: Observable<boolean> /* Media tablet observable */
|
||||
var screen$: Observable<boolean> /* Media screen observable */
|
||||
var hover$: Observable<boolean> /* Media hover observable */
|
||||
var print$: Observable<boolean> /* Media print observable */
|
||||
var alert$: Subject<string> /* Alert subject */
|
||||
var component$: Observable<Component>/* Component observable */
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user