Unified components and observable structure

This commit is contained in:
squidfunk
2020-03-05 17:17:15 +01:00
parent 6e6868b8cd
commit 9738c12cc9
149 changed files with 11475 additions and 1095 deletions

View File

@@ -29,7 +29,7 @@ import {
switchMap
} from "rxjs/operators"
import { getElement } from "observables"
import { getElement } from "browser"
/* ----------------------------------------------------------------------------
* Types

View File

@@ -35,11 +35,13 @@ import {
Viewport,
getElement,
watchViewportAt
} from "observables"
} from "browser"
import { useComponent } from "../../_"
import { paintHeaderType } from "../paint"
import { watchHeader } from "../watch"
import {
applyHeaderType,
watchHeader
} from "../react"
/* ----------------------------------------------------------------------------
* Types
@@ -104,7 +106,7 @@ export function mountHeader(
return y >= hx.offsetHeight ? "page" : "site"
}),
distinctUntilChanged(),
paintHeaderType(title)
applyHeaderType(title)
)
),
startWith<HeaderType>("site")

View File

@@ -21,6 +21,5 @@
*/
export * from "./_"
export * from "./apply"
export * from "./paint"
export * from "./watch"
export * from "./react"
export * from "./set"

View File

@@ -1,63 +0,0 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import {
MonoTypeOperatorFunction,
animationFrameScheduler,
pipe
} from "rxjs"
import { finalize, observeOn, tap } from "rxjs/operators"
import { HeaderType } from "../_"
import {
resetHeaderTitleActive,
setHeaderTitleActive
} from "../apply"
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Paint header title type
*
* @param el - Header title element
*
* @return Operator function
*/
export function paintHeaderType(
el: HTMLElement
): MonoTypeOperatorFunction<HeaderType> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(type => {
setHeaderTitleActive(el, type === "page")
}),
/* Reset on complete or error */
finalize(() => {
resetHeaderTitleActive(el)
})
)
}

View File

@@ -20,12 +20,28 @@
* IN THE SOFTWARE.
*/
import { Observable, of } from "rxjs"
import { distinctUntilKeyChanged, switchMap } from "rxjs/operators"
import {
MonoTypeOperatorFunction,
Observable,
animationFrameScheduler,
of,
pipe
} from "rxjs"
import {
distinctUntilKeyChanged,
finalize,
observeOn,
switchMap,
tap
} from "rxjs/operators"
import { Viewport } from "observables"
import { Viewport } from "browser"
import { Header } from "../_"
import { Header, HeaderType } from "../_"
import {
resetHeaderTitleActive,
setHeaderTitleActive
} from "../set"
/* ----------------------------------------------------------------------------
* Helper types
@@ -72,3 +88,30 @@ export function watchHeader(
})
)
}
/* ------------------------------------------------------------------------- */
/**
* Apply header title type
*
* @param el - Header title element
*
* @return Operator function
*/
export function applyHeaderType(
el: HTMLElement
): MonoTypeOperatorFunction<HeaderType> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(type => {
setHeaderTitleActive(el, type === "page")
}),
/* Reset on complete or error */
finalize(() => {
resetHeaderTitleActive(el)
})
)
}

View File

@@ -23,10 +23,10 @@
import { Observable, OperatorFunction, pipe } from "rxjs"
import { distinctUntilChanged, map, switchMap } from "rxjs/operators"
import { Viewport, watchViewportAt } from "observables"
import { Viewport, watchViewportAt } from "browser"
import { Header } from "../../header"
import { paintHero } from "../paint"
import { applyHero } from "../react"
/* ----------------------------------------------------------------------------
* Types
@@ -70,7 +70,7 @@ export function mountHero(
.pipe(
map(({ offset: { y } }) => ({ hidden: y >= 20 })),
distinctUntilChanged(),
paintHero(el)
applyHero(el)
)
)
)

View File

@@ -21,5 +21,5 @@
*/
export * from "./_"
export * from "./apply"
export * from "./paint"
export * from "./react"
export * from "./set"

View File

