Prepare 5.0.0b1 release

This commit is contained in:
squidfunk
2020-02-17 17:20:08 +01:00
parent dd40bc2fcf
commit 14df716e63
18 changed files with 245 additions and 153 deletions

View File

@@ -0,0 +1,103 @@
/*
* 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 { Observable, OperatorFunction, combineLatest, pipe } from "rxjs"
import { map, shareReplay, switchMap } from "rxjs/operators"
import { SearchResult } from "integrations/search"
import { Key, SearchQuery, WorkerHandler } from "observables"
import { SearchMessage } from "workers"
import { useComponent } from "../../_"
import { mountSearchQuery } from "../query"
import { mountSearchReset } from "../reset"
import { mountSearchResult } from "../result"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Search
*/
export interface Search {
query: SearchQuery /* Search query */
result: SearchResult[] /* Search result list */
}
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Mount options
*/
interface MountOptions {
keyboard$: Observable<Key> /* Keyboard observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Mount search from source observable
*
* @param handler - Worker handler
* @param options - Options
*
* @return Search observable
*/
export function mountSearch(
handler: WorkerHandler<SearchMessage>, { keyboard$ }: MountOptions
): OperatorFunction<HTMLElement, Search> {
return pipe(
switchMap(() => {
/* Mount search reset */
const reset$ = useComponent<HTMLInputElement>("search-reset")
.pipe(
mountSearchReset()
)
/* Mount search query */
const query$ = useComponent<HTMLInputElement>("search-query")
.pipe(
mountSearchQuery(handler),
shareReplay(1)
)
/* Mount search result */
const result$ = useComponent("search-result")
.pipe(
mountSearchResult(handler, { query$, keyboard$ })
)
/* Combine into a single hot observable */
return combineLatest([query$, result$, reset$])
.pipe(
map(([query, result]) => ({ query, result })),
shareReplay(1)
)
})
)
}

View File

@@ -20,84 +20,7 @@
* IN THE SOFTWARE.
*/
import { Observable, OperatorFunction, combineLatest, pipe } from "rxjs"
import { map, shareReplay, switchMap } from "rxjs/operators"
import { SearchResult } from "integrations/search"
import { Key, SearchQuery, WorkerHandler } from "observables"
import { SearchMessage } from "workers"
import { useComponent } from "../_"
import { mountSearchQuery } from "./query"
import { mountSearchReset } from "./reset"
import { mountSearchResult } from "./result"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Search
*/
export interface Search {
query: SearchQuery /* Search query */
result: SearchResult[] /* Search result list */
}
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Mount options
*/
interface MountOptions {
keyboard$: Observable<Key> /* Keyboard observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Mount search from source observable
*
* @param handler - Worker handler
* @param options - Options
*
* @return Search observable
*/
export function mountSearch(
handler: WorkerHandler<SearchMessage>, { keyboard$ }: MountOptions
): OperatorFunction<HTMLElement, Search> {
return pipe(
switchMap(() => {
/* Mount search reset */
const reset$ = useComponent<HTMLInputElement>("search-reset")
.pipe(
mountSearchReset()
)
/* Mount search query */
const query$ = useComponent<HTMLInputElement>("search-query")
.pipe(
mountSearchQuery(handler),
shareReplay(1)
)
/* Mount search result */
const result$ = useComponent("search-result")
.pipe(
mountSearchResult(handler, { query$, keyboard$ })
)
/* Combine into a single hot observable */
return combineLatest([query$, result$, reset$])
.pipe(
map(([query, result]) => ({ query, result })),
shareReplay(1)
)
})
)
}
export * from "./_"
export * from "./query"
export * from "./reset"
export * from "./result"

View File

@@ -26,12 +26,11 @@
import "../stylesheets/app.scss"
import "../stylesheets/app-palette.scss"
import { values, identity } from "ramda"
import { values } from "ramda"
import {
EMPTY,
merge,
of,
NEVER,
combineLatest,
animationFrameScheduler
} from "rxjs"
@@ -40,12 +39,7 @@ import {
map,
switchMap,
tap,
skip,
filter,
take,
bufferCount,
startWith,
pluck,
withLatestFrom,
observeOn
} from "rxjs/operators"
@@ -62,15 +56,14 @@ import {
watchKeyboard,
watchToggleMap,
useToggle,
watchViewportAt,
getElementOrThrow
getActiveElement,
mayReceiveKeyboardEvents
} from "./observables"
import { setupSearchWorker } from "./workers"
import { renderSource } from "templates"
import { fetchGitHubStats } from "integrations/source/github"
import { setToggle, setScrollLock, resetScrollLock } from "actions"
import {
Component,
mountHeader,
mountHero,
mountMain,
@@ -83,9 +76,8 @@ import {
mountHeaderTitle
} from "components"
import { mountClipboard } from "./integrations/clipboard"
import { patchTables, patchDetails } from "patches"
import { patchTables, patchDetails, patchScrollfix } from "patches"
import { takeIf, not, isConfig } from "utilities"
import { setSearchLock } from "actions/search/_"
/* ------------------------------------------------------------------------- */
@@ -189,6 +181,7 @@ export function initialize(config: unknown) {
"drawer", /* Toggle for drawer */
"search" /* Toggle for search */
], { document$ })
watchComponentMap([
"container", /* Container */
"header", /* Header */
@@ -250,22 +243,24 @@ export function initialize(config: unknown) {
/* ----------------------------------------------------------------------- */
mountClipboard({ document$ })
.subscribe()
patchTables({ document$ })
.subscribe()
patchDetails({ document$ })
.subscribe()
/* Force 1px scroll offset to trigger overflow scrolling */
if (navigator.userAgent.match(/(iPad|iPhone|iPod)/g))
patchScrollfix({ document$ })
.subscribe()
// TODO: general keyboard handler...
// put into main!?
// search$
// .pipe(
// filter(not),
// switchMapTo(keyboard$),
// filter(key => ["s", "f"].includes(key.type)),
// switchMapTo(toggle$)
// )
// .subscribe(toggle => {
// const el = getActiveElement()
// if (!(el && mayReceiveKeyboardEvents(el)))
// setToggle(toggle, true)
// })
/* ----------------------------------------------------------------------- */
// Close drawer and search on hash change
@@ -296,7 +291,7 @@ export function initialize(config: unknown) {
)
.subscribe()
// scroll lock
// Scroll lock
const toggle$ = useToggle("search")
combineLatest([
toggle$.pipe(switchMap(watchToggle)),
@@ -319,13 +314,23 @@ export function initialize(config: unknown) {
)
.subscribe()
// General keyboard handlers
keyboard$
.pipe(
takeIf(not(toggle$.pipe(switchMap(watchToggle)))),
filter(key => ["s", "f"].includes(key.type)),
withLatestFrom(toggle$)
)
.subscribe(([key, toggle]) => {
const el = getActiveElement()
if (!(el && mayReceiveKeyboardEvents(el))) {
setToggle(toggle, true)
key.claim()
}
})
/* ----------------------------------------------------------------------- */
// watchClipboard
mountClipboard({ document$ })
.subscribe()
// TODO: WIP repo rendering
repository().subscribe(facts => {
if (facts.length) {
@@ -339,31 +344,6 @@ export function initialize(config: unknown) {
}
})
patchTables({ document$ })
.subscribe()
patchDetails({ document$ })
.subscribe()
/* Force 1px scroll offset to trigger overflow scrolling */
if (true || navigator.userAgent.match(/(iPad|iPhone|iPod)/g)) {
const scrollable = document.querySelectorAll("[data-md-scrollfix]")
Array.prototype.forEach.call(scrollable, item => {
item.addEventListener("touchstart", () => {
const top = item.scrollTop
/* We're at the top of the container */
if (top === 0) {
item.scrollTop = 1
/* We're at the bottom of the container */
} else if (top + item.offsetHeight === item.scrollHeight) {
item.scrollTop = top - 1
}
})
})
}
/* ----------------------------------------------------------------------- */
const state = {

View File

@@ -204,7 +204,7 @@ export function watchAnchorList(
/* ------------------------------------------------------------------------- */
/**
* Paint anchor list from source observable
* Paint anchor list
*
* @param els - Anchor elements
*

View File

@@ -36,7 +36,7 @@ import { Main } from "../../main"
* ------------------------------------------------------------------------- */
/**
* Paint header shadow from source observable
* Paint header shadow
*
* @param el - Header element
*

View File

@@ -20,7 +20,7 @@
* IN THE SOFTWARE.
*/
import { animationFrameScheduler, pipe, MonoTypeOperatorFunction } from "rxjs"
import { MonoTypeOperatorFunction, animationFrameScheduler, pipe } from "rxjs"
import { finalize, observeOn, tap } from "rxjs/operators"
import { resetHeaderTitleActive, setHeaderTitleActive } from "actions"
@@ -30,7 +30,7 @@ import { resetHeaderTitleActive, setHeaderTitleActive } from "actions"
* ------------------------------------------------------------------------- */
/**
* Paint header title from source observable
* Paint header title
*
* @param el - Header element
*

View File

@@ -38,7 +38,7 @@ import { Viewport } from "../../agent"
* ------------------------------------------------------------------------- */
/**
* Paint hideable from source observable
* Paint hideable
*
* @param el - Hideable element
* @param offset - Additional offset

View File

@@ -124,7 +124,7 @@ export function watchSidebar(
/* ------------------------------------------------------------------------- */
/**
* Paint sidebar from source observable
* Paint sidebar
*
* @param el - Sidebar element
*

View File

@@ -111,7 +111,7 @@ export function watchNavigationLayer(
/* ------------------------------------------------------------------------- */
/**
* Paint navigation layer from source observable
* Paint navigation layer
*
* @param els - Navigation elements
*

View File

@@ -65,7 +65,7 @@ interface PaintOptions {
* ------------------------------------------------------------------------- */
/**
* Paint search results from source observable
* Paint 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

View File

@@ -21,4 +21,5 @@
*/
export * from "./details"
export * from "./scrollfix"
export * from "./table"

View File

@@ -0,0 +1,85 @@
/*
* 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 { Observable, fromEvent, merge } from "rxjs"
import {
map,
mapTo,
shareReplay,
switchMap,
tap
} from "rxjs/operators"
import { getElements } from "observables"
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Mount options
*/
interface MountOptions {
document$: Observable<Document> /* Document observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Patch all elements with `data-md-scrollfix` attributes
*
* This is a year-old patch which ensures that overflow scrolling works at the
* top and bottom of containers on iOS by ensuring a `1px` scroll offset upon
* the start of a touch event.
*
* @see https://bit.ly/2SCtAOO - Original source
*
* @param options - Options
*
* @return Elements observable
*/
export function patchScrollfix(
{ document$ }: MountOptions
): Observable<HTMLElement> {
return document$
.pipe(
map(() => getElements("[data-md-scrollfix]")),
switchMap(els => merge(
...els.map(el => fromEvent(el, "touchstart").pipe(mapTo(el))))
),
tap(el => {
const top = el.scrollTop
/* We're at the top of the container */
if (top === 0) {
el.scrollTop = 1
/* We're at the bottom of the container */
} else if (top + el.offsetHeight === el.scrollHeight) {
el.scrollTop = top - 1
}
}),
shareReplay(1)
)
}