mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2024-11-23 23:21:00 +01:00
Merge of Insiders features tied to 'Ghost Pepper' funding goal
This commit is contained in:
parent
41785f1c57
commit
887b7115fc
@ -34,11 +34,7 @@ trim_trailing_whitespace = true
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
# Makefiles
|
||||
# Python
|
||||
[*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# Makefiles
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -43,6 +43,7 @@
|
||||
*.tsbuildinfo
|
||||
.cache
|
||||
.eslintcache
|
||||
__pycache__
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# General
|
||||
|
@ -80,6 +80,7 @@
|
||||
"selector-class-pattern": null,
|
||||
"selector-combinator-space-before": null,
|
||||
"selector-descendant-combinator-no-non-space": null,
|
||||
"selector-id-pattern": null,
|
||||
"selector-max-empty-lines": 0,
|
||||
"selector-max-id": 0,
|
||||
"selector-no-qualifying-type": null,
|
||||
|
29
material/assets/javascripts/bundle.6273739e.min.js
vendored
Normal file
29
material/assets/javascripts/bundle.6273739e.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
material/assets/javascripts/bundle.6273739e.min.js.map
Normal file
7
material/assets/javascripts/bundle.6273739e.min.js.map
Normal file
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
File diff suppressed because one or more lines are too long
2
material/assets/stylesheets/main.9e98b581.min.css
vendored
Normal file
2
material/assets/stylesheets/main.9e98b581.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
material/assets/stylesheets/main.9e98b581.min.css.map
Normal file
1
material/assets/stylesheets/main.9e98b581.min.css.map
Normal file
File diff suppressed because one or more lines are too long
@ -34,7 +34,7 @@
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.75e88914.min.css' | url }}">
|
||||
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.9e98b581.min.css' | url }}">
|
||||
{% if config.theme.palette %}
|
||||
{% set palette = config.theme.palette %}
|
||||
<link rel="stylesheet" href="{{ 'assets/stylesheets/palette.9204c3b2.min.css' | url }}">
|
||||
@ -62,6 +62,7 @@
|
||||
{% for path in config["extra_css"] %}
|
||||
<link rel="stylesheet" href="{{ path | url }}">
|
||||
{% endfor %}
|
||||
{% include "partials/javascripts/base.html" %}
|
||||
{% block analytics %}
|
||||
{% include "partials/integrations/analytics.html" %}
|
||||
{% endblock %}
|
||||
@ -81,7 +82,6 @@
|
||||
<body dir="{{ direction }}">
|
||||
{% endif %}
|
||||
{% set features = config.theme.features or [] %}
|
||||
{% include "partials/javascripts/base.html" %}
|
||||
{% if not config.theme.palette is mapping %}
|
||||
{% include "partials/javascripts/palette.html" %}
|
||||
{% endif %}
|
||||
@ -105,6 +105,18 @@
|
||||
</aside>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if config.extra.version %}
|
||||
<div data-md-component="outdated" hidden>
|
||||
<aside class="md-banner md-banner--warning">
|
||||
{% if self.outdated() %}
|
||||
<div class="md-banner__inner md-grid md-typeset">
|
||||
{% block outdated %}{% endblock %}
|
||||
</div>
|
||||
{% include "partials/javascripts/outdated.html" %}
|
||||
{% endif %}
|
||||
</aside>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% block header %}
|
||||
{% include "partials/header.html" %}
|
||||
{% endblock %}
|
||||
@ -157,13 +169,12 @@
|
||||
<h1>{{ page.title | d(config.site_name, true)}}</h1>
|
||||
{% endif %}
|
||||
{{ page.content }}
|
||||
{% if page and page.meta %}
|
||||
{% if page.meta.git_revision_date_localized or
|
||||
{% if page and page.meta and (
|
||||
page.meta.git_revision_date_localized or
|
||||
page.meta.revision_date
|
||||
%}
|
||||
) %}
|
||||
{% include "partials/source-file.html" %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block disqus %}
|
||||
{% include "partials/integrations/disqus.html" %}
|
||||
@ -217,7 +228,7 @@
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script src="{{ 'assets/javascripts/bundle.ccba565e.min.js' | url }}"></script>
|
||||
<script src="{{ 'assets/javascripts/bundle.6273739e.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
2
material/overrides/assets/stylesheets/main.a9ec9cc0.min.css
vendored
Normal file
2
material/overrides/assets/stylesheets/main.a9ec9cc0.min.css
vendored
Normal file
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
@ -3,7 +3,7 @@
|
||||
-#}
|
||||
{% extends "base.html" %}
|
||||
{% block extrahead %}
|
||||
<link rel="stylesheet" href="{{ 'overrides/assets/stylesheets/main.bf3dc0a9.min.css' | url }}">
|
||||
<link rel="stylesheet" href="{{ 'overrides/assets/stylesheets/main.a9ec9cc0.min.css' | url }}">
|
||||
{% endblock %}
|
||||
{% block announce %}
|
||||
<a href="https://twitter.com/squidfunk">
|
||||
@ -16,5 +16,5 @@
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
{{ super() }}
|
||||
<script src="{{ 'overrides/assets/javascripts/bundle.525231ca.min.js' | url }}"></script>
|
||||
<script src="{{ 'overrides/assets/javascripts/bundle.35fbbc46.min.js' | url }}"></script>
|
||||
{% endblock %}
|
||||
|
@ -1,4 +1,4 @@
|
||||
{#-
|
||||
This file was automatically generated - do not edit
|
||||
-#}
|
||||
<script>function __prefix(e){return new URL("{{ base_url }}",location).pathname+"."+e}function __get(e,t=localStorage){return JSON.parse(t.getItem(__prefix(e)))}</script>
|
||||
<script>function __md_scope(e,t,_){return new URL(_||(t===localStorage?"{{ config.extra.scope | d(base_url) }}":"{{ base_url }}"),location).pathname+"."+e}function __md_get(e,t=localStorage,_){return JSON.parse(t.getItem(__md_scope(e,t,_)))}function __md_set(e,t,_=localStorage,o){try{_.setItem(__md_scope(e,_,o),JSON.stringify(t))}catch(e){}}</script>
|
||||
|
@ -1,4 +1,4 @@
|
||||
{#-
|
||||
This file was automatically generated - do not edit
|
||||
-#}
|
||||
<script>var palette=__get("__palette");if(null!==palette&&"object"==typeof palette.color)for(var key in palette.color)document.body.setAttribute("data-md-color-"+key,palette.color[key])</script>
|
||||
<script>var palette=__md_get("__palette");if(palette&&"object"==typeof palette.color)for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)</script>
|
||||
|
4
material/partials/outdated.html
Normal file
4
material/partials/outdated.html
Normal file
@ -0,0 +1,4 @@
|
||||
{#-
|
||||
This file was automatically generated - do not edit
|
||||
-#}
|
||||
<script>var el=document.querySelector("[data-md-component=outdated]"),outdated=__md_get("__outdated",sessionStorage);!0===outdated&&el&&(el.hidden=!1)</script>
|
@ -2,17 +2,20 @@
|
||||
This file was automatically generated - do not edit
|
||||
-#}
|
||||
{% import "partials/language.html" as lang with context %}
|
||||
{% set label = lang.t("source.file.date.updated") %}
|
||||
<hr>
|
||||
<div class="md-source-date">
|
||||
<small>
|
||||
{% if page.meta.git_revision_date_localized %}
|
||||
{{ label }}: {{ page.meta.git_revision_date_localized }}
|
||||
{{ lang.t("source.file.date.updated") }}:
|
||||
{{ page.meta.git_revision_date_localized }}
|
||||
{% if page.meta.git_creation_date_localized %}
|
||||
<br>{{ lang.t("source.file.date.created") }}: {{ page.meta.git_creation_date_localized }}
|
||||
<br>
|
||||
{{ lang.t("source.file.date.created") }}:
|
||||
{{ page.meta.git_creation_date_localized }}
|
||||
{% endif %}
|
||||
{% elif page.meta.revision_date %}
|
||||
{{ label }}: {{ page.meta.revision_date }}
|
||||
{{ lang.t("source.file.date.updated") }}:
|
||||
{{ page.meta.revision_date }}
|
||||
{% endif %}
|
||||
</small>
|
||||
</div>
|
||||
|
@ -30,6 +30,7 @@ import { getElementOrThrow, getLocation } from "~/browser"
|
||||
* Feature flag
|
||||
*/
|
||||
export type Flag =
|
||||
| "content.code.annotate" /* Code annotations */
|
||||
| "header.autohide" /* Hide header */
|
||||
| "navigation.expand" /* Automatic expansion */
|
||||
| "navigation.instant" /* Instant loading */
|
||||
@ -38,6 +39,7 @@ export type Flag =
|
||||
| "navigation.tabs" /* Tabs navigation */
|
||||
| "navigation.tabs.sticky" /* Tabs navigation (sticky) */
|
||||
| "navigation.top" /* Back-to-top button */
|
||||
| "navigation.tracking" /* Anchor tracking */
|
||||
| "search.highlight" /* Search highlighting */
|
||||
| "search.share" /* Search sharing */
|
||||
| "search.suggest" /* Search suggestions */
|
||||
@ -76,6 +78,7 @@ export type Translations = Record<Translation, string>
|
||||
*/
|
||||
export interface Versioning {
|
||||
provider: "mike" /* Version provider */
|
||||
default?: string /* Default version */
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,7 +24,8 @@ import {
|
||||
NEVER,
|
||||
Observable,
|
||||
fromEvent,
|
||||
fromEventPattern
|
||||
fromEventPattern,
|
||||
merge
|
||||
} from "rxjs"
|
||||
import {
|
||||
mapTo,
|
||||
@ -59,14 +60,14 @@ export function watchMedia(query: string): Observable<boolean> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch print mode, cross-browser
|
||||
* Watch print mode
|
||||
*
|
||||
* @returns Print mode observable
|
||||
* @returns Print observable
|
||||
*/
|
||||
export function watchPrint(): Observable<void> {
|
||||
return fromEvent(window, "beforeprint")
|
||||
.pipe(
|
||||
mapTo(undefined)
|
||||
export function watchPrint(): Observable<boolean> {
|
||||
return merge(
|
||||
fromEvent(window, "beforeprint").pipe(mapTo(true)),
|
||||
fromEvent(window, "afterprint").pipe(mapTo(false))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -249,6 +249,6 @@ window.keyboard$ = keyboard$ /* Keyboard observable */
|
||||
window.viewport$ = viewport$ /* Viewport observable */
|
||||
window.tablet$ = tablet$ /* Tablet observable */
|
||||
window.screen$ = screen$ /* Screen observable */
|
||||
window.print$ = print$ /* Print mode observable */
|
||||
window.print$ = print$ /* Print observable */
|
||||
window.alert$ = alert$ /* Alert subject */
|
||||
window.component$ = component$ /* Component observable */
|
||||
|
@ -38,6 +38,7 @@ export type ComponentType =
|
||||
| "header-title" /* Header title */
|
||||
| "header-topic" /* Header topic */
|
||||
| "main" /* Main area */
|
||||
| "outdated" /* Version warning */
|
||||
| "palette" /* Color palette */
|
||||
| "search" /* Search */
|
||||
| "search-query" /* Search input */
|
||||
@ -81,6 +82,7 @@ interface ComponentTypeMap {
|
||||
"header-title": HTMLElement /* Header title */
|
||||
"header-topic": HTMLElement /* Header topic */
|
||||
"main": HTMLElement /* Main area */
|
||||
"outdated": HTMLElement /* Version warning */
|
||||
"palette": HTMLElement /* Color palette */
|
||||
"search": HTMLElement /* Search */
|
||||
"search-query": HTMLInputElement /* Search input */
|
||||
|
@ -53,7 +53,7 @@ export type Content =
|
||||
interface MountOptions {
|
||||
target$: Observable<HTMLElement> /* Location target observable */
|
||||
viewport$: Observable<Viewport> /* Viewport observable */
|
||||
print$: Observable<void> /* Print mode observable */
|
||||
print$: Observable<boolean> /* Print observable */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
@ -78,7 +78,7 @@ export function mountContent(
|
||||
|
||||
/* Code blocks */
|
||||
...getElements("pre > code", el)
|
||||
.map(child => mountCodeBlock(child, { viewport$ })),
|
||||
.map(child => mountCodeBlock(child, { viewport$, print$ })),
|
||||
|
||||
/* Data tables */
|
||||
...getElements("table:not([class])", el)
|
||||
|
@ -30,23 +30,33 @@ import {
|
||||
of
|
||||
} from "rxjs"
|
||||
import {
|
||||
combineLatestWith,
|
||||
distinctUntilKeyChanged,
|
||||
finalize,
|
||||
map,
|
||||
mergeWith,
|
||||
switchMap,
|
||||
take,
|
||||
takeWhile,
|
||||
tap,
|
||||
withLatestFrom
|
||||
} from "rxjs/operators"
|
||||
|
||||
import { feature } from "~/_"
|
||||
import { resetFocusable, setFocusable } from "~/actions"
|
||||
import {
|
||||
Viewport,
|
||||
getElement,
|
||||
getElementContentSize,
|
||||
getElementOrThrow,
|
||||
getElementSize,
|
||||
getElements,
|
||||
watchMedia
|
||||
} from "~/browser"
|
||||
import { renderClipboardButton } from "~/templates"
|
||||
import {
|
||||
renderAnnotation,
|
||||
renderClipboardButton
|
||||
} from "~/templates"
|
||||
|
||||
import { Component } from "../../_"
|
||||
|
||||
@ -59,6 +69,7 @@ import { Component } from "../../_"
|
||||
*/
|
||||
export interface CodeBlock {
|
||||
scroll: boolean /* Code block overflows */
|
||||
annotations?: HTMLElement[] /* Code block annotations */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
@ -70,6 +81,7 @@ export interface CodeBlock {
|
||||
*/
|
||||
interface WatchOptions {
|
||||
viewport$: Observable<Viewport> /* Viewport observable */
|
||||
print$: Observable<boolean> /* Print observable */
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,6 +89,7 @@ interface WatchOptions {
|
||||
*/
|
||||
interface MountOptions {
|
||||
viewport$: Observable<Viewport> /* Viewport observable */
|
||||
print$: Observable<boolean> /* Print observable */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
@ -104,7 +117,7 @@ let index = 0
|
||||
* @returns Code block observable
|
||||
*/
|
||||
export function watchCodeBlock(
|
||||
el: HTMLElement, { viewport$ }: WatchOptions
|
||||
el: HTMLElement, { viewport$, print$ }: WatchOptions
|
||||
): Observable<CodeBlock> {
|
||||
const container$ = of(el)
|
||||
.pipe(
|
||||
@ -112,7 +125,7 @@ export function watchCodeBlock(
|
||||
const container = child.closest("[data-tabs]")
|
||||
if (container instanceof HTMLElement) {
|
||||
return merge(
|
||||
...getElements("input", container)
|
||||
...getElements(":scope > input", container)
|
||||
.map(input => fromEvent(input, "change"))
|
||||
)
|
||||
}
|
||||
@ -120,17 +133,76 @@ export function watchCodeBlock(
|
||||
})
|
||||
)
|
||||
|
||||
/* Transform annotations */
|
||||
const annotations: HTMLElement[] = []
|
||||
const container =
|
||||
el.closest(".highlighttable") ||
|
||||
el.closest(".highlight")
|
||||
if (container) {
|
||||
const list = container.nextElementSibling
|
||||
if (list instanceof HTMLOListElement && (
|
||||
container.classList.contains("annotate") ||
|
||||
feature("content.code.annotate")
|
||||
)) {
|
||||
const items = Array.from(list.children)
|
||||
list.remove()
|
||||
|
||||
/* Replace comments with annotations */
|
||||
for (const comment of getElements(".c, .c1, .cm", el)) {
|
||||
|
||||
/* Split comment at annotations */ // TODO: refactor when revisiting annotations
|
||||
let match: RegExpExecArray | null
|
||||
let text = comment.firstChild as Text
|
||||
do {
|
||||
match = /\((\d+)\)/.exec(text.textContent!)
|
||||
if (match && match.index) {
|
||||
const bubble = text.splitText(match.index)
|
||||
text = bubble.splitText(match[0].length) // complete match length
|
||||
|
||||
const [, j = -1] = match
|
||||
const content = items[+j - 1]
|
||||
if (typeof content !== "undefined") {
|
||||
const annotation = renderAnnotation(+j, content.childNodes)
|
||||
bubble.replaceWith(annotation)
|
||||
annotations.push(annotation)
|
||||
}
|
||||
}
|
||||
} while (match)
|
||||
}
|
||||
|
||||
/* Move elements back on print */ // TODO: refactor memleak (instant loading)
|
||||
print$.subscribe(active => {
|
||||
if (active) {
|
||||
container.insertAdjacentElement("afterend", list)
|
||||
for (const annotation of annotations) {
|
||||
const id = parseInt(annotation.getAttribute("data-index")!, 10)
|
||||
const typeset = getElement(":scope .md-typeset", annotation)!
|
||||
items[id - 1].append(...Array.from(typeset.childNodes))
|
||||
}
|
||||
} else {
|
||||
list.remove()
|
||||
for (const annotation of annotations) {
|
||||
const id = parseInt(annotation.getAttribute("data-index")!, 10)
|
||||
const nodes = items[id - 1].childNodes
|
||||
getElementOrThrow(":scope .md-typeset", annotation)
|
||||
.append(...Array.from(nodes))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/* Check overflow on resize and tab change */
|
||||
return merge(
|
||||
viewport$.pipe(distinctUntilKeyChanged("size")),
|
||||
container$
|
||||
)
|
||||
return viewport$
|
||||
.pipe(
|
||||
distinctUntilKeyChanged("size"),
|
||||
mergeWith(container$),
|
||||
map(() => {
|
||||
const visible = getElementSize(el)
|
||||
const content = getElementContentSize(el)
|
||||
return {
|
||||
scroll: content.width > visible.width
|
||||
scroll: content.width > visible.width,
|
||||
...annotations.length && { annotations }
|
||||
}
|
||||
}),
|
||||
distinctUntilKeyChanged("scroll")
|
||||
@ -163,10 +235,34 @@ export function mountCodeBlock(
|
||||
resetFocusable(el)
|
||||
})
|
||||
|
||||
/* Compute annotation position */
|
||||
internal$
|
||||
.pipe(
|
||||
take(1),
|
||||
takeWhile(({ annotations }) => !!annotations?.length),
|
||||
map(({ annotations }) => annotations!
|
||||
.map(annotation => getElementOrThrow(".md-tooltip", annotation))
|
||||
),
|
||||
combineLatestWith(viewport$
|
||||
.pipe(
|
||||
distinctUntilKeyChanged("size")
|
||||
)
|
||||
)
|
||||
)
|
||||
.subscribe(([tooltips, { size }]) => {
|
||||
for (const tooltip of tooltips) {
|
||||
const { x, width } = tooltip.getBoundingClientRect()
|
||||
if (x + width > size.width)
|
||||
tooltip.classList.add("md-tooltip--end")
|
||||
else
|
||||
tooltip.classList.remove("md-tooltip--end")
|
||||
}
|
||||
})
|
||||
|
||||
/* Render button for Clipboard.js integration */
|
||||
if (ClipboardJS.isSupported()) {
|
||||
const parent = el.closest("pre")!
|
||||
parent.id = `__code_${index++}`
|
||||
parent.id = `__code_${++index}`
|
||||
parent.insertBefore(
|
||||
renderClipboardButton(parent.id),
|
||||
el
|
||||
|
@ -20,13 +20,12 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { Observable, Subject } from "rxjs"
|
||||
import { Observable, Subject, merge } from "rxjs"
|
||||
import {
|
||||
filter,
|
||||
finalize,
|
||||
map,
|
||||
mapTo,
|
||||
mergeWith,
|
||||
tap
|
||||
} from "rxjs/operators"
|
||||
|
||||
@ -40,6 +39,7 @@ import { Component } from "../../_"
|
||||
* Details
|
||||
*/
|
||||
export interface Details {
|
||||
action: "open" | "close" /* Action */
|
||||
scroll?: boolean /* Scroll into view */
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ export interface Details {
|
||||
*/
|
||||
interface WatchOptions {
|
||||
target$: Observable<HTMLElement> /* Location target observable */
|
||||
print$: Observable<void> /* Print mode observable */
|
||||
print$: Observable<boolean> /* Print observable */
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,7 +60,7 @@ interface WatchOptions {
|
||||
*/
|
||||
interface MountOptions {
|
||||
target$: Observable<HTMLElement> /* Location target observable */
|
||||
print$: Observable<void> /* Print mode observable */
|
||||
print$: Observable<boolean> /* Print observable */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
@ -78,12 +78,26 @@ interface MountOptions {
|
||||
export function watchDetails(
|
||||
el: HTMLDetailsElement, { target$, print$ }: WatchOptions
|
||||
): Observable<Details> {
|
||||
return target$
|
||||
let open = false
|
||||
return merge(
|
||||
|
||||
/* Open and focus details on location target */
|
||||
target$
|
||||
.pipe(
|
||||
map(target => target.closest("details:not([open])")!),
|
||||
filter(details => el === details),
|
||||
mapTo({ scroll: true }),
|
||||
mergeWith(print$.pipe(mapTo({})))
|
||||
mapTo<Details>({ action: "open", scroll: true })
|
||||
),
|
||||
|
||||
/* 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)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@ -102,8 +116,11 @@ export function mountDetails(
|
||||
el: HTMLDetailsElement, options: MountOptions
|
||||
): Observable<Component<Details>> {
|
||||
const internal$ = new Subject<Details>()
|
||||
internal$.subscribe(({ scroll }) => {
|
||||
internal$.subscribe(({ action, scroll }) => {
|
||||
if (action === "open")
|
||||
el.setAttribute("open", "")
|
||||
else
|
||||
el.removeAttribute("open")
|
||||
if (scroll)
|
||||
el.scrollIntoView()
|
||||
})
|
||||
@ -113,6 +130,6 @@ export function mountDetails(
|
||||
.pipe(
|
||||
tap(state => internal$.next(state)),
|
||||
finalize(() => internal$.complete()),
|
||||
mapTo({ ref: el })
|
||||
map(state => ({ ref: el, ...state }))
|
||||
)
|
||||
}
|
||||
|
@ -75,8 +75,7 @@ export interface Palette {
|
||||
export function watchPalette(
|
||||
inputs: HTMLInputElement[]
|
||||
): Observable<Palette> {
|
||||
const data = localStorage.getItem(__prefix("__palette"))!
|
||||
const current = JSON.parse(data) || {
|
||||
const current = __md_get<Palette>("__palette") || {
|
||||
index: inputs.findIndex(input => (
|
||||
matchMedia(input.getAttribute("data-md-color-media")!).matches
|
||||
))
|
||||
@ -104,7 +103,7 @@ export function watchPalette(
|
||||
|
||||
/* Persist preference in local storage */
|
||||
palette$.subscribe(palette => {
|
||||
localStorage.setItem(__prefix("__palette"), JSON.stringify(palette))
|
||||
__md_set("__palette", palette)
|
||||
})
|
||||
|
||||
/* Return palette */
|
||||
|
@ -74,22 +74,14 @@ export function watchSource(
|
||||
el: HTMLAnchorElement
|
||||
): Observable<Source> {
|
||||
return fetch$ ||= defer(() => {
|
||||
const data = sessionStorage.getItem(__prefix("__source"))
|
||||
if (data) {
|
||||
return of<SourceFacts>(JSON.parse(data))
|
||||
} else {
|
||||
const value$ = fetchSourceFacts(el.href)
|
||||
value$.subscribe(value => {
|
||||
try {
|
||||
sessionStorage.setItem(__prefix("__source"), JSON.stringify(value))
|
||||
} catch (err) {
|
||||
/* Uncritical, just swallow */
|
||||
}
|
||||
})
|
||||
|
||||
/* Return value */
|
||||
return value$
|
||||
}
|
||||
const cached = __md_get<SourceFacts>("__source", sessionStorage)
|
||||
if (cached)
|
||||
return of(cached)
|
||||
else
|
||||
return fetchSourceFacts(el.href)
|
||||
.pipe(
|
||||
tap(facts => __md_set("__source", facts, sessionStorage))
|
||||
)
|
||||
})
|
||||
.pipe(
|
||||
catchError(() => NEVER),
|
||||
|
@ -24,7 +24,9 @@ import {
|
||||
Observable,
|
||||
Subject,
|
||||
animationFrameScheduler,
|
||||
combineLatest
|
||||
combineLatest,
|
||||
defer,
|
||||
of
|
||||
} from "rxjs"
|
||||
import {
|
||||
bufferCount,
|
||||
@ -39,6 +41,7 @@ import {
|
||||
tap
|
||||
} from "rxjs/operators"
|
||||
|
||||
import { feature } from "~/_"
|
||||
import {
|
||||
resetAnchorActive,
|
||||
resetAnchorState,
|
||||
@ -49,6 +52,7 @@ import {
|
||||
Viewport,
|
||||
getElement,
|
||||
getElements,
|
||||
getLocation,
|
||||
watchElementSize
|
||||
} from "~/browser"
|
||||
|
||||
@ -106,15 +110,18 @@ interface MountOptions {
|
||||
*
|
||||
* Note that the current anchor is the last item of the `prev` anchor list.
|
||||
*
|
||||
* @param anchors - Anchor elements
|
||||
* @param el - Table of contents element
|
||||
* @param options - Options
|
||||
*
|
||||
* @returns Table of contents observable
|
||||
*/
|
||||
export function watchTableOfContents(
|
||||
anchors: HTMLAnchorElement[], { viewport$, header$ }: WatchOptions
|
||||
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 = getElement(`[id="${id}"]`)
|
||||
@ -134,9 +141,9 @@ export function watchTableOfContents(
|
||||
distinctUntilKeyChanged("height"),
|
||||
|
||||
/* Build index to map anchor paths to vertical offsets */
|
||||
map(() => {
|
||||
switchMap(body => defer(() => {
|
||||
let path: HTMLAnchorElement[] = []
|
||||
return [...table].reduce((index, [anchor, target]) => {
|
||||
return of([...table].reduce((index, [anchor, target]) => {
|
||||
while (path.length) {
|
||||
const last = table.get(path[path.length - 1])!
|
||||
if (last.tagName >= target.tagName) {
|
||||
@ -158,21 +165,23 @@ export function watchTableOfContents(
|
||||
[...path = [...path, anchor]].reverse(),
|
||||
offset
|
||||
)
|
||||
}, new Map<HTMLAnchorElement[], number>())
|
||||
}),
|
||||
}, 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))),
|
||||
|
||||
/* Re-compute partition when viewport offset changes */
|
||||
switchMap(index => combineLatest([adjust$, viewport$])
|
||||
switchMap(index => combineLatest([viewport$, adjust$])
|
||||
.pipe(
|
||||
scan(([prev, next], [adjust, { offset: { y } }]) => {
|
||||
scan(([prev, next], [{ offset: { y }, size }, adjust]) => {
|
||||
const last = y + size.height >= Math.floor(body.height)
|
||||
|
||||
/* Look forward */
|
||||
while (next.length) {
|
||||
const [, offset] = next[0]
|
||||
if (offset - adjust < y) {
|
||||
if (offset - adjust < y || last) {
|
||||
prev = [...prev, next.shift()!]
|
||||
} else {
|
||||
break
|
||||
@ -182,7 +191,7 @@ export function watchTableOfContents(
|
||||
/* Look backward */
|
||||
while (prev.length) {
|
||||
const [, offset] = prev[prev.length - 1]
|
||||
if (offset - adjust >= y) {
|
||||
if (offset - adjust >= y && !last) {
|
||||
next = [prev.pop()!, ...next]
|
||||
} else {
|
||||
break
|
||||
@ -199,6 +208,8 @@ export function watchTableOfContents(
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
/* Compute and return anchor list migrations */
|
||||
return partition$
|
||||
@ -236,7 +247,7 @@ export function watchTableOfContents(
|
||||
/**
|
||||
* Mount table of contents
|
||||
*
|
||||
* @param el - Anchor list element
|
||||
* @param el - Table of contents element
|
||||
* @param options - Options
|
||||
*
|
||||
* @returns Table of contents component observable
|
||||
@ -262,11 +273,31 @@ export function mountTableOfContents(
|
||||
setAnchorActive(anchor, index === prev.length - 1)
|
||||
setAnchorState(anchor, "blur")
|
||||
}
|
||||
|
||||
/* Set up anchor tracking, if enabled */
|
||||
if (feature("navigation.tracking")) {
|
||||
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 */
|
||||
const anchors = getElements<HTMLAnchorElement>("[href^=\\#]", el)
|
||||
return watchTableOfContents(anchors, options)
|
||||
return watchTableOfContents(el, options)
|
||||
.pipe(
|
||||
tap(state => internal$.next(state)),
|
||||
finalize(() => internal$.complete()),
|
||||
|
@ -24,6 +24,10 @@ import ClipboardJS from "clipboard"
|
||||
import { Observable, Subject } from "rxjs"
|
||||
|
||||
import { translation } from "~/_"
|
||||
import {
|
||||
getElementOrThrow,
|
||||
getElements
|
||||
} from "~/browser"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Helper types
|
||||
@ -36,6 +40,34 @@ interface SetupOptions {
|
||||
alert$: Subject<string> /* Alert subject */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Helper functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Extract text to copy
|
||||
*
|
||||
* This function hides annotations prior to extracting the text from the given
|
||||
* code block, so they're not included in the text that is copied to clipboard.
|
||||
*
|
||||
* @param el - HTML element
|
||||
*
|
||||
* @returns Extracted text
|
||||
*/
|
||||
function extract(el: HTMLElement): string {
|
||||
const annotations = getElements(".md-annotation", el)
|
||||
for (const annotation of annotations)
|
||||
annotation.hidden = true
|
||||
|
||||
/* Extract text and show annotations */
|
||||
const text = el.innerText
|
||||
for (const annotation of annotations)
|
||||
annotation.hidden = false
|
||||
|
||||
/* Return extracted text */
|
||||
return text
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
@ -50,7 +82,14 @@ export function setupClipboardJS(
|
||||
): void {
|
||||
if (ClipboardJS.isSupported()) {
|
||||
new Observable<ClipboardJS.Event>(subscriber => {
|
||||
new ClipboardJS("[data-clipboard-target], [data-clipboard-text]")
|
||||
new ClipboardJS("[data-clipboard-target], [data-clipboard-text]", {
|
||||
text: el => (
|
||||
el.getAttribute("data-clipboard-text")! ||
|
||||
extract(getElementOrThrow(
|
||||
el.getAttribute("data-clipboard-target")!
|
||||
))
|
||||
)
|
||||
})
|
||||
.on("success", ev => subscriber.next(ev))
|
||||
})
|
||||
.subscribe(() => alert$.next(translation("clipboard.copied")))
|
||||
|
5
src/assets/javascripts/integrations/version/.eslintrc
Normal file
5
src/assets/javascripts/integrations/version/.eslintrc
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"rules": {
|
||||
"no-null/no-null": "off"
|
||||
}
|
||||
}
|
@ -20,9 +20,19 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { combineLatest } from "rxjs"
|
||||
import { map } from "rxjs/operators"
|
||||
|
||||
import { configuration } from "~/_"
|
||||
import { getElementOrThrow, requestJSON } from "~/browser"
|
||||
import { Version, renderVersionSelector } from "~/templates"
|
||||
import {
|
||||
getElementOrThrow,
|
||||
requestJSON,
|
||||
} from "~/browser"
|
||||
import { getComponentElements } from "~/components"
|
||||
import {
|
||||
Version,
|
||||
renderVersionSelector
|
||||
} from "~/templates"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
@ -33,9 +43,37 @@ import { Version, renderVersionSelector } from "~/templates"
|
||||
*/
|
||||
export function setupVersionSelector(): void {
|
||||
const config = configuration()
|
||||
requestJSON<Version[]>(new URL("../versions.json", config.base))
|
||||
.subscribe(versions => {
|
||||
const versions$ = requestJSON<Version[]>(
|
||||
new URL("../versions.json", config.base)
|
||||
)
|
||||
|
||||
/* Determine current version */
|
||||
const current$ = versions$
|
||||
.pipe(
|
||||
map(versions => {
|
||||
const [, current] = config.base.match(/([^/]+)\/?$/)!
|
||||
return versions.find(({ version, aliases }) => (
|
||||
version === current || aliases.includes(current)
|
||||
)) || versions[0]
|
||||
})
|
||||
)
|
||||
|
||||
/* Render version selector and warning */
|
||||
combineLatest([versions$, current$])
|
||||
.subscribe(([versions, current]) => {
|
||||
const topic = getElementOrThrow(".md-header__topic")
|
||||
topic.appendChild(renderVersionSelector(versions))
|
||||
topic.appendChild(renderVersionSelector(versions, current))
|
||||
|
||||
/* Check if version state was already determined */
|
||||
if (__md_get("__outdated", sessionStorage) === null) {
|
||||
const latest = config.version?.default || "latest"
|
||||
const outdated = !current.aliases.includes(latest)
|
||||
|
||||
/* Persist version state in session storage */
|
||||
__md_set("__outdated", outdated, sessionStorage)
|
||||
if (outdated)
|
||||
for (const warning of getComponentElements("outdated"))
|
||||
warning.hidden = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
50
src/assets/javascripts/templates/code/index.tsx
Normal file
50
src/assets/javascripts/templates/code/index.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { h } from "~/utilities"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Render a 'copy-to-clipboard' button
|
||||
*
|
||||
* @param id - Unique identifier
|
||||
* @param content - Annotation content
|
||||
*
|
||||
* @returns Element
|
||||
*/
|
||||
export function renderAnnotation(
|
||||
id: number, content: NodeListOf<ChildNode>
|
||||
): HTMLElement {
|
||||
return (
|
||||
<aside class="md-annotation" data-index={id} tabIndex={0}>
|
||||
<div class="md-tooltip">
|
||||
<div class="md-tooltip__inner md-typeset">
|
||||
{...Array.from(content)}
|
||||
</div>
|
||||
</div>
|
||||
<span class="md-annotation__index">{id}</span>
|
||||
</aside>
|
||||
)
|
||||
}
|
@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
export * from "./clipboard"
|
||||
export * from "./code"
|
||||
export * from "./search"
|
||||
export * from "./source"
|
||||
export * from "./table"
|
||||
|
@ -69,20 +69,13 @@ function renderVersion(version: Version): HTMLElement {
|
||||
* Render a version selector
|
||||
*
|
||||
* @param versions - Versions
|
||||
* @param active - Active version
|
||||
*
|
||||
* @returns Element
|
||||
*/
|
||||
export function renderVersionSelector(versions: Version[]): HTMLElement {
|
||||
const config = configuration()
|
||||
|
||||
/* Determine active version */
|
||||
const [, current] = config.base.match(/([^/]+)\/?$/)!
|
||||
const active =
|
||||
versions.find(({ version, aliases }) => (
|
||||
version === current || aliases.includes(current)
|
||||
)) || versions[0]
|
||||
|
||||
/* Render version selector */
|
||||
export function renderVersionSelector(
|
||||
versions: Version[], active: Version
|
||||
): HTMLElement {
|
||||
return (
|
||||
<div class="md-version">
|
||||
<button
|
||||
|
@ -42,7 +42,7 @@
|
||||
@import "main/typeset";
|
||||
|
||||
@import "main/layout/base";
|
||||
@import "main/layout/announce";
|
||||
@import "main/layout/banner";
|
||||
@import "main/layout/clipboard";
|
||||
@import "main/layout/content";
|
||||
@import "main/layout/dialog";
|
||||
@ -55,6 +55,7 @@
|
||||
@import "main/layout/sidebar";
|
||||
@import "main/layout/source";
|
||||
@import "main/layout/tabs";
|
||||
@import "main/layout/tooltip";
|
||||
@import "main/layout/top";
|
||||
@import "main/layout/version";
|
||||
|
||||
|
@ -62,9 +62,9 @@ $admonitions: (
|
||||
|
||||
// Admonition
|
||||
.admonition {
|
||||
display: flow-root;
|
||||
margin: px2em(20px, 12.8px) 0;
|
||||
padding: 0 px2rem(12px);
|
||||
overflow: hidden;
|
||||
color: var(--md-admonition-fg-color);
|
||||
font-size: px2rem(12.8px);
|
||||
page-break-inside: avoid;
|
||||
@ -121,6 +121,7 @@ $admonitions: (
|
||||
font-weight: 700;
|
||||
background-color: color.adjust($clr-blue-a200, $alpha: -0.9);
|
||||
border-left: px2rem(4px) solid $clr-blue-a200;
|
||||
border-top-left-radius: px2rem(2px);
|
||||
|
||||
// Adjust for right-to-left languages
|
||||
[dir="rtl"] & {
|
||||
|
@ -60,12 +60,6 @@
|
||||
border-radius: px2rem(2px);
|
||||
}
|
||||
}
|
||||
|
||||
// Hack: omit margin collapse
|
||||
&::after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
|
||||
// Details title
|
||||
|
@ -173,6 +173,9 @@
|
||||
[data-linenos]::before {
|
||||
position: sticky;
|
||||
left: px2em(-16px, 13.6px);
|
||||
// A `z-index` of 3 is necessary for ensuring that code block annotations
|
||||
// don't overlay line numbers, as active annotations have a `z-index` of 2.
|
||||
z-index: 3;
|
||||
float: left;
|
||||
margin-right: px2em(16px, 13.6px);
|
||||
margin-left: px2em(-16px, 13.6px);
|
||||
@ -192,7 +195,6 @@
|
||||
// Code block with line numbers
|
||||
.highlighttable {
|
||||
display: flow-root;
|
||||
overflow: hidden;
|
||||
|
||||
// Set table elements to block layout, because otherwise the whole flexbox
|
||||
// hacking won't work correctly
|
||||
@ -246,7 +248,7 @@
|
||||
// Code block container - stretch to remaining space
|
||||
.code {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,11 +40,11 @@
|
||||
order: initial;
|
||||
}
|
||||
|
||||
// Code block is the only child of a tab - remove margin and mirror
|
||||
// Code block is the first child of a tab - remove margin and mirror
|
||||
// previous (now deprecated) SuperFences code block grouping behavior
|
||||
> pre:only-child,
|
||||
> .highlight:only-child pre,
|
||||
> .highlighttable:only-child {
|
||||
> pre:first-child,
|
||||
> .highlight:first-child pre,
|
||||
> .highlighttable:first-child {
|
||||
margin: 0;
|
||||
|
||||
// Omit rounded borders
|
||||
@ -114,6 +114,12 @@
|
||||
cursor: pointer;
|
||||
transition: color 250ms;
|
||||
|
||||
// Hack: omit flickering of content tabs label on initial page load when
|
||||
// using linked content tabs.
|
||||
.no-js & {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
// Tab label on hover
|
||||
&:hover {
|
||||
color: var(--md-accent-fg-color);
|
||||
@ -276,11 +282,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Code block is the only child of a tab - remove margin and mirror
|
||||
// Code block is the first child of a tab - remove margin and mirror
|
||||
// previous (now deprecated) SuperFences code block grouping behavior
|
||||
> pre:only-child,
|
||||
> .highlight:only-child pre,
|
||||
> .highlighttable:only-child {
|
||||
> pre:first-child,
|
||||
> .highlight:first-child pre,
|
||||
> .highlighttable:first-child {
|
||||
margin: 0;
|
||||
|
||||
// Omit rounded borders
|
||||
|
@ -24,21 +24,27 @@
|
||||
// Rules
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Announcement bar
|
||||
.md-announce {
|
||||
// Banner for announcements and warnings
|
||||
.md-banner {
|
||||
overflow: auto;
|
||||
color: var(--md-footer-fg-color);
|
||||
background-color: var(--md-footer-bg-color);
|
||||
|
||||
// [print]: Hide announcement bar
|
||||
// [print]: Hide banner
|
||||
@media print {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// Announcement wrapper
|
||||
// Banner with warning
|
||||
&--warning {
|
||||
color: var(--md-default-fg-color);
|
||||
background: var(--md-typeset-mark-color);
|
||||
}
|
||||
|
||||
// Banner wrapper
|
||||
&__inner {
|
||||
margin: px2rem(12px) auto;
|
||||
padding: 0 px2rem(16px);
|
||||
color: var(--md-footer-fg-color);
|
||||
font-size: px2rem(14px);
|
||||
}
|
||||
}
|
@ -27,13 +27,10 @@
|
||||
// Content area
|
||||
.md-content {
|
||||
flex-grow: 1;
|
||||
// Hack: we must use `overflow: hidden`, so the content area is capped by
|
||||
// the dimensions of its parent. Otherwise, long code blocks might lead to
|
||||
// a wider content area which will break everything. This, however, induces
|
||||
// margin collapse, which will break scroll margins. Adding a large enough
|
||||
// scroll padding seems to do the trick, at least in Chrome and Firefox.
|
||||
overflow: hidden;
|
||||
scroll-padding-top: px2rem(1024px);
|
||||
// Hack: we must use `min-width: 0`, so the content area is capped by the
|
||||
// dimensions of its parent. Otherwise, long code blocks might lead to a
|
||||
// wider content area which will overflow. See https://bit.ly/3bP3f8k
|
||||
min-width: 0;
|
||||
|
||||
// Content wrapper
|
||||
&__inner {
|
||||
|
@ -32,7 +32,7 @@
|
||||
right: px2rem(16px);
|
||||
bottom: px2rem(16px);
|
||||
left: initial;
|
||||
z-index: 3;
|
||||
z-index: 4;
|
||||
min-width: px2rem(222px);
|
||||
padding: px2rem(8px) px2rem(12px);
|
||||
background-color: var(--md-default-fg-color);
|
||||
|
@ -31,7 +31,7 @@
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 3;
|
||||
z-index: 4;
|
||||
color: var(--md-primary-bg-color);
|
||||
background-color: var(--md-primary-fg-color);
|
||||
// Hack: reduce jitter by adding a transparent box shadow of the same size
|
||||
|
@ -46,7 +46,7 @@
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: px2rem(-242px);
|
||||
z-index: 4;
|
||||
z-index: 5;
|
||||
display: block;
|
||||
width: px2rem(242px);
|
||||
height: 100%;
|
||||
@ -163,11 +163,11 @@
|
||||
// [tablet -]: Show overlay on active drawer
|
||||
@include break-to-device(tablet) {
|
||||
|
||||
// Sidebar overlay
|
||||
// Drawer overlay
|
||||
.md-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 4;
|
||||
z-index: 5;
|
||||
width: 0;
|
||||
height: 0;
|
||||
background-color: hsla(0, 0%, 0%, 0.54);
|
||||
|
208
src/assets/stylesheets/main/layout/_tooltip.scss
Normal file
208
src/assets/stylesheets/main/layout/_tooltip.scss
Normal file
@ -0,0 +1,208 @@
|
||||
////
|
||||
/// 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
|
||||
////
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Keyframes
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Continuous pulse animation
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 var(--md-default-fg-color--lightest);
|
||||
}
|
||||
|
||||
75% {
|
||||
box-shadow: 0 0 0 px2em(10px) transparent;
|
||||
}
|
||||
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 transparent;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Rules
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Tooltip
|
||||
.md-tooltip {
|
||||
position: absolute;
|
||||
// Hack: set an explicit `z-index` so we can transition it to ensure that any
|
||||
// following elements are not overlaying the tooltip during the transition.
|
||||
z-index: 0;
|
||||
max-height: 0;
|
||||
overflow: auto;
|
||||
color: var(--md-default-fg-color);
|
||||
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 0 px2rem(1px) hsla(0, 0%, 0%, 0.25);
|
||||
transform: translateY(px2rem(8px));
|
||||
// Hack: promote to own layer to reduce jitter
|
||||
backface-visibility: hidden;
|
||||
opacity: 0;
|
||||
transition:
|
||||
transform 250ms 375ms,
|
||||
opacity 250ms,
|
||||
max-height 0ms 250ms,
|
||||
z-index 250ms;
|
||||
|
||||
// Disable animation for motion reduction preference
|
||||
@media (prefers-reduced-motion) {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
// Tooltip wrapper
|
||||
&__inner {
|
||||
padding: px2rem(16px);
|
||||
font-size: px2rem(12.8px);
|
||||
|
||||
// Adjust spacing on first child
|
||||
> :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
// Adjust spacing on last child
|
||||
> :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Tooltip on parent focus
|
||||
:focus > &,
|
||||
:focus-within > & {
|
||||
max-height: 1000%;
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
transition:
|
||||
transform 250ms cubic-bezier(0.1, 0.7, 0.1, 1),
|
||||
opacity 250ms,
|
||||
max-height 250ms 0ms,
|
||||
z-index 0ms;
|
||||
|
||||
// Disable animation for motion reduction preference
|
||||
@media (prefers-reduced-motion) {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
// Modifier for end alignment
|
||||
&--end {
|
||||
transform: translate(-100%, 0);
|
||||
}
|
||||
|
||||
// Modifier for center alignment
|
||||
&--center {
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Show outline for keyboard devices
|
||||
.focus-visible > & {
|
||||
outline: var(--md-accent-fg-color) auto;
|
||||
}
|
||||
|
||||
// Modifier for end alignment
|
||||
&--end {
|
||||
transform: translate(-100%, px2rem(8px));
|
||||
}
|
||||
|
||||
// Modifier for center alignment
|
||||
&--center {
|
||||
transform: translate(-50%, px2rem(8px));
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Annotation
|
||||
.md-annotation {
|
||||
white-space: initial;
|
||||
outline: none;
|
||||
|
||||
// Promote children to top on focus
|
||||
&:focus-within > * {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
// Annotation is visible
|
||||
&:not([hidden]) {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
// Annotation index
|
||||
&__index {
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
display: inline-block;
|
||||
min-width: 1.4em;
|
||||
padding: 0 px2em(6px);
|
||||
color: var(--md-accent-bg-color);
|
||||
text-align: center;
|
||||
background-color: var(--md-default-fg-color--lighter);
|
||||
border-radius: px2em(20px);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background-color 250ms,
|
||||
z-index 250ms;
|
||||
animation: pulse 2000ms infinite;
|
||||
user-select: none;
|
||||
|
||||
// Disable animation for motion reduction preference
|
||||
@media (prefers-reduced-motion) {
|
||||
transition: none;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
// Annotation index on focus
|
||||
:focus-within > & {
|
||||
transition:
|
||||
background-color 250ms,
|
||||
z-index 0ms;
|
||||
animation: none;
|
||||
|
||||
// Disable animation for motion reduction preference
|
||||
@media (prefers-reduced-motion) {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Annotation index on focus/hover
|
||||
:focus-within > &,
|
||||
:hover > & {
|
||||
background-color: var(--md-accent-fg-color);
|
||||
}
|
||||
}
|
||||
|
||||
// Annotation tooltip
|
||||
.md-tooltip {
|
||||
min-width: px2rem(320px);
|
||||
max-width: 60%;
|
||||
margin: px2em(-16px, 13.6px) px2em(10px, 13.6px) 0;
|
||||
font-family: var(--md-text-font-family);
|
||||
|
||||
// Modifier for center alignment
|
||||
&--center {
|
||||
margin-top: px2em(10px, 13.6px);
|
||||
}
|
||||
}
|
||||
}
|
@ -125,6 +125,9 @@
|
||||
<link rel="stylesheet" href="{{ path | url }}" />
|
||||
{% endfor %}
|
||||
|
||||
<!-- Helper functions for inline scripts -->
|
||||
{% include "partials/javascripts/base.html" %}
|
||||
|
||||
<!-- Analytics -->
|
||||
{% block analytics %}
|
||||
{% include "partials/integrations/analytics.html" %}
|
||||
@ -156,7 +159,6 @@
|
||||
|
||||
<!-- Retrieve features from configuration -->
|
||||
{% set features = config.theme.features or [] %}
|
||||
{% include "partials/javascripts/base.html" %}
|
||||
|
||||
<!-- User preference: color palette -->
|
||||
{% if not config.theme.palette is mapping %}
|
||||
@ -206,6 +208,20 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Version warning -->
|
||||
{% if config.extra.version %}
|
||||
<div data-md-component="outdated" hidden>
|
||||
<aside class="md-banner md-banner--warning">
|
||||
{% if self.outdated() %}
|
||||
<div class="md-banner__inner md-grid md-typeset">
|
||||
{% block outdated %}{% endblock %}
|
||||
</div>
|
||||
{% include "partials/javascripts/outdated.html" %}
|
||||
{% endif %}
|
||||
</aside>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Header -->
|
||||
{% block header %}
|
||||
{% include "partials/header.html" %}
|
||||
@ -303,13 +319,12 @@
|
||||
{{ page.content }}
|
||||
|
||||
<!-- Last update of source file -->
|
||||
{% if page and page.meta %}
|
||||
{% if page.meta.git_revision_date_localized or
|
||||
{% if page and page.meta and (
|
||||
page.meta.git_revision_date_localized or
|
||||
page.meta.revision_date
|
||||
%}
|
||||
) %}
|
||||
{% include "partials/source-file.html" %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
<!-- Disqus integration -->
|
||||
|
@ -38,7 +38,7 @@
|
||||
|
||||
@import "main/typeset";
|
||||
|
||||
@import "main/layout/announce";
|
||||
@import "main/layout/banner";
|
||||
@import "main/layout/hero";
|
||||
@import "main/layout/iconsearch";
|
||||
@import "main/layout/sponsorship";
|
||||
|
@ -24,8 +24,8 @@
|
||||
// Rules
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Announcement bar
|
||||
.md-announce {
|
||||
// Banner for announcements and warnings
|
||||
.md-banner {
|
||||
|
||||
// Text link, also on focus/hover
|
||||
a,
|
@ -27,13 +27,26 @@
|
||||
<script>
|
||||
|
||||
/* Prepend the base path to the given key to ensure uniqueness */
|
||||
function __prefix(key) {
|
||||
var prefix = new URL("{{ base_url }}", location)
|
||||
function __md_scope(key, storage, base) {
|
||||
var prefix = new URL(base || (
|
||||
storage === localStorage
|
||||
? "{{ config.extra.scope | d(base_url) }}"
|
||||
: "{{ base_url }}"
|
||||
), location)
|
||||
return prefix.pathname + "." + key
|
||||
}
|
||||
|
||||
/* Fetch the given key from the given storage */
|
||||
function __get(key, storage = localStorage) {
|
||||
return JSON.parse(storage.getItem(__prefix(key)))
|
||||
/* Fetch the value for a key from the given storage */
|
||||
function __md_get(key, storage = localStorage, base) {
|
||||
return JSON.parse(storage.getItem(__md_scope(key, storage, base)))
|
||||
}
|
||||
|
||||
/* Persist a key-value pair in the given storage */
|
||||
function __md_set(key, value, storage = localStorage, base) {
|
||||
try {
|
||||
storage.setItem(__md_scope(key, storage, base), JSON.stringify(value))
|
||||
} catch (err) {
|
||||
/* Uncritical, just swallow */
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -22,8 +22,8 @@
|
||||
|
||||
<!-- User preference: color palette -->
|
||||
<script>
|
||||
var palette = __get("__palette")
|
||||
if (palette !== null && typeof palette.color === "object")
|
||||
for (var key in palette.color)
|
||||
document.body.setAttribute("data-md-color-" + key, palette.color[key])
|
||||
var palette = __md_get("__palette")
|
||||
if (palette && typeof palette.color === "object")
|
||||
for (var [key, value] of Object.entries(palette.color))
|
||||
document.body.setAttribute("data-md-color-" + key, value)
|
||||
</script>
|
||||
|
29
src/partials/outdated.html
Normal file
29
src/partials/outdated.html
Normal file
@ -0,0 +1,29 @@
|
||||
<!--
|
||||
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
|
||||
IN THE SOFTWARE.
|
||||
-->
|
||||
|
||||
<!-- Version warning -->
|
||||
<script>
|
||||
var el = document.querySelector("[data-md-component=outdated]")
|
||||
var outdated = __md_get("__outdated", sessionStorage)
|
||||
if (outdated === true && el)
|
||||
el.hidden = false
|
||||
</script>
|
@ -23,22 +23,24 @@
|
||||
{% import "partials/language.html" as lang with context %}
|
||||
|
||||
<!-- Last updated date -->
|
||||
{% set label = lang.t("source.file.date.updated") %}
|
||||
<hr />
|
||||
<div class="md-source-date">
|
||||
<small>
|
||||
|
||||
<!-- mkdocs-git-revision-date-localized-plugin -->
|
||||
{% if page.meta.git_revision_date_localized %}
|
||||
{{ label }}: {{ page.meta.git_revision_date_localized }}
|
||||
|
||||
{{ lang.t("source.file.date.updated") }}:
|
||||
{{ page.meta.git_revision_date_localized }}
|
||||
{% if page.meta.git_creation_date_localized %}
|
||||
<br />{{ lang.t("source.file.date.created") }}: {{ page.meta.git_creation_date_localized }}
|
||||
<br />
|
||||
{{ lang.t("source.file.date.created") }}:
|
||||
{{ page.meta.git_creation_date_localized }}
|
||||
{% endif %}
|
||||
|
||||
<!-- mkdocs-git-revision-date-plugin -->
|
||||
{% elif page.meta.revision_date %}
|
||||
{{ label }}: {{ page.meta.revision_date }}
|
||||
{{ lang.t("source.file.date.updated") }}:
|
||||
{{ page.meta.revision_date }}
|
||||
{% endif %}
|
||||
</small>
|
||||
</div>
|
||||
|
40
typings/_/index.d.ts
vendored
40
typings/_/index.d.ts
vendored
@ -52,13 +52,47 @@ declare global {
|
||||
const __search: GlobalSearchConfig | undefined
|
||||
|
||||
/**
|
||||
* Global function to prefix storage items
|
||||
* Fetch the value for a key from the given storage
|
||||
*
|
||||
* This function is defined in `partials/javascripts/base.html`, so it can be
|
||||
* used from the templates, as well as from the application bundle.
|
||||
*
|
||||
* @template T - Data type
|
||||
*
|
||||
* @param key - Key
|
||||
* @param storage - Storage (default: local storage)
|
||||
* @param base - Base URL (default: current base)
|
||||
*
|
||||
* @return Value or nothing
|
||||
*/
|
||||
function __prefix(key: string): string
|
||||
function __md_get<T>(
|
||||
key: string, storage?: Storage, base?: string
|
||||
): T | null
|
||||
|
||||
/**
|
||||
* Persist a key-value pair in the given storage
|
||||
*
|
||||
* This function is defined in `partials/javascripts/base.html`, so it can be
|
||||
* used from the templates, as well as from the application bundle.
|
||||
*
|
||||
* @template T - Data type
|
||||
*
|
||||
* @param key - Key
|
||||
* @param value - Value
|
||||
* @param storage - Storage (default: local storage)
|
||||
* @param base - Base URL (default: current base)
|
||||
*/
|
||||
function __md_set<T>(
|
||||
key: string, value: T, storage?: Storage, base?: string
|
||||
): void
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Google Analytics
|
||||
*/
|
||||
declare global {
|
||||
function ga(...args: string[]): void
|
||||
}
|
||||
|
||||
@ -74,7 +108,7 @@ declare global {
|
||||
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 print$: Observable<boolean> /* Print observable */
|
||||
var alert$: Subject<string> /* Alert subject */
|
||||
var component$: Observable<Component>/* Component observable */
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user