mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2024-11-28 09:20:52 +01:00
Improved overall structure
This commit is contained in:
parent
9b0410962d
commit
3aa251fb03
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
24
material/assets/javascripts/bundle.4cda9c77.min.js
vendored
Normal file
24
material/assets/javascripts/bundle.4cda9c77.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
material/assets/javascripts/bundle.4cda9c77.min.js.map
Normal file
1
material/assets/javascripts/bundle.4cda9c77.min.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"assets/javascripts/bundle.js": "assets/javascripts/bundle.02fd1bf7.min.js",
|
"assets/javascripts/bundle.js": "assets/javascripts/bundle.4cda9c77.min.js",
|
||||||
"assets/javascripts/bundle.js.map": "assets/javascripts/bundle.02fd1bf7.min.js.map",
|
"assets/javascripts/bundle.js.map": "assets/javascripts/bundle.4cda9c77.min.js.map",
|
||||||
"assets/javascripts/worker/search.js": "assets/javascripts/worker/search.926ffd9e.min.js",
|
"assets/javascripts/worker/search.js": "assets/javascripts/worker/search.926ffd9e.min.js",
|
||||||
"assets/javascripts/worker/search.js.map": "assets/javascripts/worker/search.926ffd9e.min.js.map",
|
"assets/javascripts/worker/search.js.map": "assets/javascripts/worker/search.926ffd9e.min.js.map",
|
||||||
"assets/stylesheets/app-palette.scss": "assets/stylesheets/app-palette.3f90c815.min.css",
|
"assets/stylesheets/app-palette.scss": "assets/stylesheets/app-palette.3f90c815.min.css",
|
||||||
|
@ -190,7 +190,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script src="{{ 'assets/javascripts/bundle.02fd1bf7.min.js' | url }}"></script>
|
<script src="{{ 'assets/javascripts/bundle.4cda9c77.min.js' | url }}"></script>
|
||||||
<script id="__lang" type="application/json">
|
<script id="__lang" type="application/json">
|
||||||
{%- set translations = {} -%}
|
{%- set translations = {} -%}
|
||||||
{%- for key in [
|
{%- for key in [
|
||||||
|
@ -20,9 +20,9 @@
|
|||||||
* IN THE SOFTWARE.
|
* IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { NEVER, Observable, OperatorFunction, pipe } from "rxjs"
|
import { Observable, OperatorFunction, pipe } from "rxjs"
|
||||||
import {
|
import {
|
||||||
catchError,
|
filter,
|
||||||
map,
|
map,
|
||||||
shareReplay,
|
shareReplay,
|
||||||
switchMap
|
switchMap
|
||||||
@ -31,7 +31,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
Header,
|
Header,
|
||||||
Viewport,
|
Viewport,
|
||||||
getElementOrThrow,
|
getElement,
|
||||||
paintHeaderTitle,
|
paintHeaderTitle,
|
||||||
watchViewportAt
|
watchViewportAt
|
||||||
} from "observables"
|
} from "observables"
|
||||||
@ -67,15 +67,15 @@ export function mountHeaderTitle(
|
|||||||
return pipe(
|
return pipe(
|
||||||
switchMap(el => useComponent("main")
|
switchMap(el => useComponent("main")
|
||||||
.pipe(
|
.pipe(
|
||||||
map(main => getElementOrThrow("h1, h2, h3, h4, h5, h6", main)),
|
map(main => getElement("h1, h2, h3, h4, h5, h6", main)!),
|
||||||
switchMap(headline => watchViewportAt(headline, { header$, viewport$ })
|
filter(hx => typeof hx !== "undefined"),
|
||||||
|
switchMap(hx => watchViewportAt(hx, { header$, viewport$ })
|
||||||
.pipe(
|
.pipe(
|
||||||
map(({ offset: { y } }) => y >= headline.offsetHeight),
|
map(({ offset: { y } }) => y >= hx.offsetHeight),
|
||||||
paintHeaderTitle(el)
|
paintHeaderTitle(el)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
shareReplay(1),
|
shareReplay(1)
|
||||||
catchError(() => NEVER)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -20,17 +20,11 @@
|
|||||||
* IN THE SOFTWARE.
|
* IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { OperatorFunction, combineLatest, pipe } from "rxjs"
|
import { Observable, OperatorFunction, combineLatest, pipe } from "rxjs"
|
||||||
import { map, shareReplay, switchMap } from "rxjs/operators"
|
import { map, shareReplay, switchMap } from "rxjs/operators"
|
||||||
|
|
||||||
import { SearchResult } from "integrations/search"
|
import { SearchResult } from "integrations/search"
|
||||||
import { SearchQuery, WorkerHandler } from "observables"
|
import { SearchQuery } from "observables"
|
||||||
import { SearchMessage } from "workers"
|
|
||||||
|
|
||||||
import { useComponent } from "../../_"
|
|
||||||
import { mountSearchQuery } from "../query"
|
|
||||||
import { mountSearchReset } from "../reset"
|
|
||||||
import { mountSearchResult } from "../result"
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
* Types
|
* Types
|
||||||
@ -44,6 +38,19 @@ export interface Search {
|
|||||||
result: SearchResult[] /* Search result list */
|
result: SearchResult[] /* Search result list */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* Helper types
|
||||||
|
* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mount options
|
||||||
|
*/
|
||||||
|
interface MountOptions {
|
||||||
|
query$: Observable<SearchQuery> /* Search query observable */
|
||||||
|
reset$: Observable<void> /* Search reset observable */
|
||||||
|
result$: Observable<SearchResult[]> /* Search result observable */
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
* Functions
|
* Functions
|
||||||
* ------------------------------------------------------------------------- */
|
* ------------------------------------------------------------------------- */
|
||||||
@ -51,42 +58,18 @@ export interface Search {
|
|||||||
/**
|
/**
|
||||||
* Mount search from source observable
|
* Mount search from source observable
|
||||||
*
|
*
|
||||||
* @param handler - Worker handler
|
|
||||||
* @param options - Options
|
* @param options - Options
|
||||||
*
|
*
|
||||||
* @return Search observable
|
* @return Search observable
|
||||||
*/
|
*/
|
||||||
export function mountSearch(
|
export function mountSearch(
|
||||||
handler: WorkerHandler<SearchMessage>
|
{ query$, reset$, result$ }: MountOptions
|
||||||
): OperatorFunction<HTMLElement, Search> {
|
): OperatorFunction<HTMLElement, Search> {
|
||||||
return pipe(
|
return pipe(
|
||||||
switchMap(() => {
|
switchMap(() => combineLatest([query$, result$, reset$])
|
||||||
|
.pipe(
|
||||||
/* Mount search query */
|
map(([query, result]) => ({ query, result })),
|
||||||
const query$ = useComponent<HTMLInputElement>("search-query")
|
shareReplay(1)
|
||||||
.pipe(
|
))
|
||||||
mountSearchQuery(handler),
|
|
||||||
shareReplay(1)
|
|
||||||
)
|
|
||||||
|
|
||||||
/* Mount search reset */
|
|
||||||
const reset$ = useComponent("search-reset")
|
|
||||||
.pipe(
|
|
||||||
mountSearchReset()
|
|
||||||
)
|
|
||||||
|
|
||||||
/* Mount search result */
|
|
||||||
const result$ = useComponent("search-result")
|
|
||||||
.pipe(
|
|
||||||
mountSearchResult(handler, { query$ })
|
|
||||||
)
|
|
||||||
|
|
||||||
/* Combine into a single hot observable */
|
|
||||||
return combineLatest([query$, result$, reset$])
|
|
||||||
.pipe(
|
|
||||||
map(([query, result]) => ({ query, result })),
|
|
||||||
shareReplay(1)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,17 @@ import {
|
|||||||
SearchQueryMessage
|
SearchQueryMessage
|
||||||
} from "workers"
|
} from "workers"
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* Helper types
|
||||||
|
* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mount options
|
||||||
|
*/
|
||||||
|
interface MountOptions {
|
||||||
|
transform?(value: string): string /* Transformation function */
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
* Functions
|
* Functions
|
||||||
* ------------------------------------------------------------------------- */
|
* ------------------------------------------------------------------------- */
|
||||||
@ -49,16 +60,17 @@ import {
|
|||||||
* Mount search query from source observable
|
* Mount search query from source observable
|
||||||
*
|
*
|
||||||
* @param handler - Worker handler
|
* @param handler - Worker handler
|
||||||
|
* @param options - Options
|
||||||
*
|
*
|
||||||
* @return Operator function
|
* @return Operator function
|
||||||
*/
|
*/
|
||||||
export function mountSearchQuery(
|
export function mountSearchQuery(
|
||||||
{ tx$ }: WorkerHandler<SearchMessage>
|
{ tx$ }: WorkerHandler<SearchMessage>, options: MountOptions = {}
|
||||||
): OperatorFunction<HTMLInputElement, SearchQuery> {
|
): OperatorFunction<HTMLInputElement, SearchQuery> {
|
||||||
const toggle$ = useToggle("search")
|
const toggle$ = useToggle("search")
|
||||||
return pipe(
|
return pipe(
|
||||||
switchMap(el => {
|
switchMap(el => {
|
||||||
const query$ = watchSearchQuery(el)
|
const query$ = watchSearchQuery(el, options)
|
||||||
|
|
||||||
/* Subscribe worker to search query */
|
/* Subscribe worker to search query */
|
||||||
query$
|
query$
|
||||||
|
@ -29,7 +29,6 @@ import "../stylesheets/app-palette.scss"
|
|||||||
import { values } from "ramda"
|
import { values } from "ramda"
|
||||||
import {
|
import {
|
||||||
merge,
|
merge,
|
||||||
of,
|
|
||||||
combineLatest,
|
combineLatest,
|
||||||
animationFrameScheduler
|
animationFrameScheduler
|
||||||
} from "rxjs"
|
} from "rxjs"
|
||||||
@ -40,7 +39,9 @@ import {
|
|||||||
filter,
|
filter,
|
||||||
withLatestFrom,
|
withLatestFrom,
|
||||||
observeOn,
|
observeOn,
|
||||||
take
|
take,
|
||||||
|
mapTo,
|
||||||
|
shareReplay
|
||||||
} from "rxjs/operators"
|
} from "rxjs/operators"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -52,9 +53,9 @@ import {
|
|||||||
watchLocation,
|
watchLocation,
|
||||||
watchLocationHash,
|
watchLocationHash,
|
||||||
watchViewport,
|
watchViewport,
|
||||||
watchKeyboard,
|
|
||||||
watchToggleMap,
|
watchToggleMap,
|
||||||
useToggle
|
useToggle,
|
||||||
|
getElement
|
||||||
} from "./observables"
|
} from "./observables"
|
||||||
import { setupSearchWorker } from "./workers"
|
import { setupSearchWorker } from "./workers"
|
||||||
|
|
||||||
@ -69,7 +70,10 @@ import {
|
|||||||
mountTabs,
|
mountTabs,
|
||||||
useComponent,
|
useComponent,
|
||||||
watchComponentMap,
|
watchComponentMap,
|
||||||
mountHeaderTitle
|
mountHeaderTitle,
|
||||||
|
mountSearchQuery,
|
||||||
|
mountSearchReset,
|
||||||
|
mountSearchResult
|
||||||
} from "components"
|
} from "components"
|
||||||
import { setupClipboard } from "./integrations/clipboard"
|
import { setupClipboard } from "./integrations/clipboard"
|
||||||
import { setupKeyboard } from "./integrations/keyboard"
|
import { setupKeyboard } from "./integrations/keyboard"
|
||||||
@ -80,7 +84,7 @@ import {
|
|||||||
patchSource
|
patchSource
|
||||||
} from "patches"
|
} from "patches"
|
||||||
import { isConfig } from "utilities"
|
import { isConfig } from "utilities"
|
||||||
import { renderDialog } from "templates/dialog"
|
import { setupDialog } from "integrations/dialog"
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
@ -153,11 +157,34 @@ export function initialize(config: unknown) {
|
|||||||
|
|
||||||
/* ----------------------------------------------------------------------- */
|
/* ----------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* Mount search query */
|
||||||
|
const query$ = useComponent<HTMLInputElement>("search-query")
|
||||||
|
.pipe(
|
||||||
|
mountSearchQuery(worker),
|
||||||
|
shareReplay(1) // TODO: this must be put onto EVERY component!
|
||||||
|
)
|
||||||
|
|
||||||
|
/* Mount search reset */
|
||||||
|
const reset$ = useComponent("search-reset")
|
||||||
|
.pipe(
|
||||||
|
mountSearchReset()
|
||||||
|
)
|
||||||
|
|
||||||
|
/* Mount search result */
|
||||||
|
const result$ = useComponent("search-result")
|
||||||
|
.pipe(
|
||||||
|
mountSearchResult(worker, { query$ })
|
||||||
|
)
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------- */
|
||||||
|
|
||||||
const search$ = useComponent("search")
|
const search$ = useComponent("search")
|
||||||
.pipe(
|
.pipe(
|
||||||
mountSearch(worker)
|
mountSearch({ query$, reset$, result$ })
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------- */
|
||||||
|
|
||||||
const navigation$ = useComponent("navigation")
|
const navigation$ = useComponent("navigation")
|
||||||
.pipe(
|
.pipe(
|
||||||
mountNavigation({ header$, main$, viewport$, screen$ })
|
mountNavigation({ header$, main$, viewport$, screen$ })
|
||||||
@ -178,7 +205,6 @@ export function initialize(config: unknown) {
|
|||||||
mountHero({ header$, viewport$ })
|
mountHero({ header$, viewport$ })
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: make this part of mountHeader!?
|
|
||||||
const title$ = useComponent("header-title")
|
const title$ = useComponent("header-title")
|
||||||
.pipe(
|
.pipe(
|
||||||
mountHeaderTitle({ header$, viewport$ })
|
mountHeaderTitle({ header$, viewport$ })
|
||||||
@ -196,57 +222,38 @@ export function initialize(config: unknown) {
|
|||||||
if (navigator.userAgent.match(/(iPad|iPhone|iPod)/g))
|
if (navigator.userAgent.match(/(iPad|iPhone|iPod)/g))
|
||||||
patchScrollfix({ document$ })
|
patchScrollfix({ document$ })
|
||||||
|
|
||||||
// snackbar for copy to clipboard
|
/* Setup clipboard and dialog */
|
||||||
const dialog = renderDialog("Copied to Clipboard")
|
const dialog$ = setupDialog()
|
||||||
setupClipboard({ document$ })
|
const clipboard$ = setupClipboard({ document$, dialog$ })
|
||||||
.pipe(
|
|
||||||
switchMap(ev => {
|
|
||||||
ev.clearSelection()
|
|
||||||
return useComponent("container")
|
|
||||||
.pipe(
|
|
||||||
tap(el => el.appendChild(dialog)), // only set text on dialog... render once...
|
|
||||||
observeOn(animationFrameScheduler),
|
|
||||||
tap(() => dialog.dataset.mdState = "open"),
|
|
||||||
delay(2000),
|
|
||||||
tap(() => dialog.dataset.mdState = ""),
|
|
||||||
delay(400),
|
|
||||||
tap(() => dialog.remove())
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.subscribe()
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------- */
|
/* ----------------------------------------------------------------------- */
|
||||||
|
|
||||||
// Close drawer and search on hash change
|
// Close drawer and search on hash change
|
||||||
|
// put into navigation...
|
||||||
hash$.subscribe(x => {
|
hash$.subscribe(x => {
|
||||||
|
|
||||||
useToggle("drawer").subscribe(el => {
|
useToggle("drawer").subscribe(el => {
|
||||||
setToggle(el, false)
|
setToggle(el, false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO:
|
// put into search...
|
||||||
hash$
|
hash$
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap(hash => {
|
switchMap(hash => useToggle("search")
|
||||||
|
.pipe(
|
||||||
return useToggle("search")
|
filter(x => x.checked), // only active
|
||||||
.pipe(
|
tap(toggle => setToggle(toggle, false)),
|
||||||
filter(x => x.checked), // only active
|
delay(125), // ensure that it runs after the body scroll reset...
|
||||||
tap(toggle => setToggle(toggle, false)),
|
mapTo(hash)
|
||||||
delay(125), // ensure that it runs after the body scroll reset...
|
)
|
||||||
tap(() => {
|
)
|
||||||
location.hash = " "
|
|
||||||
location.hash = hash
|
|
||||||
}) // encapsulate this...
|
|
||||||
)
|
|
||||||
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
.subscribe()
|
.subscribe(hash => {
|
||||||
|
getElement(hash)!.scrollIntoView()
|
||||||
|
})
|
||||||
|
|
||||||
// Scroll lock
|
// Scroll lock // document -> document$ => { body } !?
|
||||||
|
// put into search...
|
||||||
const toggle$ = useToggle("search")
|
const toggle$ = useToggle("search")
|
||||||
combineLatest([
|
combineLatest([
|
||||||
toggle$.pipe(switchMap(watchToggle)),
|
toggle$.pipe(switchMap(watchToggle)),
|
||||||
@ -256,13 +263,13 @@ export function initialize(config: unknown) {
|
|||||||
withLatestFrom(viewport$),
|
withLatestFrom(viewport$),
|
||||||
switchMap(([[toggle, tablet], { offset: { y }}]) => {
|
switchMap(([[toggle, tablet], { offset: { y }}]) => {
|
||||||
const active = toggle && !tablet
|
const active = toggle && !tablet
|
||||||
return of(document.body)
|
return document$
|
||||||
.pipe(
|
.pipe(
|
||||||
delay(active ? 400 : 100),
|
delay(active ? 400 : 100), // TOOD: directly combine this with the hash!
|
||||||
observeOn(animationFrameScheduler),
|
observeOn(animationFrameScheduler),
|
||||||
tap(el => active
|
tap(({ body }) => active
|
||||||
? setScrollLock(el, y)
|
? setScrollLock(body, y)
|
||||||
: resetScrollLock(el)
|
: resetScrollLock(body)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -283,18 +290,21 @@ export function initialize(config: unknown) {
|
|||||||
link.style.visibility = "visible"
|
link.style.visibility = "visible"
|
||||||
})
|
})
|
||||||
|
|
||||||
// build a notification component! feed txt into it...
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------- */
|
/* ----------------------------------------------------------------------- */
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
search$,
|
search$,
|
||||||
|
clipboard$,
|
||||||
|
location$,
|
||||||
|
hash$,
|
||||||
|
keyboard$,
|
||||||
|
dialog$,
|
||||||
main$,
|
main$,
|
||||||
navigation$,
|
navigation$,
|
||||||
toc$,
|
toc$,
|
||||||
tabs$,
|
tabs$,
|
||||||
hero$,
|
hero$,
|
||||||
title$
|
title$ // TODO: header title
|
||||||
}
|
}
|
||||||
|
|
||||||
const { ...rest } = state
|
const { ...rest } = state
|
||||||
|
@ -21,11 +21,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as ClipboardJS from "clipboard"
|
import * as ClipboardJS from "clipboard"
|
||||||
import { NEVER, Observable, fromEventPattern } from "rxjs"
|
import { NEVER, Observable, Subject, fromEventPattern } from "rxjs"
|
||||||
import { shareReplay } from "rxjs/operators"
|
import { mapTo, shareReplay, tap } from "rxjs/operators"
|
||||||
|
|
||||||
import { getElements } from "observables"
|
import { getElements } from "observables"
|
||||||
import { renderClipboard } from "templates"
|
import { renderClipboard } from "templates"
|
||||||
|
import { translate } from "utilities"
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
* Helper types
|
* Helper types
|
||||||
@ -36,6 +37,7 @@ import { renderClipboard } from "templates"
|
|||||||
*/
|
*/
|
||||||
interface SetupOptions {
|
interface SetupOptions {
|
||||||
document$: Observable<Document> /* Document observable */
|
document$: Observable<Document> /* Document observable */
|
||||||
|
dialog$: Subject<string> /* Dialog subject */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
@ -53,7 +55,7 @@ interface SetupOptions {
|
|||||||
* @return Clipboard observable
|
* @return Clipboard observable
|
||||||
*/
|
*/
|
||||||
export function setupClipboard(
|
export function setupClipboard(
|
||||||
{ document$ }: SetupOptions
|
{ document$, dialog$ }: SetupOptions
|
||||||
): Observable<ClipboardJS.Event> {
|
): Observable<ClipboardJS.Event> {
|
||||||
if (!ClipboardJS.isSupported())
|
if (!ClipboardJS.isSupported())
|
||||||
return NEVER
|
return NEVER
|
||||||
@ -74,9 +76,15 @@ export function setupClipboard(
|
|||||||
new ClipboardJS(".md-clipboard").on("success", next)
|
new ClipboardJS(".md-clipboard").on("success", next)
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: integrate rendering of dialog
|
/* Display notification upon clipboard copy */
|
||||||
|
clipboard$
|
||||||
|
.pipe(
|
||||||
|
tap(ev => ev.clearSelection()),
|
||||||
|
mapTo(translate("clipboard.copied"))
|
||||||
|
)
|
||||||
|
.subscribe(dialog$)
|
||||||
|
|
||||||
/* */
|
/* Return clipboard as hot observable */
|
||||||
return clipboard$
|
return clipboard$
|
||||||
.pipe(
|
.pipe(
|
||||||
shareReplay(1)
|
shareReplay(1)
|
||||||
|
@ -20,17 +20,26 @@
|
|||||||
* IN THE SOFTWARE.
|
* IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { h } from "utilities"
|
import { Subject, animationFrameScheduler } from "rxjs"
|
||||||
|
import {
|
||||||
|
delay,
|
||||||
|
map,
|
||||||
|
observeOn,
|
||||||
|
switchMap,
|
||||||
|
tap
|
||||||
|
} from "rxjs/operators"
|
||||||
|
|
||||||
|
import { useComponent } from "components"
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
* Data
|
* Types
|
||||||
* ------------------------------------------------------------------------- */
|
* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSS classes
|
* Setup options
|
||||||
*/
|
*/
|
||||||
const css = {
|
interface SetupOptions {
|
||||||
container: "md-dialog md-typeset"
|
duration?: number /* Display duration (default: 2s) */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
@ -38,18 +47,44 @@ const css = {
|
|||||||
* ------------------------------------------------------------------------- */
|
* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a dismissable dialog
|
* Setup dialog
|
||||||
*
|
*
|
||||||
* @param text - Dialog text
|
* @param options - Options
|
||||||
*
|
*
|
||||||
* @return Element
|
* @return Dialog observable
|
||||||
*/
|
*/
|
||||||
export function renderDialog(
|
export function setupDialog(
|
||||||
text: string
|
{ duration }: SetupOptions = {}
|
||||||
): HTMLElement {
|
): Subject<string> {
|
||||||
return (
|
const dialog$ = new Subject<string>()
|
||||||
<div class={css.container}>
|
|
||||||
{text}
|
/* Create dialog */
|
||||||
</div>
|
const dialog = document.createElement("div") // TODO: improve scoping
|
||||||
)
|
dialog.classList.add("md-dialog", "md-typeset")
|
||||||
|
|
||||||
|
/* Display dialog */
|
||||||
|
dialog$
|
||||||
|
.pipe(
|
||||||
|
switchMap(text => useComponent("container")
|
||||||
|
.pipe(
|
||||||
|
map(container => container.appendChild(dialog)),
|
||||||
|
delay(1), // Strangley it doesnt work when we push things to the new animation frame...
|
||||||
|
tap(el => {
|
||||||
|
el.innerHTML = text
|
||||||
|
el.setAttribute("data-md-state", "open")
|
||||||
|
}),
|
||||||
|
delay(duration || 2000),
|
||||||
|
tap(el => el.removeAttribute("data-md-state")),
|
||||||
|
delay(400),
|
||||||
|
tap(el => {
|
||||||
|
el.innerHTML = ""
|
||||||
|
el.remove()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.subscribe()
|
||||||
|
|
||||||
|
/* Return dialog subject */
|
||||||
|
return dialog$
|
||||||
}
|
}
|
@ -198,6 +198,6 @@ export function setupKeyboard(): Observable<Keyboard> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/* Return keyboard observable */
|
/* Return keyboard */
|
||||||
return keyboard$
|
return keyboard$
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,6 @@ export function watchLocation(): Subject<string> {
|
|||||||
)
|
)
|
||||||
.subscribe(location$)
|
.subscribe(location$)
|
||||||
|
|
||||||
/* Return subject */
|
/* Return location subject */
|
||||||
return location$
|
return location$
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Observable, fromEvent } from "rxjs"
|
import { Observable, fromEvent } from "rxjs"
|
||||||
import { filter, map, share } from "rxjs/operators"
|
import { filter, map, share, startWith } from "rxjs/operators"
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
* Functions
|
* Functions
|
||||||
@ -36,22 +36,6 @@ export function getLocationHash(): string {
|
|||||||
return location.hash
|
return location.hash
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set location hash
|
|
||||||
*
|
|
||||||
* This will force a reset of the location hash, inducing an anchor jump if
|
|
||||||
* the hash matches the `id` of an element. It is implemented outside of the
|
|
||||||
* whole RxJS architecture using `setTimeout` to keep it plain and simple.
|
|
||||||
*
|
|
||||||
* @param value - Location hash
|
|
||||||
*/
|
|
||||||
export function setLocationHash(value: string): void {
|
|
||||||
location.hash = ""
|
|
||||||
setTimeout(() => {
|
|
||||||
location.hash = value
|
|
||||||
}, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,6 +47,7 @@ export function watchLocationHash(): Observable<string> {
|
|||||||
return fromEvent<HashChangeEvent>(window, "hashchange")
|
return fromEvent<HashChangeEvent>(window, "hashchange")
|
||||||
.pipe(
|
.pipe(
|
||||||
map(getLocationHash),
|
map(getLocationHash),
|
||||||
|
startWith(getLocationHash()),
|
||||||
filter(hash => hash.length > 0),
|
filter(hash => hash.length > 0),
|
||||||
share()
|
share()
|
||||||
)
|
)
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
* IN THE SOFTWARE.
|
* IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Observable, combineLatest, fromEvent, merge } from "rxjs"
|
import { Observable, fromEvent } from "rxjs"
|
||||||
import { map, shareReplay, startWith } from "rxjs/operators"
|
import { map, startWith } from "rxjs/operators"
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
* Types
|
* Types
|
||||||
|
@ -99,7 +99,7 @@ export function watchNavigationLayer(
|
|||||||
)))
|
)))
|
||||||
)
|
)
|
||||||
|
|
||||||
/* Return previous and next layer */
|
/* Return previous and next layer as hot observable */
|
||||||
return layer$
|
return layer$
|
||||||
.pipe(
|
.pipe(
|
||||||
map(next => ({ next })),
|
map(next => ({ next })),
|
||||||
|
@ -21,20 +21,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { identity } from "ramda"
|
import { identity } from "ramda"
|
||||||
import { NEVER, Observable, fromEvent, merge, of } from "rxjs"
|
import { Observable, fromEvent, merge } from "rxjs"
|
||||||
import {
|
import {
|
||||||
catchError,
|
|
||||||
filter,
|
filter,
|
||||||
map,
|
map,
|
||||||
switchMap,
|
|
||||||
switchMapTo,
|
switchMapTo,
|
||||||
|
tap
|
||||||
} from "rxjs/operators"
|
} from "rxjs/operators"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getElementOrThrow,
|
getElement,
|
||||||
getElements,
|
getElements,
|
||||||
getLocationHash,
|
|
||||||
setLocationHash,
|
|
||||||
watchMedia
|
watchMedia
|
||||||
} from "observables"
|
} from "observables"
|
||||||
|
|
||||||
@ -72,8 +69,8 @@ export function patchDetails(
|
|||||||
|
|
||||||
/* Open all details before printing */
|
/* Open all details before printing */
|
||||||
merge(
|
merge(
|
||||||
watchMedia("print").pipe(filter(identity)), // Webkit
|
watchMedia("print").pipe(filter(identity)), /* Webkit */
|
||||||
fromEvent(window, "beforeprint") // IE, FF
|
fromEvent(window, "beforeprint") /* IE, FF */
|
||||||
)
|
)
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMapTo(els$)
|
switchMapTo(els$)
|
||||||
@ -83,20 +80,16 @@ export function patchDetails(
|
|||||||
el.setAttribute("open", "")
|
el.setAttribute("open", "")
|
||||||
})
|
})
|
||||||
|
|
||||||
/* Open parent details before anchor jump */
|
/* Open parent details and fix anchor jump */
|
||||||
merge(hash$, of(getLocationHash()))
|
hash$
|
||||||
.pipe(
|
.pipe(
|
||||||
filter(hash => !!hash.length),
|
map(hash => getElement(hash)!),
|
||||||
switchMap(hash => of(getElementOrThrow<HTMLElement>(hash))
|
filter(el => typeof el !== "undefined"),
|
||||||
.pipe(
|
tap(el => {
|
||||||
map(el => [el.closest("details")!, hash] as const),
|
const details = el.closest("details")
|
||||||
filter(([el]) => el && !el.open)
|
if (details && !details.open)
|
||||||
)
|
details.setAttribute("open", "")
|
||||||
),
|
|
||||||
catchError(() => NEVER)
|
|
||||||
)
|
|
||||||
.subscribe(([el, hash]) => {
|
|
||||||
el.setAttribute("open", "")
|
|
||||||
setLocationHash(hash)
|
|
||||||
})
|
})
|
||||||
|
)
|
||||||
|
.subscribe(el => el.scrollIntoView())
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,10 @@ export function patchScrollfix(
|
|||||||
.pipe(
|
.pipe(
|
||||||
map(() => getElements("[data-md-scrollfix]")),
|
map(() => getElements("[data-md-scrollfix]")),
|
||||||
switchMap(els => merge(...els.map(el => (
|
switchMap(els => merge(...els.map(el => (
|
||||||
fromEvent(el, "touchstart").pipe(mapTo(el))
|
fromEvent(el, "touchstart")
|
||||||
|
.pipe(
|
||||||
|
mapTo(el)
|
||||||
|
)
|
||||||
))))
|
))))
|
||||||
)
|
)
|
||||||
.subscribe(el => {
|
.subscribe(el => {
|
||||||
|
@ -23,12 +23,7 @@
|
|||||||
import { Repo, User } from "github-types"
|
import { Repo, User } from "github-types"
|
||||||
import { Observable, of } from "rxjs"
|
import { Observable, of } from "rxjs"
|
||||||
import { ajax } from "rxjs/ajax"
|
import { ajax } from "rxjs/ajax"
|
||||||
import {
|
import { filter, pluck, switchMap } from "rxjs/operators"
|
||||||
filter,
|
|
||||||
pluck,
|
|
||||||
shareReplay,
|
|
||||||
switchMap
|
|
||||||
} from "rxjs/operators"
|
|
||||||
|
|
||||||
import { round } from "utilities"
|
import { round } from "utilities"
|
||||||
|
|
||||||
@ -75,7 +70,6 @@ export function fetchSourceFactsFromGitHub(
|
|||||||
`${round(public_repos || 0)} Repositories`
|
`${round(public_repos || 0)} Repositories`
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
shareReplay(1)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -23,12 +23,7 @@
|
|||||||
import { ProjectSchema } from "gitlab"
|
import { ProjectSchema } from "gitlab"
|
||||||
import { Observable } from "rxjs"
|
import { Observable } from "rxjs"
|
||||||
import { ajax } from "rxjs/ajax"
|
import { ajax } from "rxjs/ajax"
|
||||||
import {
|
import { filter, map, pluck } from "rxjs/operators"
|
||||||
filter,
|
|
||||||
map,
|
|
||||||
pluck,
|
|
||||||
shareReplay
|
|
||||||
} from "rxjs/operators"
|
|
||||||
|
|
||||||
import { round } from "utilities"
|
import { round } from "utilities"
|
||||||
|
|
||||||
@ -59,7 +54,6 @@ export function fetchSourceFactsFromGitLab(
|
|||||||
map(({ star_count, forks_count }: ProjectSchema) => ([
|
map(({ star_count, forks_count }: ProjectSchema) => ([
|
||||||
`${round(star_count)} Stars`,
|
`${round(star_count)} Stars`,
|
||||||
`${round(forks_count)} Forks`
|
`${round(forks_count)} Forks`
|
||||||
])),
|
]))
|
||||||
shareReplay(1)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -52,15 +52,15 @@ interface MountOptions {
|
|||||||
export function patchTables(
|
export function patchTables(
|
||||||
{ document$ }: MountOptions
|
{ document$ }: MountOptions
|
||||||
): void {
|
): void {
|
||||||
const placeholder = document.createElement("table")
|
const sentinel = document.createElement("table")
|
||||||
document$
|
document$
|
||||||
.pipe(
|
.pipe(
|
||||||
map(() => getElements<HTMLTableElement>("table:not([class])"))
|
map(() => getElements<HTMLTableElement>("table:not([class])"))
|
||||||
)
|
)
|
||||||
.subscribe(els => {
|
.subscribe(els => {
|
||||||
for (const el of els) {
|
for (const el of els) {
|
||||||
el.replaceWith(placeholder)
|
el.replaceWith(sentinel)
|
||||||
placeholder.replaceWith(renderTable(el))
|
sentinel.replaceWith(renderTable(el))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export * from "./clipboard"
|
export * from "./clipboard"
|
||||||
export * from "./dialog"
|
|
||||||
export * from "./search"
|
export * from "./search"
|
||||||
export * from "./source"
|
export * from "./source"
|
||||||
export * from "./table"
|
export * from "./table"
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
* IN THE SOFTWARE.
|
* IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { SourceFacts } from "integrations/source"
|
import { SourceFacts } from "patches/source"
|
||||||
import { h } from "utilities"
|
import { h } from "utilities"
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
@ -49,7 +49,9 @@ const css = {
|
|||||||
export function renderSource(
|
export function renderSource(
|
||||||
facts: SourceFacts
|
facts: SourceFacts
|
||||||
): HTMLElement {
|
): HTMLElement {
|
||||||
const children = facts.map(fact => <li class={css.fact}>{fact}</li>)
|
const children = facts.map(fact => (
|
||||||
|
<li class={css.fact}>{fact}</li>
|
||||||
|
))
|
||||||
return (
|
return (
|
||||||
<ul class={css.facts}>
|
<ul class={css.facts}>
|
||||||
{children}
|
{children}
|
||||||
|
@ -61,7 +61,7 @@ export function cache<T>(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/* Return value observable */
|
/* Return value */
|
||||||
return value$
|
return value$
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -81,14 +81,13 @@ export function truncate(value: string, n: number): string {
|
|||||||
/**
|
/**
|
||||||
* Round a number for display with source facts
|
* Round a number for display with source facts
|
||||||
*
|
*
|
||||||
* This is a reverse engineered implementation of GitHub's weird rounding
|
* This is a reverse engineered version of GitHub's weird rounding algorithm
|
||||||
* algorithm for stars, forks and all other numbers. While all numbers below
|
* for stars, forks and all other numbers. While all numbers below `1,000` are
|
||||||
* `1,000` are returned as-is, bigger numbers are converted to fixed numbers
|
* returned as-is, bigger numbers are converted to fixed numbers:
|
||||||
* in the following way:
|
|
||||||
*
|
*
|
||||||
* - `1,049` => `1k`
|
* - `1,049` => `1k`
|
||||||
* - `1,050` => `1,1k`
|
* - `1,050` => `1.1k`
|
||||||
* - `1,949` => `1,9k`
|
* - `1,949` => `1.9k`
|
||||||
* - `1,950` => `2k`
|
* - `1,950` => `2k`
|
||||||
*
|
*
|
||||||
* @param value - Original value
|
* @param value - Original value
|
||||||
|
Loading…
Reference in New Issue
Block a user