@@ -31,20 +31,20 @@ import { Hero } from "../_"
import {
resetHeroHidden,
setHeroHidden
} from "../apply"
} from "../set"
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Paint hero
* Apply hero
*
* @param el - Hero element
*
* @return Operator function
*/
export function paintHero(
export function applyHero(
el: HTMLElement
): MonoTypeOperatorFunction<Hero> {
return pipe(

View File

@@ -23,12 +23,14 @@
import { Observable, OperatorFunction, Subject, pipe } from "rxjs"
import { distinctUntilKeyChanged, switchMap, tap } from "rxjs/operators"
import { Viewport } from "observables"
import { Viewport } from "browser"
import { useComponent } from "../../_"
import { Header } from "../../header"
import { paintHeaderShadow } from "../paint"
import { watchMain } from "../watch"
import {
applyHeaderShadow,
watchMain
} from "../react"
/* ----------------------------------------------------------------------------
* Types
@@ -82,7 +84,7 @@ export function mountMain(
switchMap(header => main$
.pipe(
distinctUntilKeyChanged("active"),
paintHeaderShadow(header)
applyHeaderShadow(header)
)
)
)

View File

@@ -21,6 +21,5 @@
*/
export * from "./_"
export * from "./apply"
export * from "./paint"
export * from "./watch"
export * from "./react"
export * from "./set"

View File

@@ -1,63 +0,0 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import {
MonoTypeOperatorFunction,
animationFrameScheduler,
pipe
} from "rxjs"
import { finalize, observeOn, tap } from "rxjs/operators"
import { Main } from "../_"
import {
resetHeaderShadow,
setHeaderShadow
} from "../apply"
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Paint header shadow
*
* @param el - Header element
*
* @return Operator function
*/
export function paintHeaderShadow(
el: HTMLElement
): MonoTypeOperatorFunction<Main> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(({ active }) => {
setHeaderShadow(el, active)
}),
/* Reset on complete or error */
finalize(() => {
resetHeaderShadow(el)
})
)
}

View File

@@ -20,13 +20,30 @@
* IN THE SOFTWARE.
*/
import { Observable, combineLatest } from "rxjs"
import { distinctUntilChanged, map, pluck } from "rxjs/operators"
import {
MonoTypeOperatorFunction,
Observable,
animationFrameScheduler,
combineLatest,
pipe
} from "rxjs"
import {
distinctUntilChanged,
finalize,
map,
observeOn,
pluck,
tap
} from "rxjs/operators"
import { Viewport } from "observables"
import { Viewport } from "browser"
import { Header } from "../../header"
import { Main } from "../_"
import {
resetHeaderShadow,
setHeaderShadow
} from "../set"
/* ----------------------------------------------------------------------------
* Helper types
@@ -97,3 +114,30 @@ export function watchMain(
}))
)
}
/* ------------------------------------------------------------------------- */
/**
* Apply header shadow
*
* @param el - Header element
*
* @return Operator function
*/
export function applyHeaderShadow(
el: HTMLElement
): MonoTypeOperatorFunction<Main> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(({ active }) => {
setHeaderShadow(el, active)
}),
/* Reset on complete or error */
finalize(() => {
resetHeaderShadow(el)
})
)
}

View File

