Formatting + moved search index fetching to top level

This commit is contained in:
squidfunk 2021-02-24 18:02:09 +01:00
parent cb723d4bef
commit d6317dc514
69 changed files with 404 additions and 328 deletions

View File

@ -91,7 +91,7 @@ Material for MkDocs comes with many configuration options. The _setup_ section
explains in great detail how to configure and customize colors, fonts, icons
and much more:
<div class="tx-columns" markdown="1">
<div class="mdx-columns" markdown="1">
- [Changing the colors][5]
- [Changing the fonts][6]

View File

@ -2,15 +2,15 @@
template: overrides/main.html
---
# <span hidden>Insiders</span> :logo: :material-plus: :octicons-heart-fill-24:{: .tx-heart }
# <span hidden>Insiders</span> :logo: :material-plus: :octicons-heart-fill-24:{: .mdx-heart }
Material for MkDocs uses the _sponsorware_ release strategy, which means
that _new features are first exclusively released to sponsors_ as part of
__Insiders__. Read on to learn [how sponsorship works][1], and how easy it is
to [get access to Insiders][2].
<figure class="tx-video" markdown="1">
<div class="tx-video__inner">
<figure class="mdx-video" markdown="1">
<div class="mdx-video__inner">
<iframe src="https://streamable.com/e/zmtb00" allowfullscreen></iframe>
</div>
<figcaption markdown="1">
@ -89,10 +89,10 @@ You can cancel your sponsorship anytime.[^3]
through Stripe. As we don't receive any information regarding your payment,
and GitHub doesn't offer refunds, sponsorships are non-refundable.
[:octicons-heart-fill-24:{: .tx-heart } &nbsp; Join our <span class="tx-insiders-count"></span> awesome sponsors][5]{: .md-button .md-button--primary .tx-insiders-button }
[:octicons-heart-fill-24:{: .mdx-heart } &nbsp; Join our <span class="mdx-insiders-count"></span> awesome sponsors][5]{: .md-button .md-button--primary .mdx-insiders-button }
<div class="tx-insiders-container" markdown="1" hidden>
<div class="tx-insiders-list"></div>
<div class="mdx-insiders-container" markdown="1" hidden>
<div class="mdx-insiders-list"></div>
_If you sponsor publicly, you're automatically added here with a link to
your profile and avatar to show your support for Material for MkDocs.
Alternatively, if you wish to keep your sponsorship private, you'll be a
@ -101,7 +101,7 @@ You can cancel your sponsorship anytime.[^3]
</div>
<script>
fetch("https://gpiqp43wvb.execute-api.us-east-1.amazonaws.com/_/").then(function(e){return e.json()}).then(function(e){var t=document.querySelector(".tx-insiders-list"),n=0;for(var o of e.sponsors)if("PUBLIC"===o.type){var s;(s=document.createElement("a")).href=o.url,s.title="@"+o.name,s.className="tx-insiders-list__item",t.appendChild(s);var r=document.createElement("img");r.src=o.image,s.appendChild(r)}else n++;(s=document.createElement("a")).href="https://github.com/sponsors/squidfunk",s.title="[private]",s.innerText="+"+n,s.className="tx-insiders-list__item tx-insiders-list__item--private",t.appendChild(s),document.querySelector(".tx-insiders-count").innerText=e.sponsors.length,document.querySelector(".tx-insiders-container").removeAttribute("hidden"),document.querySelector('.tx-insiders-total').innerText=" $ "+e.total.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,")}).catch(console.log);
fetch("https://gpiqp43wvb.execute-api.us-east-1.amazonaws.com/_/").then(function(e){return e.json()}).then(function(e){var t=document.querySelector(".mdx-insiders-list"),n=0;for(var o of e.sponsors)if("PUBLIC"===o.type){var s;(s=document.createElement("a")).href=o.url,s.title="@"+o.name,s.className="mdx-insiders-list__item",t.appendChild(s);var r=document.createElement("img");r.src=o.image,s.appendChild(r)}else n++;(s=document.createElement("a")).href="https://github.com/sponsors/squidfunk",s.title="[private]",s.innerText="+"+n,s.className="mdx-insiders-list__item mdx-insiders-list__item--private",t.appendChild(s),document.querySelector(".mdx-insiders-count").innerText=e.sponsors.length,document.querySelector(".mdx-insiders-container").removeAttribute("hidden"),document.querySelector('.mdx-insiders-total').innerText=" $ "+e.total.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,")}).catch(console.log);
</script>
[5]: https://github.com/sponsors/squidfunk
@ -110,7 +110,7 @@ You can cancel your sponsorship anytime.[^3]
The following features are currently exclusively available to sponsors:
<div class="tx-columns" markdown="1">
<div class="mdx-columns" markdown="1">
- [x] [Section index pages :material-new-box:][21]
- [x] [Latest release tag][15]
@ -130,7 +130,7 @@ from time to time to learn about what's new, or follow [@squidfunk on
[6]: https://twitter.com/squidfunk
## Funding<span class="tx-insiders-total tx-insiders-count"></span>
## Funding<span class="mdx-insiders-total mdx-insiders-count"></span>
### Goals

View File

@ -17,7 +17,7 @@ popular and flexible solution for drawing diagrams.
[:octicons-file-code-24: Source][2] ·
:octicons-beaker-24: Experimental ·
[:octicons-heart-fill-24:{: .tx-heart } Insiders only][2]{: .tx-insiders }
[:octicons-heart-fill-24:{: .mdx-heart } Insiders only][2]{: .mdx-insiders }
The [SuperFences][3] extension, which is part of [Python Markdown
Extensions][4], allows for adding __custom fences__, which can be used to

View File

@ -11,15 +11,15 @@ and used in `mkdocs.yml`, documents and templates.
## Search
<div class="mdx-icon-search" data-mdx-component="icon-search">
<div class="mdx-iconsearch" data-mdx-component="iconsearch">
<input
class="md-input md-input--stretch mdx-icon-search__input"
class="md-input md-input--stretch mdx-iconsearch__input"
placeholder="Search the icon and emoji database"
data-mdx-component="icon-search-query"
data-mdx-component="iconsearch-query"
/>
<div class="mdx-icon-search-result" data-mdx-component="icon-search-result">
<div class="mdx-icon-search-result__meta"></div>
<ol class="mdx-icon-search-result__list"></ol>
<div class="mdx-iconsearch-result" data-mdx-component="iconsearch-result">
<div class="mdx-iconsearch-result__meta"></div>
<ol class="mdx-iconsearch-result__list"></ol>
</div>
</div>
<small>
@ -205,7 +205,7 @@ _Example_:
_Result_:
:octicons-heart-fill-24:{: .tx-heart }
:octicons-heart-fill-24:{: .mdx-heart }
[20]: #with-colors
[21]: https://developer.mozilla.org/en-US/docs/Web/CSS/animation

View File

@ -75,7 +75,7 @@ Some popular choices:
### Latest release
[:octicons-file-code-24: Source][5] ·
[:octicons-heart-fill-24:{: .tx-heart } Insiders only][5]{: .tx-insiders }
[:octicons-heart-fill-24:{: .mdx-heart } Insiders only][5]{: .mdx-insiders }
The visual appearance of the repository link has been improved as part of
[Insiders][5], and will now automatically include the latest release tag which

View File

@ -32,7 +32,7 @@ theme:
_Click on a tile to change the color scheme_:
<div class="tx-switch">
<div class="mdx-switch">
<button data-md-color-scheme="default"><code>default</code></button>
<button data-md-color-scheme="slate"><code>slate</code></button>
</div>
@ -77,7 +77,7 @@ theme:
_Click on a tile to change the primary color_:
<div class="tx-switch">
<div class="mdx-switch">
<button data-md-color-primary="red"><code>red</code></button>
<button data-md-color-primary="pink"><code>pink</code></button>
<button data-md-color-primary="purple"><code>purple</code></button>
@ -138,7 +138,7 @@ _Click on a tile to change the accent color_:
}
</style>
<div class="tx-switch">
<div class="mdx-switch">
<button data-md-color-accent="red"><code>red</code></button>
<button data-md-color-accent="pink"><code>pink</code></button>
<button data-md-color-accent="purple"><code>purple</code></button>
@ -185,7 +185,7 @@ _Click on a tile to change the accent color_:
[:octicons-file-code-24: Source][6] ·
:octicons-beaker-24: Experimental ·
[:octicons-heart-fill-24:{: .tx-heart } Insiders only][6]{: .tx-insiders }
[:octicons-heart-fill-24:{: .mdx-heart } Insiders only][6]{: .mdx-insiders }
[Insiders][6] can easily add multiple color palettes, including a [scheme][8],
[primary][9] and [accent][10] color each, and let the user choose. A color

View File

@ -24,7 +24,7 @@ theme:
The following languages are supported:
<div class="tx-columns" markdown="1">
<div class="mdx-columns" markdown="1">
- `af` Afrikaans
- `ar` Arabic
@ -150,7 +150,7 @@ theme:
Click on a tile to change the directionality:
<div class="tx-switch">
<div class="mdx-switch">
<button data-md-dir="ltr"><code>ltr</code></button>
<button data-md-dir="rtl"><code>rtl</code></button>
</div>

View File

@ -81,7 +81,7 @@ theme:
[:octicons-file-code-24: Source][9] ·
:octicons-unlock-24: Feature flag ·
:octicons-beaker-24: Experimental ·
[:octicons-heart-fill-24:{: .tx-heart } Insiders only][9]{: .tx-insiders }
[:octicons-heart-fill-24:{: .mdx-heart } Insiders only][9]{: .mdx-insiders }
When _sticky tabs_ are enabled, navigation tabs will lock below the header and
always remain visible when scrolling down. Just add the following two feature
@ -166,7 +166,7 @@ theme:
[:octicons-file-code-24: Source][9] ·
:octicons-unlock-24: Feature flag ·
:octicons-beaker-24: Experimental ·
[:octicons-heart-fill-24:{: .tx-heart } Insiders only][9]{: .tx-insiders }
[:octicons-heart-fill-24:{: .mdx-heart } Insiders only][9]{: .mdx-insiders }
When _section index pages_ are enabled, documents can be directly attached to
sections, which is particularly useful for providing overview pages. Add the

View File

@ -58,7 +58,7 @@ The following options are supported:
The following languages are supported:
<div class="tx-columns" markdown="1">
<div class="mdx-columns" markdown="1">
- `ar` Arabic
- `da` Danish
@ -137,7 +137,7 @@ them at your own risk._
[:octicons-file-code-24: Source][8] ·
:octicons-unlock-24: Feature flag ·
:octicons-beaker-24: Experimental ·
[:octicons-heart-fill-24:{: .tx-heart } Insiders only][8]{: .tx-insiders }
[:octicons-heart-fill-24:{: .mdx-heart } Insiders only][8]{: .mdx-insiders }
When _search suggestions_ are enabled, the search will display the likeliest
completion for the last word, saving the user many key strokes by accepting the
@ -174,7 +174,7 @@ A demo is worth a thousand words — check it out at
[:octicons-file-code-24: Source][8] ·
:octicons-unlock-24: Feature flag ·
:octicons-beaker-24: Experimental ·
[:octicons-heart-fill-24:{: .tx-heart } Insiders only][8]{: .tx-insiders }
[:octicons-heart-fill-24:{: .mdx-heart } Insiders only][8]{: .mdx-insiders }
When _search highlighting_ is enabled and a user clicks on a search result,
Material for MkDocs will highlight all occurrences after following the link.
@ -208,7 +208,7 @@ A demo is worth a thousand words — check it out at
[:octicons-file-code-24: Source][8] ·
:octicons-unlock-24: Feature flag ·
:octicons-beaker-24: Experimental ·
[:octicons-heart-fill-24:{: .tx-heart } Insiders only][8]{: .tx-insiders }
[:octicons-heart-fill-24:{: .mdx-heart } Insiders only][8]{: .mdx-insiders }
When _search sharing_ is activated, a :material-share-variant: share button is
rendered next to the reset button, which allows to deep link to the current

View File

@ -106,7 +106,7 @@ copyright: Copyright &copy; 2016 - 2020 Martin Donath
### Remove generator
[:octicons-file-code-24: Source][4] ·
[:octicons-heart-fill-24:{: .tx-heart } Insiders only][4]{: .tx-insiders }
[:octicons-heart-fill-24:{: .mdx-heart } Insiders only][4]{: .mdx-insiders }
The footer displays a _Made with Material for MkDocs_ notice to denote how
the site was generated. The notice can be removed with the following setting

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -57,5 +57,5 @@
* Copyright (C) 2020 Oliver Nightingale
* @license MIT
*/
//# sourceMappingURL=search.217ffd95.min.js
//# sourceMappingURL=search.f5903571.min.js

View File

@ -191,7 +191,7 @@
"base": base_url,
"features": features,
"translations": {},
"search": "assets/javascripts/workers/search.217ffd95.min.js" | url,
"search": "assets/javascripts/workers/search.f5903571.min.js" | url,
"version": config.extra.version or None
} -%}
{%- set translations = app.translations -%}
@ -217,7 +217,7 @@
</script>
{% endblock %}
{% block scripts %}
<script src="{{ 'assets/javascripts/bundle.6e8eee23.min.js' | url }}"></script>
<script src="{{ 'assets/javascripts/bundle.4e3fea51.min.js' | url }}"></script>
{% for path in config["extra_javascript"] %}
<script src="{{ path | url }}"></script>
{% endfor %}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,13 +5,13 @@
{% block tabs %}
{{ super() }}
<style>.md-header{position:initial}.md-main__inner{margin:0}.md-content{display:none}@media screen and (min-width:60em){.md-sidebar--secondary{display:none}}@media screen and (min-width:76.25em){.md-sidebar--primary{display:none}}</style>
<section class="tx-container">
<section class="mdx-container">
<div class="md-grid md-typeset">
<div class="tx-hero">
<div class="tx-hero__image">
<div class="mdx-hero">
<div class="mdx-hero__image">
<img src="assets/images/illustration.png" alt="" width="1659" height="1200" draggable="false">
</div>
<div class="tx-hero__content">
<div class="mdx-hero__content">
<h1>Technical documentation that just works</h1>
<p>{{ config.site_description }}. Set up in 5 minutes.</p>
<a href="{{ page.next_page.url | url }}" title="{{ page.next_page.title | e }}" class="md-button md-button--primary">

View File

@ -22,7 +22,7 @@
<meta name="twitter:title" content="{{ title }}">
<meta name="twitter:description" content="{{ config.site_description }}">
<meta name="twitter:image" content="{{ image }}">
<link rel="stylesheet" href="{{ 'overrides/assets/stylesheets/main.8036384c.min.css' | url }}">
<link rel="stylesheet" href="{{ 'overrides/assets/stylesheets/main.6c7993e4.min.css' | url }}">
{% endblock %}
{% block announce %}
<a href="https://twitter.com/squidfunk">
@ -35,7 +35,7 @@
{% endblock %}
{% block content %}
{{ super() }}
<footer class="tx-content__footer md-typeset">
<footer class="mdx-content__footer md-typeset">
<a href="{{ 'insiders/' | url }}" title="Material for MkDocs Insiders">
<hr>
<span class="twemoji">
@ -44,7 +44,7 @@
<span class="twemoji">
{% include ".icons/material/plus.svg" %}
</span>
<span class="twemoji tx-heart">
<span class="twemoji mdx-heart">
{% include ".icons/octicons/heart-fill-24.svg" %}
</span>
<hr>
@ -53,5 +53,5 @@
{% endblock %}
{% block scripts %}
{{ super() }}
<script src="{{ 'overrides/assets/javascripts/bundle.fbf44fce.min.js' | url }}"></script>
<script src="{{ 'overrides/assets/javascripts/bundle.a8da887a.min.js' | url }}"></script>
{% endblock %}

View File

@ -33,7 +33,7 @@
{% for nav_item in nav_item.children %}
{{ render_nav_item(
nav_item,
path = base ~ "-" ~ loop.index,
path = base ~ "_" ~ loop.index,
level = level + 1)
}}
{% endfor %}

View File

@ -26,7 +26,7 @@
{% endif %}
<ul class="md-nav__list" data-md-scrollfix>
{% for nav_item in nav %}
{% set path = "nav-" ~ loop.index %}
{% set path = "__nav_" ~ loop.index %}
{% set level = 1 %}
{% include "partials/nav-item.html" %}
{% endfor %}

View File

@ -123,7 +123,7 @@ export function feature(flag: Flag): boolean {
* Retrieve the translation for the given key
*
* @param key - Key to be translated
* @param value - Value to be replaced
* @param value - Positional value, if any
*
* @returns Translation
*/

View File

@ -30,9 +30,8 @@ import { mapTo } from "rxjs/operators"
/**
* Watch document
*
* Documents must be implemented as subjects, so all downstream observables are
* automatically updated when a new document is emitted. This enabled features
* like instant loading.
* Documents are implemented as subjects, so all downstream observables are
* automatically updated when a new document is emitted.
*
* @returns Document subject
*/

View File

@ -21,9 +21,16 @@
*/
import { Observable, fromEvent, merge } from "rxjs"
import { distinctUntilChanged, map, startWith } from "rxjs/operators"
import {
distinctUntilChanged,
map,
startWith
} from "rxjs/operators"
import { getElementContentSize, getElementSize } from "../size"
import {
getElementContentSize,
getElementSize
} from "../size"
/* ----------------------------------------------------------------------------
* Types

View File

@ -29,10 +29,10 @@ import { Subject } from "rxjs"
/**
* Retrieve location
*
* This function will return a `URL` object (and not `Location`) in order to
* normalize typings across the application. Furthermore, locations need to be
* tracked without setting them and `Location` is a singleton which represents
* the current location.
* This function returns a `URL` object (and not `Location`) to normalize the
* typings across the application. Furthermore, locations need to be tracked
* without setting them and `Location` is a singleton which represents the
* current location.
*
* @returns URL
*/

View File

@ -31,10 +31,11 @@ import {
switchMap
} from "rxjs/operators"
import { feature } from "./_"
import { configuration, feature } from "./_"
import {
at,
getElement,
requestJSON,
setToggle,
watchDocument,
watchKeyboard,
@ -60,6 +61,7 @@ import {
watchMain
} from "./components"
import {
SearchIndex,
setupClipboardJS,
setupInstantLoading
} from "./integrations"
@ -89,6 +91,12 @@ const tablet$ = watchMedia("(min-width: 960px)")
const screen$ = watchMedia("(min-width: 1220px)")
const print$ = watchPrint()
/* Retrieve search index */
const config = configuration()
const index$ = __search?.index || requestJSON<SearchIndex>(
`${config.base}/search/search_index.json`
)
/* Set up Clipboard.js integration */
const alert$ = new Subject<string>()
setupClipboardJS({ alert$ })
@ -160,11 +168,11 @@ const control$ = merge(
/* Search */
...getComponentElements("search")
.map(el => mountSearch(el, { keyboard$ })),
.map(el => mountSearch(el, { index$, keyboard$ })),
/* Repository information */
...getComponentElements("source")
.map(el => mountSource(el as HTMLAnchorElement)),
.map(el => mountSource(el)),
/* Navigation tabs */
...getComponentElements("tabs")

View File

@ -61,6 +61,32 @@ export type Component<
ref: U /* Component reference */
}
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Component type map
*/
interface ComponentTypeMap {
"announce": HTMLElement /* Announcement bar */
"container": HTMLElement /* Container */
"content": HTMLElement /* Content */
"dialog": HTMLElement /* Dialog */
"header": HTMLElement /* Header */
"header-title": HTMLElement /* Header title */
"header-topic": HTMLElement /* Header topic */
"main": HTMLElement /* Main area */
"search": HTMLElement /* Search */
"search-query": HTMLInputElement /* Search input */
"search-result": HTMLElement /* Search results */
"sidebar": HTMLElement /* Sidebar */
"skip": HTMLAnchorElement /* Skip link */
"source": HTMLAnchorElement /* Repository information */
"tabs": HTMLElement /* Navigation tabs */
"toc": HTMLElement /* Table of contents */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
@ -68,31 +94,31 @@ export type Component<
/**
* Retrieve the element for a given component or throw a reference error
*
* @template T - Element type
* @template T - Component type
*
* @param type - Component type
* @param node - Node of reference
*
* @returns Element
*/
export function getComponentElement<T extends HTMLElement>(
type: ComponentType, node: ParentNode = document
): T {
export function getComponentElement<T extends ComponentType>(
type: T, node: ParentNode = document
): ComponentTypeMap[T] {
return getElementOrThrow(`[data-md-component=${type}]`, node)
}
/**
* Retrieve all elements for a given component
*
* @template T - Element type
* @template T - Component type
*
* @param type - Component type
* @param node - Node of reference
*
* @returns Elements
*/
export function getComponentElements<T extends HTMLElement>(
type: ComponentType, node: ParentNode = document
): T[] {
export function getComponentElements<T extends ComponentType>(
type: T, node: ParentNode = document
): ComponentTypeMap[T][] {
return getElements(`[data-md-component=${type}]`, node)
}

View File

@ -95,8 +95,8 @@ let index = 0
/**
* Watch code block
*
* This function will monitor size changes of the viewport, as well as switches
* of content tabs with embedded code blocks, as both may trigger overflow.
* 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
* @param options - Options
@ -163,7 +163,7 @@ export function mountCodeBlock(
resetFocusable(el)
})
/* Inject button for Clipboard.js integration */
/* Render button for Clipboard.js integration */
if (ClipboardJS.isSupported()) {
const parent = el.closest("pre")!
parent.id = `__code_${index++}`

View File

@ -52,8 +52,8 @@ const sentinel = createElement("table")
/**
* Mount data table
*
* This function wraps a data table in another scrollable container, so they
* can be scrolled on smaller screen sizes and won't break the layout.
* 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
*

View File

@ -105,8 +105,8 @@ export function watchDialog(
/**
* Mount dialog
*
* This function makes the dialog in the right corner appear when a new alert
* is emitted through the subject that is passed as part of the options.
* This function reveals the dialog in the right cornerwhen a new alert is
* emitted through the subject that is passed as part of the options.
*
* @param el - Dialog element
* @param options - Options

View File

@ -28,7 +28,6 @@ import {
Keyboard,
getActiveElement,
getElements,
requestJSON,
setElementFocus,
setElementSelection,
setToggle
@ -63,24 +62,10 @@ export type Search =
* Mount options
*/
interface MountOptions {
index$: ObservableInput<SearchIndex> /* Search index observable */
keyboard$: Observable<Keyboard> /* Keyboard observable */
}
/* ----------------------------------------------------------------------------
* Helper functions
* ------------------------------------------------------------------------- */
/**
* Fetch search index
*
* @param url - Search index URL
*
* @returns Promise or observable
*/
function fetchSearchIndex(url: string): ObservableInput<SearchIndex> {
return __search?.index || requestJSON<SearchIndex>(url)
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
@ -97,16 +82,14 @@ function fetchSearchIndex(url: string): ObservableInput<SearchIndex> {
* @returns Search component observable
*/
export function mountSearch(
el: HTMLElement, { keyboard$ }: MountOptions
el: HTMLElement, { index$, keyboard$ }: MountOptions
): Observable<Component<Search>> {
if (location.protocol === "file:")
return NEVER
/* Set up search worker */
const config = configuration()
const worker = setupSearchWorker(config.search, fetchSearchIndex(
`${config.base}/search/search_index.json`
))
const worker = setupSearchWorker(config.search, index$)
/* Retrieve nested components */
const query = getComponentElement("search-query", el)
@ -193,7 +176,7 @@ export function mountSearch(
})
/* Create and return component */
const query$ = mountSearchQuery(query as HTMLInputElement, worker)
const query$ = mountSearchQuery(query, worker)
return merge(
query$,
mountSearchResult(result, worker, { query$ })

View File

@ -33,7 +33,6 @@ import {
distinctUntilKeyChanged,
finalize,
map,
startWith,
takeLast,
takeUntil,
tap
@ -96,7 +95,6 @@ export function watchSearchQuery(
)
.pipe(
map(() => fn(el.value)),
startWith(fn(el.value)),
distinctUntilChanged()
)

View File

@ -89,8 +89,8 @@ interface MountOptions {
/**
* Mount search result list
*
* This function will perform a lazy rendering of the search results, depending
* on the vertical offset of the search result container.
* 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 worker - Search worker

View File

@ -32,7 +32,7 @@ import {
import { setSourceFacts, setSourceState } from "~/actions"
import { renderSourceFacts } from "~/templates"
import { hash } from "~/utilities"
import { digest } from "~/utilities"
import { Component } from "../../_"
import { SourceFacts, fetchSourceFacts } from "../facts"
@ -53,7 +53,7 @@ export interface Source {
* ------------------------------------------------------------------------- */
/**
* Repository facts observable
* Repository information observable
*/
let fetch$: Observable<Source>
@ -64,8 +64,8 @@ let fetch$: Observable<Source>
/**
* Watch repository information
*
* This function will try to read the repository facts from session storage,
* and if unsuccessful, fetch them from the underlying provider.
* 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
*
@ -74,18 +74,15 @@ let fetch$: Observable<Source>
export function watchSource(
el: HTMLAnchorElement
): Observable<Source> {
const digest = hash(el.href).toString()
/* Fetch repository facts once */
return fetch$ ||= defer(() => {
const data = sessionStorage.getItem(digest)
const data = sessionStorage.getItem(digest("__repo"))
if (data) {
return of(JSON.parse(data))
return of<SourceFacts>(JSON.parse(data))
} else {
const value$ = fetchSourceFacts(el.href)
value$.subscribe(value => {
try {
sessionStorage.setItem(digest, JSON.stringify(value))
sessionStorage.setItem(digest("__repo"), JSON.stringify(value))
} catch (err) {
/* Uncritical, just swallow */
}

View File

@ -84,7 +84,7 @@ function setupSearchIndex(
/**
* Set up search worker
*
* This function will create a web worker to set up and query the search index
* This function creates a web worker to set up and query the search index,
* which is done using Lunr.js. The index must be passed as an observable to
* enable hacks like _localsearch_ via search index embedding as JSON.
*

View File

@ -62,8 +62,8 @@ let index: Search
/**
* Fetch (= import) multi-language support through `lunr-languages`
*
* This function will automatically import the stemmers necessary to process
* the languages which were given through the search index configuration.
* This function automatically imports the stemmers necessary to process the
* languages, which are defined through the search index configuration.
*
* If the worker runs inside of an `iframe` (when using `iframe-worker` as
* a shim), the base URL for the stemmers to be loaded must be determined by

View File

@ -42,7 +42,7 @@ interface PatchOptions {
/**
* Patch indeterminate checkboxes
*
* This function will replace the indeterminate "pseudo state" with the actual
* This function replaces the indeterminate "pseudo state" with the actual
* indeterminate state, which is used to keep navigation always expanded.
*
* @param options - Options

View File

@ -20,6 +20,8 @@
* IN THE SOFTWARE.
*/
import { configuration } from "~/_"
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
@ -88,3 +90,18 @@ export function hash(value: string): number {
}
return h
}
/**
* Add a digest to a value to ensure uniqueness
*
* When a single account hosts multiple sites on the same GitHub Pages domain,
* entries would collide, because session and local storage are not unique.
*
* @param value - Value
*
* @returns Value with digest
*/
export function digest(value: string): string {
const config = configuration()
return `${value}[${hash(config.base)}]`
}

View File

@ -316,7 +316,7 @@
<h1>{{ page.title | d(config.site_name, true)}}</h1>
{% endif %}
<!-- Content -->
<!-- Markdown content -->
{{ page.content }}
<!-- Last update of source file -->

View File

@ -37,12 +37,12 @@ import { setupAnalytics } from "./integrations"
setupAnalytics()
/* Set up extra component observables */
window.document$
document$
.pipe(
switchMap(() => merge(
/* Icon search */
...getComponentElements("icon-search")
...getComponentElements("iconsearch")
.map(el => mountIconSearch(el))
))
)

View File

@ -30,9 +30,9 @@ import { getElementOrThrow, getElements } from "~/browser"
* Component
*/
export type ComponentType =
| "icon-search" /* Icon search */
| "icon-search-query" /* Icon search input */
| "icon-search-result" /* Icon search results */
| "iconsearch" /* Icon search */
| "iconsearch-query" /* Icon search input */
| "iconsearch-result" /* Icon search results */
/**
* A component
@ -48,6 +48,19 @@ export type Component<
ref: U /* Component reference */
}
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Component type map
*/
interface ComponentTypeMap {
"iconsearch": HTMLElement /* Icon search */
"iconsearch-query": HTMLInputElement /* Icon search input */
"iconsearch-result": HTMLElement /* Icon search results */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
@ -55,31 +68,31 @@ export type Component<
/**
* Retrieve the element for a given component or throw a reference error
*
* @template T - Element type
* @template T - Component type
*
* @param type - Component type
* @param node - Node of reference
*
* @returns Element
*/
export function getComponentElement<T extends HTMLElement>(
type: ComponentType, node: ParentNode = document
): T {
export function getComponentElement<T extends ComponentType>(
type: T, node: ParentNode = document
): ComponentTypeMap[T] {
return getElementOrThrow(`[data-mdx-component=${type}]`, node)
}
/**
* Retrieve all elements for a given component
*
* @template T - Element type
* @template T - Component type
*
* @param type - Component type
* @param node - Node of reference
*
* @returns Elements
*/
export function getComponentElements<T extends HTMLElement>(
type: ComponentType, node: ParentNode = document
): T[] {
export function getComponentElements<T extends ComponentType>(
type: T, node: ParentNode = document
): ComponentTypeMap[T][] {
return getElements(`[data-mdx-component=${type}]`, node)
}

View File

@ -80,15 +80,15 @@ export function mountIconSearch(
): Observable<Component<IconSearch>> {
const config = configuration()
const index$ = requestJSON<IconSearchIndex>(
`${config.base}/overrides/assets/javascripts/icon_search_index.json`
`${config.base}/overrides/assets/javascripts/iconsearch_index.json`
)
/* Retrieve nested components */
const query = getComponentElement("icon-search-query", el)
const result = getComponentElement("icon-search-result", el)
const query = getComponentElement("iconsearch-query", el)
const result = getComponentElement("iconsearch-result", el)
/* Create and return component */
const query$ = mountIconSearchQuery(query as HTMLInputElement)
const query$ = mountIconSearchQuery(query)
return merge(
query$,
mountIconSearchResult(result, { index$, query$ })

View File

@ -20,13 +20,17 @@
* IN THE SOFTWARE.
*/
import { Observable, combineLatest, fromEvent, merge } from "rxjs"
import {
Observable,
combineLatest,
fromEvent,
merge
} from "rxjs"
import {
delay,
distinctUntilChanged,
filter,
map,
startWith,
withLatestFrom
} from "rxjs/operators"
@ -69,7 +73,6 @@ export function mountIconSearchQuery(
)
.pipe(
map(() => el.value),
startWith(el.value),
distinctUntilChanged()
)
@ -81,7 +84,8 @@ export function mountIconSearchQuery(
)
.subscribe(([, value]) => {
const path = document.location.pathname
ga("send", "pageview", `${path}?q=[icon]+${value}`)
if (value.length)
ga("send", "pageview", `${path}?q=[icon]+${value}`)
})
/* Combine into single observable */

View File

@ -21,4 +21,4 @@
*/
export * from "./_"
export * from "./icon-search"
export * from "./iconsearch"

View File

@ -49,7 +49,7 @@ export interface Icon {
*
* @returns Highlighted result
*/
function highlight(icon: Icon, query: string) {
function highlight(icon: Icon, query: string): string {
return wrap(icon.shortcode, query, {
wrap: {
tagOpen: "<b>",
@ -74,7 +74,7 @@ export function renderIconSearchResult(
icon: Icon, query: string
): HTMLElement {
return (
<li class="mdx-icon-search-result__item">
<li class="mdx-iconsearch-result__item">
<span class="twemoji">
<img src={icon.url} />
</span>

View File

@ -20,4 +20,4 @@
* IN THE SOFTWARE.
*/
export * from "./icon-search"
export * from "./iconsearch"

View File

@ -41,5 +41,6 @@
@import "main/layout/announce";
@import "main/layout/content";
@import "main/layout/hero";
@import "main/layout/iconsearch";
@import "main/shame";

View File

@ -23,108 +23,3 @@
// ----------------------------------------------------------------------------
// Nothing to see here, move along
// ----------------------------------------------------------------------------
// Scoped in typesetted content to match specificity of regular content
.md-typeset {
// Icon search
.mdx-icon-search {
position: relative;
background-color: var(--md-default-bg-color);
border-radius: px2rem(2px);
box-shadow:
0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),
0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.1);
transition: box-shadow 125ms;
// Icon search on focus/hover
&:focus-within,
&:hover {
box-shadow:
0 px2rem(8px) px2rem(20px) hsla(0, 0%, 0%, 0.15),
0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.15);
}
// Icon search input
.md-input {
background: var(--md-default-bg-color);
box-shadow: 0 0 px2rem(12px) hsla(0, 0%, 0%, 0.07);
// Slate theme, i.e. dark mode
[data-md-color-scheme="slate"] & {
background: var(--md-code-bg-color);
}
}
}
// Icon search result
.mdx-icon-search-result {
max-height: 50vh;
overflow-y: auto;
// Hack: promote to own layer to reduce jitter
backface-visibility: hidden;
touch-action: pan-y;
scrollbar-width: thin;
scrollbar-color: var(--md-default-fg-color--lighter) transparent;
// Webkit scrollbar
&::-webkit-scrollbar {
width: px2rem(4px);
height: px2rem(4px);
}
// Webkit scrollbar thumb
&::-webkit-scrollbar-thumb {
background-color: var(--md-default-fg-color--lighter);
// Webkit scrollbar thumb on hover
&:hover {
background-color: var(--md-accent-fg-color);
}
}
// Icon search result metadata
&__meta {
position: absolute;
top: px2rem(8px);
right: px2rem(12px);
color: var(--md-default-fg-color--lighter);
font-size: px2rem(12.8px);
}
// Icon search result list
&__list {
margin: 0;
padding: 0;
list-style: none;
}
// Icon search result item
&__item {
margin: 0;
padding: px2rem(4px) px2rem(12px);
border-bottom: px2rem(1px) solid var(--md-default-fg-color--lightest);
// Omit border on last child
&:last-child {
border-bottom: none;
}
// Item content
> * {
margin-right: px2rem(12px);
}
// Set icon dimensions to fit
img {
width: px2rem(18px);
height: px2rem(18px);
// Slate theme, i.e. dark mode
[data-md-color-scheme="slate"] &[src*=squidfunk] {
filter: invert(1);
}
}
}
}
}

View File

@ -25,7 +25,7 @@
// ----------------------------------------------------------------------------
// Pumping heart animation
@keyframes tx-heart {
@keyframes mdx-heart {
0%,
40%,
80%,
@ -57,7 +57,7 @@
}
// Insiders video
.tx-video {
.mdx-video {
width: auto;
// Insiders video container
@ -81,27 +81,27 @@
}
// Pumping heart
.tx-heart {
animation: tx-heart 1000ms infinite;
.mdx-heart {
animation: mdx-heart 1000ms infinite;
}
// Insiders color (for links, etc.)
.tx-insiders {
.mdx-insiders {
color: $clr-pink-500;
}
// Insiders button
.tx-insiders-button {
.mdx-insiders-button {
font-weight: 400;
}
// Insiders count
.tx-insiders-count {
.mdx-insiders-count {
font-weight: 700;
}
// Insiders list
.tx-insiders-list {
.mdx-insiders-list {
margin: 2em 0;
overflow: auto;
@ -152,7 +152,7 @@
}
// Switch buttons
.tx-switch button {
.mdx-switch button {
cursor: pointer;
transition: opacity 250ms;
@ -171,7 +171,7 @@
}
// Two-column layout
.tx-columns {
.mdx-columns {
// Column
ol,

View File

@ -25,7 +25,7 @@
// ----------------------------------------------------------------------------
// Content footer
.tx-content__footer {
.mdx-content__footer {
margin-top: px2rem(20px);
text-align: center;

View File

@ -25,7 +25,7 @@
// ----------------------------------------------------------------------------
// Landing page container
.tx-container {
.mdx-container {
padding-top: px2rem(20px);
background:
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1123 258'><path d='M1124,2c0,0 0,256 0,256l-1125,0l0,-48c0,0 16,5 55,5c116,0 197,-92 325,-92c121,0 114,46 254,46c140,0 214,-167 572,-166Z' style='fill: hsla(0, 0%, 100%, 1)' /></svg>") no-repeat bottom,
@ -50,7 +50,7 @@
}
// Landing page hero
.tx-hero {
.mdx-hero {
margin: 0 px2rem(16px);
color: var(--md-primary-bg-color);

View File

@ -0,0 +1,130 @@
////
/// Copyright (c) 2016-2021 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
////
// ----------------------------------------------------------------------------
// Rules
// ----------------------------------------------------------------------------
// Scoped in typesetted content to match specificity of regular content
.md-typeset {
// Icon search
.mdx-iconsearch {
position: relative;
background-color: var(--md-default-bg-color);
border-radius: px2rem(2px);
box-shadow:
0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.1),
0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.1);
transition: box-shadow 125ms;
// Icon search on focus/hover
&:focus-within,
&:hover {
box-shadow:
0 px2rem(8px) px2rem(20px) hsla(0, 0%, 0%, 0.15),
0 px2rem(0.5px) px2rem(1px) hsla(0, 0%, 0%, 0.15);
}
// Icon search input
.md-input {
background: var(--md-default-bg-color);
box-shadow: 0 0 px2rem(12px) hsla(0, 0%, 0%, 0.07);
// Slate theme, i.e. dark mode
[data-md-color-scheme="slate"] & {
background: var(--md-code-bg-color);
}
}
}
// Icon search result
.mdx-iconsearch-result {
max-height: 50vh;
overflow-y: auto;
// Hack: promote to own layer to reduce jitter
backface-visibility: hidden;
touch-action: pan-y;
scrollbar-width: thin;
scrollbar-color: var(--md-default-fg-color--lighter) transparent;
// Webkit scrollbar
&::-webkit-scrollbar {
width: px2rem(4px);
height: px2rem(4px);
}
// Webkit scrollbar thumb
&::-webkit-scrollbar-thumb {
background-color: var(--md-default-fg-color--lighter);
// Webkit scrollbar thumb on hover
&:hover {
background-color: var(--md-accent-fg-color);
}
}
// Icon search result metadata
&__meta {
position: absolute;
top: px2rem(8px);
right: px2rem(12px);
color: var(--md-default-fg-color--lighter);
font-size: px2rem(12.8px);
}
// Icon search result list
&__list {
margin: 0;
padding: 0;
list-style: none;
}
// Icon search result item
&__item {
margin: 0;
padding: px2rem(4px) px2rem(12px);
border-bottom: px2rem(1px) solid var(--md-default-fg-color--lightest);
// Omit border on last child
&:last-child {
border-bottom: none;
}
// Item content
> * {
margin-right: px2rem(12px);
}
// Set icon dimensions to fit
img {
width: px2rem(18px);
height: px2rem(18px);
// Slate theme, i.e. dark mode
[data-md-color-scheme="slate"] &[src*=squidfunk] {
filter: invert(1);
}
}
}
}
}

View File

@ -60,12 +60,12 @@
</style>
<!-- Hero for landing page -->
<section class="tx-container">
<section class="mdx-container">
<div class="md-grid md-typeset">
<div class="tx-hero">
<div class="mdx-hero">
<!-- Hero image -->
<div class="tx-hero__image">
<div class="mdx-hero__image">
<img
src="assets/images/illustration.png"
alt=""
@ -76,7 +76,7 @@
</div>
<!-- Hero content -->
<div class="tx-hero__content">
<div class="mdx-hero__content">
<h1>Technical documentation that just works</h1>
<p>{{ config.site_description }}. Set up in 5 minutes.</p>
<a

View File

@ -75,7 +75,7 @@
{{ super() }}
<!-- Content footer -->
<footer class="tx-content__footer md-typeset">
<footer class="mdx-content__footer md-typeset">
<a href="{{ 'insiders/' | url }}" title="Material for MkDocs Insiders">
<hr />
<span class="twemoji">
@ -84,7 +84,7 @@
<span class="twemoji">
{% include ".icons/material/plus.svg" %}
</span>
<span class="twemoji tx-heart">
<span class="twemoji mdx-heart">
{% include ".icons/octicons/heart-fill-24.svg" %}
</span>
<hr />

View File

@ -1,5 +1,5 @@
<!--
Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
Copyright (c) 2016-2021 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

View File

@ -84,7 +84,7 @@
{% for nav_item in nav_item.children %}
{{ render_nav_item(
nav_item,
path = base ~ "-" ~ loop.index,
path = base ~ "_" ~ loop.index,
level = level + 1)
}}
{% endfor %}

View File

@ -65,7 +65,7 @@
<!-- Render item list -->
<ul class="md-nav__list" data-md-scrollfix>
{% for nav_item in nav %}
{% set path = "nav-" ~ loop.index %}
{% set path = "__nav_" ~ loop.index %}
{% set level = 1 %}
{% include "partials/nav-item.html" %}
{% endfor %}

View File

@ -84,7 +84,7 @@ export function copy(
/**
* Copy all files matching the given pattern
*
* Note that this function will rebase all files that match the pattern to the
* Note that this function rebases all files that match the pattern to the
* target folder, even if the pattern resolves to a parent folder.
*
* @param pattern - Pattern

View File

@ -267,7 +267,7 @@ const index$ = zip(icons$, emojis$)
} as IconSearchIndex
}),
switchMap(data => fs.writeFile(
`${base}/overrides/assets/javascripts/icon_search_index.json`,
`${base}/overrides/assets/javascripts/iconsearch_index.json`,
JSON.stringify(data)
))
)

22
typings/_/index.d.ts vendored
View File

@ -61,16 +61,14 @@ declare global {
* ------------------------------------------------------------------------- */
declare global {
interface Window {
document$: Observable<Document> /* Document observable */
location$: Subject<URL> /* Location subject */
target$: Observable<HTMLElement> /* Location target observable */
keyboard$: Observable<Keyboard> /* Keyboard observable */
viewport$: Observable<Viewport> /* Viewport obsevable */
tablet$: Observable<boolean> /* Tablet breakpoint observable */
screen$: Observable<boolean> /* Screen breakpoint observable */
print$: Observable<void> /* Print mode observable */
alert$: Subject<string> /* Alert subject */
component$: Observable<Component> /* Component observable */
}
var document$: Observable<Document> /* Document observable */
var location$: Subject<URL> /* Location subject */
var target$: Observable<HTMLElement> /* Location target observable */
var keyboard$: Observable<Keyboard> /* Keyboard observable */
var viewport$: Observable<Viewport> /* Viewport obsevable */
var tablet$: Observable<boolean> /* Tablet breakpoint observable */
var screen$: Observable<boolean> /* Screen breakpoint observable */
var print$: Observable<void> /* Print mode observable */
var alert$: Subject<string> /* Alert subject */
var component$: Observable<Component>/* Component observable */
}