diff --git a/src/assets/javascripts/actions/index.ts b/src/assets/javascripts/actions/index.ts index 654a1da88..225224192 100644 --- a/src/assets/javascripts/actions/index.ts +++ b/src/assets/javascripts/actions/index.ts @@ -23,5 +23,6 @@ export * from "./anchor" export * from "./header" export * from "./hidden" +export * from "./scrolling" export * from "./search" export * from "./sidebar" diff --git a/src/assets/javascripts/actions/scrolling/index.ts b/src/assets/javascripts/actions/scrolling/index.ts new file mode 100644 index 000000000..bfd61af2f --- /dev/null +++ b/src/assets/javascripts/actions/scrolling/index.ts @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016-2019 Martin Donath + * + * 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 overflow scrolling + * + * @param el - Scrollable element + */ +export function setOverflowScrolling( + el: HTMLElement +): void { + // el.style.backgroundColor = "yellow" // TODO: debugging + el.style.webkitOverflowScrolling = "touch" +} + +/** + * Reset overflow scrolling + * + * @param el - Scrollable element + */ +export function resetOverflowScrolling( + el: HTMLElement +): void { + // el.style.backgroundColor = "" // TODO: debugging + el.style.webkitOverflowScrolling = "" +} diff --git a/src/assets/javascripts/components/navigation/layer/index.ts b/src/assets/javascripts/components/navigation/layer/index.ts new file mode 100644 index 000000000..ccd968ed0 --- /dev/null +++ b/src/assets/javascripts/components/navigation/layer/index.ts @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2016-2019 Martin Donath + * + * 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 { findLast } from "ramda" +import { + MonoTypeOperatorFunction, + Observable, + animationFrameScheduler, + fromEvent, + merge, + pipe +} from "rxjs" +import { + bufferCount, + delay, + map, + observeOn, + shareReplay, + tap +} from "rxjs/operators" + +import { + resetOverflowScrolling, + setOverflowScrolling +} from "actions" +import { getElement, getElements } from "utilities" + +/* ---------------------------------------------------------------------------- + * Types + * ------------------------------------------------------------------------- */ + +/** + * Active layer + */ +export interface ActiveLayer { + prev?: HTMLElement /* Anchors (previous) */ + next: HTMLElement /* Anchors (next) */ +} + +/* ---------------------------------------------------------------------------- + * Functions + * ------------------------------------------------------------------------- */ + +/** + * Watch active layer + * + * On iOS we want to add `-webkit-overflow-scrolling: touch` for the menus + * contained in the drawer, but as the navigational layers are nested, we can + * only add it to the active layer because otherwise weird stuff will happen. + * This implementation keeps track of the previous and currently active layer. + * + * @param el - Navigation element (top-level) + * + * @return Active layer observable + */ +export function watchActiveLayer( + el: HTMLElement +): Observable { + const table = new Map() + for (const nav of getElements("nav", el)) { + const label = getElement("label", nav) + if (typeof label !== "undefined") { + const input = getElement(`#${label.htmlFor}`)! + table.set(input, nav) + } + } + + /* Determine active layer */ + const active$ = merge( + ...[...table.keys()].map(input => fromEvent(input, "change")) + ) + .pipe( + map(() => getElement(".md-nav__list", table.get( + findLast(({ checked }) => checked, [...table.keys()])! + ))!) + ) + + /* Return previous and next layer */ + return active$ + .pipe( + // TODO: this doesnt emit correctly + bufferCount(2, 1), + map(([prev, next]) => ({ prev, next })), + shareReplay(1) + ) +} + +/* ------------------------------------------------------------------------- */ + +/** + * Paint active layer from source observable + * + * @param els - Anchor elements + * + * @return Operator function + */ +export function paintActiveLayer(): MonoTypeOperatorFunction { + return pipe( + + /* Unset overflow scrolling on previous layer */ + observeOn(animationFrameScheduler), + tap(({ prev }) => { + if (prev) resetOverflowScrolling(prev) + }), + + /* Wait until transition has finished */ + delay(250), + + /* Set overflow scrolling on next layer */ + observeOn(animationFrameScheduler), + tap(({ next }) => { + setOverflowScrolling(next) + }) + ) +} diff --git a/src/assets/javascripts/components/toc/anchor/index.ts b/src/assets/javascripts/components/toc/anchor/index.ts index 134464a91..f5bc726e4 100644 --- a/src/assets/javascripts/components/toc/anchor/index.ts +++ b/src/assets/javascripts/components/toc/anchor/index.ts @@ -45,7 +45,7 @@ import { setAnchorActive, setAnchorBlur } from "actions" -import { Agent, getElement } from "utilities" +import { Agent, getElement } from "utilities" import { HeaderState } from "../../header" diff --git a/src/assets/javascripts/index.ts b/src/assets/javascripts/index.ts index 84ac1a009..365da402d 100644 --- a/src/assets/javascripts/index.ts +++ b/src/assets/javascripts/index.ts @@ -90,6 +90,7 @@ import { import { renderSource } from "templates" import { switchMapIf, not, takeIf } from "extensions" import { renderClipboard } from "templates/clipboard" +import { watchActiveLayer, paintActiveLayer } from "components/navigation/layer" /* ---------------------------------------------------------------------------- * Types @@ -601,6 +602,18 @@ export function initialize(config: unknown) { /* ----------------------------------------------------------------------- */ + const navigationlayer$ = component("navigation") + .pipe( + switchMapIf(not(agent.media.tablet$), el => watchActiveLayer(el) + .pipe( + paintActiveLayer() + ) + ) + ) + .subscribe(console.log) + + /* ----------------------------------------------------------------------- */ + const state = { search: { query$,