@@ -23,18 +23,18 @@
import { Observable, OperatorFunction, pipe } from "rxjs"
import { map, switchMap } from "rxjs/operators"
import { Viewport, getElements } from "observables"
import { Viewport, getElements } from "browser"
import { Header } from "../../header"
import { Main } from "../../main"
import {
Sidebar,
paintSidebar,
applySidebar,
watchSidebar
} from "../../shared"
import {
NavigationLayer,
paintNavigationLayer,
applyNavigationLayer,
watchNavigationLayer
} from "../layer"
@@ -102,7 +102,7 @@ export function mountNavigation(
if (screen) {
return watchSidebar(el, { main$, viewport$ })
.pipe(
paintSidebar(el, { header$ }),
applySidebar(el, { header$ }),
map(sidebar => ({ sidebar }))
)
@@ -111,7 +111,7 @@ export function mountNavigation(
const els = getElements("nav", el)
return watchNavigationLayer(els)
.pipe(
paintNavigationLayer(els),
applyNavigationLayer(els),
map(layer => ({ layer }))
)
}

View File

@@ -21,6 +21,5 @@
*/
export * from "./_"
export * from "./apply"
export * from "./paint"
export * from "./watch"
export * from "./react"
export * from "./set"

View File

@@ -1,83 +0,0 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import {
MonoTypeOperatorFunction,
animationFrameScheduler,
pipe
} from "rxjs"
import {
delay,
finalize,
observeOn,
tap
} from "rxjs/operators"
import { getElementOrThrow } from "observables"
import { NavigationLayer } from "../_"
import {
resetOverflowScrolling,
setOverflowScrolling
} from "../apply"
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Paint navigation layer
*
* @param els - Navigation elements
*
* @return Operator function
*/
export function paintNavigationLayer(
els: HTMLElement[]
): MonoTypeOperatorFunction<NavigationLayer> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(({ prev }) => {
if (prev)
resetOverflowScrolling(prev)
}),
/* Wait until transition has finished */
delay(250),
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(({ next }) => {
setOverflowScrolling(next)
}),
/* Reset on complete or error */
finalize(() => {
for (const el of els)
resetOverflowScrolling(
getElementOrThrow(".md-nav__list", el)
)
})
)
}

View File

@@ -21,15 +21,30 @@
*/
import { findLast } from "ramda"
import { Observable, fromEvent, merge } from "rxjs"
import { map, scan } from "rxjs/operators"
import {
getElement,
getElementOrThrow
} from "observables"
MonoTypeOperatorFunction,
Observable,
animationFrameScheduler,
fromEvent,
merge,
pipe
} from "rxjs"
import {
delay,
finalize,
map,
observeOn,
scan,
tap
} from "rxjs/operators"
import { getElement, getElementOrThrow } from "browser"
import { NavigationLayer } from "../_"
import {
resetOverflowScrolling,
setOverflowScrolling
} from "../set"
/* ----------------------------------------------------------------------------
* Functions
@@ -76,3 +91,43 @@ export function watchNavigationLayer(
scan(({ next: prev }, { next }) => ({ prev, next }))
)
}
/* ------------------------------------------------------------------------- */
/**
* Apply navigation layer
*
* @param els - Navigation elements
*
* @return Operator function
*/
export function applyNavigationLayer(
els: HTMLElement[]
): MonoTypeOperatorFunction<NavigationLayer> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(({ prev }) => {
if (prev)
resetOverflowScrolling(prev)
}),
/* Wait until transition has finished */
delay(250),
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(({ next }) => {
setOverflowScrolling(next)
}),
/* Reset on complete or error */
finalize(() => {
for (const el of els)
resetOverflowScrolling(
getElementOrThrow(".md-nav__list", el)
)
})
)
}

View File

