mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2024-11-12 10:00:52 +01:00
Fixed Travis and added more components
This commit is contained in:
parent
9cae185391
commit
c409fe3953
@ -44,7 +44,6 @@ install:
|
||||
|
||||
# Perform build and release
|
||||
script:
|
||||
- make -v
|
||||
# - npm run lint
|
||||
- npm run clean
|
||||
- npm run build
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -606,6 +606,7 @@ hr {
|
||||
.md-header-nav__topic {
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: calc(100% - 1rem);
|
||||
-webkit-transition: opacity 0.15s, -webkit-transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1);
|
||||
transition: opacity 0.15s, -webkit-transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1);
|
||||
transition: transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1), opacity 0.15s;
|
||||
@ -614,8 +615,8 @@ hr {
|
||||
white-space: nowrap;
|
||||
overflow: hidden; }
|
||||
.md-header-nav__topic + .md-header-nav__topic {
|
||||
-webkit-transform: translateY(1.25rem);
|
||||
transform: translateY(1.25rem);
|
||||
-webkit-transform: translateX(1.25rem);
|
||||
transform: translateX(1.25rem);
|
||||
-webkit-transition: opacity 0.15s, -webkit-transform 0.4s cubic-bezier(1, 0.7, 0.1, 0.1);
|
||||
transition: opacity 0.15s, -webkit-transform 0.4s cubic-bezier(1, 0.7, 0.1, 0.1);
|
||||
transition: transform 0.4s cubic-bezier(1, 0.7, 0.1, 0.1), opacity 0.15s;
|
||||
@ -624,8 +625,8 @@ hr {
|
||||
z-index: -1;
|
||||
pointer-events: none; }
|
||||
[dir="rtl"] .md-header-nav__topic + .md-header-nav__topic {
|
||||
-webkit-transform: translateY(-1.25rem);
|
||||
transform: translateY(-1.25rem); }
|
||||
-webkit-transform: translateX(-1.25rem);
|
||||
transform: translateX(-1.25rem); }
|
||||
.no-js .md-header-nav__topic {
|
||||
position: initial; }
|
||||
.no-js .md-header-nav__topic + .md-header-nav__topic {
|
||||
@ -635,8 +636,8 @@ hr {
|
||||
font-size: 0.9rem;
|
||||
line-height: 2.4rem; }
|
||||
.md-header-nav__title[data-md-state="active"] .md-header-nav__topic {
|
||||
-webkit-transform: translateY(-1.25rem);
|
||||
transform: translateY(-1.25rem);
|
||||
-webkit-transform: translateX(-1.25rem);
|
||||
transform: translateX(-1.25rem);
|
||||
-webkit-transition: opacity 0.15s, -webkit-transform 0.4s cubic-bezier(1, 0.7, 0.1, 0.1);
|
||||
transition: opacity 0.15s, -webkit-transform 0.4s cubic-bezier(1, 0.7, 0.1, 0.1);
|
||||
transition: transform 0.4s cubic-bezier(1, 0.7, 0.1, 0.1), opacity 0.15s;
|
||||
@ -645,11 +646,11 @@ hr {
|
||||
z-index: -1;
|
||||
pointer-events: none; }
|
||||
[dir="rtl"] .md-header-nav__title[data-md-state="active"] .md-header-nav__topic {
|
||||
-webkit-transform: translateY(1.25rem);
|
||||
transform: translateY(1.25rem); }
|
||||
-webkit-transform: translateX(1.25rem);
|
||||
transform: translateX(1.25rem); }
|
||||
.md-header-nav__title[data-md-state="active"] .md-header-nav__topic + .md-header-nav__topic {
|
||||
-webkit-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
-webkit-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
-webkit-transition: opacity 0.15s, -webkit-transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1);
|
||||
transition: opacity 0.15s, -webkit-transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1);
|
||||
transition: transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1), opacity 0.15s;
|
||||
|
File diff suppressed because one or more lines are too long
2
material/assets/stylesheets/app.min.css
vendored
2
material/assets/stylesheets/app.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -31,7 +31,7 @@
|
||||
<meta name="author" content="{{ config.site_author }}">
|
||||
{% endif %}
|
||||
<link rel="shortcut icon" href="{{ config.theme.favicon | url }}">
|
||||
<meta name="generator" content="mkdocs-{{ mkdocs_version }}, mkdocs-material-4.4.2">
|
||||
<meta name="generator" content="mkdocs-{{ mkdocs_version }}, $md-name$-$md-version$">
|
||||
{% endblock %}
|
||||
{% block htmltitle %}
|
||||
{% if page and page.meta and page.meta.title %}
|
||||
@ -192,7 +192,7 @@
|
||||
{% if language == "ja" %}
|
||||
<script src="{{ (path ~ 'tinyseg.js') | url }}"></script>
|
||||
{% endif %}
|
||||
{% if language in ("da","de","du","es","fi","fr","hu","it","ja","jp","nl","no","pt","ro","ru","sv","tr") %}
|
||||
{% if language in ($md-lunr-languages$) %}
|
||||
<script src="{{ (path ~ 'lunr.' ~ language ~ '.js') | url }}"></script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
@ -219,7 +219,7 @@
|
||||
{%- endfor -%}
|
||||
{{ translations | tojson }}
|
||||
</script>
|
||||
<script>app({version:"{{ mkdocs_version }}",url:{base:"{{ base_url }}"}})</script>
|
||||
<script>app=initialize({base:"{{ base_url }}"})</script>
|
||||
{% for path in config["extra_javascript"] %}
|
||||
<script src="{{ path | url }}"></script>
|
||||
{% endfor %}
|
||||
|
@ -20,17 +20,44 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { shareReplay, switchMap } from "rxjs/operators"
|
||||
import { identity } from "ramda"
|
||||
import {
|
||||
EMPTY,
|
||||
MonoTypeOperatorFunction,
|
||||
NEVER,
|
||||
Observable,
|
||||
fromEvent,
|
||||
merge,
|
||||
of,
|
||||
pipe
|
||||
} from "rxjs"
|
||||
import {
|
||||
delay,
|
||||
filter,
|
||||
map,
|
||||
shareReplay,
|
||||
switchMap,
|
||||
switchMapTo,
|
||||
tap,
|
||||
withLatestFrom
|
||||
} from "rxjs/operators"
|
||||
|
||||
import { isConfig } from "./config"
|
||||
import {
|
||||
setupSidebar,
|
||||
Component,
|
||||
paintHidden,
|
||||
paintSidebar,
|
||||
switchComponent,
|
||||
switchMapIfActive,
|
||||
watchBottomOffset,
|
||||
watchComponentMap,
|
||||
watchHeader,
|
||||
watchMain
|
||||
watchMain,
|
||||
watchSearchReset,
|
||||
watchSidebar,
|
||||
watchToggle,
|
||||
watchTopOffset
|
||||
} from "./theme"
|
||||
import { paintHeaderShadow } from "./theme/component/header/shadow"
|
||||
import {
|
||||
watchDocument,
|
||||
watchDocumentSwitch,
|
||||
@ -40,9 +67,15 @@ import {
|
||||
watchViewportOffset,
|
||||
watchViewportSize
|
||||
} from "./ui"
|
||||
import {
|
||||
getElement,
|
||||
not,
|
||||
switchMapIf
|
||||
} from "./utilities"
|
||||
|
||||
// TBD
|
||||
|
||||
// TODO: put this somewhere else... (merge with config!) JSON schema!?
|
||||
const names = [
|
||||
"header", /* Header */
|
||||
"title", /* Header title */
|
||||
@ -56,7 +89,11 @@ const names = [
|
||||
"tabs", /* Tabs */
|
||||
"navigation", /* Navigation */
|
||||
"toc" /* Table of contents */
|
||||
] as const // TODO: put this somewhere else... (merge with config!) JSON schema!?
|
||||
] as const
|
||||
|
||||
// modernizr for the poor
|
||||
document.documentElement.classList.remove("no-js")
|
||||
document.documentElement.classList.add("js")
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
@ -82,66 +119,212 @@ export function initialize(config: unknown) {
|
||||
const tablet$ = watchMedia("(min-width: 960px)")
|
||||
|
||||
/* Create location observables */
|
||||
const url$ = watchLocation()
|
||||
const location$ = watchLocation()
|
||||
const fragment$ = watchLocationFragment()
|
||||
|
||||
/* Create document observables */
|
||||
const load$ = watchDocument()
|
||||
const switch$ = watchDocumentSwitch({ url$ })
|
||||
const switch$ = watchDocumentSwitch({ location$ })
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
/* Create component map observable */
|
||||
const components$ = watchComponentMap(names, { load$, switch$ })
|
||||
|
||||
const component = (name: Component): Observable<HTMLElement> => {
|
||||
return components$
|
||||
.pipe(
|
||||
switchComponent(name)
|
||||
)
|
||||
}
|
||||
|
||||
/* Create header observable */
|
||||
const header$ = components$
|
||||
const header$ = component("header")
|
||||
.pipe(
|
||||
switchComponent("header"),
|
||||
switchMap(watchHeader)
|
||||
)
|
||||
|
||||
/* Create main area observable */
|
||||
const main$ = components$
|
||||
const main$ = component("main")
|
||||
.pipe(
|
||||
switchComponent("main"),
|
||||
switchMap(el => watchMain(el, { size$, offset$, header$ })),
|
||||
shareReplay(1)
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// WIP
|
||||
load$
|
||||
.pipe(
|
||||
switchMap(({ body }) => fromEvent(body, "click")),
|
||||
switchMap(ev => {
|
||||
if (ev.target instanceof HTMLElement) {
|
||||
const el = ev.target.closest("a") || undefined
|
||||
if (el) {
|
||||
if (!/^(https?:|#)/.test(el.getAttribute("href")!)) {
|
||||
ev.preventDefault()
|
||||
}
|
||||
const href = el.href
|
||||
history.pushState({}, "", href) // TODO: reference necessary!?
|
||||
return of(href)
|
||||
}
|
||||
}
|
||||
return EMPTY
|
||||
}),
|
||||
|
||||
// try to reduce the jiggle upon instant page load. ideally, the location
|
||||
// should directly be resolved and the respective document loaded, but
|
||||
// we must scroll to the top at first and wait at least 250ms.
|
||||
//
|
||||
// Furthermore, this doesn't include the back/next buttons of the browser
|
||||
// which must be delayed
|
||||
tap(url => {
|
||||
if (!/#/.test(url))
|
||||
scrollTo({ top: 0 })
|
||||
}), // only when loading something we havent loaded!
|
||||
delay(250)
|
||||
)
|
||||
.subscribe(location$)
|
||||
|
||||
location$.subscribe(x => {
|
||||
console.log("L", x)
|
||||
})
|
||||
switch$.subscribe(x => {
|
||||
console.log("S", x)
|
||||
})
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
/* Create sidebar with navigation */
|
||||
screen$
|
||||
/* Create header shadow toggle */
|
||||
component("header")
|
||||
.pipe(
|
||||
switchMapIfActive(() => components$ // TODO: write an observable creation function...
|
||||
switchMap(el => main$
|
||||
.pipe(
|
||||
switchComponent("navigation"),
|
||||
switchMap(el => setupSidebar(el, { offset$, main$ }))
|
||||
paintHeaderShadow(el)
|
||||
)
|
||||
)
|
||||
)
|
||||
.subscribe()
|
||||
|
||||
/* Create sidebar with table of contents (missing on 404 page) */
|
||||
tablet$
|
||||
/* Create sidebar with navigation */
|
||||
component("navigation")
|
||||
.pipe(
|
||||
switchMapIfActive(() => components$
|
||||
switchMapIf(screen$, el => watchSidebar(el, { offset$, main$ })
|
||||
.pipe(
|
||||
switchComponent("toc"),
|
||||
switchMap(el => setupSidebar(el, { offset$, main$ }))
|
||||
paintSidebar(el)
|
||||
)
|
||||
),
|
||||
shareReplay(1)
|
||||
)
|
||||
.subscribe()
|
||||
|
||||
/* Create sidebar with table of contents */
|
||||
component("toc")
|
||||
.pipe(
|
||||
switchMapIf(tablet$, el => watchSidebar(el, { offset$, main$ })
|
||||
.pipe(
|
||||
paintSidebar(el)
|
||||
)
|
||||
),
|
||||
shareReplay(1)
|
||||
)
|
||||
.subscribe()
|
||||
|
||||
/* Create tabs visibility toggle */
|
||||
component("tabs")
|
||||
.pipe(
|
||||
switchMapIf(screen$, el => watchTopOffset(el, { size$, offset$, header$ })
|
||||
.pipe(
|
||||
paintHidden(el, 8)
|
||||
)
|
||||
),
|
||||
shareReplay(1)
|
||||
)
|
||||
.subscribe()
|
||||
|
||||
/* Create hero visibility toggle */
|
||||
component("hero")
|
||||
.pipe(
|
||||
switchMap(el => watchTopOffset(el, { size$, offset$, header$ })
|
||||
.pipe(
|
||||
paintHidden(el, 20)
|
||||
)
|
||||
),
|
||||
shareReplay(1)
|
||||
)
|
||||
.subscribe()
|
||||
|
||||
/* Create header title toggle */
|
||||
component("main")
|
||||
.pipe(
|
||||
delay(1000), // initial delay
|
||||
switchMap(el => typeof getElement("h1", el) !== "undefined"
|
||||
? watchBottomOffset(getElement("h1", el)!, { size$, offset$, header$ })
|
||||
.pipe(
|
||||
map(({ y }) => y >= 0),
|
||||
withLatestFrom(component("title")),
|
||||
tap(([active, title]) => {
|
||||
title.dataset.mdState = active ? "active" : ""
|
||||
})
|
||||
)
|
||||
: NEVER
|
||||
)
|
||||
)
|
||||
.subscribe(console.log)
|
||||
.subscribe()
|
||||
|
||||
/* Return all observables */
|
||||
// TODO: replace title as inner text
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
const drawer = getElement<HTMLInputElement>("[data-md-toggle=drawer]")!
|
||||
const search = getElement<HTMLInputElement>("[data-md-toggle=search]")!
|
||||
|
||||
// watchToggle
|
||||
|
||||
// --> watchSearchQuery?
|
||||
|
||||
// watchSearch
|
||||
// watchSearchReset
|
||||
|
||||
// toggles stay the same...
|
||||
|
||||
const a$ = watchToggle(search)
|
||||
.pipe(
|
||||
filter(identity),
|
||||
delay(400)
|
||||
)
|
||||
|
||||
// watchSearchReset()
|
||||
|
||||
const b$ = component("reset")
|
||||
.pipe(
|
||||
switchMap(watchSearchReset)
|
||||
)
|
||||
|
||||
function focusQuery(): MonoTypeOperatorFunction<HTMLElement> {
|
||||
return pipe(
|
||||
tap(el => el.focus())
|
||||
)
|
||||
}
|
||||
|
||||
merge(a$, b$)
|
||||
.pipe(
|
||||
switchMapTo(component("query")),
|
||||
focusQuery()
|
||||
)
|
||||
.subscribe()
|
||||
|
||||
/* Return observable factories */
|
||||
return {
|
||||
ui: {
|
||||
document: { load$, switch$ },
|
||||
location: { url$, fragment$ },
|
||||
media: { screen$, tablet$ },
|
||||
viewport: { offset$, size$ }
|
||||
}
|
||||
|
||||
/* User interface */
|
||||
watchDocument: () => load$,
|
||||
watchDocumentSwitch: () => switch$,
|
||||
watchLocation: () => location$,
|
||||
watchLocationFragment: () => fragment$,
|
||||
watchMediaScreen: () => screen$,
|
||||
watchMediaTablet: () => tablet$,
|
||||
watchViewportOffset: () => offset$,
|
||||
watchViewportSize: () => size$
|
||||
}
|
||||
}
|
||||
|
48
src/assets/javascripts/theme/action/header/index.ts
Normal file
48
src/assets/javascripts/theme/action/header/index.ts
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set header shadow
|
||||
*
|
||||
* @param el - Header element
|
||||
* @param value - Whether the shadow is shown
|
||||
*/
|
||||
export function setHeaderShadow(
|
||||
el: HTMLElement, value: boolean
|
||||
): void {
|
||||
el.setAttribute("data-md-state", value ? "shadow" : "")
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset header shadow
|
||||
*
|
||||
* @param el - Header element
|
||||
*/
|
||||
export function resetHeaderShadow(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
el.removeAttribute("data-md-state")
|
||||
}
|
48
src/assets/javascripts/theme/action/hidden/index.ts
Normal file
48
src/assets/javascripts/theme/action/hidden/index.ts
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set hidden
|
||||
*
|
||||
* @param el - Hideable element
|
||||
* @param value - Whether the element is hidden
|
||||
*/
|
||||
export function setHidden(
|
||||
el: HTMLElement, value: boolean
|
||||
): void {
|
||||
el.setAttribute("data-md-state", value ? "hidden" : "")
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset hidden
|
||||
*
|
||||
* @param el - Hideable element
|
||||
*/
|
||||
export function resetHidden(
|
||||
el: HTMLElement
|
||||
): void {
|
||||
el.removeAttribute("data-md-state")
|
||||
}
|
25
src/assets/javascripts/theme/action/index.ts
Normal file
25
src/assets/javascripts/theme/action/index.ts
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
export * from "./header"
|
||||
export * from "./hidden"
|
||||
export * from "./sidebar"
|
@ -24,14 +24,14 @@ import { keys } from "ramda"
|
||||
import { NEVER, Observable, OperatorFunction, merge, of, pipe } from "rxjs"
|
||||
import { map, scan, shareReplay, switchMap } from "rxjs/operators"
|
||||
|
||||
import { getElement } from "../../utilities"
|
||||
import { getElement } from "../../../utilities"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Types
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Components
|
||||
* Component names
|
||||
*/
|
||||
export type Component =
|
||||
| "header" /* Header */
|
||||
@ -76,7 +76,7 @@ interface Options {
|
||||
* This function returns an observable that will maintain bindings to the given
|
||||
* components in-between document switches and update the document in-place.
|
||||
*
|
||||
* @param names - Components
|
||||
* @param names - Component names
|
||||
* @param options - Options
|
||||
*
|
||||
* @return Component map observable
|
||||
@ -133,7 +133,7 @@ export function watchComponentMap(
|
||||
*
|
||||
* @template T - Element type
|
||||
*
|
||||
* @param name - Component
|
||||
* @param name - Component name
|
||||
*
|
||||
* @return Operator function
|
||||
*/
|
||||
|
@ -21,3 +21,4 @@
|
||||
*/
|
||||
|
||||
export * from "./_"
|
||||
export * from "./offset"
|
||||
|
111
src/assets/javascripts/theme/component/header/offset/index.ts
Normal file
111
src/assets/javascripts/theme/component/header/offset/index.ts
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { Observable, combineLatest } from "rxjs"
|
||||
import {
|
||||
distinctUntilChanged,
|
||||
map,
|
||||
shareReplay,
|
||||
switchMapTo
|
||||
} from "rxjs/operators"
|
||||
|
||||
import { ViewportOffset, ViewportSize } from "../../../../ui"
|
||||
import { Header } from "../_"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Function types
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Options
|
||||
*/
|
||||
interface Options {
|
||||
size$: Observable<ViewportSize> /* Viewport size observable */
|
||||
offset$: Observable<ViewportOffset> /* Viewport offset observable */
|
||||
header$: Observable<Header> /* Header observable */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Watch viewport offset relative to an element's top
|
||||
*
|
||||
* This function returns an observable that computes the relative offset to the
|
||||
* top of the given element based on the current viewport offset.
|
||||
*
|
||||
* @param el - Element
|
||||
* @param options - Options
|
||||
*
|
||||
* @return Viewport offset observable
|
||||
*/
|
||||
export function watchTopOffset(
|
||||
el: HTMLElement, { size$, offset$, header$ }: Options
|
||||
): Observable<ViewportOffset> {
|
||||
|
||||
/* Compute necessary adjustment for offset */
|
||||
const adjust$ = size$
|
||||
.pipe(
|
||||
switchMapTo(header$),
|
||||
map(({ height }) => el.offsetTop - height),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
|
||||
/* Compute relative offset and return as hot observable */
|
||||
return combineLatest([offset$, adjust$])
|
||||
.pipe(
|
||||
map(([{ x, y }, adjust]) => ({ x, y: y - adjust })),
|
||||
shareReplay(1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch viewport offset relative to an element's bottom
|
||||
*
|
||||
* This function returns an observable that computes the relative offset to the
|
||||
* bottom of the given element based on the current viewport offset.
|
||||
*
|
||||
* @param el - Element
|
||||
* @param options - Options
|
||||
*
|
||||
* @return Viewport offset observable
|
||||
*/
|
||||
export function watchBottomOffset(
|
||||
el: HTMLElement, { size$, offset$, header$ }: Options
|
||||
): Observable<ViewportOffset> {
|
||||
|
||||
/* Compute necessary adjustment for offset */
|
||||
const adjust$ = size$
|
||||
.pipe(
|
||||
switchMapTo(header$),
|
||||
map(({ height }) => el.offsetTop + el.offsetHeight - height),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
|
||||
/* Compute relative offset and return as hot observable */
|
||||
return combineLatest([offset$, adjust$])
|
||||
.pipe(
|
||||
map(([{ x, y }, adjust]) => ({ x, y: y - adjust })),
|
||||
shareReplay(1)
|
||||
)
|
||||
}
|
@ -20,43 +20,50 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { EMPTY, Observable, OperatorFunction, pipe } from "rxjs"
|
||||
import { switchMap } from "rxjs/operators"
|
||||
import {
|
||||
MonoTypeOperatorFunction,
|
||||
animationFrameScheduler,
|
||||
pipe
|
||||
} from "rxjs"
|
||||
import {
|
||||
distinctUntilKeyChanged,
|
||||
finalize,
|
||||
observeOn,
|
||||
tap
|
||||
} from "rxjs/operators"
|
||||
|
||||
import {
|
||||
resetHeaderShadow,
|
||||
setHeaderShadow
|
||||
} from "../../../action"
|
||||
import { Main } from "../../main"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Switch to another observable if source observable emits `true`
|
||||
* Paint header shadow from source observable
|
||||
*
|
||||
* @template T - Observable value type
|
||||
*
|
||||
* @param project - Project function
|
||||
* @param el - Header element
|
||||
*
|
||||
* @return Operator function
|
||||
*/
|
||||
export function switchMapIfActive<T>(
|
||||
project: (value: boolean) => Observable<T>
|
||||
): OperatorFunction<boolean, T> {
|
||||
export function paintHeaderShadow(
|
||||
el: HTMLElement
|
||||
): MonoTypeOperatorFunction<Main> {
|
||||
return pipe(
|
||||
switchMap(value => value ? project(value) : EMPTY)
|
||||
)
|
||||
}
|
||||
distinctUntilKeyChanged("active"),
|
||||
|
||||
/**
|
||||
* Switch to another observable if source observable emits `false`
|
||||
*
|
||||
* @template T - Observable value type
|
||||
*
|
||||
* @param project - Project function
|
||||
*
|
||||
* @return Operator function
|
||||
*/
|
||||
export function switchMapIfNotActive<T>(
|
||||
project: (value: boolean) => Observable<T>
|
||||
): OperatorFunction<boolean, T> {
|
||||
return pipe(
|
||||
switchMap(value => value ? EMPTY : project(value))
|
||||
/* Defer repaint to next animation frame */
|
||||
observeOn(animationFrameScheduler),
|
||||
tap(({ active }) => {
|
||||
setHeaderShadow(el, active)
|
||||
}),
|
||||
|
||||
/* Reset on complete or error */
|
||||
finalize(() => {
|
||||
resetHeaderShadow(el)
|
||||
})
|
||||
)
|
||||
}
|
65
src/assets/javascripts/theme/component/hidden/index.ts
Normal file
65
src/assets/javascripts/theme/component/hidden/index.ts
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { OperatorFunction, animationFrameScheduler, pipe } from "rxjs"
|
||||
import {
|
||||
distinctUntilChanged,
|
||||
finalize,
|
||||
map,
|
||||
observeOn,
|
||||
tap
|
||||
} from "rxjs/operators"
|
||||
|
||||
import { ViewportOffset } from "../../../ui"
|
||||
import { resetHidden, setHidden } from "../../action"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Paint hideable from source observable
|
||||
*
|
||||
* @param el - Hideable element
|
||||
* @param offset - Additional offset
|
||||
*
|
||||
* @return Operator function
|
||||
*/
|
||||
export function paintHidden(
|
||||
el: HTMLElement, offset: number = 0
|
||||
): OperatorFunction<ViewportOffset, boolean> {
|
||||
return pipe(
|
||||
map(({ y }) => y >= offset),
|
||||
distinctUntilChanged(),
|
||||
|
||||
/* Defer repaint to next animation frame */
|
||||
observeOn(animationFrameScheduler),
|
||||
tap(value => {
|
||||
setHidden(el, value)
|
||||
}),
|
||||
|
||||
/* Reset on complete or error */
|
||||
finalize(() => {
|
||||
resetHidden(el)
|
||||
})
|
||||
)
|
||||
}
|
@ -22,5 +22,7 @@
|
||||
|
||||
export * from "./_"
|
||||
export * from "./header"
|
||||
export * from "./hidden"
|
||||
export * from "./main"
|
||||
export * from "./search"
|
||||
export * from "./sidebar"
|
||||
|
24
src/assets/javascripts/theme/component/search/index.ts
Normal file
24
src/assets/javascripts/theme/component/search/index.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// export * from "./query"
|
||||
export * from "./reset"
|
22
src/assets/javascripts/theme/component/search/query/index.ts
Normal file
22
src/assets/javascripts/theme/component/search/query/index.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
44
src/assets/javascripts/theme/component/search/reset/index.ts
Normal file
44
src/assets/javascripts/theme/component/search/reset/index.ts
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { Observable, fromEvent } from "rxjs"
|
||||
import { mapTo } from "rxjs/operators"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Watch search reset
|
||||
*
|
||||
* @param el - Search reset element
|
||||
*
|
||||
* @return Search reset observable
|
||||
*/
|
||||
export function watchSearchReset(
|
||||
el: HTMLElement
|
||||
): Observable<boolean> {
|
||||
return fromEvent(el, "click")
|
||||
.pipe(
|
||||
mapTo(true)
|
||||
)
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { equals } from "ramda"
|
||||
import { Observable, animationFrameScheduler, combineLatest } from "rxjs"
|
||||
import {
|
||||
distinctUntilChanged,
|
||||
finalize,
|
||||
map,
|
||||
observeOn,
|
||||
shareReplay,
|
||||
tap
|
||||
} from "rxjs/operators"
|
||||
|
||||
import { ViewportOffset } from "../../../../ui"
|
||||
import { Main } from "../../main"
|
||||
import {
|
||||
resetSidebarHeight,
|
||||
resetSidebarLock,
|
||||
setSidebarHeight,
|
||||
setSidebarLock
|
||||
} from "../element"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Types
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Sidebar
|
||||
*/
|
||||
export interface Sidebar {
|
||||
height: number /* Sidebar height */
|
||||
lock: boolean /* Sidebar lock */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Function types
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Options
|
||||
*/
|
||||
interface Options {
|
||||
offset$: Observable<ViewportOffset> /* Viewport offset observable */
|
||||
main$: Observable<Main> /* Main area observable */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Watch sidebar
|
||||
*
|
||||
* This function returns an observable that computes the visual parameters of
|
||||
* the given element (a sidebar) from the vertical viewport offset, as well as
|
||||
* the height of the main area. When the page is scrolled beyond the header,
|
||||
* the sidebar is locked and fills the remaining space.
|
||||
*
|
||||
* @param el - Sidebar element
|
||||
* @param options - Options
|
||||
*
|
||||
* @return Sidebar observable
|
||||
*/
|
||||
export function watchSidebar(
|
||||
el: HTMLElement, { offset$, main$ }: Options
|
||||
): Observable<Sidebar> {
|
||||
|
||||
/* Adjust for internal main area offset */
|
||||
const adjust = parseFloat(
|
||||
getComputedStyle(el.parentElement!)
|
||||
.getPropertyValue("padding-top")
|
||||
)
|
||||
|
||||
/* Compute the sidebar's available height */
|
||||
const height$ = combineLatest([offset$, main$])
|
||||
.pipe(
|
||||
map(([{ y }, { offset, height }]) => {
|
||||
return height - adjust + Math.min(adjust, Math.max(0, y - offset))
|
||||
})
|
||||
)
|
||||
|
||||
/* Compute whether the sidebar should be locked */
|
||||
const lock$ = combineLatest([offset$, main$])
|
||||
.pipe(
|
||||
map(([{ y }, { offset }]) => y >= offset + adjust)
|
||||
)
|
||||
|
||||
/* Combine into single hot observable */
|
||||
return combineLatest([height$, lock$])
|
||||
.pipe(
|
||||
map(([height, lock]) => ({ height, lock })),
|
||||
distinctUntilChanged<Sidebar>(equals),
|
||||
shareReplay(1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup sidebar
|
||||
*
|
||||
* @param el - Sidebar element
|
||||
* @param options - Options
|
||||
*
|
||||
* @return Sidebar observable
|
||||
*/
|
||||
export function setupSidebar(
|
||||
el: HTMLElement, options: Options
|
||||
): Observable<Sidebar> {
|
||||
return watchSidebar(el, options)
|
||||
.pipe(
|
||||
observeOn(animationFrameScheduler),
|
||||
|
||||
/* Apply mutations (side effects) */
|
||||
tap(({ height, lock }) => {
|
||||
setSidebarHeight(el, height)
|
||||
setSidebarLock(el, lock)
|
||||
}),
|
||||
|
||||
/* Reset on complete or error */
|
||||
finalize(() => {
|
||||
resetSidebarHeight(el)
|
||||
resetSidebarLock(el)
|
||||
})
|
||||
)
|
||||
}
|
@ -20,5 +20,131 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
export * from "./_"
|
||||
export * from "./element"
|
||||
import { equals } from "ramda"
|
||||
import {
|
||||
MonoTypeOperatorFunction,
|
||||
Observable,
|
||||
animationFrameScheduler,
|
||||
combineLatest,
|
||||
pipe
|
||||
} from "rxjs"
|
||||
import {
|
||||
distinctUntilChanged,
|
||||
finalize,
|
||||
map,
|
||||
observeOn,
|
||||
shareReplay,
|
||||
tap
|
||||
} from "rxjs/operators"
|
||||
|
||||
import { ViewportOffset } from "../../../ui"
|
||||
import {
|
||||
resetSidebarHeight,
|
||||
resetSidebarLock,
|
||||
setSidebarHeight,
|
||||
setSidebarLock
|
||||
} from "../../action"
|
||||
import { Main } from "../main"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Types
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Sidebar
|
||||
*/
|
||||
export interface Sidebar {
|
||||
height: number /* Sidebar height */
|
||||
lock: boolean /* Sidebar lock */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Function types
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Options
|
||||
*/
|
||||
interface Options {
|
||||
offset$: Observable<ViewportOffset> /* Viewport offset observable */
|
||||
main$: Observable<Main> /* Main area observable */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Watch sidebar
|
||||
*
|
||||
* This function returns an observable that computes the visual parameters of
|
||||
* the given element (a sidebar) from the vertical viewport offset, as well as
|
||||
* the height of the main area. When the page is scrolled beyond the header,
|
||||
* the sidebar is locked and fills the remaining space.
|
||||
*
|
||||
* @param el - Sidebar element
|
||||
* @param options - Options
|
||||
*
|
||||
* @return Sidebar observable
|
||||
*/
|
||||
export function watchSidebar(
|
||||
el: HTMLElement, { offset$, main$ }: Options
|
||||
): Observable<Sidebar> {
|
||||
|
||||
/* Adjust for internal main area offset */
|
||||
const adjust = parseFloat(
|
||||
getComputedStyle(el.parentElement!)
|
||||
.getPropertyValue("padding-top")
|
||||
)
|
||||
|
||||
/* Compute the sidebar's available height */
|
||||
const height$ = combineLatest([offset$, main$])
|
||||
.pipe(
|
||||
map(([{ y }, { offset, height }]) => {
|
||||
return height - adjust + Math.min(adjust, Math.max(0, y - offset))
|
||||
})
|
||||
)
|
||||
|
||||
/* Compute whether the sidebar should be locked */
|
||||
const lock$ = combineLatest([offset$, main$])
|
||||
.pipe(
|
||||
map(([{ y }, { offset }]) => y >= offset + adjust)
|
||||
)
|
||||
|
||||
/* Combine into single hot observable */
|
||||
return combineLatest([height$, lock$])
|
||||
.pipe(
|
||||
map(([height, lock]) => ({ height, lock })),
|
||||
distinctUntilChanged<Sidebar>(equals),
|
||||
shareReplay(1)
|
||||
)
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Paint sidebar from source observable
|
||||
*
|
||||
* @param el - Sidebar element
|
||||
*
|
||||
* @return Operator function
|
||||
*/
|
||||
export function paintSidebar(
|
||||
el: HTMLElement
|
||||
): MonoTypeOperatorFunction<Sidebar> {
|
||||
return pipe(
|
||||
|
||||
/* Defer repaint to next animation frame */
|
||||
observeOn(animationFrameScheduler),
|
||||
tap(({ height, lock }) => {
|
||||
setSidebarHeight(el, height)
|
||||
setSidebarLock(el, lock)
|
||||
}),
|
||||
|
||||
/* Reset on complete or error */
|
||||
finalize(() => {
|
||||
resetSidebarHeight(el)
|
||||
resetSidebarLock(el)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
export * from "./action"
|
||||
export * from "./component"
|
||||
export * from "./utilities"
|
||||
export * from "./toggle"
|
||||
// export * from "./worker"
|
||||
|
44
src/assets/javascripts/theme/toggle/index.ts
Normal file
44
src/assets/javascripts/theme/toggle/index.ts
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { Observable, fromEvent } from "rxjs"
|
||||
import { pluck } from "rxjs/operators"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Watch toggle
|
||||
*
|
||||
* @param el - Toggle element
|
||||
*
|
||||
* @return Toggle observable
|
||||
*/
|
||||
export function watchToggle(
|
||||
el: HTMLInputElement
|
||||
): Observable<boolean> {
|
||||
return fromEvent(el, "change")
|
||||
.pipe(
|
||||
pluck("checked")
|
||||
)
|
||||
}
|
@ -41,7 +41,7 @@ import {
|
||||
* Switch options
|
||||
*/
|
||||
interface SwitchOptions {
|
||||
url$: Observable<string> /* Location observable */
|
||||
location$: Observable<string> /* Location observable */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
@ -83,14 +83,16 @@ export function watchDocument(): Observable<Document> {
|
||||
* @return Document switch observable
|
||||
*/
|
||||
export function watchDocumentSwitch(
|
||||
{ url$ }: SwitchOptions
|
||||
{ location$ }: SwitchOptions
|
||||
): Observable<Document> {
|
||||
return url$
|
||||
return location$
|
||||
.pipe(
|
||||
startWith(location.href),
|
||||
map(url => url.replace(/#[^#]+$/, "")),
|
||||
distinctUntilChanged(),
|
||||
skip(1),
|
||||
|
||||
/* Fetch document */
|
||||
switchMap(url => ajax({
|
||||
url,
|
||||
responseType: "document",
|
||||
|
86
src/assets/javascripts/utilities/operator/index.ts
Normal file
86
src/assets/javascripts/utilities/operator/index.ts
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import {
|
||||
EMPTY,
|
||||
Observable,
|
||||
OperatorFunction,
|
||||
combineLatest,
|
||||
of,
|
||||
pipe
|
||||
} from "rxjs"
|
||||
import {
|
||||
filter,
|
||||
map,
|
||||
switchMap,
|
||||
takeUntil
|
||||
} from "rxjs/operators"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Invert boolean value of source observable
|
||||
*
|
||||
* @param toggle$ - Toggle observable
|
||||
*
|
||||
* @return Inverted toggle observable
|
||||
*/
|
||||
export function not(
|
||||
toggle$: Observable<boolean>
|
||||
): Observable<boolean> {
|
||||
return toggle$
|
||||
.pipe(
|
||||
map(active => !active)
|
||||
)
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Toggle switch map with another observable
|
||||
*
|
||||
* @template T - Source value type
|
||||
* @template U - Target value type
|
||||
*
|
||||
* @param toggle$ - Toggle observable
|
||||
* @param project - Projection
|
||||
*
|
||||
* @return Operator function
|
||||
*/
|
||||
export function switchMapIf<T, U>(
|
||||
toggle$: Observable<boolean>, project: (value: T) => Observable<U>
|
||||
): OperatorFunction<T, U> {
|
||||
const begin$ = toggle$.pipe(filter(value => value))
|
||||
const end$ = toggle$.pipe(filter(value => !value))
|
||||
return pipe(
|
||||
switchMap(value => combineLatest([of(value), begin$])),
|
||||
switchMap(([value, active]) => active
|
||||
? project(value)
|
||||
.pipe(
|
||||
takeUntil(end$)
|
||||
)
|
||||
: EMPTY
|
||||
)
|
||||
)
|
||||
}
|
@ -32,8 +32,8 @@
|
||||
// Local imports
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@import "helpers/break";
|
||||
@import "helpers/px2em";
|
||||
@import "utilities/break";
|
||||
@import "utilities/px2em";
|
||||
|
||||
@import "config";
|
||||
|
||||
|
@ -32,8 +32,8 @@
|
||||
// Local imports
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@import "helpers/break";
|
||||
@import "helpers/px2em";
|
||||
@import "utilities/break";
|
||||
@import "utilities/px2em";
|
||||
|
||||
@import "config";
|
||||
|
||||
|
@ -124,6 +124,7 @@
|
||||
&__topic {
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: calc(100% - #{px2rem(20px)});
|
||||
transition:
|
||||
transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1),
|
||||
opacity 0.15s;
|
||||
@ -133,7 +134,7 @@
|
||||
|
||||
// Page title
|
||||
& + & {
|
||||
transform: translateY(px2rem(25px));
|
||||
transform: translateX(px2rem(25px));
|
||||
transition:
|
||||
transform 0.4s cubic-bezier(1, 0.7, 0.1, 0.1),
|
||||
opacity 0.15s;
|
||||
@ -143,7 +144,7 @@
|
||||
|
||||
// Adjust for RTL languages
|
||||
[dir="rtl"] & {
|
||||
transform: translateY(px2rem(-25px));
|
||||
transform: translateX(px2rem(-25px));
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,7 +167,7 @@
|
||||
|
||||
// Show page title
|
||||
&[data-md-state="active"] .md-header-nav__topic {
|
||||
transform: translateY(px2rem(-25px));
|
||||
transform: translateX(px2rem(-25px));
|
||||
transition:
|
||||
transform 0.4s cubic-bezier(1, 0.7, 0.1, 0.1),
|
||||
opacity 0.15s;
|
||||
@ -176,12 +177,12 @@
|
||||
|
||||
// Adjust for RTL languages
|
||||
[dir="rtl"] & {
|
||||
transform: translateY(px2rem(25px));
|
||||
transform: translateX(px2rem(25px));
|
||||
}
|
||||
|
||||
// Page title
|
||||
& + .md-header-nav__topic {
|
||||
transform: translateY(0);
|
||||
transform: translateX(0);
|
||||
transition:
|
||||
transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1),
|
||||
opacity 0.15s;
|
||||
|
@ -419,11 +419,8 @@
|
||||
|
||||
<!-- Application initialization -->
|
||||
<script>
|
||||
app({
|
||||
version: "{{ mkdocs_version }}",
|
||||
url: {
|
||||
base: "{{ base_url }}"
|
||||
}
|
||||
app = initialize({
|
||||
base: "{{ base_url }}"
|
||||
});
|
||||
</script>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user