mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2024-11-27 17:00:54 +01:00
Fixed observable completion semantics
This commit is contained in:
parent
abe475e151
commit
c30c3d196e
29
material/assets/javascripts/bundle.054bf2ee.min.js
vendored
Normal file
29
material/assets/javascripts/bundle.054bf2ee.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
material/assets/javascripts/bundle.054bf2ee.min.js.map
Normal file
7
material/assets/javascripts/bundle.054bf2ee.min.js.map
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
@ -213,7 +213,7 @@
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script src="{{ 'assets/javascripts/bundle.7c769d4b.min.js' | url }}"></script>
|
||||
<script src="{{ 'assets/javascripts/bundle.054bf2ee.min.js' | url }}"></script>
|
||||
{% for path in config["extra_javascript"] %}
|
||||
<script src="{{ path | url }}"></script>
|
||||
{% endfor %}
|
||||
|
18
material/overrides/assets/javascripts/bundle.a08d04cf.min.js
vendored
Normal file
18
material/overrides/assets/javascripts/bundle.a08d04cf.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
@ -16,5 +16,5 @@
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
{{ super() }}
|
||||
<script src="{{ 'overrides/assets/javascripts/bundle.afb943e6.min.js' | url }}"></script>
|
||||
<script src="{{ 'overrides/assets/javascripts/bundle.a08d04cf.min.js' | url }}"></script>
|
||||
{% endblock %}
|
||||
|
@ -1,78 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set focusable property
|
||||
*
|
||||
* @param el - Element
|
||||
* @param value - Tabindex value
|
||||
*/
|
||||
export function setFocusable(
|
||||
el: HTMLElement, value = 0
|
||||
): void {
|
||||
el.setAttribute("tabindex", value.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset focusable property
|
||||
*
|
||||
* @param el - Element
|
||||
*/
|
||||
export function resetFocusable(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
el.removeAttribute("tabindex")
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set scroll lock
|
||||
*
|
||||
* @param el - Scrollable element
|
||||
* @param value - Vertical offset
|
||||
*/
|
||||
export function setScrollLock(
|
||||
el: HTMLElement, value: number
|
||||
): void {
|
||||
el.setAttribute("data-md-state", "lock")
|
||||
el.style.top = `-${value}px`
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset scroll lock
|
||||
*
|
||||
* @param el - Scrollable element
|
||||
*/
|
||||
export function resetScrollLock(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
const value = -1 * parseInt(el.style.top, 10)
|
||||
el.removeAttribute("data-md-state")
|
||||
el.style.top = ""
|
||||
if (value)
|
||||
window.scrollTo(0, value)
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set anchor state
|
||||
*
|
||||
* @param el - Anchor element
|
||||
* @param state - Anchor state
|
||||
*/
|
||||
export function setAnchorState(
|
||||
el: HTMLElement, state: "blur"
|
||||
): void {
|
||||
el.setAttribute("data-md-state", state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset anchor state
|
||||
*
|
||||
* @param el - Anchor element
|
||||
*/
|
||||
export function resetAnchorState(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
el.removeAttribute("data-md-state")
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set anchor active
|
||||
*
|
||||
* @param el - Anchor element
|
||||
* @param value - Whether the anchor is active
|
||||
*/
|
||||
export function setAnchorActive(
|
||||
el: HTMLElement, value: boolean
|
||||
): void {
|
||||
el.classList.toggle("md-nav__link--active", value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset anchor active
|
||||
*
|
||||
* @param el - Anchor element
|
||||
*/
|
||||
export function resetAnchorActive(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
el.classList.remove("md-nav__link--active")
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set dialog message
|
||||
*
|
||||
* @param el - Dialog element
|
||||
* @param value - Dialog message
|
||||
*/
|
||||
export function setDialogMessage(
|
||||
el: HTMLElement, value: string
|
||||
): void {
|
||||
el.firstElementChild!.innerHTML = value
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set dialog state
|
||||
*
|
||||
* @param el - Dialog element
|
||||
* @param state - Dialog state
|
||||
*/
|
||||
export function setDialogState(
|
||||
el: HTMLElement, state: "open"
|
||||
): void {
|
||||
el.setAttribute("data-md-state", state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset dialog state
|
||||
*
|
||||
* @param el - Dialog element
|
||||
*/
|
||||
export function resetDialogState(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
el.removeAttribute("data-md-state")
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set header state
|
||||
*
|
||||
* @param el - Header element
|
||||
* @param state - Header state
|
||||
*/
|
||||
export function setHeaderState(
|
||||
el: HTMLElement, state: "shadow" | "hidden"
|
||||
): void {
|
||||
el.setAttribute("data-md-state", state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset header state
|
||||
*
|
||||
* @param el - Header element
|
||||
*/
|
||||
export function resetHeaderState(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
el.removeAttribute("data-md-state")
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from "./_"
|
||||
export * from "./title"
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set header title state
|
||||
*
|
||||
* @param el - Header title element
|
||||
* @param state - Header title state
|
||||
*/
|
||||
export function setHeaderTitleState(
|
||||
el: HTMLElement, state: "active"
|
||||
): void {
|
||||
el.setAttribute("data-md-state", state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset header title state
|
||||
*
|
||||
* @param el - Header title element
|
||||
*/
|
||||
export function resetHeaderTitleState(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
el.removeAttribute("data-md-state")
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from "./_"
|
||||
export * from "./anchor"
|
||||
export * from "./dialog"
|
||||
export * from "./header"
|
||||
export * from "./search"
|
||||
export * from "./sidebar"
|
||||
export * from "./source"
|
||||
export * from "./tabs"
|
||||
export * from "./top"
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from "./query"
|
||||
export * from "./result"
|
@ -1,50 +0,0 @@
|
||||
/*
|
||||
* 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 { translation } from "~/_"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set search query placeholder
|
||||
*
|
||||
* @param el - Search query element
|
||||
* @param value - Placeholder
|
||||
*/
|
||||
export function setSearchQueryPlaceholder(
|
||||
el: HTMLInputElement, value: string
|
||||
): void {
|
||||
el.placeholder = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset search query placeholder
|
||||
*
|
||||
* @param el - Search query element
|
||||
*/
|
||||
export function resetSearchQueryPlaceholder(
|
||||
el: HTMLInputElement
|
||||
): void {
|
||||
el.placeholder = translation("search.placeholder")
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
/*
|
||||
* 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 { translation } from "~/_"
|
||||
import { round } from "~/utilities"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set number of search results
|
||||
*
|
||||
* @param el - Search result metadata element
|
||||
* @param value - Number of results
|
||||
*/
|
||||
export function setSearchResultMeta(
|
||||
el: HTMLElement, value: number
|
||||
): void {
|
||||
switch (value) {
|
||||
|
||||
/* No results */
|
||||
case 0:
|
||||
el.textContent = translation("search.result.none")
|
||||
break
|
||||
|
||||
/* One result */
|
||||
case 1:
|
||||
el.textContent = translation("search.result.one")
|
||||
break
|
||||
|
||||
/* Multiple result */
|
||||
default:
|
||||
el.textContent = translation("search.result.other", round(value))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset number of search results
|
||||
*
|
||||
* @param el - Search result metadata element
|
||||
*/
|
||||
export function resetSearchResultMeta(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
el.textContent = translation("search.result.placeholder")
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Add an element to the search result list
|
||||
*
|
||||
* @param el - Search result list element
|
||||
* @param child - Search result element
|
||||
*/
|
||||
export function addToSearchResultList(
|
||||
el: HTMLElement, child: Element
|
||||
): void {
|
||||
el.appendChild(child)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset search result list
|
||||
*
|
||||
* @param el - Search result list element
|
||||
*/
|
||||
export function resetSearchResultList(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
el.innerHTML = ""
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set sidebar offset
|
||||
*
|
||||
* @param el - Sidebar element
|
||||
* @param value - Sidebar offset
|
||||
*/
|
||||
export function setSidebarOffset(
|
||||
el: HTMLElement, value: number
|
||||
): void {
|
||||
el.style.top = `${value}px`
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset sidebar offset
|
||||
*
|
||||
* @param el - Sidebar element
|
||||
*/
|
||||
export function resetSidebarOffset(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
el.style.top = ""
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set sidebar height
|
||||
*
|
||||
* This function doesn't set the height of the actual sidebar, but of its first
|
||||
* child – the `.md-sidebar__scrollwrap` element in order to mitigiate jittery
|
||||
* sidebars when the footer is scrolled into view. At some point we switched
|
||||
* from `absolute` / `fixed` positioning to `sticky` positioning, significantly
|
||||
* reducing jitter in some browsers (respectively Firefox and Safari) when
|
||||
* scrolling from the top. However, top-aligned sticky positioning means that
|
||||
* the sidebar snaps to the bottom when the end of the container is reached.
|
||||
* This is what leads to the mentioned jitter, as the sidebar's height may be
|
||||
* updated too slowly.
|
||||
*
|
||||
* This behaviour can be mitigiated by setting the height of the sidebar to `0`
|
||||
* while preserving the padding, and the height on its first element.
|
||||
*
|
||||
* @param el - Sidebar element
|
||||
* @param value - Sidebar height
|
||||
*/
|
||||
export function setSidebarHeight(
|
||||
el: HTMLElement, value: number
|
||||
): void {
|
||||
const scrollwrap = el.firstElementChild as HTMLElement
|
||||
scrollwrap.style.height = `${value - 2 * scrollwrap.offsetTop}px`
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset sidebar height
|
||||
*
|
||||
* @param el - Sidebar element
|
||||
*/
|
||||
export function resetSidebarHeight(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
const scrollwrap = el.firstElementChild as HTMLElement
|
||||
scrollwrap.style.height = ""
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set repository facts
|
||||
*
|
||||
* @param el - Repository element
|
||||
* @param child - Repository facts element
|
||||
*/
|
||||
export function setSourceFacts(
|
||||
el: HTMLElement, child: Element
|
||||
): void {
|
||||
el.lastElementChild!.appendChild(child)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set repository state
|
||||
*
|
||||
* @param el - Repository element
|
||||
* @param state - Repository state
|
||||
*/
|
||||
export function setSourceState(
|
||||
el: HTMLElement, state: "done"
|
||||
): void {
|
||||
el.lastElementChild!.setAttribute("data-md-state", state)
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set tabs state
|
||||
*
|
||||
* @param el - Tabs element
|
||||
* @param state - Tabs state
|
||||
*/
|
||||
export function setTabsState(
|
||||
el: HTMLElement, state: "hidden"
|
||||
): void {
|
||||
el.setAttribute("data-md-state", state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset tabs state
|
||||
*
|
||||
* @param el - Tabs element
|
||||
*/
|
||||
export function resetTabsState(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
el.removeAttribute("data-md-state")
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set back-to-top state
|
||||
*
|
||||
* @param el - Back-to-top element
|
||||
* @param state - Back-to-top state
|
||||
*/
|
||||
export function setBackToTopState(
|
||||
el: HTMLElement, state: "hidden"
|
||||
): void {
|
||||
el.setAttribute("data-md-state", state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset back-to-top state
|
||||
*
|
||||
* @param el - Back-to-top element
|
||||
*/
|
||||
export function resetBackToTopState(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
el.removeAttribute("data-md-state")
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set back-to-top offset
|
||||
*
|
||||
* @param el - Back-to-top element
|
||||
* @param value - Back-to-top offset
|
||||
*/
|
||||
export function setBackToTopOffset(
|
||||
el: HTMLElement, value: number
|
||||
): void {
|
||||
el.style.top = `${value}px`
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset back-to-top offset
|
||||
*
|
||||
* @param el - Back-to-top element
|
||||
*/
|
||||
export function resetBackToTopOffset(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
el.style.top = ""
|
||||
}
|
@ -34,23 +34,6 @@ import { getActiveElement } from "../_"
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set element focus
|
||||
*
|
||||
* @param el - Element
|
||||
* @param value - Whether the element should be focused
|
||||
*/
|
||||
export function setElementFocus(
|
||||
el: HTMLElement, value = true
|
||||
): void {
|
||||
if (value)
|
||||
el.focus()
|
||||
else
|
||||
el.blur()
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Watch element focus
|
||||
*
|
||||
|
@ -23,6 +23,5 @@
|
||||
export * from "./_"
|
||||
export * from "./focus"
|
||||
export * from "./offset"
|
||||
export * from "./selection"
|
||||
export * from "./size"
|
||||
export * from "./visibility"
|
||||
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set element text selection
|
||||
*
|
||||
* @param el - Element
|
||||
*/
|
||||
export function setElementSelection(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
if (el instanceof HTMLInputElement)
|
||||
el.select()
|
||||
else
|
||||
throw new Error("Not implemented")
|
||||
}
|
@ -32,7 +32,6 @@ import {
|
||||
merge,
|
||||
of,
|
||||
shareReplay,
|
||||
startWith,
|
||||
switchMap,
|
||||
tap
|
||||
} from "rxjs"
|
||||
@ -113,7 +112,7 @@ export function watchElementVisibility(
|
||||
* @param el - Element
|
||||
* @param threshold - Threshold
|
||||
*
|
||||
* @returns Element threshold observable
|
||||
* @returns Element boundary observable
|
||||
*/
|
||||
export function watchElementBoundary(
|
||||
el: HTMLElement, threshold = 16
|
||||
|
@ -21,7 +21,7 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
NEVER,
|
||||
EMPTY,
|
||||
Observable,
|
||||
fromEvent,
|
||||
fromEventPattern,
|
||||
@ -86,6 +86,6 @@ export function at<T>(
|
||||
): Observable<T> {
|
||||
return query$
|
||||
.pipe(
|
||||
switchMap(active => active ? factory() : NEVER)
|
||||
switchMap(active => active ? factory() : EMPTY)
|
||||
)
|
||||
}
|
||||
|
@ -59,17 +59,6 @@ export function getViewportOffset(): ViewportOffset {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set viewport offset
|
||||
*
|
||||
* @param offset - Viewport offset
|
||||
*/
|
||||
export function setViewportOffset(
|
||||
{ x, y }: Partial<ViewportOffset>
|
||||
): void {
|
||||
window.scrollTo(x || 0, y || 0)
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
import "focus-visible"
|
||||
import {
|
||||
EMPTY,
|
||||
NEVER,
|
||||
Subject,
|
||||
defer,
|
||||
@ -198,13 +199,13 @@ const content$ = defer(() => merge(
|
||||
|
||||
/* Content */
|
||||
...getComponentElements("content")
|
||||
.map(el => mountContent(el, { target$, viewport$, hover$, print$ })),
|
||||
.map(el => mountContent(el, { target$, hover$, print$ })),
|
||||
|
||||
/* Search highlighting */
|
||||
...getComponentElements("content")
|
||||
.map(el => feature("search.highlight")
|
||||
? mountSearchHiglight(el, { index$, location$ })
|
||||
: NEVER
|
||||
: EMPTY
|
||||
),
|
||||
|
||||
/* Header title */
|
||||
|
@ -36,6 +36,7 @@ import {
|
||||
|
||||
import {
|
||||
ElementOffset,
|
||||
getElement,
|
||||
watchElementContentOffset,
|
||||
watchElementFocus,
|
||||
watchElementOffset
|
||||
@ -122,7 +123,7 @@ export function mountAnnotation(
|
||||
})
|
||||
|
||||
/* Blur open annotation on click (= close) */
|
||||
const index = el.lastElementChild!
|
||||
const index = getElement(":scope > :last-child")
|
||||
const blur$ = fromEvent(index, "mousedown", { once: true })
|
||||
push$
|
||||
.pipe(
|
||||
|
@ -120,36 +120,36 @@ export function mountAnnotationList(
|
||||
|
||||
/* Create and return component */
|
||||
return defer(() => {
|
||||
const push$ = new Subject<Annotation>()
|
||||
const done$ = new Subject<void>()
|
||||
|
||||
/* Handle print mode - see https://bit.ly/3rgPdpt */
|
||||
print$
|
||||
.pipe(
|
||||
startWith(false),
|
||||
takeUntil(push$.pipe(takeLast(1)))
|
||||
takeUntil(done$.pipe(takeLast(1)))
|
||||
)
|
||||
.subscribe(active => {
|
||||
el.hidden = !active
|
||||
|
||||
/* Move annotation contents back into list */
|
||||
for (const [id, annotation] of annotations) {
|
||||
const tooltip = getElement(".md-typeset", annotation)
|
||||
const inner = getElement(".md-typeset", annotation)
|
||||
const child = getElement(`li:nth-child(${id})`, el)
|
||||
if (!active)
|
||||
swap(child, tooltip)
|
||||
swap(child, inner)
|
||||
else
|
||||
swap(tooltip, child)
|
||||
swap(inner, child)
|
||||
}
|
||||
})
|
||||
|
||||
/* Create and return component */
|
||||
return merge(
|
||||
...[...annotations].map(([, annotation]) => (
|
||||
return merge(...[...annotations]
|
||||
.map(([, annotation]) => (
|
||||
mountAnnotation(annotation, container)
|
||||
))
|
||||
)
|
||||
.pipe(
|
||||
finalize(() => push$.complete()),
|
||||
finalize(() => done$.complete()),
|
||||
share()
|
||||
)
|
||||
})
|
||||
|
@ -23,6 +23,7 @@
|
||||
import {
|
||||
Observable,
|
||||
Subject,
|
||||
defer,
|
||||
filter,
|
||||
finalize,
|
||||
map,
|
||||
@ -41,7 +42,7 @@ import { Component } from "../../_"
|
||||
* Details
|
||||
*/
|
||||
export interface Details {
|
||||
action: "open" | "close" /* Action */
|
||||
action: "open" | "close" /* Details state */
|
||||
scroll?: boolean /* Scroll into view */
|
||||
}
|
||||
|
||||
@ -117,21 +118,23 @@ export function watchDetails(
|
||||
export function mountDetails(
|
||||
el: HTMLDetailsElement, options: MountOptions
|
||||
): Observable<Component<Details>> {
|
||||
const internal$ = new Subject<Details>()
|
||||
internal$.subscribe(({ action, scroll }) => {
|
||||
if (action === "open")
|
||||
el.setAttribute("open", "")
|
||||
else
|
||||
el.removeAttribute("open")
|
||||
if (scroll)
|
||||
el.scrollIntoView()
|
||||
})
|
||||
return defer(() => {
|
||||
const push$ = new Subject<Details>()
|
||||
push$.subscribe(({ action, scroll }) => {
|
||||
if (action === "open")
|
||||
el.setAttribute("open", "")
|
||||
else
|
||||
el.removeAttribute("open")
|
||||
if (scroll)
|
||||
el.scrollIntoView()
|
||||
})
|
||||
|
||||
/* Create and return component */
|
||||
return watchDetails(el, options)
|
||||
.pipe(
|
||||
tap(state => internal$.next(state)),
|
||||
finalize(() => internal$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
/* Create and return component */
|
||||
return watchDetails(el, options)
|
||||
.pipe(
|
||||
tap(state => push$.next(state)),
|
||||
finalize(() => push$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
import {
|
||||
Observable,
|
||||
Subject,
|
||||
defer,
|
||||
finalize,
|
||||
fromEvent,
|
||||
map,
|
||||
@ -33,6 +34,7 @@ import {
|
||||
|
||||
import {
|
||||
getElement,
|
||||
getElementOffset,
|
||||
getElements
|
||||
} from "~/browser"
|
||||
|
||||
@ -64,7 +66,11 @@ export function watchContentTabs(
|
||||
el: HTMLElement
|
||||
): Observable<ContentTabs> {
|
||||
return merge(...getElements(":scope > input", el)
|
||||
.map(input => fromEvent(input, "change").pipe(mapTo(input.id)))
|
||||
.map(input => fromEvent(input, "change")
|
||||
.pipe(
|
||||
mapTo(input.id)
|
||||
)
|
||||
)
|
||||
)
|
||||
.pipe(
|
||||
map(id => ({
|
||||
@ -76,6 +82,11 @@ export function watchContentTabs(
|
||||
/**
|
||||
* Mount content tabs
|
||||
*
|
||||
* This function scrolls the active tab into view. While this functionality is
|
||||
* provided by browsers as part of `scrollInfoView`, browsers will always also
|
||||
* scroll the vertical axis, which we do not want. Thus, we decided to provide
|
||||
* this functionality ourselves.
|
||||
*
|
||||
* @param el - Content tabs element
|
||||
*
|
||||
* @returns Content tabs component observable
|
||||
@ -83,25 +94,23 @@ export function watchContentTabs(
|
||||
export function mountContentTabs(
|
||||
el: HTMLElement
|
||||
): Observable<Component<ContentTabs>> {
|
||||
const internal$ = new Subject<ContentTabs>()
|
||||
internal$.subscribe(({ active }) => {
|
||||
// TODO: Hack, scrollIntoView is too buggy
|
||||
const container = active.parentElement!
|
||||
if (
|
||||
active.offsetLeft + active.offsetWidth > container.scrollLeft + container.offsetWidth ||
|
||||
active.offsetLeft < container.scrollLeft
|
||||
)
|
||||
const container = getElement(".tabbed-labels", el)
|
||||
return defer(() => {
|
||||
const push$ = new Subject<ContentTabs>()
|
||||
push$.subscribe(({ active }) => {
|
||||
const { x } = getElementOffset(active)
|
||||
container.scrollTo({
|
||||
behavior: "smooth",
|
||||
left: active.offsetLeft
|
||||
left: x
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
/* Create and return component */
|
||||
return watchContentTabs(el)
|
||||
.pipe(
|
||||
tap(state => internal$.next(state)),
|
||||
finalize(() => internal$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
/* Create and return component */
|
||||
return watchContentTabs(el)
|
||||
.pipe(
|
||||
tap(state => push$.next(state)),
|
||||
finalize(() => push$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -23,22 +23,17 @@
|
||||
import {
|
||||
Observable,
|
||||
Subject,
|
||||
animationFrameScheduler,
|
||||
defer,
|
||||
delay,
|
||||
finalize,
|
||||
map,
|
||||
merge,
|
||||
observeOn,
|
||||
of,
|
||||
switchMap,
|
||||
tap
|
||||
} from "rxjs"
|
||||
|
||||
import {
|
||||
resetDialogState,
|
||||
setDialogMessage,
|
||||
setDialogState
|
||||
} from "~/actions"
|
||||
import { getElement } from "~/browser"
|
||||
|
||||
import { Component } from "../_"
|
||||
|
||||
@ -103,7 +98,7 @@ export function watchDialog(
|
||||
/**
|
||||
* Mount dialog
|
||||
*
|
||||
* This function reveals the dialog in the right cornerwhen a new alert is
|
||||
* This function reveals the dialog in the right corner when a new alert is
|
||||
* emitted through the subject that is passed as part of the options.
|
||||
*
|
||||
* @param el - Dialog element
|
||||
@ -114,24 +109,23 @@ export function watchDialog(
|
||||
export function mountDialog(
|
||||
el: HTMLElement, options: MountOptions
|
||||
): Observable<Component<Dialog>> {
|
||||
const internal$ = new Subject<Dialog>()
|
||||
internal$
|
||||
.pipe(
|
||||
observeOn(animationFrameScheduler)
|
||||
)
|
||||
.subscribe(({ message, open }) => {
|
||||
setDialogMessage(el, message)
|
||||
if (open)
|
||||
setDialogState(el, "open")
|
||||
else
|
||||
resetDialogState(el)
|
||||
})
|
||||
const inner = getElement(".md-typeset", el)
|
||||
return defer(() => {
|
||||
const push$ = new Subject<Dialog>()
|
||||
push$.subscribe(({ message, open }) => {
|
||||
inner.textContent = message
|
||||
if (open)
|
||||
el.setAttribute("data-md-state", "open")
|
||||
else
|
||||
el.removeAttribute("data-md-state")
|
||||
})
|
||||
|
||||
/* Create and return component */
|
||||
return watchDialog(el, options)
|
||||
.pipe(
|
||||
tap(state => internal$.next(state)),
|
||||
finalize(() => internal$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
/* Create and return component */
|
||||
return watchDialog(el, options)
|
||||
.pipe(
|
||||
tap(state => push$.next(state)),
|
||||
finalize(() => push$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -23,7 +23,6 @@
|
||||
import {
|
||||
Observable,
|
||||
Subject,
|
||||
animationFrameScheduler,
|
||||
bufferCount,
|
||||
combineLatest,
|
||||
combineLatestWith,
|
||||
@ -32,18 +31,15 @@ import {
|
||||
distinctUntilKeyChanged,
|
||||
filter,
|
||||
map,
|
||||
observeOn,
|
||||
of,
|
||||
shareReplay,
|
||||
startWith,
|
||||
switchMap
|
||||
switchMap,
|
||||
takeLast,
|
||||
takeUntil
|
||||
} from "rxjs"
|
||||
|
||||
import { feature } from "~/_"
|
||||
import {
|
||||
resetHeaderState,
|
||||
setHeaderState
|
||||
} from "~/actions"
|
||||
import {
|
||||
Viewport,
|
||||
watchElementSize,
|
||||
@ -184,24 +180,28 @@ export function watchHeader(
|
||||
export function mountHeader(
|
||||
el: HTMLElement, { header$, main$ }: MountOptions
|
||||
): Observable<Component<Header>> {
|
||||
const internal$ = new Subject<Main>()
|
||||
internal$
|
||||
.pipe(
|
||||
distinctUntilKeyChanged("active"),
|
||||
combineLatestWith(header$),
|
||||
observeOn(animationFrameScheduler)
|
||||
)
|
||||
.subscribe(([{ active }, { hidden }]) => {
|
||||
if (active)
|
||||
setHeaderState(el, hidden ? "hidden" : "shadow")
|
||||
else
|
||||
resetHeaderState(el)
|
||||
})
|
||||
return defer(() => {
|
||||
const push$ = new Subject<Main>()
|
||||
push$
|
||||
.pipe(
|
||||
distinctUntilKeyChanged("active"),
|
||||
combineLatestWith(header$)
|
||||
)
|
||||
.subscribe(([{ active }, { hidden }]) => {
|
||||
if (active)
|
||||
el.setAttribute("data-md-state", hidden ? "hidden" : "shadow")
|
||||
else
|
||||
el.removeAttribute("data-md-state")
|
||||
})
|
||||
|
||||
/* Connect to long-living subject and return component */
|
||||
main$.subscribe(main => internal$.next(main))
|
||||
return header$
|
||||
.pipe(
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
/* Link to main area */
|
||||
main$.subscribe(push$)
|
||||
|
||||
/* Create and return component */
|
||||
return header$
|
||||
.pipe(
|
||||
takeUntil(push$.pipe(takeLast(1))),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -21,21 +21,16 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
NEVER,
|
||||
EMPTY,
|
||||
Observable,
|
||||
Subject,
|
||||
animationFrameScheduler,
|
||||
defer,
|
||||
distinctUntilKeyChanged,
|
||||
finalize,
|
||||
map,
|
||||
observeOn,
|
||||
tap
|
||||
} from "rxjs"
|
||||
|
||||
import {
|
||||
resetHeaderTitleState,
|
||||
setHeaderTitleState
|
||||
} from "~/actions"
|
||||
import {
|
||||
Viewport,
|
||||
getElementSize,
|
||||
@ -118,28 +113,26 @@ export function watchHeaderTitle(
|
||||
export function mountHeaderTitle(
|
||||
el: HTMLElement, options: MountOptions
|
||||
): Observable<Component<HeaderTitle>> {
|
||||
const internal$ = new Subject<HeaderTitle>()
|
||||
internal$
|
||||
.pipe(
|
||||
observeOn(animationFrameScheduler)
|
||||
)
|
||||
.subscribe(({ active }) => {
|
||||
if (active)
|
||||
setHeaderTitleState(el, "active")
|
||||
else
|
||||
resetHeaderTitleState(el)
|
||||
})
|
||||
return defer(() => {
|
||||
const push$ = new Subject<HeaderTitle>()
|
||||
push$.subscribe(({ active }) => {
|
||||
if (active)
|
||||
el.setAttribute("data-md-state", "active")
|
||||
else
|
||||
el.removeAttribute("data-md-state")
|
||||
})
|
||||
|
||||
/* Obtain headline, if any */
|
||||
const headline = getOptionalElement<HTMLHeadingElement>("article h1")
|
||||
if (typeof headline === "undefined")
|
||||
return NEVER
|
||||
/* Obtain headline, if any */
|
||||
const heading = getOptionalElement<HTMLHeadingElement>("article h1")
|
||||
if (typeof heading === "undefined")
|
||||
return EMPTY
|
||||
|
||||
/* Create and return component */
|
||||
return watchHeaderTitle(headline, options)
|
||||
.pipe(
|
||||
tap(state => internal$.next(state)),
|
||||
finalize(() => internal$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
/* Create and return component */
|
||||
return watchHeaderTitle(heading, options)
|
||||
.pipe(
|
||||
tap(state => push$.next(state)),
|
||||
finalize(() => push$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
import {
|
||||
Observable,
|
||||
Subject,
|
||||
defer,
|
||||
finalize,
|
||||
fromEvent,
|
||||
map,
|
||||
@ -74,9 +75,9 @@ export function watchPalette(
|
||||
inputs: HTMLInputElement[]
|
||||
): Observable<Palette> {
|
||||
const current = __md_get<Palette>("__palette") || {
|
||||
index: inputs.findIndex(input => (
|
||||
matchMedia(input.getAttribute("data-md-color-media")!).matches
|
||||
))
|
||||
index: inputs.findIndex(input => matchMedia(
|
||||
input.getAttribute("data-md-color-media")!
|
||||
).matches)
|
||||
}
|
||||
|
||||
/* Emit changes in color palette */
|
||||
@ -99,11 +100,6 @@ export function watchPalette(
|
||||
shareReplay(1)
|
||||
)
|
||||
|
||||
/* Persist preference in local storage */
|
||||
palette$.subscribe(palette => {
|
||||
__md_set("__palette", palette)
|
||||
})
|
||||
|
||||
/* Return palette */
|
||||
return palette$
|
||||
}
|
||||
@ -118,28 +114,33 @@ export function watchPalette(
|
||||
export function mountPalette(
|
||||
el: HTMLElement
|
||||
): Observable<Component<Palette>> {
|
||||
const internal$ = new Subject<Palette>()
|
||||
return defer(() => {
|
||||
const push$ = new Subject<Palette>()
|
||||
push$.subscribe(palette => {
|
||||
|
||||
/* Set color palette */
|
||||
internal$.subscribe(palette => {
|
||||
for (const [key, value] of Object.entries(palette.color))
|
||||
if (typeof value === "string")
|
||||
document.body.setAttribute(`data-md-color-${key}`, value)
|
||||
/* Set color palette */
|
||||
for (const [key, value] of Object.entries(palette.color))
|
||||
if (typeof value === "string")
|
||||
document.body.setAttribute(`data-md-color-${key}`, value)
|
||||
|
||||
/* Toggle visibility */
|
||||
for (let index = 0; index < inputs.length; index++) {
|
||||
const label = inputs[index].nextElementSibling
|
||||
if (label instanceof HTMLElement)
|
||||
label.hidden = palette.index !== index
|
||||
}
|
||||
/* Toggle visibility */
|
||||
for (let index = 0; index < inputs.length; index++) {
|
||||
const label = inputs[index].nextElementSibling
|
||||
if (label instanceof HTMLElement)
|
||||
label.hidden = palette.index !== index
|
||||
}
|
||||
|
||||
/* Persist preference in local storage */
|
||||
__md_set("__palette", palette)
|
||||
})
|
||||
|
||||
/* Create and return component */
|
||||
const inputs = getElements<HTMLInputElement>("input", el)
|
||||
return watchPalette(inputs)
|
||||
.pipe(
|
||||
tap(state => push$.next(state)),
|
||||
finalize(() => push$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
})
|
||||
|
||||
/* Create and return component */
|
||||
const inputs = getElements<HTMLInputElement>("input", el)
|
||||
return watchPalette(inputs)
|
||||
.pipe(
|
||||
tap(state => internal$.next(state)),
|
||||
finalize(() => internal$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
}
|
||||
|
@ -36,8 +36,6 @@ import {
|
||||
Keyboard,
|
||||
getActiveElement,
|
||||
getElements,
|
||||
setElementFocus,
|
||||
setElementSelection,
|
||||
setToggle
|
||||
} from "~/browser"
|
||||
import {
|
||||
@ -166,14 +164,14 @@ export function mountSearch(
|
||||
case "Escape":
|
||||
case "Tab":
|
||||
setToggle("search", false)
|
||||
setElementFocus(query, false)
|
||||
query.blur()
|
||||
break
|
||||
|
||||
/* Vertical arrows: select previous or next search result */
|
||||
case "ArrowUp":
|
||||
case "ArrowDown":
|
||||
if (typeof active === "undefined") {
|
||||
setElementFocus(query)
|
||||
query.focus()
|
||||
} else {
|
||||
const els = [query, ...getElements(
|
||||
":not(details) > [href], summary, details[open] [href]",
|
||||
@ -184,7 +182,7 @@ export function mountSearch(
|
||||
key.type === "ArrowUp" ? -1 : +1
|
||||
)
|
||||
) % els.length)
|
||||
setElementFocus(els[i])
|
||||
els[i].focus()
|
||||
}
|
||||
|
||||
/* Prevent scrolling of page */
|
||||
@ -194,7 +192,7 @@ export function mountSearch(
|
||||
/* All other keys: hand to search query */
|
||||
default:
|
||||
if (query !== getActiveElement())
|
||||
setElementFocus(query)
|
||||
query.focus()
|
||||
}
|
||||
})
|
||||
|
||||
@ -210,8 +208,10 @@ export function mountSearch(
|
||||
case "f":
|
||||
case "s":
|
||||
case "/":
|
||||
setElementFocus(query)
|
||||
setElementSelection(query)
|
||||
query.focus()
|
||||
query.select()
|
||||
|
||||
/* Prevent scrolling of page */
|
||||
key.claim()
|
||||
break
|
||||
}
|
||||
|
@ -40,13 +40,9 @@ import {
|
||||
tap
|
||||
} from "rxjs"
|
||||
|
||||
import {
|
||||
resetSearchQueryPlaceholder,
|
||||
setSearchQueryPlaceholder
|
||||
} from "~/actions"
|
||||
import { translation } from "~/_"
|
||||
import {
|
||||
getLocation,
|
||||
setElementFocus,
|
||||
setToggle,
|
||||
watchElementFocus
|
||||
} from "~/browser"
|
||||
@ -143,10 +139,10 @@ export function watchSearchQuery(
|
||||
export function mountSearchQuery(
|
||||
el: HTMLInputElement, { tx$, rx$ }: SearchWorker
|
||||
): Observable<Component<SearchQuery, HTMLInputElement>> {
|
||||
const internal$ = new Subject<SearchQuery>()
|
||||
const push$ = new Subject<SearchQuery>()
|
||||
|
||||
/* Handle value changes */
|
||||
internal$
|
||||
push$
|
||||
.pipe(
|
||||
distinctUntilKeyChanged("value"),
|
||||
map(({ value }): SearchQueryMessage => ({
|
||||
@ -157,31 +153,31 @@ export function mountSearchQuery(
|
||||
.subscribe(tx$.next.bind(tx$))
|
||||
|
||||
/* Handle focus changes */
|
||||
internal$
|
||||
push$
|
||||
.pipe(
|
||||
distinctUntilKeyChanged("focus")
|
||||
)
|
||||
.subscribe(({ focus }) => {
|
||||
if (focus) {
|
||||
setToggle("search", focus)
|
||||
setSearchQueryPlaceholder(el, "")
|
||||
el.placeholder = ""
|
||||
} else {
|
||||
resetSearchQueryPlaceholder(el)
|
||||
el.placeholder = translation("search.placeholder")
|
||||
}
|
||||
})
|
||||
|
||||
/* Handle reset */
|
||||
fromEvent(el.form!, "reset")
|
||||
.pipe(
|
||||
takeUntil(internal$.pipe(takeLast(1)))
|
||||
takeUntil(push$.pipe(takeLast(1)))
|
||||
)
|
||||
.subscribe(() => setElementFocus(el))
|
||||
.subscribe(() => el.focus())
|
||||
|
||||
/* Create and return component */
|
||||
return watchSearchQuery(el, { tx$, rx$ })
|
||||
.pipe(
|
||||
tap(state => internal$.next(state)),
|
||||
finalize(() => internal$.complete()),
|
||||
tap(state => push$.next(state)),
|
||||
finalize(() => push$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
}
|
||||
|
@ -39,12 +39,7 @@ import {
|
||||
zipWith
|
||||
} from "rxjs"
|
||||
|
||||
import {
|
||||
addToSearchResultList,
|
||||
resetSearchResultList,
|
||||
resetSearchResultMeta,
|
||||
setSearchResultMeta
|
||||
} from "~/actions"
|
||||
import { translation } from "~/_"
|
||||
import {
|
||||
getElement,
|
||||
watchElementBoundary
|
||||
@ -56,6 +51,7 @@ import {
|
||||
isSearchResultMessage
|
||||
} from "~/integrations"
|
||||
import { renderSearchResultItem } from "~/templates"
|
||||
import { round } from "~/utilities"
|
||||
|
||||
import { Component } from "../../_"
|
||||
import { SearchQuery } from "../query"
|
||||
@ -90,7 +86,7 @@ interface MountOptions {
|
||||
export function mountSearchResult(
|
||||
el: HTMLElement, { rx$ }: SearchWorker, { query$ }: MountOptions
|
||||
): Observable<Component<SearchResult>> {
|
||||
const internal$ = new Subject<SearchResult>()
|
||||
const push$ = new Subject<SearchResult>()
|
||||
const boundary$ = watchElementBoundary(el.parentElement!)
|
||||
.pipe(
|
||||
filter(Boolean)
|
||||
@ -108,24 +104,43 @@ export function mountSearchResult(
|
||||
)
|
||||
|
||||
/* Update search result metadata */
|
||||
internal$
|
||||
push$
|
||||
.pipe(
|
||||
observeOn(animationFrameScheduler),
|
||||
withLatestFrom(query$),
|
||||
skipUntil(ready$)
|
||||
)
|
||||
.subscribe(([{ items }, { value }]) => {
|
||||
if (value)
|
||||
setSearchResultMeta(meta, items.length)
|
||||
else
|
||||
resetSearchResultMeta(meta)
|
||||
if (value) {
|
||||
switch (items.length) {
|
||||
|
||||
/* No results */
|
||||
case 0:
|
||||
meta.textContent = translation("search.result.none")
|
||||
break
|
||||
|
||||
/* One result */
|
||||
case 1:
|
||||
meta.textContent = translation("search.result.one")
|
||||
break
|
||||
|
||||
/* Multiple result */
|
||||
default:
|
||||
meta.textContent = translation(
|
||||
"search.result.other",
|
||||
round(items.length)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
meta.textContent = translation("search.result.placeholder")
|
||||
}
|
||||
})
|
||||
|
||||
/* Update search result list */
|
||||
internal$
|
||||
push$
|
||||
.pipe(
|
||||
observeOn(animationFrameScheduler),
|
||||
tap(() => resetSearchResultList(list)),
|
||||
tap(() => list.innerHTML = ""),
|
||||
switchMap(({ items }) => merge(
|
||||
of(...items.slice(0, 10)),
|
||||
of(...items.slice(10))
|
||||
@ -136,9 +151,9 @@ export function mountSearchResult(
|
||||
)
|
||||
))
|
||||
)
|
||||
.subscribe(result => {
|
||||
addToSearchResultList(list, renderSearchResultItem(result))
|
||||
})
|
||||
.subscribe(result => list.appendChild(
|
||||
renderSearchResultItem(result)
|
||||
))
|
||||
|
||||
/* Filter search result message */
|
||||
const result$ = rx$
|
||||
@ -150,8 +165,8 @@ export function mountSearchResult(
|
||||
/* Create and return component */
|
||||
return result$
|
||||
.pipe(
|
||||
tap(state => internal$.next(state)),
|
||||
finalize(() => internal$.complete()),
|
||||
tap(state => push$.next(state)),
|
||||
finalize(() => push$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
}
|
||||
|
@ -101,8 +101,8 @@ export function watchSearchShare(
|
||||
export function mountSearchShare(
|
||||
el: HTMLAnchorElement, options: MountOptions
|
||||
): Observable<Component<SearchShare>> {
|
||||
const internal$ = new Subject<SearchShare>()
|
||||
internal$.subscribe(({ url }) => {
|
||||
const push$ = new Subject<SearchShare>()
|
||||
push$.subscribe(({ url }) => {
|
||||
el.setAttribute("data-clipboard-text", el.href)
|
||||
el.href = `${url}`
|
||||
})
|
||||
@ -114,8 +114,8 @@ export function mountSearchShare(
|
||||
/* Create and return component */
|
||||
return watchSearchShare(el, options)
|
||||
.pipe(
|
||||
tap(state => internal$.next(state)),
|
||||
finalize(() => internal$.complete()),
|
||||
tap(state => push$.next(state)),
|
||||
finalize(() => push$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ interface MountOptions {
|
||||
export function mountSearchSuggest(
|
||||
el: HTMLElement, { rx$ }: SearchWorker, { keyboard$ }: MountOptions
|
||||
): Observable<Component<SearchSuggest>> {
|
||||
const internal$ = new Subject<SearchResult>()
|
||||
const push$ = new Subject<SearchResult>()
|
||||
|
||||
/* Retrieve query component and track all changes */
|
||||
const query = getComponentElement("search-query")
|
||||
@ -98,7 +98,7 @@ export function mountSearchSuggest(
|
||||
)
|
||||
|
||||
/* Update search suggestions */
|
||||
internal$
|
||||
push$
|
||||
.pipe(
|
||||
combineLatestWith(query$),
|
||||
map(([{ suggestions }, value]) => {
|
||||
@ -147,8 +147,8 @@ export function mountSearchSuggest(
|
||||
/* Create and return component */
|
||||
return result$
|
||||
.pipe(
|
||||
tap(state => internal$.next(state)),
|
||||
finalize(() => internal$.complete()),
|
||||
tap(state => push$.next(state)),
|
||||
finalize(() => push$.complete()),
|
||||
map(() => ({ ref: el }))
|
||||
)
|
||||
}
|
||||
|
@ -24,22 +24,21 @@ import {
|
||||
Observable,
|
||||
Subject,
|
||||
animationFrameScheduler,
|
||||
auditTime,
|
||||
combineLatest,
|
||||
defer,
|
||||
distinctUntilChanged,
|
||||
finalize,
|
||||
map,
|
||||
observeOn,
|
||||
tap,
|
||||
withLatestFrom
|
||||
} from "rxjs"
|
||||
|
||||
import {
|
||||
resetSidebarHeight,
|
||||
resetSidebarOffset,
|
||||
setSidebarHeight,
|
||||
setSidebarOffset
|
||||
} from "~/actions"
|
||||
import { Viewport } from "~/browser"
|
||||
Viewport,
|
||||
getElement,
|
||||
getElementOffset
|
||||
} from "~/browser"
|
||||
|
||||
import { Component } from "../_"
|
||||
import { Header } from "../header"
|
||||
@ -125,6 +124,19 @@ export function watchSidebar(
|
||||
/**
|
||||
* Mount sidebar
|
||||
*
|
||||
* This function doesn't set the height of the actual sidebar, but of its first
|
||||
* child – the `.md-sidebar__scrollwrap` element in order to mitigiate jittery
|
||||
* sidebars when the footer is scrolled into view. At some point we switched
|
||||
* from `absolute` / `fixed` positioning to `sticky` positioning, significantly
|
||||
* reducing jitter in some browsers (respectively Firefox and Safari) when
|
||||
* scrolling from the top. However, top-aligned sticky positioning means that
|
||||
* the sidebar snaps to the bottom when the end of the container is reached.
|
||||
* This is what leads to the mentioned jitter, as the sidebar's height may be
|
||||
* updated too slowly.
|
||||
*
|
||||
* This behaviour can be mitigiated by setting the height of the sidebar to `0`
|
||||
* while preserving the padding, and the height on its first element.
|
||||
*
|
||||
* @param el - Sidebar element
|
||||
* @param options - Options
|
||||
*
|
||||
@ -133,32 +145,36 @@ export function watchSidebar(
|
||||
export function mountSidebar(
|
||||
el: HTMLElement, { header$, ...options }: MountOptions
|
||||
): Observable<Component<Sidebar>> {
|
||||
const internal$ = new Subject<Sidebar>()
|
||||
internal$
|
||||
.pipe(
|
||||
observeOn(animationFrameScheduler),
|
||||
withLatestFrom(header$)
|
||||
)
|
||||
.subscribe({
|
||||
const inner = getElement(".md-sidebar__scrollwrap", el)
|
||||
const { y } = getElementOffset(inner)
|
||||
return defer(() => {
|
||||
const push$ = new Subject<Sidebar>()
|
||||
push$
|
||||
.pipe(
|
||||
auditTime(0, animationFrameScheduler),
|
||||
withLatestFrom(header$)
|
||||
)
|
||||
.subscribe({
|
||||
|
||||
/* Update height and offset */
|
||||
next([{ height }, { height: offset }]) {
|
||||
setSidebarHeight(el, height)
|
||||
setSidebarOffset(el, offset)
|
||||
},
|
||||
/* Handle emission */
|
||||
next([{ height }, { height: offset }]) {
|
||||
inner.style.height = `${height - 2 * y}px`
|
||||
el.style.top = `${offset}px`
|
||||
},
|
||||
|
||||
/* Reset on complete */
|
||||
complete() {
|
||||
resetSidebarOffset(el)
|
||||
resetSidebarHeight(el)
|
||||
}
|
||||
})
|
||||
/* Handle complete */
|
||||
complete() {
|
||||
inner.style.height = ""
|
||||
el.style.top = ""
|
||||
}
|
||||
})
|
||||
|
||||
/* Create and return component */
|
||||
return watchSidebar(el, options)
|
||||
.pipe(
|
||||
tap(state => internal$.next(state)),
|
||||
finalize(() => internal$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
/* Create and return component */
|
||||
return watchSidebar(el, options)
|
||||
.pipe(
|
||||
tap(state => push$.next(state)),
|
||||
finalize(() => push$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
NEVER,
|
||||
EMPTY,
|
||||
Observable,
|
||||
Subject,
|
||||
catchError,
|
||||
@ -34,10 +34,7 @@ import {
|
||||
tap
|
||||
} from "rxjs"
|
||||
|
||||
import {
|
||||
setSourceFacts,
|
||||
setSourceState
|
||||
} from "~/actions"
|
||||
import { getElement } from "~/browser"
|
||||
import { renderSourceFacts } from "~/templates"
|
||||
|
||||
import { Component } from "../../_"
|
||||
@ -94,7 +91,7 @@ export function watchSource(
|
||||
)
|
||||
})
|
||||
.pipe(
|
||||
catchError(() => NEVER),
|
||||
catchError(() => EMPTY),
|
||||
filter(facts => Object.keys(facts).length > 0),
|
||||
map(facts => ({ facts })),
|
||||
shareReplay(1)
|
||||
@ -111,17 +108,20 @@ export function watchSource(
|
||||
export function mountSource(
|
||||
el: HTMLAnchorElement
|
||||
): Observable<Component<Source>> {
|
||||
const internal$ = new Subject<Source>()
|
||||
internal$.subscribe(({ facts }) => {
|
||||
setSourceFacts(el, renderSourceFacts(facts))
|
||||
setSourceState(el, "done")
|
||||
})
|
||||
const inner = getElement(":scope > :last-child", el)
|
||||
return defer(() => {
|
||||
const push$ = new Subject<Source>()
|
||||
push$.subscribe(({ facts }) => {
|
||||
inner.appendChild(renderSourceFacts(facts))
|
||||
inner.setAttribute("data-md-state", "done")
|
||||
})
|
||||
|
||||
/* Create and return component */
|
||||
return watchSource(el)
|
||||
.pipe(
|
||||
tap(state => internal$.next(state)),
|
||||
finalize(() => internal$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
/* Create and return component */
|
||||
return watchSource(el)
|
||||
.pipe(
|
||||
tap(state => push$.next(state)),
|
||||
finalize(() => push$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { NEVER, Observable } from "rxjs"
|
||||
import { EMPTY, Observable } from "rxjs"
|
||||
|
||||
import { fetchSourceFactsFromGitHub } from "../github"
|
||||
import { fetchSourceFactsFromGitLab } from "../gitlab"
|
||||
@ -83,6 +83,6 @@ export function fetchSourceFacts(
|
||||
|
||||
/* Everything else */
|
||||
default:
|
||||
return NEVER
|
||||
return EMPTY
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ interface Release {
|
||||
/**
|
||||
* Fetch GitHub repository facts
|
||||
*
|
||||
* @param user - GitHub user
|
||||
* @param user - GitHub user or organization
|
||||
* @param repo - GitHub repository
|
||||
*
|
||||
* @returns Repository facts observable
|
||||
|
@ -23,21 +23,16 @@
|
||||
import {
|
||||
Observable,
|
||||
Subject,
|
||||
animationFrameScheduler,
|
||||
defer,
|
||||
distinctUntilKeyChanged,
|
||||
finalize,
|
||||
map,
|
||||
observeOn,
|
||||
of,
|
||||
switchMap,
|
||||
tap
|
||||
} from "rxjs"
|
||||
|
||||
import { feature } from "~/_"
|
||||
import {
|
||||
resetTabsState,
|
||||
setTabsState
|
||||
} from "~/actions"
|
||||
import {
|
||||
Viewport,
|
||||
watchElementSize,
|
||||
@ -119,36 +114,34 @@ export function watchTabs(
|
||||
export function mountTabs(
|
||||
el: HTMLElement, options: MountOptions
|
||||
): Observable<Component<Tabs>> {
|
||||
const internal$ = new Subject<Tabs>()
|
||||
internal$
|
||||
.pipe(
|
||||
observeOn(animationFrameScheduler)
|
||||
)
|
||||
.subscribe({
|
||||
|
||||
/* Update state */
|
||||
next({ hidden }) {
|
||||
if (hidden)
|
||||
setTabsState(el, "hidden")
|
||||
else
|
||||
resetTabsState(el)
|
||||
},
|
||||
|
||||
/* Reset on complete */
|
||||
complete() {
|
||||
resetTabsState(el)
|
||||
}
|
||||
})
|
||||
|
||||
/* Create and return component */
|
||||
return (
|
||||
feature("navigation.tabs.sticky")
|
||||
? of({ hidden: false })
|
||||
: watchTabs(el, options)
|
||||
)
|
||||
.pipe(
|
||||
tap(state => internal$.next(state)),
|
||||
finalize(() => internal$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
return defer(() => {
|
||||
const push$ = new Subject<Tabs>()
|
||||
push$.subscribe({
|
||||
|
||||
/* Handle emission */
|
||||
next({ hidden }) {
|
||||
if (hidden)
|
||||
el.setAttribute("data-md-state", "hidden")
|
||||
else
|
||||
el.removeAttribute("data-md-state")
|
||||
},
|
||||
|
||||
/* Handle complete */
|
||||
complete() {
|
||||
el.removeAttribute("data-md-state")
|
||||
}
|
||||
})
|
||||
|
||||
/* Create and return component */
|
||||
return (
|
||||
feature("navigation.tabs.sticky")
|
||||
? of({ hidden: false })
|
||||
: watchTabs(el, options)
|
||||
)
|
||||
.pipe(
|
||||
tap(state => push$.next(state)),
|
||||
finalize(() => push$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -23,7 +23,6 @@
|
||||
import {
|
||||
Observable,
|
||||
Subject,
|
||||
animationFrameScheduler,
|
||||
bufferCount,
|
||||
combineLatest,
|
||||
defer,
|
||||
@ -31,7 +30,6 @@ import {
|
||||
distinctUntilKeyChanged,
|
||||
finalize,
|
||||
map,
|
||||
observeOn,
|
||||
of,
|
||||
scan,
|
||||
startWith,
|
||||
@ -40,12 +38,6 @@ import {
|
||||
} from "rxjs"
|
||||
|
||||
import { feature } from "~/_"
|
||||
import {
|
||||
resetAnchorActive,
|
||||
resetAnchorState,
|
||||
setAnchorActive,
|
||||
setAnchorState
|
||||
} from "~/actions"
|
||||
import {
|
||||
Viewport,
|
||||
getElements,
|
||||
@ -253,52 +245,55 @@ export function watchTableOfContents(
|
||||
export function mountTableOfContents(
|
||||
el: HTMLElement, options: MountOptions
|
||||
): Observable<Component<TableOfContents>> {
|
||||
const internal$ = new Subject<TableOfContents>()
|
||||
internal$
|
||||
.pipe(
|
||||
observeOn(animationFrameScheduler),
|
||||
)
|
||||
.subscribe(({ prev, next }) => {
|
||||
return defer(() => {
|
||||
const push$ = new Subject<TableOfContents>()
|
||||
push$.subscribe(({ prev, next }) => {
|
||||
|
||||
/* Look forward */
|
||||
for (const [anchor] of next) {
|
||||
resetAnchorActive(anchor)
|
||||
resetAnchorState(anchor)
|
||||
}
|
||||
/* Look forward */
|
||||
for (const [anchor] of next) {
|
||||
anchor.removeAttribute("data-md-state")
|
||||
anchor.classList.remove(
|
||||
"md-nav__link--active"
|
||||
)
|
||||
}
|
||||
|
||||
/* Look backward */
|
||||
for (const [index, [anchor]] of prev.entries()) {
|
||||
setAnchorActive(anchor, index === prev.length - 1)
|
||||
setAnchorState(anchor, "blur")
|
||||
}
|
||||
/* Look backward */
|
||||
for (const [index, [anchor]] of prev.entries()) {
|
||||
anchor.setAttribute("data-md-state", "blur")
|
||||
anchor.classList.toggle(
|
||||
"md-nav__link--active",
|
||||
index === prev.length - 1
|
||||
)
|
||||
}
|
||||
|
||||
/* Set up anchor tracking, if enabled */
|
||||
if (feature("navigation.tracking")) {
|
||||
const url = getLocation()
|
||||
/* Set up anchor tracking, if enabled */
|
||||
if (feature("navigation.tracking")) {
|
||||
const url = getLocation()
|
||||
|
||||
/* Set hash fragment to active anchor */
|
||||
const anchor = prev[prev.length - 1]
|
||||
if (anchor && anchor.length) {
|
||||
const [active] = anchor
|
||||
const { hash } = new URL(active.href)
|
||||
if (url.hash !== hash) {
|
||||
url.hash = hash
|
||||
history.replaceState({}, "", `${url}`)
|
||||
}
|
||||
|
||||
/* Reset anchor when at the top */
|
||||
} else {
|
||||
url.hash = ""
|
||||
/* Set hash fragment to active anchor */
|
||||
const anchor = prev[prev.length - 1]
|
||||
if (anchor && anchor.length) {
|
||||
const [active] = anchor
|
||||
const { hash } = new URL(active.href)
|
||||
if (url.hash !== hash) {
|
||||
url.hash = hash
|
||||
history.replaceState({}, "", `${url}`)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/* Create and return component */
|
||||
return watchTableOfContents(el, options)
|
||||
.pipe(
|
||||
tap(state => internal$.next(state)),
|
||||
finalize(() => internal$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
/* Reset anchor when at the top */
|
||||
} else {
|
||||
url.hash = ""
|
||||
history.replaceState({}, "", `${url}`)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/* Create and return component */
|
||||
return watchTableOfContents(el, options)
|
||||
.pipe(
|
||||
tap(state => push$.next(state)),
|
||||
finalize(() => push$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -23,30 +23,17 @@
|
||||
import {
|
||||
Observable,
|
||||
Subject,
|
||||
animationFrameScheduler,
|
||||
bufferCount,
|
||||
combineLatest,
|
||||
distinctUntilChanged,
|
||||
distinctUntilKeyChanged,
|
||||
finalize,
|
||||
map,
|
||||
observeOn,
|
||||
tap,
|
||||
withLatestFrom
|
||||
} from "rxjs"
|
||||
|
||||
import {
|
||||
resetBackToTopOffset,
|
||||
resetBackToTopState,
|
||||
resetFocusable,
|
||||
setBackToTopOffset,
|
||||
setBackToTopState,
|
||||
setFocusable
|
||||
} from "~/actions"
|
||||
import {
|
||||
Viewport,
|
||||
setElementFocus
|
||||
} from "~/browser"
|
||||
import { Viewport } from "~/browser"
|
||||
|
||||
import { Component } from "../_"
|
||||
import { Header } from "../header"
|
||||
@ -141,10 +128,9 @@ export function watchBackToTop(
|
||||
export function mountBackToTop(
|
||||
el: HTMLElement, { viewport$, header$, main$ }: MountOptions
|
||||
): Observable<Component<BackToTop>> {
|
||||
const internal$ = new Subject<BackToTop>()
|
||||
internal$
|
||||
const push$ = new Subject<BackToTop>()
|
||||
push$
|
||||
.pipe(
|
||||
observeOn(animationFrameScheduler),
|
||||
withLatestFrom(header$
|
||||
.pipe(
|
||||
distinctUntilKeyChanged("height")
|
||||
@ -153,32 +139,33 @@ export function mountBackToTop(
|
||||
)
|
||||
.subscribe({
|
||||
|
||||
/* Update state */
|
||||
/* Handle emission */
|
||||
next([{ hidden }, { height }]) {
|
||||
setBackToTopOffset(el, height + 16)
|
||||
el.style.top = `${height + 16}px`
|
||||
if (hidden) {
|
||||
setBackToTopState(el, "hidden")
|
||||
setElementFocus(el, false)
|
||||
setFocusable(el, -1)
|
||||
el.setAttribute("data-md-state", "hidden")
|
||||
el.setAttribute("tabindex", "-1")
|
||||
el.blur()
|
||||
} else {
|
||||
resetBackToTopState(el)
|
||||
resetFocusable(el)
|
||||
el.style.top = ""
|
||||
el.removeAttribute("data-md-state")
|
||||
el.removeAttribute("tabindex")
|
||||
}
|
||||
},
|
||||
|
||||
/* Reset on complete */
|
||||
/* Handle complete */
|
||||
complete() {
|
||||
resetBackToTopOffset(el)
|
||||
resetBackToTopState(el)
|
||||
resetFocusable(el)
|
||||
el.style.top = ""
|
||||
el.removeAttribute("data-md-state")
|
||||
el.removeAttribute("tabindex")
|
||||
}
|
||||
})
|
||||
|
||||
/* Create and return component */
|
||||
return watchBackToTop(el, { viewport$, header$, main$ })
|
||||
.pipe(
|
||||
tap(state => internal$.next(state)),
|
||||
finalize(() => internal$.complete()),
|
||||
tap(state => push$.next(state)),
|
||||
finalize(() => push$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
}
|
||||
|
@ -52,8 +52,7 @@ import {
|
||||
request,
|
||||
requestXML,
|
||||
setLocation,
|
||||
setLocationHash,
|
||||
setViewportOffset
|
||||
setLocationHash
|
||||
} from "~/browser"
|
||||
import { getComponentElement } from "~/components"
|
||||
import { h } from "~/utilities"
|
||||
@ -329,11 +328,11 @@ export function setupInstantLoading(
|
||||
.pipe(
|
||||
sample(document$),
|
||||
)
|
||||
.subscribe(({ url, offset }) => {
|
||||
.subscribe(({ url, offset = { y: 0 } }) => {
|
||||
if (url.hash && !offset) {
|
||||
setLocationHash(url.hash)
|
||||
} else {
|
||||
setViewportOffset(offset || { y: 0 })
|
||||
window.scrollTo(0, offset.y)
|
||||
}
|
||||
})
|
||||
|
||||
@ -355,7 +354,7 @@ export function setupInstantLoading(
|
||||
filter(([a, b]) => a.url.pathname === b.url.pathname),
|
||||
map(([, state]) => state)
|
||||
)
|
||||
.subscribe(({ offset }) => {
|
||||
setViewportOffset(offset || { y: 0 })
|
||||
.subscribe(({ offset = { y: 0 } }) => {
|
||||
window.scrollTo(0, offset.y)
|
||||
})
|
||||
}
|
||||
|
@ -32,10 +32,6 @@ import {
|
||||
withLatestFrom
|
||||
} from "rxjs"
|
||||
|
||||
import {
|
||||
resetScrollLock,
|
||||
setScrollLock
|
||||
} from "~/actions"
|
||||
import {
|
||||
Viewport,
|
||||
watchToggle
|
||||
@ -82,9 +78,15 @@ export function patchScrolllock(
|
||||
withLatestFrom(viewport$)
|
||||
)
|
||||
.subscribe(([active, { offset: { y }}]) => {
|
||||
if (active)
|
||||
setScrollLock(document.body, y)
|
||||
else
|
||||
resetScrollLock(document.body)
|
||||
if (active) {
|
||||
document.body.setAttribute("data-md-state", "lock")
|
||||
document.body.style.top = `-${y}px`
|
||||
} else {
|
||||
const value = -1 * parseInt(document.body.style.top, 10)
|
||||
document.body.removeAttribute("data-md-state")
|
||||
document.body.style.top = ""
|
||||
if (value)
|
||||
window.scrollTo(0, value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -70,21 +70,3 @@ export function round(value: number): string {
|
||||
return value.toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple hash function
|
||||
*
|
||||
* @see https://bit.ly/2wsVjJ4 - Original source
|
||||
*
|
||||
* @param value - Value to be hashed
|
||||
*
|
||||
* @returns Hash as 32bit integer
|
||||
*/
|
||||
export function hash(value: string): number {
|
||||
let h = 0
|
||||
for (let i = 0, len = value.length; i < len; i++) {
|
||||
h = ((h << 5) - h) + value.charCodeAt(i)
|
||||
h |= 0 // Convert to 32bit integer
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
@ -40,16 +40,12 @@ import {
|
||||
zipWith
|
||||
} from "rxjs"
|
||||
|
||||
import {
|
||||
addToSearchResultList,
|
||||
resetSearchResultList,
|
||||
resetSearchResultMeta,
|
||||
setSearchResultMeta
|
||||
} from "~/actions"
|
||||
import { translation } from "~/_"
|
||||
import {
|
||||
getElement,
|
||||
watchElementBoundary
|
||||
} from "~/browser"
|
||||
import { round } from "~/utilities"
|
||||
|
||||
import { Icon, renderIconSearchResult } from "_/templates"
|
||||
|
||||
@ -146,7 +142,7 @@ export function watchIconSearchResult(
|
||||
export function mountIconSearchResult(
|
||||
el: HTMLElement, { index$, query$ }: MountOptions
|
||||
): Observable<Component<IconSearchResult, HTMLElement>> {
|
||||
const internal$ = new Subject<IconSearchResult>()
|
||||
const push$ = new Subject<IconSearchResult>()
|
||||
const boundary$ = watchElementBoundary(el)
|
||||
.pipe(
|
||||
filter(Boolean)
|
||||
@ -154,24 +150,43 @@ export function mountIconSearchResult(
|
||||
|
||||
/* Update search result metadata */
|
||||
const meta = getElement(":scope > :first-child", el)
|
||||
internal$
|
||||
push$
|
||||
.pipe(
|
||||
observeOn(animationFrameScheduler),
|
||||
withLatestFrom(query$)
|
||||
)
|
||||
.subscribe(([{ data }, { value }]) => {
|
||||
if (value)
|
||||
setSearchResultMeta(meta, data.length)
|
||||
else
|
||||
resetSearchResultMeta(meta)
|
||||
if (value) {
|
||||
switch (data.length) {
|
||||
|
||||
/* No results */
|
||||
case 0:
|
||||
meta.textContent = translation("search.result.none")
|
||||
break
|
||||
|
||||
/* One result */
|
||||
case 1:
|
||||
meta.textContent = translation("search.result.one")
|
||||
break
|
||||
|
||||
/* Multiple result */
|
||||
default:
|
||||
meta.textContent = translation(
|
||||
"search.result.other",
|
||||
round(data.length)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
meta.textContent = translation("search.result.placeholder")
|
||||
}
|
||||
})
|
||||
|
||||
/* Update icon search result list */
|
||||
const list = getElement(":scope > :last-child", el)
|
||||
internal$
|
||||
push$
|
||||
.pipe(
|
||||
observeOn(animationFrameScheduler),
|
||||
tap(() => resetSearchResultList(list)),
|
||||
tap(() => list.innerHTML = ""),
|
||||
switchMap(({ data }) => merge(
|
||||
of(...data.slice(0, 10)),
|
||||
of(...data.slice(10))
|
||||
@ -183,15 +198,15 @@ export function mountIconSearchResult(
|
||||
)),
|
||||
withLatestFrom(query$)
|
||||
)
|
||||
.subscribe(([result, { value }]) => {
|
||||
addToSearchResultList(list, renderIconSearchResult(result, value))
|
||||
})
|
||||
.subscribe(([result, { value }]) => list.appendChild(
|
||||
renderIconSearchResult(result, value)
|
||||
))
|
||||
|
||||
/* Create and return component */
|
||||
return watchIconSearchResult(el, { query$, index$ })
|
||||
.pipe(
|
||||
tap(state => internal$.next(state)),
|
||||
finalize(() => internal$.complete()),
|
||||
tap(state => push$.next(state)),
|
||||
finalize(() => push$.complete()),
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ import { build as esbuild } from "esbuild"
|
||||
import * as path from "path"
|
||||
import postcss from "postcss"
|
||||
import {
|
||||
NEVER,
|
||||
EMPTY,
|
||||
Observable,
|
||||
catchError,
|
||||
concat,
|
||||
@ -132,7 +132,7 @@ export function transformStyle(
|
||||
),
|
||||
catchError(err => {
|
||||
console.log(err.formatted || err.message)
|
||||
return NEVER
|
||||
return EMPTY
|
||||
}),
|
||||
switchMap(({ css, map }) => {
|
||||
const file = digest(options.to, css)
|
||||
@ -182,7 +182,7 @@ export function transformScript(
|
||||
map: Buffer.from(data, "base64")
|
||||
})
|
||||
}),
|
||||
catchError(() => NEVER),
|
||||
catchError(() => EMPTY),
|
||||
switchMap(({ js, map }) => {
|
||||
const file = digest(options.to, js)
|
||||
return concat(
|
||||
|
Loading…
Reference in New Issue
Block a user