@@ -24,22 +24,17 @@ import { OperatorFunction, pipe } from "rxjs"
import {
distinctUntilKeyChanged,
map,
switchMap,
withLatestFrom
switchMap
} from "rxjs/operators"
import {
WorkerHandler,
setToggle,
useToggle
} from "observables"
import { WorkerHandler, setToggle } from "browser"
import {
SearchMessage,
SearchMessageType,
SearchQueryMessage
} from "workers"
import { watchSearchQuery } from "../watch"
import { watchSearchQuery } from "../react"
/* ----------------------------------------------------------------------------
* Types
@@ -79,7 +74,6 @@ interface MountOptions {
export function mountSearchQuery(
{ tx$ }: WorkerHandler<SearchMessage>, options: MountOptions = {}
): OperatorFunction<HTMLInputElement, SearchQuery> {
const toggle$ = useToggle("search")
return pipe(
switchMap(el => {
const query$ = watchSearchQuery(el, options)
@@ -98,12 +92,11 @@ export function mountSearchQuery(
/* Toggle search on focus */
query$
.pipe(
distinctUntilKeyChanged("focus"),
withLatestFrom(toggle$)
distinctUntilKeyChanged("focus")
)
.subscribe(([{ focus }, toggle]) => {
.subscribe(({ focus }) => {
if (focus)
setToggle(toggle, focus)
setToggle("search", focus)
})
/* Return search query */

View File

@@ -21,4 +21,4 @@
*/
export * from "./_"
export * from "./watch"
export * from "./react"

View File

@@ -28,7 +28,7 @@ import {
startWith
} from "rxjs/operators"
import { watchElementFocus } from "observables"
import { watchElementFocus } from "browser"
import { SearchQuery } from "../_"

View File

@@ -29,10 +29,10 @@ import {
tap
} from "rxjs/operators"
import { setElementFocus } from "observables"
import { setElementFocus } from "browser"
import { useComponent } from "../../../_"
import { watchSearchReset } from "../watch"
import { watchSearchReset } from "../react"
/* ----------------------------------------------------------------------------
* Functions

View File

@@ -21,4 +21,4 @@
*/
export * from "./_"
export * from "./watch"
export * from "./react"

View File

@@ -30,18 +30,15 @@ import {
switchMap
} from "rxjs/operators"
import { WorkerHandler, watchElementOffset } from "browser"
import { SearchResult } from "integrations/search"
import {
WorkerHandler,
watchElementOffset
} from "observables"
import {
SearchMessage,
isSearchResultMessage
} from "workers"
import { SearchQuery } from "../../query"
import { paintSearchResult } from "../paint"
import { applySearchResult } from "../react"
/* ----------------------------------------------------------------------------
* Helper types
@@ -83,12 +80,12 @@ export function mountSearchResult(
filter(identity)
)
/* Paint search results */
/* Apply search results */
return rx$
.pipe(
filter(isSearchResultMessage),
pluck("data"),
paintSearchResult(el, { query$, fetch$ })
applySearchResult(el, { query$, fetch$ })
)
})
)

View File

@@ -21,5 +21,5 @@
*/
export * from "./_"
export * from "./apply"
export * from "./paint"
export * from "./react"
export * from "./set"

View File

