summaryrefslogtreecommitdiffstatshomepage
path: root/docs/src/templates/assets/javascripts/components
diff options
context:
space:
mode:
Diffstat (limited to 'docs/src/templates/assets/javascripts/components')
-rw-r--r--docs/src/templates/assets/javascripts/components/_/index.ts138
-rw-r--r--docs/src/templates/assets/javascripts/components/announce/index.ts110
-rw-r--r--docs/src/templates/assets/javascripts/components/consent/index.ts116
-rw-r--r--docs/src/templates/assets/javascripts/components/content/_/index.ts125
-rw-r--r--docs/src/templates/assets/javascripts/components/content/annotation/_/index.ts272
-rw-r--r--docs/src/templates/assets/javascripts/components/content/annotation/block/index.ts88
-rw-r--r--docs/src/templates/assets/javascripts/components/content/annotation/index.ts25
-rw-r--r--docs/src/templates/assets/javascripts/components/content/annotation/list/index.ts209
-rw-r--r--docs/src/templates/assets/javascripts/components/content/code/_/index.ts238
-rw-r--r--docs/src/templates/assets/javascripts/components/content/code/index.ts23
-rw-r--r--docs/src/templates/assets/javascripts/components/content/details/index.ts138
-rw-r--r--docs/src/templates/assets/javascripts/components/content/index.ts28
-rw-r--r--docs/src/templates/assets/javascripts/components/content/mermaid/index.css430
-rw-r--r--docs/src/templates/assets/javascripts/components/content/mermaid/index.ts133
-rw-r--r--docs/src/templates/assets/javascripts/components/content/table/index.ts70
-rw-r--r--docs/src/templates/assets/javascripts/components/content/tabs/index.ts265
-rw-r--r--docs/src/templates/assets/javascripts/components/dialog/index.ts128
-rw-r--r--docs/src/templates/assets/javascripts/components/header/_/index.ts200
-rw-r--r--docs/src/templates/assets/javascripts/components/header/index.ts24
-rw-r--r--docs/src/templates/assets/javascripts/components/header/title/index.ts144
-rw-r--r--docs/src/templates/assets/javascripts/components/index.ts37
-rw-r--r--docs/src/templates/assets/javascripts/components/main/index.ts125
-rw-r--r--docs/src/templates/assets/javascripts/components/palette/index.ts180
-rw-r--r--docs/src/templates/assets/javascripts/components/progress/index.ts87
-rw-r--r--docs/src/templates/assets/javascripts/components/search/_/index.ts239
-rw-r--r--docs/src/templates/assets/javascripts/components/search/highlight/.eslintrc5
-rw-r--r--docs/src/templates/assets/javascripts/components/search/highlight/index.ts115
-rw-r--r--docs/src/templates/assets/javascripts/components/search/index.ts28
-rw-r--r--docs/src/templates/assets/javascripts/components/search/query/index.ts206
-rw-r--r--docs/src/templates/assets/javascripts/components/search/result/index.ts197
-rw-r--r--docs/src/templates/assets/javascripts/components/search/share/index.ts135
-rw-r--r--docs/src/templates/assets/javascripts/components/search/suggest/index.ts154
-rw-r--r--docs/src/templates/assets/javascripts/components/sidebar/index.ts227
-rw-r--r--docs/src/templates/assets/javascripts/components/source/_/index.ts142
-rw-r--r--docs/src/templates/assets/javascripts/components/source/facts/_/index.ts88
-rw-r--r--docs/src/templates/assets/javascripts/components/source/facts/github/index.ts103
-rw-r--r--docs/src/templates/assets/javascripts/components/source/facts/gitlab/index.ts61
-rw-r--r--docs/src/templates/assets/javascripts/components/source/facts/index.ts25
-rw-r--r--docs/src/templates/assets/javascripts/components/source/index.ts24
-rw-r--r--docs/src/templates/assets/javascripts/components/tabs/index.ts144
-rw-r--r--docs/src/templates/assets/javascripts/components/toc/index.ts379
-rw-r--r--docs/src/templates/assets/javascripts/components/top/index.ts184
42 files changed, 0 insertions, 5789 deletions
diff --git a/docs/src/templates/assets/javascripts/components/_/index.ts b/docs/src/templates/assets/javascripts/components/_/index.ts
deleted file mode 100644
index 61c471d9..00000000
--- a/docs/src/templates/assets/javascripts/components/_/index.ts
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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 { getElement, getElements } from "~/browser"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Component type
- */
-export type ComponentType =
- | "announce" /* Announcement bar */
- | "container" /* Container */
- | "consent" /* Consent */
- | "content" /* Content */
- | "dialog" /* Dialog */
- | "header" /* Header */
- | "header-title" /* Header title */
- | "header-topic" /* Header topic */
- | "main" /* Main area */
- | "outdated" /* Version warning */
- | "palette" /* Color palette */
- | "progress" /* Progress indicator */
- | "search" /* Search */
- | "search-query" /* Search input */
- | "search-result" /* Search results */
- | "search-share" /* Search sharing */
- | "search-suggest" /* Search suggestions */
- | "sidebar" /* Sidebar */
- | "skip" /* Skip link */
- | "source" /* Repository information */
- | "tabs" /* Navigation tabs */
- | "toc" /* Table of contents */
- | "top" /* Back-to-top button */
-
-/**
- * Component
- *
- * @template T - Component type
- * @template U - Reference type
- */
-export type Component<
- T extends {} = {},
- U extends HTMLElement = HTMLElement
-> =
- T & {
- ref: U /* Component reference */
- }
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Component type map
- */
-interface ComponentTypeMap {
- "announce": HTMLElement /* Announcement bar */
- "container": HTMLElement /* Container */
- "consent": HTMLElement /* Consent */
- "content": HTMLElement /* Content */
- "dialog": HTMLElement /* Dialog */
- "header": HTMLElement /* Header */
- "header-title": HTMLElement /* Header title */
- "header-topic": HTMLElement /* Header topic */
- "main": HTMLElement /* Main area */
- "outdated": HTMLElement /* Version warning */
- "palette": HTMLElement /* Color palette */
- "progress": HTMLElement /* Progress indicator */
- "search": HTMLElement /* Search */
- "search-query": HTMLInputElement /* Search input */
- "search-result": HTMLElement /* Search results */
- "search-share": HTMLAnchorElement /* Search sharing */
- "search-suggest": HTMLElement /* Search suggestions */
- "sidebar": HTMLElement /* Sidebar */
- "skip": HTMLAnchorElement /* Skip link */
- "source": HTMLAnchorElement /* Repository information */
- "tabs": HTMLElement /* Navigation tabs */
- "toc": HTMLElement /* Table of contents */
- "top": HTMLAnchorElement /* Back-to-top button */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Retrieve the element for a given component or throw a reference error
- *
- * @template T - Component type
- *
- * @param type - Component type
- * @param node - Node of reference
- *
- * @returns Element
- */
-export function getComponentElement<T extends ComponentType>(
- type: T, node: ParentNode = document
-): ComponentTypeMap[T] {
- return getElement(`[data-md-component=${type}]`, node)
-}
-
-/**
- * Retrieve all elements for a given component
- *
- * @template T - Component type
- *
- * @param type - Component type
- * @param node - Node of reference
- *
- * @returns Elements
- */
-export function getComponentElements<T extends ComponentType>(
- type: T, node: ParentNode = document
-): ComponentTypeMap[T][] {
- return getElements(`[data-md-component=${type}]`, node)
-}
diff --git a/docs/src/templates/assets/javascripts/components/announce/index.ts b/docs/src/templates/assets/javascripts/components/announce/index.ts
deleted file mode 100644
index dd04b4ff..00000000
--- a/docs/src/templates/assets/javascripts/components/announce/index.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- defer,
- finalize,
- fromEvent,
- map,
- tap
-} from "rxjs"
-
-import { feature } from "~/_"
-import { getElement } from "~/browser"
-
-import { Component } from "../_"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Announcement bar
- */
-export interface Announce {
- hash: number /* Content hash */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch announcement bar
- *
- * @param el - Announcement bar element
- *
- * @returns Announcement bar observable
- */
-export function watchAnnounce(
- el: HTMLElement
-): Observable<Announce> {
- const button = getElement(".md-typeset > :first-child", el)
- return fromEvent(button, "click", { once: true })
- .pipe(
- map(() => getElement(".md-typeset", el)),
- map(content => ({ hash: __md_hash(content.innerHTML) }))
- )
-}
-
-/**
- * Mount announcement bar
- *
- * @param el - Announcement bar element
- *
- * @returns Announcement bar component observable
- */
-export function mountAnnounce(
- el: HTMLElement
-): Observable<Component<Announce>> {
- if (!feature("announce.dismiss") || !el.childElementCount)
- return EMPTY
-
- /* Support instant navigation - see https://t.ly/3FTme */
- if (!el.hidden) {
- const content = getElement(".md-typeset", el)
- if (__md_hash(content.innerHTML) === __md_get("__announce"))
- el.hidden = true
- }
-
- /* Mount component on subscription */
- return defer(() => {
- const push$ = new Subject<Announce>()
- push$.subscribe(({ hash }) => {
- el.hidden = true
-
- /* Persist preference in local storage */
- __md_set<number>("__announce", hash)
- })
-
- /* Create and return component */
- return watchAnnounce(el)
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(state => ({ ref: el, ...state }))
- )
- })
-}
diff --git a/docs/src/templates/assets/javascripts/components/consent/index.ts b/docs/src/templates/assets/javascripts/components/consent/index.ts
deleted file mode 100644
index bc99db58..00000000
--- a/docs/src/templates/assets/javascripts/components/consent/index.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- finalize,
- map,
- tap
-} from "rxjs"
-
-import { Component } from "../_"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Consent
- */
-export interface Consent {
- hidden: boolean /* Consent is hidden */
-}
-
-/**
- * Consent defaults
- */
-export interface ConsentDefaults {
- analytics?: boolean /* Consent for Analytics */
- github?: boolean /* Consent for GitHub */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch options
- */
-interface WatchOptions {
- target$: Observable<HTMLElement> /* Target observable */
-}
-
-/**
- * Mount options
- */
-interface MountOptions {
- target$: Observable<HTMLElement> /* Target observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch consent
- *
- * @param el - Consent element
- * @param options - Options
- *
- * @returns Consent observable
- */
-export function watchConsent(
- el: HTMLElement, { target$ }: WatchOptions
-): Observable<Consent> {
- return target$
- .pipe(
- map(target => ({ hidden: target !== el }))
- )
-}
-
-/* ------------------------------------------------------------------------- */
-
-/**
- * Mount consent
- *
- * @param el - Consent element
- * @param options - Options
- *
- * @returns Consent component observable
- */
-export function mountConsent(
- el: HTMLElement, options: MountOptions
-): Observable<Component<Consent>> {
- const internal$ = new Subject<Consent>()
- internal$.subscribe(({ hidden }) => {
- el.hidden = hidden
- })
-
- /* Create and return component */
- return watchConsent(el, options)
- .pipe(
- tap(state => internal$.next(state)),
- finalize(() => internal$.complete()),
- map(state => ({ ref: el, ...state }))
- )
-}
diff --git a/docs/src/templates/assets/javascripts/components/content/_/index.ts b/docs/src/templates/assets/javascripts/components/content/_/index.ts
deleted file mode 100644
index 899a695c..00000000
--- a/docs/src/templates/assets/javascripts/components/content/_/index.ts
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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, merge } from "rxjs"
-
-import { Viewport, getElements } from "~/browser"
-
-import { Component } from "../../_"
-import {
- Annotation,
- mountAnnotationBlock
-} from "../annotation"
-import {
- CodeBlock,
- mountCodeBlock
-} from "../code"
-import {
- Details,
- mountDetails
-} from "../details"
-import {
- Mermaid,
- mountMermaid
-} from "../mermaid"
-import {
- DataTable,
- mountDataTable
-} from "../table"
-import {
- ContentTabs,
- mountContentTabs
-} from "../tabs"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Content
- */
-export type Content =
- | Annotation
- | CodeBlock
- | ContentTabs
- | DataTable
- | Details
- | Mermaid
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount options
- */
-interface MountOptions {
- viewport$: Observable<Viewport> /* Viewport observable */
- target$: Observable<HTMLElement> /* Location target observable */
- print$: Observable<boolean> /* Media print observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount content
- *
- * This function mounts all components that are found in the content of the
- * actual article, including code blocks, data tables and details.
- *
- * @param el - Content element
- * @param options - Options
- *
- * @returns Content component observable
- */
-export function mountContent(
- el: HTMLElement, { viewport$, target$, print$ }: MountOptions
-): Observable<Component<Content>> {
- return merge(
-
- /* Annotations */
- ...getElements(".annotate:not(.highlight)", el)
- .map(child => mountAnnotationBlock(child, { target$, print$ })),
-
- /* Code blocks */
- ...getElements("pre:not(.mermaid) > code", el)
- .map(child => mountCodeBlock(child, { target$, print$ })),
-
- /* Mermaid diagrams */
- ...getElements("pre.mermaid", el)
- .map(child => mountMermaid(child)),
-
- /* Data tables */
- ...getElements("table:not([class])", el)
- .map(child => mountDataTable(child)),
-
- /* Details */
- ...getElements("details", el)
- .map(child => mountDetails(child, { target$, print$ })),
-
- /* Content tabs */
- ...getElements("[data-tabs]", el)
- .map(child => mountContentTabs(child, { viewport$ }))
- )
-}
diff --git a/docs/src/templates/assets/javascripts/components/content/annotation/_/index.ts b/docs/src/templates/assets/javascripts/components/content/annotation/_/index.ts
deleted file mode 100644
index c5138fa4..00000000
--- a/docs/src/templates/assets/javascripts/components/content/annotation/_/index.ts
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- animationFrameScheduler,
- auditTime,
- combineLatest,
- debounceTime,
- defer,
- delay,
- endWith,
- filter,
- finalize,
- fromEvent,
- ignoreElements,
- map,
- merge,
- switchMap,
- take,
- takeUntil,
- tap,
- throttleTime,
- withLatestFrom
-} from "rxjs"
-
-import {
- ElementOffset,
- getActiveElement,
- getElementSize,
- watchElementContentOffset,
- watchElementFocus,
- watchElementOffset,
- watchElementVisibility
-} from "~/browser"
-
-import { Component } from "../../../_"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Annotation
- */
-export interface Annotation {
- active: boolean /* Annotation is active */
- offset: ElementOffset /* Annotation offset */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount options
- */
-interface MountOptions {
- target$: Observable<HTMLElement> /* Location target observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch annotation
- *
- * @param el - Annotation element
- * @param container - Containing element
- *
- * @returns Annotation observable
- */
-export function watchAnnotation(
- el: HTMLElement, container: HTMLElement
-): Observable<Annotation> {
- const offset$ = defer(() => combineLatest([
- watchElementOffset(el),
- watchElementContentOffset(container)
- ]))
- .pipe(
- map(([{ x, y }, scroll]): ElementOffset => {
- const { width, height } = getElementSize(el)
- return ({
- x: x - scroll.x + width / 2,
- y: y - scroll.y + height / 2
- })
- })
- )
-
- /* Actively watch annotation on focus */
- return watchElementFocus(el)
- .pipe(
- switchMap(active => offset$
- .pipe(
- map(offset => ({ active, offset })),
- take(+!active || Infinity)
- )
- )
- )
-}
-
-/**
- * Mount annotation
- *
- * @param el - Annotation element
- * @param container - Containing element
- * @param options - Options
- *
- * @returns Annotation component observable
- */
-export function mountAnnotation(
- el: HTMLElement, container: HTMLElement, { target$ }: MountOptions
-): Observable<Component<Annotation>> {
- const [tooltip, index] = Array.from(el.children)
-
- /* Mount component on subscription */
- return defer(() => {
- const push$ = new Subject<Annotation>()
- const done$ = push$.pipe(ignoreElements(), endWith(true))
- push$.subscribe({
-
- /* Handle emission */
- next({ offset }) {
- el.style.setProperty("--md-tooltip-x", `${offset.x}px`)
- el.style.setProperty("--md-tooltip-y", `${offset.y}px`)
- },
-
- /* Handle complete */
- complete() {
- el.style.removeProperty("--md-tooltip-x")
- el.style.removeProperty("--md-tooltip-y")
- }
- })
-
- /* Start animation only when annotation is visible */
- watchElementVisibility(el)
- .pipe(
- takeUntil(done$)
- )
- .subscribe(visible => {
- el.toggleAttribute("data-md-visible", visible)
- })
-
- /* Toggle tooltip presence to mitigate empty lines when copying */
- merge(
- push$.pipe(filter(({ active }) => active)),
- push$.pipe(debounceTime(250), filter(({ active }) => !active))
- )
- .subscribe({
-
- /* Handle emission */
- next({ active }) {
- if (active)
- el.prepend(tooltip)
- else
- tooltip.remove()
- },
-
- /* Handle complete */
- complete() {
- el.prepend(tooltip)
- }
- })
-
- /* Toggle tooltip visibility */
- push$
- .pipe(
- auditTime(16, animationFrameScheduler)
- )
- .subscribe(({ active }) => {
- tooltip.classList.toggle("md-tooltip--active", active)
- })
-
- /* Track relative origin of tooltip */
- push$
- .pipe(
- throttleTime(125, animationFrameScheduler),
- filter(() => !!el.offsetParent),
- map(() => el.offsetParent!.getBoundingClientRect()),
- map(({ x }) => x)
- )
- .subscribe({
-
- /* Handle emission */
- next(origin) {
- if (origin)
- el.style.setProperty("--md-tooltip-0", `${-origin}px`)
- else
- el.style.removeProperty("--md-tooltip-0")
- },
-
- /* Handle complete */
- complete() {
- el.style.removeProperty("--md-tooltip-0")
- }
- })
-
- /* Allow to copy link without scrolling to anchor */
- fromEvent<MouseEvent>(index, "click")
- .pipe(
- takeUntil(done$),
- filter(ev => !(ev.metaKey || ev.ctrlKey))
- )
- .subscribe(ev => {
- ev.stopPropagation()
- ev.preventDefault()
- })
-
- /* Allow to open link in new tab or blur on close */
- fromEvent<MouseEvent>(index, "mousedown")
- .pipe(
- takeUntil(done$),
- withLatestFrom(push$)
- )
- .subscribe(([ev, { active }]) => {
-
- /* Open in new tab */
- if (ev.button !== 0 || ev.metaKey || ev.ctrlKey) {
- ev.preventDefault()
-
- /* Close annotation */
- } else if (active) {
- ev.preventDefault()
-
- /* Focus parent annotation, if any */
- const parent = el.parentElement!.closest(".md-annotation")
- if (parent instanceof HTMLElement)
- parent.focus()
- else
- getActiveElement()?.blur()
- }
- })
-
- /* Open and focus annotation on location target */
- target$
- .pipe(
- takeUntil(done$),
- filter(target => target === tooltip),
- delay(125)
- )
- .subscribe(() => el.focus())
-
- /* Create and return component */
- return watchAnnotation(el, container)
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(state => ({ ref: el, ...state }))
- )
- })
-}
diff --git a/docs/src/templates/assets/javascripts/components/content/annotation/block/index.ts b/docs/src/templates/assets/javascripts/components/content/annotation/block/index.ts
deleted file mode 100644
index c73b01fa..00000000
--- a/docs/src/templates/assets/javascripts/components/content/annotation/block/index.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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, defer } from "rxjs"
-
-import { Component } from "../../../_"
-import { Annotation } from "../_"
-import { mountAnnotationList } from "../list"
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount options
- */
-interface MountOptions {
- target$: Observable<HTMLElement> /* Location target observable */
- print$: Observable<boolean> /* Media print observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Find list element directly following a block
- *
- * @param el - Annotation block element
- *
- * @returns List element or nothing
- */
-function findList(el: HTMLElement): HTMLElement | undefined {
- if (el.nextElementSibling) {
- const sibling = el.nextElementSibling as HTMLElement
- if (sibling.tagName === "OL")
- return sibling
-
- /* Skip empty paragraphs - see https://bit.ly/3r4ZJ2O */
- else if (sibling.tagName === "P" && !sibling.children.length)
- return findList(sibling)
- }
-
- /* Everything else */
- return undefined
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount annotation block
- *
- * @param el - Annotation block element
- * @param options - Options
- *
- * @returns Annotation component observable
- */
-export function mountAnnotationBlock(
- el: HTMLElement, options: MountOptions
-): Observable<Component<Annotation>> {
- return defer(() => {
- const list = findList(el)
- return typeof list !== "undefined"
- ? mountAnnotationList(list, el, options)
- : EMPTY
- })
-}
diff --git a/docs/src/templates/assets/javascripts/components/content/annotation/index.ts b/docs/src/templates/assets/javascripts/components/content/annotation/index.ts
deleted file mode 100644
index c593b723..00000000
--- a/docs/src/templates/assets/javascripts/components/content/annotation/index.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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 "./_"
-export * from "./block"
-export * from "./list"
diff --git a/docs/src/templates/assets/javascripts/components/content/annotation/list/index.ts b/docs/src/templates/assets/javascripts/components/content/annotation/list/index.ts
deleted file mode 100644
index 725dd583..00000000
--- a/docs/src/templates/assets/javascripts/components/content/annotation/list/index.ts
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- defer,
- endWith,
- finalize,
- ignoreElements,
- merge,
- share,
- takeUntil
-} from "rxjs"
-
-import {
- getElement,
- getElements,
- getOptionalElement
-} from "~/browser"
-import { renderAnnotation } from "~/templates"
-
-import { Component } from "../../../_"
-import {
- Annotation,
- mountAnnotation
-} from "../_"
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount options
- */
-interface MountOptions {
- target$: Observable<HTMLElement> /* Location target observable */
- print$: Observable<boolean> /* Media print observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Find all annotation hosts in the containing element
- *
- * @param container - Containing element
- *
- * @returns Annotation hosts
- */
-function findHosts(container: HTMLElement): HTMLElement[] {
- return container.tagName === "CODE"
- ? getElements(".c, .c1, .cm", container)
- : [container]
-}
-
-/**
- * Find all annotation markers in the containing element
- *
- * @param container - Containing element
- *
- * @returns Annotation markers
- */
-function findMarkers(container: HTMLElement): Text[] {
- const markers: Text[] = []
- for (const el of findHosts(container)) {
- const nodes: Text[] = []
-
- /* Find all text nodes in current element */
- const it = document.createNodeIterator(el, NodeFilter.SHOW_TEXT)
- for (let node = it.nextNode(); node; node = it.nextNode())
- nodes.push(node as Text)
-
- /* Find all markers in each text node */
- for (let text of nodes) {
- let match: RegExpExecArray | null
-
- /* Split text at marker and add to list */
- while ((match = /(\(\d+\))(!)?/.exec(text.textContent!))) {
- const [, id, force] = match
- if (typeof force === "undefined") {
- const marker = text.splitText(match.index)
- text = marker.splitText(id.length)
- markers.push(marker)
-
- /* Replace entire text with marker */
- } else {
- text.textContent = id
- markers.push(text)
- break
- }
- }
- }
- }
- return markers
-}
-
-/**
- * Swap the child nodes of two elements
- *
- * @param source - Source element
- * @param target - Target element
- */
-function swap(source: HTMLElement, target: HTMLElement): void {
- target.append(...Array.from(source.childNodes))
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount annotation list
- *
- * This function analyzes the containing code block and checks for markers
- * referring to elements in the given annotation list. If no markers are found,
- * the list is left untouched. Otherwise, list elements are rendered as
- * annotations inside the code block.
- *
- * @param el - Annotation list element
- * @param container - Containing element
- * @param options - Options
- *
- * @returns Annotation component observable
- */
-export function mountAnnotationList(
- el: HTMLElement, container: HTMLElement, { target$, print$ }: MountOptions
-): Observable<Component<Annotation>> {
-
- /* Compute prefix for tooltip anchors */
- const parent = container.closest("[id]")
- const prefix = parent?.id
-
- /* Find and replace all markers with empty annotations */
- const annotations = new Map<string, HTMLElement>()
- for (const marker of findMarkers(container)) {
- const [, id] = marker.textContent!.match(/\((\d+)\)/)!
- if (getOptionalElement(`:scope > li:nth-child(${id})`, el)) {
- annotations.set(id, renderAnnotation(id, prefix))
- marker.replaceWith(annotations.get(id)!)
- }
- }
-
- /* Keep list if there are no annotations to render */
- if (annotations.size === 0)
- return EMPTY
-
- /* Mount component on subscription */
- return defer(() => {
- const push$ = new Subject()
- const done$ = push$.pipe(ignoreElements(), endWith(true))
-
- /* Retrieve container pairs for swapping */
- const pairs: [HTMLElement, HTMLElement][] = []
- for (const [id, annotation] of annotations)
- pairs.push([
- getElement(".md-typeset", annotation),
- getElement(`:scope > li:nth-child(${id})`, el)
- ])
-
- /* Handle print mode - see https://bit.ly/3rgPdpt */
- print$.pipe(takeUntil(done$))
- .subscribe(active => {
- el.hidden = !active
-
- /* Add class to discern list element */
- el.classList.toggle("md-annotation-list", active)
-
- /* Show annotations in code block or list (print) */
- for (const [inner, child] of pairs)
- if (!active)
- swap(child, inner)
- else
- swap(inner, child)
- })
-
- /* Create and return component */
- return merge(...[...annotations]
- .map(([, annotation]) => (
- mountAnnotation(annotation, container, { target$ })
- ))
- )
- .pipe(
- finalize(() => push$.complete()),
- share()
- )
- })
-}
diff --git a/docs/src/templates/assets/javascripts/components/content/code/_/index.ts b/docs/src/templates/assets/javascripts/components/content/code/_/index.ts
deleted file mode 100644
index ccc09339..00000000
--- a/docs/src/templates/assets/javascripts/components/content/code/_/index.ts
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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 ClipboardJS from "clipboard"
-import {
- EMPTY,
- Observable,
- Subject,
- defer,
- distinctUntilChanged,
- distinctUntilKeyChanged,
- filter,
- finalize,
- map,
- mergeWith,
- switchMap,
- take,
- tap
-} from "rxjs"
-
-import { feature } from "~/_"
-import {
- getElementContentSize,
- watchElementSize,
- watchElementVisibility
-} from "~/browser"
-import { renderClipboardButton } from "~/templates"
-
-import { Component } from "../../../_"
-import {
- Annotation,
- mountAnnotationList
-} from "../../annotation"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Code block
- */
-export interface CodeBlock {
- scrollable: boolean /* Code block overflows */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount options
- */
-interface MountOptions {
- target$: Observable<HTMLElement> /* Location target observable */
- print$: Observable<boolean> /* Media print observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Data
- * ------------------------------------------------------------------------- */
-
-/**
- * Global sequence number for code blocks
- */
-let sequence = 0
-
-/* ----------------------------------------------------------------------------
- * Helper functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Find candidate list element directly following a code block
- *
- * @param el - Code block element
- *
- * @returns List element or nothing
- */
-function findCandidateList(el: HTMLElement): HTMLElement | undefined {
- if (el.nextElementSibling) {
- const sibling = el.nextElementSibling as HTMLElement
- if (sibling.tagName === "OL")
- return sibling
-
- /* Skip empty paragraphs - see https://bit.ly/3r4ZJ2O */
- else if (sibling.tagName === "P" && !sibling.children.length)
- return findCandidateList(sibling)
- }
-
- /* Everything else */
- return undefined
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch code block
- *
- * This function monitors size changes of the viewport, as well as switches of
- * content tabs with embedded code blocks, as both may trigger overflow.
- *
- * @param el - Code block element
- *
- * @returns Code block observable
- */
-export function watchCodeBlock(
- el: HTMLElement
-): Observable<CodeBlock> {
- return watchElementSize(el)
- .pipe(
- map(({ width }) => {
- const content = getElementContentSize(el)
- return {
- scrollable: content.width > width
- }
- }),
- distinctUntilKeyChanged("scrollable")
- )
-}
-
-/**
- * Mount code block
- *
- * This function ensures that an overflowing code block is focusable through
- * keyboard, so it can be scrolled without a mouse to improve on accessibility.
- * Furthermore, if code annotations are enabled, they are mounted if and only
- * if the code block is currently visible, e.g., not in a hidden content tab.
- *
- * Note that code blocks may be mounted eagerly or lazily. If they're mounted
- * lazily (on first visibility), code annotation anchor links will not work,
- * as they are evaluated on initial page load, and code annotations in general
- * might feel a little bumpier.
- *
- * @param el - Code block element
- * @param options - Options
- *
- * @returns Code block and annotation component observable
- */
-export function mountCodeBlock(
- el: HTMLElement, options: MountOptions
-): Observable<Component<CodeBlock | Annotation>> {
- const { matches: hover } = matchMedia("(hover)")
-
- /* Defer mounting of code block - see https://bit.ly/3vHVoVD */
- const factory$ = defer(() => {
- const push$ = new Subject<CodeBlock>()
- push$.subscribe(({ scrollable }) => {
- if (scrollable && hover)
- el.setAttribute("tabindex", "0")
- else
- el.removeAttribute("tabindex")
- })
-
- /* Render button for Clipboard.js integration */
- if (ClipboardJS.isSupported()) {
- if (el.closest(".copy") || (
- feature("content.code.copy") && !el.closest(".no-copy")
- )) {
- const parent = el.closest("pre")!
- parent.id = `__code_${sequence++}`
- parent.insertBefore(
- renderClipboardButton(parent.id),
- el
- )
- }
- }
-
- /* Handle code annotations */
- const container = el.closest(".highlight")
- if (container instanceof HTMLElement) {
- const list = findCandidateList(container)
-
- /* Mount code annotations, if enabled */
- if (typeof list !== "undefined" && (
- container.classList.contains("annotate") ||
- feature("content.code.annotate")
- )) {
- const annotations$ = mountAnnotationList(list, el, options)
-
- /* Create and return component */
- return watchCodeBlock(el)
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(state => ({ ref: el, ...state })),
- mergeWith(
- watchElementSize(container)
- .pipe(
- map(({ width, height }) => width && height),
- distinctUntilChanged(),
- switchMap(active => active ? annotations$ : EMPTY)
- )
- )
- )
- }
- }
-
- /* Create and return component */
- return watchCodeBlock(el)
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(state => ({ ref: el, ...state }))
- )
- })
-
- /* Mount code block lazily */
- if (feature("content.lazy"))
- return watchElementVisibility(el)
- .pipe(
- filter(visible => visible),
- take(1),
- switchMap(() => factory$)
- )
-
- /* Mount code block */
- return factory$
-}
diff --git a/docs/src/templates/assets/javascripts/components/content/code/index.ts b/docs/src/templates/assets/javascripts/components/content/code/index.ts
deleted file mode 100644
index 3f86e2b4..00000000
--- a/docs/src/templates/assets/javascripts/components/content/code/index.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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 "./_"
diff --git a/docs/src/templates/assets/javascripts/components/content/details/index.ts b/docs/src/templates/assets/javascripts/components/content/details/index.ts
deleted file mode 100644
index 17bfae45..00000000
--- a/docs/src/templates/assets/javascripts/components/content/details/index.ts
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- defer,
- filter,
- finalize,
- map,
- merge,
- tap
-} from "rxjs"
-
-import { Component } from "../../_"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Details
- */
-export interface Details {
- action: "open" | "close" /* Details state */
- reveal?: boolean /* Details is revealed */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch options
- */
-interface WatchOptions {
- target$: Observable<HTMLElement> /* Location target observable */
- print$: Observable<boolean> /* Media print observable */
-}
-
-/**
- * Mount options
- */
-interface MountOptions {
- target$: Observable<HTMLElement> /* Location target observable */
- print$: Observable<boolean> /* Media print observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch details
- *
- * @param el - Details element
- * @param options - Options
- *
- * @returns Details observable
- */
-export function watchDetails(
- el: HTMLDetailsElement, { target$, print$ }: WatchOptions
-): Observable<Details> {
- let open = true
- return merge(
-
- /* Open and focus details on location target */
- target$
- .pipe(
- map(target => target.closest("details:not([open])")!),
- filter(details => el === details),
- map(() => ({
- action: "open", reveal: true
- }) as Details)
- ),
-
- /* Open details on print and close afterwards */
- print$
- .pipe(
- filter(active => active || !open),
- tap(() => open = el.open),
- map(active => ({
- action: active ? "open" : "close"
- }) as Details)
- )
- )
-}
-
-/**
- * Mount details
- *
- * This function ensures that `details` tags are opened on anchor jumps and
- * prior to printing, so the whole content of the page is visible.
- *
- * @param el - Details element
- * @param options - Options
- *
- * @returns Details component observable
- */
-export function mountDetails(
- el: HTMLDetailsElement, options: MountOptions
-): Observable<Component<Details>> {
- return defer(() => {
- const push$ = new Subject<Details>()
- push$.subscribe(({ action, reveal }) => {
- el.toggleAttribute("open", action === "open")
- if (reveal)
- el.scrollIntoView()
- })
-
- /* Create and return component */
- return watchDetails(el, options)
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(state => ({ ref: el, ...state }))
- )
- })
-}
diff --git a/docs/src/templates/assets/javascripts/components/content/index.ts b/docs/src/templates/assets/javascripts/components/content/index.ts
deleted file mode 100644
index a29d8b41..00000000
--- a/docs/src/templates/assets/javascripts/components/content/index.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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 "./_"
-export * from "./annotation"
-export * from "./code"
-export * from "./details"
-export * from "./table"
-export * from "./tabs"
diff --git a/docs/src/templates/assets/javascripts/components/content/mermaid/index.css b/docs/src/templates/assets/javascripts/components/content/mermaid/index.css
deleted file mode 100644
index 3092b8ec..00000000
--- a/docs/src/templates/assets/javascripts/components/content/mermaid/index.css
+++ /dev/null
@@ -1,430 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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.
- */
-
-/* ----------------------------------------------------------------------------
- * Rules: general
- * ------------------------------------------------------------------------- */
-
-/* General node */
-.node circle,
-.node ellipse,
-.node path,
-.node polygon,
-.node rect {
- fill: var(--md-mermaid-node-bg-color);
- stroke: var(--md-mermaid-node-fg-color);
-}
-
-/* General marker */
-marker {
- fill: var(--md-mermaid-edge-color) !important;
-}
-
-/* General edge label */
-.edgeLabel .label rect {
- fill: transparent;
-}
-
-/* ----------------------------------------------------------------------------
- * Rules: flowcharts
- * ------------------------------------------------------------------------- */
-
-/* Flowchart node label */
-.label {
- color: var(--md-mermaid-label-fg-color);
- font-family: var(--md-mermaid-font-family);
-}
-
-/* Flowchart node label container */
-.label foreignObject {
- overflow: visible;
- line-height: initial;
-}
-
-/* Flowchart edge label in node label */
-.label div .edgeLabel {
- color: var(--md-mermaid-label-fg-color);
- background-color: var(--md-mermaid-label-bg-color);
-}
-
-/* Flowchart edge label */
-.edgeLabel,
-.edgeLabel rect {
- color: var(--md-mermaid-edge-color);
- background-color: var(--md-mermaid-label-bg-color);
- fill: var(--md-mermaid-label-bg-color);
-}
-
-/* Flowchart edge path */
-.edgePath .path,
-.flowchart-link {
- stroke: var(--md-mermaid-edge-color);
- stroke-width: .05rem;
-}
-
-/* Flowchart arrow head */
-.edgePath .arrowheadPath {
- fill: var(--md-mermaid-edge-color);
- stroke: none;
-}
-
-/* Flowchart subgraph */
-.cluster rect {
- fill: var(--md-default-fg-color--lightest);
- stroke: var(--md-default-fg-color--lighter);
-}
-
-/* Flowchart subgraph labels */
-.cluster span {
- color: var(--md-mermaid-label-fg-color);
- font-family: var(--md-mermaid-font-family);
-}
-
-/* Flowchart markers */
-g #flowchart-circleStart,
-g #flowchart-circleEnd,
-g #flowchart-crossStart,
-g #flowchart-crossEnd,
-g #flowchart-pointStart,
-g #flowchart-pointEnd {
- stroke: none;
-}
-
-/* ----------------------------------------------------------------------------
- * Rules: class diagrams
- * ------------------------------------------------------------------------- */
-
-/* Class group node */
-g.classGroup line,
-g.classGroup rect {
- fill: var(--md-mermaid-node-bg-color);
- stroke: var(--md-mermaid-node-fg-color);
-}
-
-/* Class group node text */
-g.classGroup text {
- font-family: var(--md-mermaid-font-family);
- fill: var(--md-mermaid-label-fg-color);
-}
-
-/* Class label box */
-.classLabel .box {
- background-color: var(--md-mermaid-label-bg-color);
- opacity: 1;
- fill: var(--md-mermaid-label-bg-color);
-}
-
-/* Class label text */
-.classLabel .label {
- font-family: var(--md-mermaid-font-family);
- fill: var(--md-mermaid-label-fg-color);
-}
-
-/* Class group divider */
-.node .divider {
- stroke: var(--md-mermaid-node-fg-color);
-}
-
-/* Class relation */
-.relation {
- stroke: var(--md-mermaid-edge-color);
-}
-
-/* Class relation cardinality */
-.cardinality {
- font-family: var(--md-mermaid-font-family);
- fill: var(--md-mermaid-label-fg-color);
-}
-
-/* Class relation cardinality text */
-.cardinality text {
- fill: inherit !important;
-}
-
-/* Class extension, composition and dependency marker */
-defs #classDiagram-extensionStart,
-defs #classDiagram-extensionEnd,
-defs #classDiagram-compositionStart,
-defs #classDiagram-compositionEnd,
-defs #classDiagram-dependencyStart,
-defs #classDiagram-dependencyEnd {
- fill: var(--md-mermaid-edge-color) !important;
- stroke: var(--md-mermaid-edge-color) !important;
-}
-
-/* Class aggregation marker */
-defs #classDiagram-aggregationStart,
-defs #classDiagram-aggregationEnd {
- fill: var(--md-mermaid-label-bg-color) !important;
- stroke: var(--md-mermaid-edge-color) !important;
-}
-
-/* ----------------------------------------------------------------------------
- * Rules: state diagrams
- * ------------------------------------------------------------------------- */
-
-/* State group node */
-g.stateGroup rect {
- fill: var(--md-mermaid-node-bg-color);
- stroke: var(--md-mermaid-node-fg-color);
-}
-
-/* State group title */
-g.stateGroup .state-title {
- font-family: var(--md-mermaid-font-family);
- fill: var(--md-mermaid-label-fg-color) !important;
-}
-
-/* State group background */
-g.stateGroup .composit {
- fill: var(--md-mermaid-label-bg-color);
-}
-
-/* State node label */
-.nodeLabel {
- color: var(--md-mermaid-label-fg-color);
- font-family: var(--md-mermaid-font-family);
-}
-
-/* State start and end marker */
-.start-state,
-.node circle.state-start,
-.node circle.state-end {
- fill: var(--md-mermaid-edge-color);
- stroke: none;
-}
-
-/* State end marker */
-.end-state-outer,
-.end-state-inner {
- fill: var(--md-mermaid-edge-color);
-}
-
-/* State end marker */
-.end-state-inner,
-.node circle.state-end {
- stroke: var(--md-mermaid-label-bg-color);
-}
-
-/* State transition */
-.transition {
- stroke: var(--md-mermaid-edge-color);
-}
-
-/* State fork and join */
-[id^=state-fork] rect,
-[id^=state-join] rect {
- fill: var(--md-mermaid-edge-color) !important;
- stroke: none !important;
-}
-
-/* State cluster (yes, 2x... Mermaid WTF) */
-.statediagram-cluster.statediagram-cluster .inner {
- fill: var(--md-default-bg-color);
-}
-
-/* State cluster node */
-.statediagram-cluster rect {
- fill: var(--md-mermaid-node-bg-color);
- stroke: var(--md-mermaid-node-fg-color);
-}
-
-/* State cluster divider */
-.statediagram-state rect.divider {
- fill: var(--md-default-fg-color--lightest);
- stroke: var(--md-default-fg-color--lighter);
-}
-
-/* State diagram markers */
-defs #statediagram-barbEnd {
- stroke: var(--md-mermaid-edge-color);
-}
-
-/* ----------------------------------------------------------------------------
- * Rules: entity-relationship diagrams
- * ------------------------------------------------------------------------- */
-
-/* Attribute box */
-.attributeBoxEven,
-.attributeBoxOdd {
- fill: var(--md-mermaid-node-bg-color);
- stroke: var(--md-mermaid-node-fg-color);
-}
-
-/* Entity node */
-.entityBox {
- fill: var(--md-mermaid-label-bg-color);
- stroke: var(--md-mermaid-node-fg-color);
-}
-
-/* Entity node label */
-.entityLabel {
- font-family: var(--md-mermaid-font-family);
- fill: var(--md-mermaid-label-fg-color);
-}
-
-/* Entity relationship label container */
-.relationshipLabelBox {
- background-color: var(--md-mermaid-label-bg-color);
- opacity: 1;
- fill: var(--md-mermaid-label-bg-color);
- fill-opacity: 1;
-}
-
-/* Entity relationship label */
-.relationshipLabel {
- fill: var(--md-mermaid-label-fg-color);
-}
-
-/* Entity relationship line { */
-.relationshipLine {
- stroke: var(--md-mermaid-edge-color);
-}
-
-/* Entity relationship line markers */
-defs #ZERO_OR_ONE_START *,
-defs #ZERO_OR_ONE_END *,
-defs #ZERO_OR_MORE_START *,
-defs #ZERO_OR_MORE_END *,
-defs #ONLY_ONE_START *,
-defs #ONLY_ONE_END *,
-defs #ONE_OR_MORE_START *,
-defs #ONE_OR_MORE_END * {
- stroke: var(--md-mermaid-edge-color) !important;
-}
-
-/* Entity relationship line markers */
-defs #ZERO_OR_MORE_START circle,
-defs #ZERO_OR_MORE_END circle {
- fill: var(--md-mermaid-label-bg-color);
-}
-
-/* ----------------------------------------------------------------------------
- * Rules: sequence diagrams
- * ------------------------------------------------------------------------- */
-
-/* Sequence actor */
-.actor {
- fill: var(--md-mermaid-sequence-actor-bg-color);
- stroke: var(--md-mermaid-sequence-actor-border-color);
-}
-
-/* Sequence actor text */
-text.actor > tspan {
- font-family: var(--md-mermaid-font-family);
- fill: var(--md-mermaid-sequence-actor-fg-color);
-}
-
-/* Sequence actor line */
-line {
- stroke: var(--md-mermaid-sequence-actor-line-color);
-}
-
-/* Sequence actor */
-.actor-man circle,
-.actor-man line {
- fill: var(--md-mermaid-sequence-actorman-bg-color);
- stroke: var(--md-mermaid-sequence-actorman-line-color);
-}
-
-/* Sequence message line */
-.messageLine0,
-.messageLine1 {
- stroke: var(--md-mermaid-sequence-message-line-color);
-}
-
-/* Sequence note */
-.note {
- fill: var(--md-mermaid-sequence-note-bg-color);
- stroke: var(--md-mermaid-sequence-note-border-color);
-}
-
-/* Sequence message, loop and note text */
-.messageText,
-.loopText,
-.loopText > tspan,
-.noteText > tspan {
- font-family: var(--md-mermaid-font-family) !important;
- stroke: none;
-}
-
-/* Sequence message text */
-.messageText {
- fill: var(--md-mermaid-sequence-message-fg-color);
-}
-
-/* Sequence loop text */
-.loopText,
-.loopText > tspan {
- fill: var(--md-mermaid-sequence-loop-fg-color);
-}
-
-/* Sequence note text */
-.noteText > tspan {
- fill: var(--md-mermaid-sequence-note-fg-color);
-}
-
-/* Sequence arrow head */
-#arrowhead path {
- fill: var(--md-mermaid-sequence-message-line-color);
- stroke: none;
-}
-
-/* Sequence loop line */
-.loopLine {
- fill: var(--md-mermaid-sequence-loop-bg-color);
- stroke: var(--md-mermaid-sequence-loop-border-color);
-}
-
-/* Sequence label box */
-.labelBox {
- fill: var(--md-mermaid-sequence-label-bg-color);
- stroke: none;
-}
-
-/* Sequence label text */
-.labelText,
-.labelText > span {
- font-family: var(--md-mermaid-font-family);
- fill: var(--md-mermaid-sequence-label-fg-color);
-}
-
-/* Sequence number */
-.sequenceNumber {
- fill: var(--md-mermaid-sequence-number-fg-color);
-}
-
-/* Sequence rectangle */
-rect.rect {
- fill: var(--md-mermaid-sequence-box-bg-color);
- stroke: none;
-}
-
-/* Sequence rectangle text */
-rect.rect + text.text {
- fill: var(--md-mermaid-sequence-box-fg-color);
-}
-
-/* Sequence diagram markers */
-defs #sequencenumber {
- fill: var(--md-mermaid-sequence-number-bg-color) !important;
-}
diff --git a/docs/src/templates/assets/javascripts/components/content/mermaid/index.ts b/docs/src/templates/assets/javascripts/components/content/mermaid/index.ts
deleted file mode 100644
index 3f6480fd..00000000
--- a/docs/src/templates/assets/javascripts/components/content/mermaid/index.ts
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- map,
- of,
- shareReplay,
- tap
-} from "rxjs"
-
-import { watchScript } from "~/browser"
-import { h } from "~/utilities"
-
-import { Component } from "../../_"
-
-import themeCSS from "./index.css"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Mermaid diagram
- */
-export interface Mermaid {}
-
-/* ----------------------------------------------------------------------------
- * Data
- * ------------------------------------------------------------------------- */
-
-/**
- * Mermaid instance observable
- */
-let mermaid$: Observable<void>
-
-/**
- * Global sequence number for diagrams
- */
-let sequence = 0
-
-/* ----------------------------------------------------------------------------
- * Helper functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Fetch Mermaid script
- *
- * @returns Mermaid scripts observable
- */
-function fetchScripts(): Observable<void> {
- return typeof mermaid === "undefined" || mermaid instanceof Element
- ? watchScript("https://unpkg.com/mermaid@9.4.3/dist/mermaid.min.js")
- : of(undefined)
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount Mermaid diagram
- *
- * @param el - Code block element
- *
- * @returns Mermaid diagram component observable
- */
-export function mountMermaid(
- el: HTMLElement
-): Observable<Component<Mermaid>> {
- el.classList.remove("mermaid") // Hack: mitigate https://bit.ly/3CiN6Du
- mermaid$ ||= fetchScripts()
- .pipe(
- tap(() => mermaid.initialize({
- startOnLoad: false,
- themeCSS,
- sequence: {
- actorFontSize: "16px", // Hack: mitigate https://bit.ly/3y0NEi3
- messageFontSize: "16px",
- noteFontSize: "16px"
- }
- })),
- map(() => undefined),
- shareReplay(1)
- )
-
- /* Render diagram */
- mermaid$.subscribe(() => {
- el.classList.add("mermaid") // Hack: mitigate https://bit.ly/3CiN6Du
- const id = `__mermaid_${sequence++}`
-
- /* Create host element to replace code block */
- const host = h("div", { class: "mermaid" })
- const text = el.textContent
-
- /* Render and inject diagram */
- mermaid.mermaidAPI.render(id, text, (svg: string, fn: Function) => {
-
- /* Create a shadow root and inject diagram */
- const shadow = host.attachShadow({ mode: "closed" })
- shadow.innerHTML = svg
-
- /* Replace code block with diagram and bind functions */
- el.replaceWith(host)
- fn?.(shadow)
- })
- })
-
- /* Create and return component */
- return mermaid$
- .pipe(
- map(() => ({ ref: el }))
- )
-}
diff --git a/docs/src/templates/assets/javascripts/components/content/table/index.ts b/docs/src/templates/assets/javascripts/components/content/table/index.ts
deleted file mode 100644
index c318e7a6..00000000
--- a/docs/src/templates/assets/javascripts/components/content/table/index.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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, of } from "rxjs"
-
-import { renderTable } from "~/templates"
-import { h } from "~/utilities"
-
-import { Component } from "../../_"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Data table
- */
-export interface DataTable {}
-
-/* ----------------------------------------------------------------------------
- * Data
- * ------------------------------------------------------------------------- */
-
-/**
- * Sentinel for replacement
- */
-const sentinel = h("table")
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount data table
- *
- * This function wraps a data table in another scrollable container, so it can
- * be smoothly scrolled on smaller screen sizes and won't break the layout.
- *
- * @param el - Data table element
- *
- * @returns Data table component observable
- */
-export function mountDataTable(
- el: HTMLElement
-): Observable<Component<DataTable>> {
- el.replaceWith(sentinel)
- sentinel.replaceWith(renderTable(el))
-
- /* Create and return component */
- return of({ ref: el })
-}
diff --git a/docs/src/templates/assets/javascripts/components/content/tabs/index.ts b/docs/src/templates/assets/javascripts/components/content/tabs/index.ts
deleted file mode 100644
index f57447e2..00000000
--- a/docs/src/templates/assets/javascripts/components/content/tabs/index.ts
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- animationFrameScheduler,
- asyncScheduler,
- auditTime,
- combineLatest,
- defer,
- endWith,
- finalize,
- fromEvent,
- ignoreElements,
- map,
- merge,
- skip,
- startWith,
- subscribeOn,
- takeUntil,
- tap,
- withLatestFrom
-} from "rxjs"
-
-import { feature } from "~/_"
-import {
- Viewport,
- getElement,
- getElementContentOffset,
- getElementContentSize,
- getElementOffset,
- getElementSize,
- getElements,
- watchElementContentOffset,
- watchElementSize
-} from "~/browser"
-import { renderTabbedControl } from "~/templates"
-
-import { Component } from "../../_"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Content tabs
- */
-export interface ContentTabs {
- active: HTMLLabelElement /* Active tab label */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount options
- */
-interface MountOptions {
- viewport$: Observable<Viewport> /* Viewport observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch content tabs
- *
- * @param el - Content tabs element
- *
- * @returns Content tabs observable
- */
-export function watchContentTabs(
- el: HTMLElement
-): Observable<ContentTabs> {
- const inputs = getElements<HTMLInputElement>(":scope > input", el)
- const initial = inputs.find(input => input.checked) || inputs[0]
- return merge(...inputs.map(input => fromEvent(input, "change")
- .pipe(
- map(() => getElement<HTMLLabelElement>(`label[for="${input.id}"]`))
- )
- ))
- .pipe(
- startWith(getElement<HTMLLabelElement>(`label[for="${initial.id}"]`)),
- map(active => ({ active }))
- )
-}
-
-/**
- * Mount content tabs
- *
- * This function scrolls the active tab into view. While this functionality is
- * provided by browsers as part of `scrollInfoView`, browsers will always also
- * scroll the vertical axis, which we do not want. Thus, we decided to provide
- * this functionality ourselves.
- *
- * @param el - Content tabs element
- * @param options - Options
- *
- * @returns Content tabs component observable
- */
-export function mountContentTabs(
- el: HTMLElement, { viewport$ }: MountOptions
-): Observable<Component<ContentTabs>> {
-
- /* Render content tab previous button for pagination */
- const prev = renderTabbedControl("prev")
- el.append(prev)
-
- /* Render content tab next button for pagination */
- const next = renderTabbedControl("next")
- el.append(next)
-
- /* Mount component on subscription */
- const container = getElement(".tabbed-labels", el)
- return defer(() => {
- const push$ = new Subject<ContentTabs>()
- const done$ = push$.pipe(ignoreElements(), endWith(true))
- combineLatest([push$, watchElementSize(el)])
- .pipe(
- auditTime(1, animationFrameScheduler),
- takeUntil(done$)
- )
- .subscribe({
-
- /* Handle emission */
- next([{ active }, size]) {
- const offset = getElementOffset(active)
- const { width } = getElementSize(active)
-
- /* Set tab indicator offset and width */
- el.style.setProperty("--md-indicator-x", `${offset.x}px`)
- el.style.setProperty("--md-indicator-width", `${width}px`)
-
- /* Scroll container to active content tab */
- const content = getElementContentOffset(container)
- if (
- offset.x < content.x ||
- offset.x + width > content.x + size.width
- )
- container.scrollTo({
- left: Math.max(0, offset.x - 16),
- behavior: "smooth"
- })
- },
-
- /* Handle complete */
- complete() {
- el.style.removeProperty("--md-indicator-x")
- el.style.removeProperty("--md-indicator-width")
- }
- })
-
- /* Hide content tab buttons on borders */
- combineLatest([
- watchElementContentOffset(container),
- watchElementSize(container)
- ])
- .pipe(
- takeUntil(done$)
- )
- .subscribe(([offset, size]) => {
- const content = getElementContentSize(container)
- prev.hidden = offset.x < 16
- next.hidden = offset.x > content.width - size.width - 16
- })
-
- /* Paginate content tab container on click */
- merge(
- fromEvent(prev, "click").pipe(map(() => -1)),
- fromEvent(next, "click").pipe(map(() => +1))
- )
- .pipe(
- takeUntil(done$)
- )
- .subscribe(direction => {
- const { width } = getElementSize(container)
- container.scrollBy({
- left: width * direction,
- behavior: "smooth"
- })
- })
-
- /* Set up linking of content tabs, if enabled */
- if (feature("content.tabs.link"))
- push$.pipe(
- skip(1),
- withLatestFrom(viewport$)
- )
- .subscribe(([{ active }, { offset }]) => {
- const tab = active.innerText.trim()
- if (active.hasAttribute("data-md-switching")) {
- active.removeAttribute("data-md-switching")
-
- /* Determine viewport offset of active tab */
- } else {
- const y = el.offsetTop - offset.y
-
- /* Passively activate other tabs */
- for (const set of getElements("[data-tabs]"))
- for (const input of getElements<HTMLInputElement>(
- ":scope > input", set
- )) {
- const label = getElement(`label[for="${input.id}"]`)
- if (
- label !== active &&
- label.innerText.trim() === tab
- ) {
- label.setAttribute("data-md-switching", "")
- input.click()
- break
- }
- }
-
- /* Bring active tab into view */
- window.scrollTo({
- top: el.offsetTop - y
- })
-
- /* Persist active tabs in local storage */
- const tabs = __md_get<string[]>("__tabs") || []
- __md_set("__tabs", [...new Set([tab, ...tabs])])
- }
- })
-
- /* Pause media (audio, video) on switch - see https://bit.ly/3Bk6cel */
- push$.pipe(takeUntil(done$))
- .subscribe(() => {
- for (const media of getElements<HTMLAudioElement>("audio, video", el))
- media.pause()
- })
-
- /* Create and return component */
- return watchContentTabs(el)
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(state => ({ ref: el, ...state }))
- )
- })
- .pipe(
- subscribeOn(asyncScheduler)
- )
-}
diff --git a/docs/src/templates/assets/javascripts/components/dialog/index.ts b/docs/src/templates/assets/javascripts/components/dialog/index.ts
deleted file mode 100644
index 6ff1bd44..00000000
--- a/docs/src/templates/assets/javascripts/components/dialog/index.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- defer,
- delay,
- finalize,
- map,
- merge,
- of,
- switchMap,
- tap
-} from "rxjs"
-
-import { getElement } from "~/browser"
-
-import { Component } from "../_"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Dialog
- */
-export interface Dialog {
- message: string /* Dialog message */
- active: boolean /* Dialog is active */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch options
- */
-interface WatchOptions {
- alert$: Subject<string> /* Alert subject */
-}
-
-/**
- * Mount options
- */
-interface MountOptions {
- alert$: Subject<string> /* Alert subject */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch dialog
- *
- * @param _el - Dialog element
- * @param options - Options
- *
- * @returns Dialog observable
- */
-export function watchDialog(
- _el: HTMLElement, { alert$ }: WatchOptions
-): Observable<Dialog> {
- return alert$
- .pipe(
- switchMap(message => merge(
- of(true),
- of(false).pipe(delay(2000))
- )
- .pipe(
- map(active => ({ message, active }))
- )
- )
- )
-}
-
-/**
- * Mount dialog
- *
- * This function reveals the dialog in the right corner when a new alert is
- * emitted through the subject that is passed as part of the options.
- *
- * @param el - Dialog element
- * @param options - Options
- *
- * @returns Dialog component observable
- */
-export function mountDialog(
- el: HTMLElement, options: MountOptions
-): Observable<Component<Dialog>> {
- const inner = getElement(".md-typeset", el)
- return defer(() => {
- const push$ = new Subject<Dialog>()
- push$.subscribe(({ message, active }) => {
- el.classList.toggle("md-dialog--active", active)
- inner.textContent = message
- })
-
- /* Create and return component */
- return watchDialog(el, options)
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(state => ({ ref: el, ...state }))
- )
- })
-}
diff --git a/docs/src/templates/assets/javascripts/components/header/_/index.ts b/docs/src/templates/assets/javascripts/components/header/_/index.ts
deleted file mode 100644
index 0f33eb48..00000000
--- a/docs/src/templates/assets/javascripts/components/header/_/index.ts
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- bufferCount,
- combineLatest,
- combineLatestWith,
- defer,
- distinctUntilChanged,
- distinctUntilKeyChanged,
- endWith,
- filter,
- ignoreElements,
- map,
- of,
- shareReplay,
- startWith,
- switchMap,
- takeUntil
-} from "rxjs"
-
-import { feature } from "~/_"
-import {
- Viewport,
- watchElementSize,
- watchToggle
-} from "~/browser"
-
-import { Component } from "../../_"
-import { Main } from "../../main"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Header
- */
-export interface Header {
- height: number /* Header visible height */
- hidden: boolean /* Header is hidden */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch options
- */
-interface WatchOptions {
- viewport$: Observable<Viewport> /* Viewport observable */
-}
-
-/**
- * Mount options
- */
-interface MountOptions {
- viewport$: Observable<Viewport> /* Viewport observable */
- header$: Observable<Header> /* Header observable */
- main$: Observable<Main> /* Main area observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Compute whether the header is hidden
- *
- * If the user scrolls past a certain threshold, the header can be hidden when
- * scrolling down, and shown when scrolling up.
- *
- * @param options - Options
- *
- * @returns Toggle observable
- */
-function isHidden({ viewport$ }: WatchOptions): Observable<boolean> {
- if (!feature("header.autohide"))
- return of(false)
-
- /* Compute direction and turning point */
- const direction$ = viewport$
- .pipe(
- map(({ offset: { y } }) => y),
- bufferCount(2, 1),
- map(([a, b]) => [a < b, b] as const),
- distinctUntilKeyChanged(0)
- )
-
- /* Compute whether header should be hidden */
- const hidden$ = combineLatest([viewport$, direction$])
- .pipe(
- filter(([{ offset }, [, y]]) => Math.abs(y - offset.y) > 100),
- map(([, [direction]]) => direction),
- distinctUntilChanged()
- )
-
- /* Compute threshold for hiding */
- const search$ = watchToggle("search")
- return combineLatest([viewport$, search$])
- .pipe(
- map(([{ offset }, search]) => offset.y > 400 && !search),
- distinctUntilChanged(),
- switchMap(active => active ? hidden$ : of(false)),
- startWith(false)
- )
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch header
- *
- * @param el - Header element
- * @param options - Options
- *
- * @returns Header observable
- */
-export function watchHeader(
- el: HTMLElement, options: WatchOptions
-): Observable<Header> {
- return defer(() => combineLatest([
- watchElementSize(el),
- isHidden(options)
- ]))
- .pipe(
- map(([{ height }, hidden]) => ({
- height,
- hidden
- })),
- distinctUntilChanged((a, b) => (
- a.height === b.height &&
- a.hidden === b.hidden
- )),
- shareReplay(1)
- )
-}
-
-/**
- * Mount header
- *
- * This function manages the different states of the header, i.e. whether it's
- * hidden or rendered with a shadow. This depends heavily on the main area.
- *
- * @param el - Header element
- * @param options - Options
- *
- * @returns Header component observable
- */
-export function mountHeader(
- el: HTMLElement, { header$, main$ }: MountOptions
-): Observable<Component<Header>> {
- return defer(() => {
- const push$ = new Subject<Main>()
- const done$ = push$.pipe(ignoreElements(), endWith(true))
- push$
- .pipe(
- distinctUntilKeyChanged("active"),
- combineLatestWith(header$)
- )
- .subscribe(([{ active }, { hidden }]) => {
- el.classList.toggle("md-header--shadow", active && !hidden)
- el.hidden = hidden
- })
-
- /* Link to main area */
- main$.subscribe(push$)
-
- /* Create and return component */
- return header$
- .pipe(
- takeUntil(done$),
- map(state => ({ ref: el, ...state }))
- )
- })
-}
diff --git a/docs/src/templates/assets/javascripts/components/header/index.ts b/docs/src/templates/assets/javascripts/components/header/index.ts
deleted file mode 100644
index cf23ec1a..00000000
--- a/docs/src/templates/assets/javascripts/components/header/index.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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 "./_"
-export * from "./title"
diff --git a/docs/src/templates/assets/javascripts/components/header/title/index.ts b/docs/src/templates/assets/javascripts/components/header/title/index.ts
deleted file mode 100644
index f3bc0d08..00000000
--- a/docs/src/templates/assets/javascripts/components/header/title/index.ts
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- defer,
- distinctUntilKeyChanged,
- finalize,
- map,
- tap
-} from "rxjs"
-
-import {
- Viewport,
- getElementSize,
- getOptionalElement,
- watchViewportAt
-} from "~/browser"
-
-import { Component } from "../../_"
-import { Header } from "../_"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Header
- */
-export interface HeaderTitle {
- active: boolean /* Header title is active */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch options
- */
-interface WatchOptions {
- viewport$: Observable<Viewport> /* Viewport observable */
- header$: Observable<Header> /* Header observable */
-}
-
-/**
- * Mount options
- */
-interface MountOptions {
- viewport$: Observable<Viewport> /* Viewport observable */
- header$: Observable<Header> /* Header observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch header title
- *
- * @param el - Heading element
- * @param options - Options
- *
- * @returns Header title observable
- */
-export function watchHeaderTitle(
- el: HTMLElement, { viewport$, header$ }: WatchOptions
-): Observable<HeaderTitle> {
- return watchViewportAt(el, { viewport$, header$ })
- .pipe(
- map(({ offset: { y } }) => {
- const { height } = getElementSize(el)
- return {
- active: y >= height
- }
- }),
- distinctUntilKeyChanged("active")
- )
-}
-
-/**
- * Mount header title
- *
- * This function swaps the header title from the site title to the title of the
- * current page when the user scrolls past the first headline.
- *
- * @param el - Header title element
- * @param options - Options
- *
- * @returns Header title component observable
- */
-export function mountHeaderTitle(
- el: HTMLElement, options: MountOptions
-): Observable<Component<HeaderTitle>> {
- return defer(() => {
- const push$ = new Subject<HeaderTitle>()
- push$.subscribe({
-
- /* Handle emission */
- next({ active }) {
- el.classList.toggle("md-header__title--active", active)
- },
-
- /* Handle complete */
- complete() {
- el.classList.remove("md-header__title--active")
- }
- })
-
- /* Obtain headline, if any */
- const heading = getOptionalElement(".md-content h1")
- if (typeof heading === "undefined")
- return EMPTY
-
- /* Create and return component */
- return watchHeaderTitle(heading, options)
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(state => ({ ref: el, ...state }))
- )
- })
-}
diff --git a/docs/src/templates/assets/javascripts/components/index.ts b/docs/src/templates/assets/javascripts/components/index.ts
deleted file mode 100644
index 3d4391d1..00000000
--- a/docs/src/templates/assets/javascripts/components/index.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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 "./_"
-export * from "./announce"
-export * from "./consent"
-export * from "./content"
-export * from "./dialog"
-export * from "./header"
-export * from "./main"
-export * from "./palette"
-export * from "./progress"
-export * from "./search"
-export * from "./sidebar"
-export * from "./source"
-export * from "./tabs"
-export * from "./toc"
-export * from "./top"
diff --git a/docs/src/templates/assets/javascripts/components/main/index.ts b/docs/src/templates/assets/javascripts/components/main/index.ts
deleted file mode 100644
index 2509f9b9..00000000
--- a/docs/src/templates/assets/javascripts/components/main/index.ts
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- distinctUntilChanged,
- distinctUntilKeyChanged,
- map,
- switchMap
-} from "rxjs"
-
-import {
- Viewport,
- watchElementSize
-} from "~/browser"
-
-import { Header } from "../header"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Main area
- */
-export interface Main {
- offset: number /* Main area top offset */
- height: number /* Main area visible height */
- active: boolean /* Main area is active */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch options
- */
-interface WatchOptions {
- viewport$: Observable<Viewport> /* Viewport observable */
- header$: Observable<Header> /* Header observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch main area
- *
- * This function returns an observable that computes the visual parameters of
- * the main area which depends on the viewport vertical offset and height, as
- * well as the height of the header element, if the header is fixed.
- *
- * @param el - Main area element
- * @param options - Options
- *
- * @returns Main area observable
- */
-export function watchMain(
- el: HTMLElement, { viewport$, header$ }: WatchOptions
-): Observable<Main> {
-
- /* Compute necessary adjustment for header */
- const adjust$ = header$
- .pipe(
- map(({ height }) => height),
- distinctUntilChanged()
- )
-
- /* Compute the main area's top and bottom borders */
- const border$ = adjust$
- .pipe(
- switchMap(() => watchElementSize(el)
- .pipe(
- map(({ height }) => ({
- top: el.offsetTop,
- bottom: el.offsetTop + height
- })),
- distinctUntilKeyChanged("bottom")
- )
- )
- )
-
- /* Compute the main area's offset, visible height and if we scrolled past */
- return combineLatest([adjust$, border$, viewport$])
- .pipe(
- map(([header, { top, bottom }, { offset: { y }, size: { height } }]) => {
- height = Math.max(0, height
- - Math.max(0, top - y, header)
- - Math.max(0, height + y - bottom)
- )
- return {
- offset: top - header,
- height,
- active: top - header <= y
- }
- }),
- distinctUntilChanged((a, b) => (
- a.offset === b.offset &&
- a.height === b.height &&
- a.active === b.active
- ))
- )
-}
diff --git a/docs/src/templates/assets/javascripts/components/palette/index.ts b/docs/src/templates/assets/javascripts/components/palette/index.ts
deleted file mode 100644
index cf578f60..00000000
--- a/docs/src/templates/assets/javascripts/components/palette/index.ts
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- asyncScheduler,
- defer,
- finalize,
- fromEvent,
- map,
- mergeMap,
- observeOn,
- of,
- shareReplay,
- startWith,
- tap
-} from "rxjs"
-
-import { getElements } from "~/browser"
-import { h } from "~/utilities"
-
-import {
- Component,
- getComponentElement
-} from "../_"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Palette colors
- */
-export interface PaletteColor {
- scheme?: string /* Color scheme */
- primary?: string /* Primary color */
- accent?: string /* Accent color */
-}
-
-/**
- * Palette
- */
-export interface Palette {
- index: number /* Palette index */
- color: PaletteColor /* Palette colors */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch color palette
- *
- * @param inputs - Color palette element
- *
- * @returns Color palette observable
- */
-export function watchPalette(
- inputs: HTMLInputElement[]
-): Observable<Palette> {
- const current = __md_get<Palette>("__palette") || {
- index: inputs.findIndex(input => matchMedia(
- input.getAttribute("data-md-color-media")!
- ).matches)
- }
-
- /* Emit changes in color palette */
- return of(...inputs)
- .pipe(
- mergeMap(input => fromEvent(input, "change")
- .pipe(
- map(() => input)
- )
- ),
- startWith(inputs[Math.max(0, current.index)]),
- map(input => ({
- index: inputs.indexOf(input),
- color: {
- scheme: input.getAttribute("data-md-color-scheme"),
- primary: input.getAttribute("data-md-color-primary"),
- accent: input.getAttribute("data-md-color-accent")
- }
- } as Palette)),
- shareReplay(1)
- )
-}
-
-/**
- * Mount color palette
- *
- * @param el - Color palette element
- *
- * @returns Color palette component observable
- */
-export function mountPalette(
- el: HTMLElement
-): Observable<Component<Palette>> {
- const meta = h("meta", { name: "theme-color" })
- document.head.appendChild(meta)
-
- // Add color scheme meta tag
- const scheme = h("meta", { name: "color-scheme" })
- document.head.appendChild(scheme)
-
- /* Mount component on subscription */
- return defer(() => {
- const push$ = new Subject<Palette>()
- push$.subscribe(palette => {
- document.body.setAttribute("data-md-color-switching", "")
-
- /* Set color palette */
- for (const [key, value] of Object.entries(palette.color))
- document.body.setAttribute(`data-md-color-${key}`, value)
-
- /* Toggle visibility */
- for (let index = 0; index < inputs.length; index++) {
- const label = inputs[index].nextElementSibling
- if (label instanceof HTMLElement)
- label.hidden = palette.index !== index
- }
-
- /* Persist preference in local storage */
- __md_set("__palette", palette)
- })
-
- /* Update theme-color meta tag */
- push$
- .pipe(
- map(() => {
- const header = getComponentElement("header")
- const style = window.getComputedStyle(header)
-
- // Set color scheme
- scheme.content = style.colorScheme
-
- /* Return color in hexadecimal format */
- return style.backgroundColor.match(/\d+/g)!
- .map(value => (+value).toString(16).padStart(2, "0"))
- .join("")
- })
- )
- .subscribe(color => meta.content = `#${color}`)
-
- /* Revert transition durations after color switch */
- push$.pipe(observeOn(asyncScheduler))
- .subscribe(() => {
- document.body.removeAttribute("data-md-color-switching")
- })
-
- /* Create and return component */
- const inputs = getElements<HTMLInputElement>("input", el)
- return watchPalette(inputs)
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(state => ({ ref: el, ...state }))
- )
- })
-}
diff --git a/docs/src/templates/assets/javascripts/components/progress/index.ts b/docs/src/templates/assets/javascripts/components/progress/index.ts
deleted file mode 100644
index 30c722b8..00000000
--- a/docs/src/templates/assets/javascripts/components/progress/index.ts
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- defer,
- finalize,
- map,
- tap
-} from "rxjs"
-
-import { Component } from "../_"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Progress indicator
- */
-export interface Progress {
- value: number // Progress value
-}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount options
- */
-interface MountOptions {
- progress$: Subject<number> // Progress subject
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount progress indicator
- *
- * @param el - Progress indicator element
- * @param options - Options
- *
- * @returns Progress indicator component observable
- */
-export function mountProgress(
- el: HTMLElement, { progress$ }: MountOptions
-): Observable<Component<Progress>> {
-
- // Mount component on subscription
- return defer(() => {
- const push$ = new Subject<Progress>()
- push$.subscribe(({ value }) => {
- el.style.setProperty("--md-progress-value", `${value}`)
- })
-
- // Create and return component
- return progress$
- .pipe(
- tap(value => push$.next({ value })),
- finalize(() => push$.complete()),
- map(value => ({ ref: el, value }))
- )
- })
-}
diff --git a/docs/src/templates/assets/javascripts/components/search/_/index.ts b/docs/src/templates/assets/javascripts/components/search/_/index.ts
deleted file mode 100644
index aa963b47..00000000
--- a/docs/src/templates/assets/javascripts/components/search/_/index.ts
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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 {
- NEVER,
- Observable,
- ObservableInput,
- filter,
- fromEvent,
- merge,
- mergeWith
-} from "rxjs"
-
-import { configuration } from "~/_"
-import {
- Keyboard,
- getActiveElement,
- getElements,
- setToggle
-} from "~/browser"
-import {
- SearchIndex,
- SearchResult,
- setupSearchWorker
-} from "~/integrations"
-
-import {
- Component,
- getComponentElement,
- getComponentElements
-} from "../../_"
-import {
- SearchQuery,
- mountSearchQuery
-} from "../query"
-import { mountSearchResult } from "../result"
-import {
- SearchShare,
- mountSearchShare
-} from "../share"
-import {
- SearchSuggest,
- mountSearchSuggest
-} from "../suggest"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Search
- */
-export type Search =
- | SearchQuery
- | SearchResult
- | SearchShare
- | SearchSuggest
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount options
- */
-interface MountOptions {
- index$: ObservableInput<SearchIndex> /* Search index observable */
- keyboard$: Observable<Keyboard> /* Keyboard observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount search
- *
- * This function sets up the search functionality, including the underlying
- * web worker and all keyboard bindings.
- *
- * @param el - Search element
- * @param options - Options
- *
- * @returns Search component observable
- */
-export function mountSearch(
- el: HTMLElement, { index$, keyboard$ }: MountOptions
-): Observable<Component<Search>> {
- const config = configuration()
- try {
- const worker$ = setupSearchWorker(config.search, index$)
-
- /* Retrieve query and result components */
- const query = getComponentElement("search-query", el)
- const result = getComponentElement("search-result", el)
-
- /* Always close search on result selection */
- fromEvent<PointerEvent>(el, "click")
- .pipe(
- filter(({ target }) => (
- target instanceof Element && !!target.closest("a")
- ))
- )
- .subscribe(() => setToggle("search", false))
-
- /* Set up search keyboard handlers */
- keyboard$
- .pipe(
- filter(({ mode }) => mode === "search")
- )
- .subscribe(key => {
- const active = getActiveElement()
- switch (key.type) {
-
- /* Enter: go to first (best) result */
- case "Enter":
- if (active === query) {
- const anchors = new Map<HTMLAnchorElement, number>()
- for (const anchor of getElements<HTMLAnchorElement>(
- ":first-child [href]", result
- )) {
- const article = anchor.firstElementChild!
- anchors.set(anchor, parseFloat(
- article.getAttribute("data-md-score")!
- ))
- }
-
- /* Go to result with highest score, if any */
- if (anchors.size) {
- const [[best]] = [...anchors].sort(([, a], [, b]) => b - a)
- best.click()
- }
-
- /* Otherwise omit form submission */
- key.claim()
- }
- break
-
- /* Escape or Tab: close search */
- case "Escape":
- case "Tab":
- setToggle("search", false)
- query.blur()
- break
-
- /* Vertical arrows: select previous or next search result */
- case "ArrowUp":
- case "ArrowDown":
- if (typeof active === "undefined") {
- query.focus()
- } else {
- const els = [query, ...getElements(
- ":not(details) > [href], summary, details[open] [href]",
- result
- )]
- const i = Math.max(0, (
- Math.max(0, els.indexOf(active)) + els.length + (
- key.type === "ArrowUp" ? -1 : +1
- )
- ) % els.length)
- els[i].focus()
- }
-
- /* Prevent scrolling of page */
- key.claim()
- break
-
- /* All other keys: hand to search query */
- default:
- if (query !== getActiveElement())
- query.focus()
- }
- })
-
- /* Set up global keyboard handlers */
- keyboard$
- .pipe(
- filter(({ mode }) => mode === "global")
- )
- .subscribe(key => {
- switch (key.type) {
-
- /* Open search and select query */
- case "f":
- case "s":
- case "/":
- query.focus()
- query.select()
-
- /* Prevent scrolling of page */
- key.claim()
- break
- }
- })
-
- /* Create and return component */
- const query$ = mountSearchQuery(query, { worker$ })
- return merge(
- query$,
- mountSearchResult(result, { worker$, query$ })
- )
- .pipe(
- mergeWith(
-
- /* Search sharing */
- ...getComponentElements("search-share", el)
- .map(child => mountSearchShare(child, { query$ })),
-
- /* Search suggestions */
- ...getComponentElements("search-suggest", el)
- .map(child => mountSearchSuggest(child, { worker$, keyboard$ }))
- )
- )
-
- /* Gracefully handle broken search */
- } catch (err) {
- el.hidden = true
- return NEVER
- }
-}
diff --git a/docs/src/templates/assets/javascripts/components/search/highlight/.eslintrc b/docs/src/templates/assets/javascripts/components/search/highlight/.eslintrc
deleted file mode 100644
index 38a5714d..00000000
--- a/docs/src/templates/assets/javascripts/components/search/highlight/.eslintrc
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "rules": {
- "no-null/no-null": "off"
- }
-}
diff --git a/docs/src/templates/assets/javascripts/components/search/highlight/index.ts b/docs/src/templates/assets/javascripts/components/search/highlight/index.ts
deleted file mode 100644
index bc3f94c9..00000000
--- a/docs/src/templates/assets/javascripts/components/search/highlight/index.ts
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- ObservableInput,
- combineLatest,
- filter,
- map,
- startWith
-} from "rxjs"
-
-import { getLocation } from "~/browser"
-import {
- SearchIndex,
- setupSearchHighlighter
-} from "~/integrations"
-import { h } from "~/utilities"
-
-import { Component } from "../../_"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Search highlighting
- */
-export interface SearchHighlight {
- nodes: Map<ChildNode, string> /* Map of replacements */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount options
- */
-interface MountOptions {
- index$: ObservableInput<SearchIndex> /* Search index observable */
- location$: Observable<URL> /* Location observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount search highlighting
- *
- * @param el - Content element
- * @param options - Options
- *
- * @returns Search highlighting component observable
- */
-export function mountSearchHiglight(
- el: HTMLElement, { index$, location$ }: MountOptions
-): Observable<Component<SearchHighlight>> {
- return combineLatest([
- index$,
- location$
- .pipe(
- startWith(getLocation()),
- filter(url => !!url.searchParams.get("h"))
- )
- ])
- .pipe(
- map(([index, url]) => setupSearchHighlighter(index.config)(
- url.searchParams.get("h")!
- )),
- map(fn => {
- const nodes = new Map<ChildNode, string>()
-
- /* Traverse text nodes and collect matches */
- const it = document.createNodeIterator(el, NodeFilter.SHOW_TEXT)
- for (let node = it.nextNode(); node; node = it.nextNode()) {
- if (node.parentElement?.offsetHeight) {
- const original = node.textContent!
- const replaced = fn(original)
- if (replaced.length > original.length)
- nodes.set(node as ChildNode, replaced)
- }
- }
-
- /* Replace original nodes with matches */
- for (const [node, text] of nodes) {
- const { childNodes } = h("span", null, text)
- node.replaceWith(...Array.from(childNodes))
- }
-
- /* Return component */
- return { ref: el, nodes }
- })
- )
-}
diff --git a/docs/src/templates/assets/javascripts/components/search/index.ts b/docs/src/templates/assets/javascripts/components/search/index.ts
deleted file mode 100644
index 846d8685..00000000
--- a/docs/src/templates/assets/javascripts/components/search/index.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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 "./_"
-export * from "./highlight"
-export * from "./query"
-export * from "./result"
-export * from "./share"
-export * from "./suggest"
diff --git a/docs/src/templates/assets/javascripts/components/search/query/index.ts b/docs/src/templates/assets/javascripts/components/search/query/index.ts
deleted file mode 100644
index 4ce21279..00000000
--- a/docs/src/templates/assets/javascripts/components/search/query/index.ts
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- combineLatest,
- distinctUntilChanged,
- distinctUntilKeyChanged,
- endWith,
- finalize,
- first,
- fromEvent,
- ignoreElements,
- map,
- merge,
- shareReplay,
- takeUntil,
- tap
-} from "rxjs"
-
-import {
- getElement,
- getLocation,
- setToggle,
- watchElementFocus,
- watchToggle
-} from "~/browser"
-import {
- SearchMessage,
- SearchMessageType,
- isSearchReadyMessage
-} from "~/integrations"
-
-import { Component } from "../../_"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Search query
- */
-export interface SearchQuery {
- value: string /* Query value */
- focus: boolean /* Query focus */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch options
- */
-interface WatchOptions {
- worker$: Subject<SearchMessage> /* Search worker */
-}
-
-/**
- * Mount options
- */
-interface MountOptions {
- worker$: Subject<SearchMessage> /* Search worker */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch search query
- *
- * Note that the focus event which triggers re-reading the current query value
- * is delayed by `1ms` so the input's empty state is allowed to propagate.
- *
- * @param el - Search query element
- * @param options - Options
- *
- * @returns Search query observable
- */
-export function watchSearchQuery(
- el: HTMLInputElement, { worker$ }: WatchOptions
-): Observable<SearchQuery> {
-
- /* Support search deep linking */
- const { searchParams } = getLocation()
- if (searchParams.has("q")) {
- setToggle("search", true)
-
- /* Set query from parameter */
- el.value = searchParams.get("q")!
- el.focus()
-
- /* Remove query parameter on close */
- watchToggle("search")
- .pipe(
- first(active => !active)
- )
- .subscribe(() => {
- const url = getLocation()
- url.searchParams.delete("q")
- history.replaceState({}, "", `${url}`)
- })
- }
-
- /* Intercept focus and input events */
- const focus$ = watchElementFocus(el)
- const value$ = merge(
- worker$.pipe(first(isSearchReadyMessage)),
- fromEvent(el, "keyup"),
- focus$
- )
- .pipe(
- map(() => el.value),
- distinctUntilChanged()
- )
-
- /* Combine into single observable */
- return combineLatest([value$, focus$])
- .pipe(
- map(([value, focus]) => ({ value, focus })),
- shareReplay(1)
- )
-}
-
-/**
- * Mount search query
- *
- * @param el - Search query element
- * @param options - Options
- *
- * @returns Search query component observable
- */
-export function mountSearchQuery(
- el: HTMLInputElement, { worker$ }: MountOptions
-): Observable<Component<SearchQuery, HTMLInputElement>> {
- const push$ = new Subject<SearchQuery>()
- const done$ = push$.pipe(ignoreElements(), endWith(true))
-
- /* Handle value change */
- combineLatest([
- worker$.pipe(first(isSearchReadyMessage)),
- push$
- ], (_, query) => query)
- .pipe(
- distinctUntilKeyChanged("value")
- )
- .subscribe(({ value }) => worker$.next({
- type: SearchMessageType.QUERY,
- data: value
- }))
-
- /* Handle focus change */
- push$
- .pipe(
- distinctUntilKeyChanged("focus")
- )
- .subscribe(({ focus }) => {
- if (focus)
- setToggle("search", focus)
- })
-
- /* Handle reset */
- fromEvent(el.form!, "reset")
- .pipe(
- takeUntil(done$)
- )
- .subscribe(() => el.focus())
-
- // Focus search query on label click - note that this is necessary to bring
- // up the keyboard on iOS and other mobile platforms, as the search dialog is
- // not visible at first, and programatically focusing an input element must
- // be triggered by a user interaction - see https://t.ly/Cb30n
- const label = getElement("header [for=__search]")
- fromEvent(label, "click")
- .subscribe(() => el.focus())
-
- /* Create and return component */
- return watchSearchQuery(el, { worker$ })
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(state => ({ ref: el, ...state })),
- shareReplay(1)
- )
-}
diff --git a/docs/src/templates/assets/javascripts/components/search/result/index.ts b/docs/src/templates/assets/javascripts/components/search/result/index.ts
deleted file mode 100644
index c3c9ef20..00000000
--- a/docs/src/templates/assets/javascripts/components/search/result/index.ts
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- bufferCount,
- filter,
- finalize,
- first,
- fromEvent,
- map,
- merge,
- mergeMap,
- of,
- share,
- skipUntil,
- switchMap,
- takeUntil,
- tap,
- withLatestFrom,
- zipWith
-} from "rxjs"
-
-import { translation } from "~/_"
-import {
- getElement,
- getOptionalElement,
- watchElementBoundary,
- watchToggle
-} from "~/browser"
-import {
- SearchMessage,
- SearchResult,
- isSearchReadyMessage,
- isSearchResultMessage
-} from "~/integrations"
-import { renderSearchResultItem } from "~/templates"
-import { round } from "~/utilities"
-
-import { Component } from "../../_"
-import { SearchQuery } from "../query"
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount options
- */
-interface MountOptions {
- query$: Observable<SearchQuery> /* Search query observable */
- worker$: Subject<SearchMessage> /* Search worker */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount search result list
- *
- * This function performs a lazy rendering of the search results, depending on
- * the vertical offset of the search result container.
- *
- * @param el - Search result list element
- * @param options - Options
- *
- * @returns Search result list component observable
- */
-export function mountSearchResult(
- el: HTMLElement, { worker$, query$ }: MountOptions
-): Observable<Component<SearchResult>> {
- const push$ = new Subject<SearchResult>()
- const boundary$ = watchElementBoundary(el.parentElement!)
- .pipe(
- filter(Boolean)
- )
-
- /* Retrieve container */
- const container = el.parentElement!
-
- /* Retrieve nested components */
- const meta = getElement(":scope > :first-child", el)
- const list = getElement(":scope > :last-child", el)
-
- /* Reveal to accessibility tree – see https://bit.ly/3iAA7t8 */
- watchToggle("search")
- .subscribe(active => list.setAttribute(
- "role", active ? "list" : "presentation"
- ))
-
- /* Update search result metadata */
- push$
- .pipe(
- withLatestFrom(query$),
- skipUntil(worker$.pipe(first(isSearchReadyMessage)))
- )
- .subscribe(([{ items }, { value }]) => {
- switch (items.length) {
-
- /* No results */
- case 0:
- meta.textContent = value.length
- ? translation("search.result.none")
- : translation("search.result.placeholder")
- break
-
- /* One result */
- case 1:
- meta.textContent = translation("search.result.one")
- break
-
- /* Multiple result */
- default:
- const count = round(items.length)
- meta.textContent = translation("search.result.other", count)
- }
- })
-
- /* Render search result item */
- const render$ = push$
- .pipe(
- tap(() => list.innerHTML = ""),
- switchMap(({ items }) => merge(
- of(...items.slice(0, 10)),
- of(...items.slice(10))
- .pipe(
- bufferCount(4),
- zipWith(boundary$),
- switchMap(([chunk]) => chunk)
- )
- )),
- map(renderSearchResultItem),
- share()
- )
-
- /* Update search result list */
- render$.subscribe(item => list.appendChild(item))
- render$
- .pipe(
- mergeMap(item => {
- const details = getOptionalElement("details", item)
- if (typeof details === "undefined")
- return EMPTY
-
- /* Keep position of details element stable */
- return fromEvent(details, "toggle")
- .pipe(
- takeUntil(push$),
- map(() => details)
- )
- })
- )
- .subscribe(details => {
- if (
- details.open === false &&
- details.offsetTop <= container.scrollTop
- )
- container.scrollTo({ top: details.offsetTop })
- })
-
- /* Filter search result message */
- const result$ = worker$
- .pipe(
- filter(isSearchResultMessage),
- map(({ data }) => data)
- )
-
- /* Create and return component */
- return result$
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(state => ({ ref: el, ...state }))
- )
-}
diff --git a/docs/src/templates/assets/javascripts/components/search/share/index.ts b/docs/src/templates/assets/javascripts/components/search/share/index.ts
deleted file mode 100644
index 3db382c8..00000000
--- a/docs/src/templates/assets/javascripts/components/search/share/index.ts
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- endWith,
- finalize,
- fromEvent,
- ignoreElements,
- map,
- takeUntil,
- tap
-} from "rxjs"
-
-import { getLocation } from "~/browser"
-
-import { Component } from "../../_"
-import { SearchQuery } from "../query"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Search sharing
- */
-export interface SearchShare {
- url: URL /* Deep link for sharing */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch options
- */
-interface WatchOptions {
- query$: Observable<SearchQuery> /* Search query observable */
-}
-
-/**
- * Mount options
- */
-interface MountOptions {
- query$: Observable<SearchQuery> /* Search query observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount search sharing
- *
- * @param _el - Search sharing element
- * @param options - Options
- *
- * @returns Search sharing observable
- */
-export function watchSearchShare(
- _el: HTMLElement, { query$ }: WatchOptions
-): Observable<SearchShare> {
- return query$
- .pipe(
- map(({ value }) => {
- const url = getLocation()
- url.hash = ""
-
- /* Compute readable query strings */
- value = value
- .replace(/\s+/g, "+") /* Collapse whitespace */
- .replace(/&/g, "%26") /* Escape '&' character */
- .replace(/=/g, "%3D") /* Escape '=' character */
-
- /* Replace query string */
- url.search = `q=${value}`
- return { url }
- })
- )
-}
-
-/**
- * Mount search sharing
- *
- * @param el - Search sharing element
- * @param options - Options
- *
- * @returns Search sharing component observable
- */
-export function mountSearchShare(
- el: HTMLAnchorElement, options: MountOptions
-): Observable<Component<SearchShare>> {
- const push$ = new Subject<SearchShare>()
- const done$ = push$.pipe(ignoreElements(), endWith(true))
- push$.subscribe(({ url }) => {
- el.setAttribute("data-clipboard-text", el.href)
- el.href = `${url}`
- })
-
- /* Prevent following of link */
- fromEvent(el, "click")
- .pipe(
- takeUntil(done$)
- )
- .subscribe(ev => ev.preventDefault())
-
- /* Create and return component */
- return watchSearchShare(el, options)
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(state => ({ ref: el, ...state }))
- )
-}
diff --git a/docs/src/templates/assets/javascripts/components/search/suggest/index.ts b/docs/src/templates/assets/javascripts/components/search/suggest/index.ts
deleted file mode 100644
index e7881475..00000000
--- a/docs/src/templates/assets/javascripts/components/search/suggest/index.ts
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- asyncScheduler,
- combineLatestWith,
- distinctUntilChanged,
- filter,
- finalize,
- fromEvent,
- map,
- merge,
- observeOn,
- tap
-} from "rxjs"
-
-import { Keyboard } from "~/browser"
-import {
- SearchMessage,
- SearchResult,
- isSearchResultMessage
-} from "~/integrations"
-
-import { Component, getComponentElement } from "../../_"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Search suggestions
- */
-export interface SearchSuggest {}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount options
- */
-interface MountOptions {
- keyboard$: Observable<Keyboard> /* Keyboard observable */
- worker$: Subject<SearchMessage> /* Search worker */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Mount search suggestions
- *
- * This function will perform a lazy rendering of the search results, depending
- * on the vertical offset of the search result container.
- *
- * @param el - Search result list element
- * @param options - Options
- *
- * @returns Search result list component observable
- */
-export function mountSearchSuggest(
- el: HTMLElement, { worker$, keyboard$ }: MountOptions
-): Observable<Component<SearchSuggest>> {
- const push$ = new Subject<SearchResult>()
-
- /* Retrieve query component and track all changes */
- const query = getComponentElement("search-query")
- const query$ = merge(
- fromEvent(query, "keydown"),
- fromEvent(query, "focus")
- )
- .pipe(
- observeOn(asyncScheduler),
- map(() => query.value),
- distinctUntilChanged(),
- )
-
- /* Update search suggestions */
- push$
- .pipe(
- combineLatestWith(query$),
- map(([{ suggest }, value]) => {
- const words = value.split(/([\s-]+)/)
- if (suggest?.length && words[words.length - 1]) {
- const last = suggest[suggest.length - 1]
- if (last.startsWith(words[words.length - 1]))
- words[words.length - 1] = last
- } else {
- words.length = 0
- }
- return words
- })
- )
- .subscribe(words => el.innerHTML = words
- .join("")
- .replace(/\s/g, "&nbsp;")
- )
-
- /* Set up search keyboard handlers */
- keyboard$
- .pipe(
- filter(({ mode }) => mode === "search")
- )
- .subscribe(key => {
- switch (key.type) {
-
- /* Right arrow: accept current suggestion */
- case "ArrowRight":
- if (
- el.innerText.length &&
- query.selectionStart === query.value.length
- )
- query.value = el.innerText
- break
- }
- })
-
- /* Filter search result message */
- const result$ = worker$
- .pipe(
- filter(isSearchResultMessage),
- map(({ data }) => data)
- )
-
- /* Create and return component */
- return result$
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(() => ({ ref: el }))
- )
-}
diff --git a/docs/src/templates/assets/javascripts/components/sidebar/index.ts b/docs/src/templates/assets/javascripts/components/sidebar/index.ts
deleted file mode 100644
index 82f3d03e..00000000
--- a/docs/src/templates/assets/javascripts/components/sidebar/index.ts
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- animationFrameScheduler,
- asyncScheduler,
- auditTime,
- combineLatest,
- defer,
- distinctUntilChanged,
- endWith,
- finalize,
- first,
- from,
- fromEvent,
- ignoreElements,
- map,
- mergeMap,
- observeOn,
- takeUntil,
- tap,
- withLatestFrom
-} from "rxjs"
-
-import {
- Viewport,
- getElement,
- getElementContainer,
- getElementOffset,
- getElementSize,
- getElements
-} from "~/browser"
-
-import { Component } from "../_"
-import { Header } from "../header"
-import { Main } from "../main"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Sidebar
- */
-export interface Sidebar {
- height: number /* Sidebar height */
- locked: boolean /* Sidebar is locked */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch options
- */
-interface WatchOptions {
- viewport$: Observable<Viewport> /* Viewport observable */
- main$: Observable<Main> /* Main area observable */
-}
-
-/**
- * Mount options
- */
-interface MountOptions {
- viewport$: Observable<Viewport> /* Viewport observable */
- header$: Observable<Header> /* Header observable */
- main$: Observable<Main> /* Main area observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch sidebar
- *
- * This function returns an observable that computes the visual parameters of
- * the sidebar which depends on 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
- *
- * @returns Sidebar observable
- */
-export function watchSidebar(
- el: HTMLElement, { viewport$, main$ }: WatchOptions
-): Observable<Sidebar> {
- const parent = el.closest<HTMLElement>(".md-grid")!
- const adjust =
- parent.offsetTop -
- parent.parentElement!.offsetTop
-
- /* Compute the sidebar's available height and if it should be locked */
- return combineLatest([main$, viewport$])
- .pipe(
- map(([{ offset, height }, { offset: { y } }]) => {
- height = height
- + Math.min(adjust, Math.max(0, y - offset))
- - adjust
- return {
- height,
- locked: y >= offset + adjust
- }
- }),
- distinctUntilChanged((a, b) => (
- a.height === b.height &&
- a.locked === b.locked
- ))
- )
-}
-
-/**
- * Mount sidebar
- *
- * This function doesn't set the height of the actual sidebar, but of its first
- * child – the `.md-sidebar__scrollwrap` element in order to mitigiate jittery
- * sidebars when the footer is scrolled into view. At some point we switched
- * from `absolute` / `fixed` positioning to `sticky` positioning, significantly
- * reducing jitter in some browsers (respectively Firefox and Safari) when
- * scrolling from the top. However, top-aligned sticky positioning means that
- * the sidebar snaps to the bottom when the end of the container is reached.
- * This is what leads to the mentioned jitter, as the sidebar's height may be
- * updated too slowly.
- *
- * This behaviour can be mitigiated by setting the height of the sidebar to `0`
- * while preserving the padding, and the height on its first element.
- *
- * @param el - Sidebar element
- * @param options - Options
- *
- * @returns Sidebar component observable
- */
-export function mountSidebar(
- el: HTMLElement, { header$, ...options }: MountOptions
-): Observable<Component<Sidebar>> {
- const inner = getElement(".md-sidebar__scrollwrap", el)
- const { y } = getElementOffset(inner)
- return defer(() => {
- const push$ = new Subject<Sidebar>()
- const done$ = push$.pipe(ignoreElements(), endWith(true))
- const next$ = push$
- .pipe(
- auditTime(0, animationFrameScheduler)
- )
-
- /* Update sidebar height and offset */
- next$.pipe(withLatestFrom(header$))
- .subscribe({
-
- /* Handle emission */
- next([{ height }, { height: offset }]) {
- inner.style.height = `${height - 2 * y}px`
- el.style.top = `${offset}px`
- },
-
- /* Handle complete */
- complete() {
- inner.style.height = ""
- el.style.top = ""
- }
- })
-
- /* Bring active item into view on initial load */
- next$.pipe(first())
- .subscribe(() => {
- for (const item of getElements(".md-nav__link--active[href]", el)) {
- const container = getElementContainer(item)
- if (typeof container !== "undefined") {
- const offset = item.offsetTop - container.offsetTop
- const { height } = getElementSize(container)
- container.scrollTo({
- top: offset - height / 2
- })
- }
- }
- })
-
- /* Handle accessibility for expandable items, see https://bit.ly/3jaod9p */
- from(getElements<HTMLLabelElement>("label[tabindex]", el))
- .pipe(
- mergeMap(label => fromEvent(label, "click")
- .pipe(
- observeOn(asyncScheduler),
- map(() => label),
- takeUntil(done$)
- )
- )
- )
- .subscribe(label => {
- const input = getElement<HTMLInputElement>(`[id="${label.htmlFor}"]`)
- const nav = getElement(`[aria-labelledby="${label.id}"]`)
- nav.setAttribute("aria-expanded", `${input.checked}`)
- })
-
- /* Create and return component */
- return watchSidebar(el, options)
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(state => ({ ref: el, ...state }))
- )
- })
-}
diff --git a/docs/src/templates/assets/javascripts/components/source/_/index.ts b/docs/src/templates/assets/javascripts/components/source/_/index.ts
deleted file mode 100644
index 5f6c4d11..00000000
--- a/docs/src/templates/assets/javascripts/components/source/_/index.ts
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- catchError,
- defer,
- filter,
- finalize,
- map,
- of,
- shareReplay,
- tap
-} from "rxjs"
-
-import { getElement } from "~/browser"
-import { ConsentDefaults } from "~/components/consent"
-import { renderSourceFacts } from "~/templates"
-
-import {
- Component,
- getComponentElements
-} from "../../_"
-import {
- SourceFacts,
- fetchSourceFacts
-} from "../facts"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Repository information
- */
-export interface Source {
- facts: SourceFacts /* Repository facts */
-}
-
-/* ----------------------------------------------------------------------------
- * Data
- * ------------------------------------------------------------------------- */
-
-/**
- * Repository information observable
- */
-let fetch$: Observable<Source>
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch repository information
- *
- * This function tries to read the repository facts from session storage, and
- * if unsuccessful, fetches them from the underlying provider.
- *
- * @param el - Repository information element
- *
- * @returns Repository information observable
- */
-export function watchSource(
- el: HTMLAnchorElement
-): Observable<Source> {
- return fetch$ ||= defer(() => {
- const cached = __md_get<SourceFacts>("__source", sessionStorage)
- if (cached) {
- return of(cached)
- } else {
-
- /* Check if consent is configured and was given */
- const els = getComponentElements("consent")
- if (els.length) {
- const consent = __md_get<ConsentDefaults>("__consent")
- if (!(consent && consent.github))
- return EMPTY
- }
-
- /* Fetch repository facts */
- return fetchSourceFacts(el.href)
- .pipe(
- tap(facts => __md_set("__source", facts, sessionStorage))
- )
- }
- })
- .pipe(
- catchError(() => EMPTY),
- filter(facts => Object.keys(facts).length > 0),
- map(facts => ({ facts })),
- shareReplay(1)
- )
-}
-
-/**
- * Mount repository information
- *
- * @param el - Repository information element
- *
- * @returns Repository information component observable
- */
-export function mountSource(
- el: HTMLAnchorElement
-): Observable<Component<Source>> {
- const inner = getElement(":scope > :last-child", el)
- return defer(() => {
- const push$ = new Subject<Source>()
- push$.subscribe(({ facts }) => {
- inner.appendChild(renderSourceFacts(facts))
- inner.classList.add("md-source__repository--active")
- })
-
- /* Create and return component */
- return watchSource(el)
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(state => ({ ref: el, ...state }))
- )
- })
-}
diff --git a/docs/src/templates/assets/javascripts/components/source/facts/_/index.ts b/docs/src/templates/assets/javascripts/components/source/facts/_/index.ts
deleted file mode 100644
index 154f229f..00000000
--- a/docs/src/templates/assets/javascripts/components/source/facts/_/index.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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 } from "rxjs"
-
-import { fetchSourceFactsFromGitHub } from "../github"
-import { fetchSourceFactsFromGitLab } from "../gitlab"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Repository facts for repositories
- */
-export interface RepositoryFacts {
- stars?: number /* Number of stars */
- forks?: number /* Number of forks */
- version?: string /* Latest version */
-}
-
-/**
- * Repository facts for organizations
- */
-export interface OrganizationFacts {
- repositories?: number /* Number of repositories */
-}
-
-/* ------------------------------------------------------------------------- */
-
-/**
- * Repository facts
- */
-export type SourceFacts =
- | RepositoryFacts
- | OrganizationFacts
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Fetch repository facts
- *
- * @param url - Repository URL
- *
- * @returns Repository facts observable
- */
-export function fetchSourceFacts(
- url: string
-): Observable<SourceFacts> {
-
- /* Try to match GitHub repository */
- let match = url.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i)
- if (match) {
- const [, user, repo] = match
- return fetchSourceFactsFromGitHub(user, repo)
- }
-
- /* Try to match GitLab repository */
- match = url.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i)
- if (match) {
- const [, base, slug] = match
- return fetchSourceFactsFromGitLab(base, slug)
- }
-
- /* Fallback */
- return EMPTY
-}
diff --git a/docs/src/templates/assets/javascripts/components/source/facts/github/index.ts b/docs/src/templates/assets/javascripts/components/source/facts/github/index.ts
deleted file mode 100644
index 12cc55e0..00000000
--- a/docs/src/templates/assets/javascripts/components/source/facts/github/index.ts
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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 { Repo, User } from "github-types"
-import {
- EMPTY,
- Observable,
- catchError,
- defaultIfEmpty,
- map,
- zip
-} from "rxjs"
-
-import { requestJSON } from "~/browser"
-
-import { SourceFacts } from "../_"
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * GitHub release (partial)
- */
-interface Release {
- tag_name: string /* Tag name */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Fetch GitHub repository facts
- *
- * @param user - GitHub user or organization
- * @param repo - GitHub repository
- *
- * @returns Repository facts observable
- */
-export function fetchSourceFactsFromGitHub(
- user: string, repo?: string
-): Observable<SourceFacts> {
- if (typeof repo !== "undefined") {
- const url = `https://api.github.com/repos/${user}/${repo}`
- return zip(
-
- /* Fetch version */
- requestJSON<Release>(`${url}/releases/latest`)
- .pipe(
- catchError(() => EMPTY), // @todo refactor instant loading
- map(release => ({
- version: release.tag_name
- })),
- defaultIfEmpty({})
- ),
-
- /* Fetch stars and forks */
- requestJSON<Repo>(url)
- .pipe(
- catchError(() => EMPTY), // @todo refactor instant loading
- map(info => ({
- stars: info.stargazers_count,
- forks: info.forks_count
- })),
- defaultIfEmpty({})
- )
- )
- .pipe(
- map(([release, info]) => ({ ...release, ...info }))
- )
-
- /* User or organization */
- } else {
- const url = `https://api.github.com/users/${user}`
- return requestJSON<User>(url)
- .pipe(
- map(info => ({
- repositories: info.public_repos
- })),
- defaultIfEmpty({})
- )
- }
-}
diff --git a/docs/src/templates/assets/javascripts/components/source/facts/gitlab/index.ts b/docs/src/templates/assets/javascripts/components/source/facts/gitlab/index.ts
deleted file mode 100644
index d85d4afd..00000000
--- a/docs/src/templates/assets/javascripts/components/source/facts/gitlab/index.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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 { ProjectSchema } from "gitlab"
-import {
- EMPTY,
- Observable,
- catchError,
- defaultIfEmpty,
- map
-} from "rxjs"
-
-import { requestJSON } from "~/browser"
-
-import { SourceFacts } from "../_"
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Fetch GitLab repository facts
- *
- * @param base - GitLab base
- * @param project - GitLab project
- *
- * @returns Repository facts observable
- */
-export function fetchSourceFactsFromGitLab(
- base: string, project: string
-): Observable<SourceFacts> {
- const url = `https://${base}/api/v4/projects/${encodeURIComponent(project)}`
- return requestJSON<ProjectSchema>(url)
- .pipe(
- catchError(() => EMPTY), // @todo refactor instant loading
- map(({ star_count, forks_count }) => ({
- stars: star_count,
- forks: forks_count
- })),
- defaultIfEmpty({})
- )
-}
diff --git a/docs/src/templates/assets/javascripts/components/source/facts/index.ts b/docs/src/templates/assets/javascripts/components/source/facts/index.ts
deleted file mode 100644
index f9bda64d..00000000
--- a/docs/src/templates/assets/javascripts/components/source/facts/index.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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 "./_"
-export * from "./github"
-export * from "./gitlab"
diff --git a/docs/src/templates/assets/javascripts/components/source/index.ts b/docs/src/templates/assets/javascripts/components/source/index.ts
deleted file mode 100644
index 7fac4813..00000000
--- a/docs/src/templates/assets/javascripts/components/source/index.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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 "./_"
-export * from "./facts"
diff --git a/docs/src/templates/assets/javascripts/components/tabs/index.ts b/docs/src/templates/assets/javascripts/components/tabs/index.ts
deleted file mode 100644
index 1e69df28..00000000
--- a/docs/src/templates/assets/javascripts/components/tabs/index.ts
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- defer,
- distinctUntilKeyChanged,
- finalize,
- map,
- of,
- switchMap,
- tap
-} from "rxjs"
-
-import { feature } from "~/_"
-import {
- Viewport,
- watchElementSize,
- watchViewportAt
-} from "~/browser"
-
-import { Component } from "../_"
-import { Header } from "../header"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Navigation tabs
- */
-export interface Tabs {
- hidden: boolean /* Navigation tabs are hidden */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch options
- */
-interface WatchOptions {
- viewport$: Observable<Viewport> /* Viewport observable */
- header$: Observable<Header> /* Header observable */
-}
-
-/**
- * Mount options
- */
-interface MountOptions {
- viewport$: Observable<Viewport> /* Viewport observable */
- header$: Observable<Header> /* Header observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch navigation tabs
- *
- * @param el - Navigation tabs element
- * @param options - Options
- *
- * @returns Navigation tabs observable
- */
-export function watchTabs(
- el: HTMLElement, { viewport$, header$ }: WatchOptions
-): Observable<Tabs> {
- return watchElementSize(document.body)
- .pipe(
- switchMap(() => watchViewportAt(el, { header$, viewport$ })),
- map(({ offset: { y } }) => {
- return {
- hidden: y >= 10
- }
- }),
- distinctUntilKeyChanged("hidden")
- )
-}
-
-/**
- * Mount navigation tabs
- *
- * This function hides the navigation tabs when scrolling past the threshold
- * and makes them reappear in a nice CSS animation when scrolling back up.
- *
- * @param el - Navigation tabs element
- * @param options - Options
- *
- * @returns Navigation tabs component observable
- */
-export function mountTabs(
- el: HTMLElement, options: MountOptions
-): Observable<Component<Tabs>> {
- return defer(() => {
- const push$ = new Subject<Tabs>()
- push$.subscribe({
-
- /* Handle emission */
- next({ hidden }) {
- el.hidden = hidden
- },
-
- /* Handle complete */
- complete() {
- el.hidden = false
- }
- })
-
- /* Create and return component */
- return (
- feature("navigation.tabs.sticky")
- ? of({ hidden: false })
- : watchTabs(el, options)
- )
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(state => ({ ref: el, ...state }))
- )
- })
-}
diff --git a/docs/src/templates/assets/javascripts/components/toc/index.ts b/docs/src/templates/assets/javascripts/components/toc/index.ts
deleted file mode 100644
index 04b8d85f..00000000
--- a/docs/src/templates/assets/javascripts/components/toc/index.ts
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- asyncScheduler,
- bufferCount,
- combineLatestWith,
- debounceTime,
- defer,
- distinctUntilChanged,
- distinctUntilKeyChanged,
- endWith,
- filter,
- finalize,
- ignoreElements,
- map,
- merge,
- observeOn,
- of,
- repeat,
- scan,
- share,
- skip,
- startWith,
- switchMap,
- takeUntil,
- tap,
- withLatestFrom
-} from "rxjs"
-
-import { feature } from "~/_"
-import {
- Viewport,
- getElement,
- getElementContainer,
- getElementSize,
- getElements,
- getLocation,
- getOptionalElement,
- watchElementSize
-} from "~/browser"
-
-import {
- Component,
- getComponentElement
-} from "../_"
-import { Header } from "../header"
-import { Main } from "../main"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Table of contents
- */
-export interface TableOfContents {
- prev: HTMLAnchorElement[][] /* Anchors (previous) */
- next: HTMLAnchorElement[][] /* Anchors (next) */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch options
- */
-interface WatchOptions {
- viewport$: Observable<Viewport> /* Viewport observable */
- header$: Observable<Header> /* Header observable */
-}
-
-/**
- * Mount options
- */
-interface MountOptions {
- viewport$: Observable<Viewport> /* Viewport observable */
- header$: Observable<Header> /* Header observable */
- main$: Observable<Main> /* Main area observable */
- target$: Observable<HTMLElement> /* Location target observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch table of contents
- *
- * This is effectively a scroll spy implementation which will account for the
- * fixed header and automatically re-calculate anchor offsets when the viewport
- * is resized. The returned observable will only emit if the table of contents
- * needs to be repainted.
- *
- * This implementation tracks an anchor element's entire path starting from its
- * level up to the top-most anchor element, e.g. `[h3, h2, h1]`. Although the
- * Material theme currently doesn't make use of this information, it enables
- * the styling of the entire hierarchy through customization.
- *
- * Note that the current anchor is the last item of the `prev` anchor list.
- *
- * @param el - Table of contents element
- * @param options - Options
- *
- * @returns Table of contents observable
- */
-export function watchTableOfContents(
- el: HTMLElement, { viewport$, header$ }: WatchOptions
-): Observable<TableOfContents> {
- const table = new Map<HTMLAnchorElement, HTMLElement>()
-
- /* Compute anchor-to-target mapping */
- const anchors = getElements<HTMLAnchorElement>("[href^=\\#]", el)
- for (const anchor of anchors) {
- const id = decodeURIComponent(anchor.hash.substring(1))
- const target = getOptionalElement(`[id="${id}"]`)
- if (typeof target !== "undefined")
- table.set(anchor, target)
- }
-
- /* Compute necessary adjustment for header */
- const adjust$ = header$
- .pipe(
- distinctUntilKeyChanged("height"),
- map(({ height }) => {
- const main = getComponentElement("main")
- const grid = getElement(":scope > :first-child", main)
- return height + 0.8 * (
- grid.offsetTop -
- main.offsetTop
- )
- }),
- share()
- )
-
- /* Compute partition of previous and next anchors */
- const partition$ = watchElementSize(document.body)
- .pipe(
- distinctUntilKeyChanged("height"),
-
- /* Build index to map anchor paths to vertical offsets */
- switchMap(body => defer(() => {
- let path: HTMLAnchorElement[] = []
- return of([...table].reduce((index, [anchor, target]) => {
- while (path.length) {
- const last = table.get(path[path.length - 1])!
- if (last.tagName >= target.tagName) {
- path.pop()
- } else {
- break
- }
- }
-
- /* If the current anchor is hidden, continue with its parent */
- let offset = target.offsetTop
- while (!offset && target.parentElement) {
- target = target.parentElement
- offset = target.offsetTop
- }
-
- /* Fix anchor offsets in tables - see https://bit.ly/3CUFOcn */
- let parent = target.offsetParent as HTMLElement
- for (; parent; parent = parent.offsetParent as HTMLElement)
- offset += parent.offsetTop
-
- /* Map reversed anchor path to vertical offset */
- return index.set(
- [...path = [...path, anchor]].reverse(),
- offset
- )
- }, new Map<HTMLAnchorElement[], number>()))
- })
- .pipe(
-
- /* Sort index by vertical offset (see https://bit.ly/30z6QSO) */
- map(index => new Map([...index].sort(([, a], [, b]) => a - b))),
- combineLatestWith(adjust$),
-
- /* Re-compute partition when viewport offset changes */
- switchMap(([index, adjust]) => viewport$
- .pipe(
- scan(([prev, next], { offset: { y }, size }) => {
- const last = y + size.height >= Math.floor(body.height)
-
- /* Look forward */
- while (next.length) {
- const [, offset] = next[0]
- if (offset - adjust < y || last) {
- prev = [...prev, next.shift()!]
- } else {
- break
- }
- }
-
- /* Look backward */
- while (prev.length) {
- const [, offset] = prev[prev.length - 1]
- if (offset - adjust >= y && !last) {
- next = [prev.pop()!, ...next]
- } else {
- break
- }
- }
-
- /* Return partition */
- return [prev, next]
- }, [[], [...index]]),
- distinctUntilChanged((a, b) => (
- a[0] === b[0] &&
- a[1] === b[1]
- ))
- )
- )
- )
- )
- )
-
- /* Compute and return anchor list migrations */
- return partition$
- .pipe(
- map(([prev, next]) => ({
- prev: prev.map(([path]) => path),
- next: next.map(([path]) => path)
- })),
-
- /* Extract anchor list migrations */
- startWith({ prev: [], next: [] }),
- bufferCount(2, 1),
- map(([a, b]) => {
-
- /* Moving down */
- if (a.prev.length < b.prev.length) {
- return {
- prev: b.prev.slice(Math.max(0, a.prev.length - 1), b.prev.length),
- next: []
- }
-
- /* Moving up */
- } else {
- return {
- prev: b.prev.slice(-1),
- next: b.next.slice(0, b.next.length - a.next.length)
- }
- }
- })
- )
-}
-
-/* ------------------------------------------------------------------------- */
-
-/**
- * Mount table of contents
- *
- * @param el - Table of contents element
- * @param options - Options
- *
- * @returns Table of contents component observable
- */
-export function mountTableOfContents(
- el: HTMLElement, { viewport$, header$, main$, target$ }: MountOptions
-): Observable<Component<TableOfContents>> {
- return defer(() => {
- const push$ = new Subject<TableOfContents>()
- const done$ = push$.pipe(ignoreElements(), endWith(true))
- push$.subscribe(({ prev, next }) => {
-
- /* Look forward */
- for (const [anchor] of next) {
- anchor.classList.remove("md-nav__link--passed")
- anchor.classList.remove("md-nav__link--active")
- }
-
- /* Look backward */
- for (const [index, [anchor]] of prev.entries()) {
- anchor.classList.add("md-nav__link--passed")
- anchor.classList.toggle(
- "md-nav__link--active",
- index === prev.length - 1
- )
- }
- })
-
- /* Set up following, if enabled */
- if (feature("toc.follow")) {
-
- /* Toggle smooth scrolling only for anchor clicks */
- const smooth$ = merge(
- viewport$.pipe(debounceTime(1), map(() => undefined)),
- viewport$.pipe(debounceTime(250), map(() => "smooth" as const))
- )
-
- /* Bring active anchor into view */ // @todo: refactor
- push$
- .pipe(
- filter(({ prev }) => prev.length > 0),
- combineLatestWith(main$.pipe(observeOn(asyncScheduler))),
- withLatestFrom(smooth$)
- )
- .subscribe(([[{ prev }], behavior]) => {
- const [anchor] = prev[prev.length - 1]
- if (anchor.offsetHeight) {
-
- /* Retrieve overflowing container and scroll */
- const container = getElementContainer(anchor)
- if (typeof container !== "undefined") {
- const offset = anchor.offsetTop - container.offsetTop
- const { height } = getElementSize(container)
- container.scrollTo({
- top: offset - height / 2,
- behavior
- })
- }
- }
- })
- }
-
- /* Set up anchor tracking, if enabled */
- if (feature("navigation.tracking"))
- viewport$
- .pipe(
- takeUntil(done$),
- distinctUntilKeyChanged("offset"),
- debounceTime(250),
- skip(1),
- takeUntil(target$.pipe(skip(1))),
- repeat({ delay: 250 }),
- withLatestFrom(push$)
- )
- .subscribe(([, { prev }]) => {
- const url = getLocation()
-
- /* Set hash fragment to active anchor */
- const anchor = prev[prev.length - 1]
- if (anchor && anchor.length) {
- const [active] = anchor
- const { hash } = new URL(active.href)
- if (url.hash !== hash) {
- url.hash = hash
- history.replaceState({}, "", `${url}`)
- }
-
- /* Reset anchor when at the top */
- } else {
- url.hash = ""
- history.replaceState({}, "", `${url}`)
- }
- })
-
- /* Create and return component */
- return watchTableOfContents(el, { viewport$, header$ })
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(state => ({ ref: el, ...state }))
- )
- })
-}
diff --git a/docs/src/templates/assets/javascripts/components/top/index.ts b/docs/src/templates/assets/javascripts/components/top/index.ts
deleted file mode 100644
index 82e88b61..00000000
--- a/docs/src/templates/assets/javascripts/components/top/index.ts
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (c) 2016-2023 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,
- Subject,
- bufferCount,
- combineLatest,
- distinctUntilChanged,
- distinctUntilKeyChanged,
- endWith,
- finalize,
- fromEvent,
- ignoreElements,
- map,
- repeat,
- skip,
- takeUntil,
- tap
-} from "rxjs"
-
-import { Viewport } from "~/browser"
-
-import { Component } from "../_"
-import { Header } from "../header"
-import { Main } from "../main"
-
-/* ----------------------------------------------------------------------------
- * Types
- * ------------------------------------------------------------------------- */
-
-/**
- * Back-to-top button
- */
-export interface BackToTop {
- hidden: boolean /* Back-to-top button is hidden */
-}
-
-/* ----------------------------------------------------------------------------
- * Helper types
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch options
- */
-interface WatchOptions {
- viewport$: Observable<Viewport> /* Viewport observable */
- main$: Observable<Main> /* Main area observable */
- target$: Observable<HTMLElement> /* Location target observable */
-}
-
-/**
- * Mount options
- */
-interface MountOptions {
- viewport$: Observable<Viewport> /* Viewport observable */
- header$: Observable<Header> /* Header observable */
- main$: Observable<Main> /* Main area observable */
- target$: Observable<HTMLElement> /* Location target observable */
-}
-
-/* ----------------------------------------------------------------------------
- * Functions
- * ------------------------------------------------------------------------- */
-
-/**
- * Watch back-to-top
- *
- * @param _el - Back-to-top element
- * @param options - Options
- *
- * @returns Back-to-top observable
- */
-export function watchBackToTop(
- _el: HTMLElement, { viewport$, main$, target$ }: WatchOptions
-): Observable<BackToTop> {
-
- /* Compute direction */
- const direction$ = viewport$
- .pipe(
- map(({ offset: { y } }) => y),
- bufferCount(2, 1),
- map(([a, b]) => a > b && b > 0),
- distinctUntilChanged()
- )
-
- /* Compute whether main area is active */
- const active$ = main$
- .pipe(
- map(({ active }) => active)
- )
-
- /* Compute threshold for hiding */
- return combineLatest([active$, direction$])
- .pipe(
- map(([active, direction]) => !(active && direction)),
- distinctUntilChanged(),
- takeUntil(target$.pipe(skip(1))),
- endWith(true),
- repeat({ delay: 250 }),
- map(hidden => ({ hidden }))
- )
-}
-
-/* ------------------------------------------------------------------------- */
-
-/**
- * Mount back-to-top
- *
- * @param el - Back-to-top element
- * @param options - Options
- *
- * @returns Back-to-top component observable
- */
-export function mountBackToTop(
- el: HTMLElement, { viewport$, header$, main$, target$ }: MountOptions
-): Observable<Component<BackToTop>> {
- const push$ = new Subject<BackToTop>()
- const done$ = push$.pipe(ignoreElements(), endWith(true))
- push$.subscribe({
-
- /* Handle emission */
- next({ hidden }) {
- el.hidden = hidden
- if (hidden) {
- el.setAttribute("tabindex", "-1")
- el.blur()
- } else {
- el.removeAttribute("tabindex")
- }
- },
-
- /* Handle complete */
- complete() {
- el.style.top = ""
- el.hidden = true
- el.removeAttribute("tabindex")
- }
- })
-
- /* Watch header height */
- header$
- .pipe(
- takeUntil(done$),
- distinctUntilKeyChanged("height")
- )
- .subscribe(({ height }) => {
- el.style.top = `${height + 16}px`
- })
-
- /* Go back to top */
- fromEvent(el, "click")
- .subscribe(ev => {
- ev.preventDefault()
- window.scrollTo({ top: 0 })
- })
-
- /* Create and return component */
- return watchBackToTop(el, { viewport$, main$, target$ })
- .pipe(
- tap(state => push$.next(state)),
- finalize(() => push$.complete()),
- map(state => ({ ref: el, ...state }))
- )
-}