@@ -36,8 +36,8 @@ import {
withLatestFrom
} from "rxjs/operators"
import { getElementOrThrow } from "browser"
import { SearchResult } from "integrations/search"
import { getElementOrThrow } from "observables"
import { renderSearchResult } from "templates"
import { SearchQuery } from "../../query"
@@ -46,16 +46,16 @@ import {
resetSearchResultList,
resetSearchResultMeta,
setSearchResultMeta
} from "../apply"
} from "../set"
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Paint options
* Apply options
*/
interface PaintOptions {
interface ApplyOptions {
query$: Observable<SearchQuery> /* Search query observable */
fetch$: Observable<boolean> /* Result fetch observable */
}
@@ -65,7 +65,7 @@ interface PaintOptions {
* ------------------------------------------------------------------------- */
/**
* Paint search results
* Apply search results
*
* This function will perform a lazy rendering of the search results, depending
* on the vertical offset of the search result container. When the scroll offset
@@ -76,14 +76,14 @@ interface PaintOptions {
*
* @return Operator function
*/
export function paintSearchResult(
el: HTMLElement, { query$, fetch$ }: PaintOptions
export function applySearchResult(
el: HTMLElement, { query$, fetch$ }: ApplyOptions
): MonoTypeOperatorFunction<SearchResult[]> {
const list = getElementOrThrow(".md-search-result__list", el)
const meta = getElementOrThrow(".md-search-result__meta", el)
return pipe(
/* Paint search result metadata */
/* Apply search result metadata */
withLatestFrom(query$),
map(([result, query]) => {
if (query.value) {
@@ -94,7 +94,7 @@ export function paintSearchResult(
return result
}),
/* Paint search result list */
/* Apply search result list */
switchMap(result => fetch$
.pipe(

View File

@@ -21,6 +21,5 @@
*/
export * from "./_"
export * from "./apply"
export * from "./paint"
export * from "./watch"
export * from "./react"
export * from "./set"

View File

@@ -1,100 +0,0 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import {
MonoTypeOperatorFunction,
Observable,
animationFrameScheduler,
pipe
} from "rxjs"
import {
finalize,
map,
observeOn,
tap,
withLatestFrom
} from "rxjs/operators"
import { Header } from "../../../header"
import { Sidebar } from "../_"
import {
resetSidebarHeight,
resetSidebarLock,
resetSidebarOffset,
setSidebarHeight,
setSidebarLock,
setSidebarOffset
} from "../apply"
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Paint options
*/
interface PaintOptions {
header$: Observable<Header> /* Header observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Paint sidebar
*
* @param el - Sidebar element
* @param options - Options
*
* @return Operator function
*/
export function paintSidebar(
el: HTMLElement, { header$ }: PaintOptions
): MonoTypeOperatorFunction<Sidebar> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
withLatestFrom(header$),
tap(([{ height, lock }, { height: offset }]) => {
setSidebarHeight(el, height)
setSidebarLock(el, lock)
/* Set offset in locked state depending on header height */
if (lock)
setSidebarOffset(el, offset)
else
resetSidebarOffset(el)
}),
/* Re-map to sidebar */
map(([sidebar]) => sidebar),
/* Reset on complete or error */
finalize(() => {
resetSidebarOffset(el)
resetSidebarHeight(el)
resetSidebarLock(el)
})
)
}

View File

@@ -20,18 +20,36 @@
* IN THE SOFTWARE.
*/
import { Observable, combineLatest } from "rxjs"
import {
MonoTypeOperatorFunction,
Observable,
animationFrameScheduler,
combineLatest,
pipe
} from "rxjs"
import {
distinctUntilChanged,
distinctUntilKeyChanged,
finalize,
map,
observeOn,
tap,
withLatestFrom
} from "rxjs/operators"
import { Viewport } from "observables"
import { Viewport } from "browser"
import { Header } from "../../../header"
import { Main } from "../../../main"
import { Sidebar } from "../_"
import {
resetSidebarHeight,
resetSidebarLock,
resetSidebarOffset,
setSidebarHeight,
setSidebarLock,
setSidebarOffset
} from "../set"
/* ----------------------------------------------------------------------------
* Helper types
@@ -45,6 +63,13 @@ interface WatchOptions {
viewport$: Observable<Viewport> /* Viewport observable */
}
/**
* Apply options
*/
interface ApplyOptions {
header$: Observable<Header> /* Header observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
@@ -105,3 +130,44 @@ export function watchSidebar(
map(([height, lock]) => ({ height, lock }))
)
}
/* ------------------------------------------------------------------------- */
/**
* Apply sidebar
*
* @param el - Sidebar element
* @param options - Options
*
* @return Operator function
*/
export function applySidebar(
el: HTMLElement, { header$ }: ApplyOptions
): MonoTypeOperatorFunction<Sidebar> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
withLatestFrom(header$),
tap(([{ height, lock }, { height: offset }]) => {
setSidebarHeight(el, height)
setSidebarLock(el, lock)
/* Set offset in locked state depending on header height */
if (lock)
setSidebarOffset(el, offset)
else
resetSidebarOffset(el)
}),
/* Re-map to sidebar */
map(([sidebar]) => sidebar),
/* Reset on complete or error */
finalize(() => {
resetSidebarOffset(el)
resetSidebarHeight(el)
resetSidebarLock(el)
})
)
}

View File

@@ -23,10 +23,10 @@
import { Observable, OperatorFunction, of, pipe } from "rxjs"
import { distinctUntilChanged, map, switchMap } from "rxjs/operators"
import { Viewport, watchViewportAt } from "observables"
import { Viewport, watchViewportAt } from "browser"
import { Header } from "../../header"
import { paintTabs } from "../paint"
import { applyTabs } from "../react"
/* ----------------------------------------------------------------------------
* Types
@@ -77,7 +77,7 @@ export function mountTabs(
.pipe(
map(({ offset: { y } }) => ({ hidden: y >= 10 })),
distinctUntilChanged(),
paintTabs(el)
applyTabs(el)
)
/* [screen -]: Unmount tabs below screen breakpoint */

View File

@@ -21,5 +21,5 @@
*/
export * from "./_"
export * from "./apply"
export * from "./paint"
export * from "./react"
export * from "./set"

View File

@@ -31,20 +31,20 @@ import { Tabs } from "../_"
import {
resetTabsHidden,
setTabsHidden
} from "../apply"
} from "../set"
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Paint tabs
* Apply tabs
*
* @param el - Tabs element
*
* @return Operator function
*/
export function paintTabs(
export function applyTabs(
el: HTMLElement
): MonoTypeOperatorFunction<Tabs> {
return pipe(

View File

@@ -29,18 +29,18 @@ import {
} from "rxjs"
import { map, switchMap } from "rxjs/operators"
import { Viewport, getElements } from "observables"
import { Viewport, getElements } from "browser"
import { Header } from "../../header"
import { Main } from "../../main"
import {
Sidebar,
paintSidebar,
applySidebar,
watchSidebar
} from "../../shared"
import {
AnchorList,
paintAnchorList,
applyAnchorList,
watchAnchorList
} from "../anchor"
@@ -107,16 +107,16 @@ export function mountTableOfContents(
if (tablet) {
const els = getElements<HTMLAnchorElement>(".md-nav__link", el)
/* Watch and paint sidebar */
/* Watch and apply sidebar */
const sidebar$ = watchSidebar(el, { main$, viewport$ })
.pipe(
paintSidebar(el, { header$ })
applySidebar(el, { header$ })
)
/* Watch and paint anchor list (scroll spy) */
/* Watch and apply anchor list (scroll spy) */
const anchors$ = watchAnchorList(els, { header$, viewport$ })
.pipe(
paintAnchorList(els)
applyAnchorList(els)
)
/* Combine into a single hot observable */

View File

@@ -21,6 +21,5 @@
*/
export * from "./_"
export * from "./apply"
export * from "./paint"
export * from "./watch"
export * from "./react"
export * from "./set"

View File

@@ -1,79 +0,0 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import {
MonoTypeOperatorFunction,
animationFrameScheduler,
pipe
} from "rxjs"
import { finalize, observeOn, tap } from "rxjs/operators"
import { AnchorList } from "../_"
import {
resetAnchorActive,
resetAnchorBlur,
setAnchorActive,
setAnchorBlur
} from "../apply"
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Paint anchor list
*
* @param els - Anchor elements
*
* @return Operator function
*/
export function paintAnchorList(
els: HTMLAnchorElement[]
): MonoTypeOperatorFunction<AnchorList> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(({ prev, next }) => {
/* Look forward */
for (const [el] of next) {
resetAnchorActive(el)
resetAnchorBlur(el)
}
/* Look backward */
for (const [index, [el]] of prev.entries()) {
setAnchorActive(el, index === prev.length - 1)
setAnchorBlur(el, true)
}
}),
/* Reset on complete or error */
finalize(() => {
for (const el of els) {
resetAnchorActive(el)
resetAnchorBlur(el)
}
})
)
}

View File

@@ -21,21 +21,36 @@
*/
import { reverse } from "ramda"
import { Observable, combineLatest } from "rxjs"
import {
MonoTypeOperatorFunction,
Observable,
animationFrameScheduler,
combineLatest,
pipe
} from "rxjs"
import {
bufferCount,
distinctUntilChanged,
distinctUntilKeyChanged,
finalize,
map,
observeOn,
scan,
startWith,
switchMap
switchMap,
tap
} from "rxjs/operators"
import { Viewport, getElement } from "observables"
import { Viewport, getElement } from "browser"
import { Header } from "../../../header"
import { AnchorList } from "../_"
import {
resetAnchorActive,
resetAnchorBlur,
setAnchorActive,
setAnchorBlur
} from "../set"
/* ----------------------------------------------------------------------------
* Helper types
@@ -189,3 +204,44 @@ export function watchAnchorList(
})
)
}
/* ------------------------------------------------------------------------- */
/**
* Apply anchor list
*
* @param els - Anchor elements
*
* @return Operator function
*/
export function applyAnchorList(
els: HTMLAnchorElement[]
): MonoTypeOperatorFunction<AnchorList> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(({ prev, next }) => {
/* Look forward */
for (const [el] of next) {
resetAnchorActive(el)
resetAnchorBlur(el)
}
/* Look backward */
prev.forEach(([el], index) => {
setAnchorActive(el, index === prev.length - 1)
setAnchorBlur(el, true)
})
}),
/* Reset on complete or error */
finalize(() => {
for (const el of els) {
resetAnchorActive(el)
resetAnchorBlur(el)
}
})
)
}