mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2024-06-14 11:52:32 +03: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]
|
[*.md]
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
# Makefiles
|
# Python
|
||||||
[*.py]
|
[*.py]
|
||||||
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
# Makefiles
|
|
||||||
[Makefile]
|
|
||||||
indent_style = tab
|
|
||||||
indent_size = 8
|
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -43,6 +43,7 @@
|
|||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
.cache
|
.cache
|
||||||
.eslintcache
|
.eslintcache
|
||||||
|
__pycache__
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# General
|
# General
|
||||||
|
@ -80,6 +80,7 @@
|
|||||||
"selector-class-pattern": null,
|
"selector-class-pattern": null,
|
||||||
"selector-combinator-space-before": null,
|
"selector-combinator-space-before": null,
|
||||||
"selector-descendant-combinator-no-non-space": null,
|
"selector-descendant-combinator-no-non-space": null,
|
||||||
|
"selector-id-pattern": null,
|
||||||
"selector-max-empty-lines": 0,
|
"selector-max-empty-lines": 0,
|
||||||
"selector-max-id": 0,
|
"selector-max-id": 0,
|
||||||
"selector-no-qualifying-type": null,
|
"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 %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block styles %}
|
{% 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 %}
|
{% if config.theme.palette %}
|
||||||
{% set palette = config.theme.palette %}
|
{% set palette = config.theme.palette %}
|
||||||
<link rel="stylesheet" href="{{ 'assets/stylesheets/palette.9204c3b2.min.css' | url }}">
|
<link rel="stylesheet" href="{{ 'assets/stylesheets/palette.9204c3b2.min.css' | url }}">
|
||||||
@ -62,6 +62,7 @@
|
|||||||
{% for path in config["extra_css"] %}
|
{% for path in config["extra_css"] %}
|
||||||
<link rel="stylesheet" href="{{ path | url }}">
|
<link rel="stylesheet" href="{{ path | url }}">
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% include "partials/javascripts/base.html" %}
|
||||||
{% block analytics %}
|
{% block analytics %}
|
||||||
{% include "partials/integrations/analytics.html" %}
|
{% include "partials/integrations/analytics.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -81,7 +82,6 @@
|
|||||||
<body dir="{{ direction }}">
|
<body dir="{{ direction }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% set features = config.theme.features or [] %}
|
{% set features = config.theme.features or [] %}
|
||||||
{% include "partials/javascripts/base.html" %}
|
|
||||||
{% if not config.theme.palette is mapping %}
|
{% if not config.theme.palette is mapping %}
|
||||||
{% include "partials/javascripts/palette.html" %}
|
{% include "partials/javascripts/palette.html" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -105,6 +105,18 @@
|
|||||||
</aside>
|
</aside>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</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 %}
|
{% block header %}
|
||||||
{% include "partials/header.html" %}
|
{% include "partials/header.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -157,13 +169,12 @@
|
|||||||
<h1>{{ page.title | d(config.site_name, true)}}</h1>
|
<h1>{{ page.title | d(config.site_name, true)}}</h1>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ page.content }}
|
{{ page.content }}
|
||||||
{% if page and page.meta %}
|
{% if page and page.meta and (
|
||||||
{% if page.meta.git_revision_date_localized or
|
page.meta.git_revision_date_localized or
|
||||||
page.meta.revision_date
|
page.meta.revision_date
|
||||||
%}
|
) %}
|
||||||
{% include "partials/source-file.html" %}
|
{% include "partials/source-file.html" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block disqus %}
|
{% block disqus %}
|
||||||
{% include "partials/integrations/disqus.html" %}
|
{% include "partials/integrations/disqus.html" %}
|
||||||
@ -217,7 +228,7 @@
|
|||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block scripts %}
|
{% 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"] %}
|
{% for path in config["extra_javascript"] %}
|
||||||
<script src="{{ path | url }}"></script>
|
<script src="{{ path | url }}"></script>
|
||||||
{% endfor %}
|
{% 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" %}
|
{% extends "base.html" %}
|
||||||
{% block extrahead %}
|
{% 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 %}
|
{% endblock %}
|
||||||
{% block announce %}
|
{% block announce %}
|
||||||
<a href="https://twitter.com/squidfunk">
|
<a href="https://twitter.com/squidfunk">
|
||||||
@ -16,5 +16,5 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
<script src="{{ 'overrides/assets/javascripts/bundle.525231ca.min.js' | url }}"></script>
|
<script src="{{ 'overrides/assets/javascripts/bundle.35fbbc46.min.js' | url }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{#-
|
{#-
|
||||||
This file was automatically generated - do not edit
|
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
|
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
|
This file was automatically generated - do not edit
|
||||||
-#}
|
-#}
|
||||||
{% import "partials/language.html" as lang with context %}
|
{% import "partials/language.html" as lang with context %}
|
||||||
{% set label = lang.t("source.file.date.updated") %}
|
|
||||||
<hr>
|
<hr>
|
||||||
<div class="md-source-date">
|
<div class="md-source-date">
|
||||||
<small>
|
<small>
|
||||||
{% if page.meta.git_revision_date_localized %}
|
{% 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 %}
|
{% 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 %}
|
{% endif %}
|
||||||
{% elif page.meta.revision_date %}
|
{% elif page.meta.revision_date %}
|
||||||
{{ label }}: {{ page.meta.revision_date }}
|
{{ lang.t("source.file.date.updated") }}:
|
||||||
|
{{ page.meta.revision_date }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
@ -30,6 +30,7 @@ import { getElementOrThrow, getLocation } from "~/browser"
|
|||||||
* Feature flag
|
* Feature flag
|
||||||
*/
|
*/
|
||||||
export type Flag =
|
export type Flag =
|
||||||
|
| "content.code.annotate" /* Code annotations */
|
||||||
| "header.autohide" /* Hide header */
|
| "header.autohide" /* Hide header */
|
||||||
| "navigation.expand" /* Automatic expansion */
|
| "navigation.expand" /* Automatic expansion */
|
||||||
| "navigation.instant" /* Instant loading */
|
| "navigation.instant" /* Instant loading */
|
||||||
@ -38,6 +39,7 @@ export type Flag =
|
|||||||
| "navigation.tabs" /* Tabs navigation */
|
| "navigation.tabs" /* Tabs navigation */
|
||||||
| "navigation.tabs.sticky" /* Tabs navigation (sticky) */
|
| "navigation.tabs.sticky" /* Tabs navigation (sticky) */
|
||||||
| "navigation.top" /* Back-to-top button */
|
| "navigation.top" /* Back-to-top button */
|
||||||
|
| "navigation.tracking" /* Anchor tracking */
|
||||||
| "search.highlight" /* Search highlighting */
|
| "search.highlight" /* Search highlighting */
|
||||||
| "search.share" /* Search sharing */
|
| "search.share" /* Search sharing */
|
||||||
| "search.suggest" /* Search suggestions */
|
| "search.suggest" /* Search suggestions */
|
||||||
@ -76,6 +78,7 @@ export type Translations = Record<Translation, string>
|
|||||||
*/
|
*/
|
||||||
export interface Versioning {
|
export interface Versioning {
|
||||||
provider: "mike" /* Version provider */
|
provider: "mike" /* Version provider */
|
||||||
|
default?: string /* Default version */
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,7 +24,8 @@ import {
|
|||||||
NEVER,
|
NEVER,
|
||||||
Observable,
|
Observable,
|
||||||
fromEvent,
|
fromEvent,
|
||||||
fromEventPattern
|
fromEventPattern,
|
||||||
|
merge
|
||||||
} from "rxjs"
|
} from "rxjs"
|
||||||
import {
|
import {
|
||||||
mapTo,
|
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> {
|
export function watchPrint(): Observable<boolean> {
|
||||||
return fromEvent(window, "beforeprint")
|
return merge(
|
||||||
.pipe(
|
fromEvent(window, "beforeprint").pipe(mapTo(true)),
|
||||||
mapTo(undefined)
|
fromEvent(window, "afterprint").pipe(mapTo(false))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,6 +249,6 @@ window.keyboard$ = keyboard$ /* Keyboard observable */
|
|||||||
window.viewport$ = viewport$ /* Viewport observable */
|
window.viewport$ = viewport$ /* Viewport observable */
|
||||||
window.tablet$ = tablet$ /* Tablet observable */
|
window.tablet$ = tablet$ /* Tablet observable */
|
||||||
window.screen$ = screen$ /* Screen observable */
|
window.screen$ = screen$ /* Screen observable */
|
||||||
window.print$ = print$ /* Print mode observable */
|
window.print$ = print$ /* Print observable */
|
||||||
window.alert$ = alert$ /* Alert subject */
|
window.alert$ = alert$ /* Alert subject */
|
||||||
window.component$ = component$ /* Component observable */
|
window.component$ = component$ /* Component observable */
|
||||||
|
@ -38,6 +38,7 @@ export type ComponentType =
|
|||||||
| "header-title" /* Header title */
|
| "header-title" /* Header title */
|
||||||
| "header-topic" /* Header topic */
|
| "header-topic" /* Header topic */
|
||||||
| "main" /* Main area */
|
| "main" /* Main area */
|
||||||
|
| "outdated" /* Version warning */
|
||||||
| "palette" /* Color palette */
|
| "palette" /* Color palette */
|
||||||
| "search" /* Search */
|
| "search" /* Search */
|
||||||
| "search-query" /* Search input */
|
| "search-query" /* Search input */
|
||||||
@ -81,6 +82,7 @@ interface ComponentTypeMap {
|
|||||||
"header-title": HTMLElement /* Header title */
|
"header-title": HTMLElement /* Header title */
|
||||||
"header-topic": HTMLElement /* Header topic */
|
"header-topic": HTMLElement /* Header topic */
|
||||||
"main": HTMLElement /* Main area */
|
"main": HTMLElement /* Main area */
|
||||||
|
"outdated": HTMLElement /* Version warning */
|
||||||
"palette": HTMLElement /* Color palette */
|
"palette": HTMLElement /* Color palette */
|
||||||
"search": HTMLElement /* Search */
|
"search": HTMLElement /* Search */
|
||||||
"search-query": HTMLInputElement /* Search input */
|
"search-query": HTMLInputElement /* Search input */
|
||||||
|
@ -53,7 +53,7 @@ export type Content =
|
|||||||
interface MountOptions {
|
interface MountOptions {
|
||||||
target$: Observable<HTMLElement> /* Location target observable */
|
target$: Observable<HTMLElement> /* Location target observable */
|
||||||
viewport$: Observable<Viewport> /* Viewport 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 */
|
/* Code blocks */
|
||||||
...getElements("pre > code", el)
|
...getElements("pre > code", el)
|
||||||
.map(child => mountCodeBlock(child, { viewport$ })),
|
.map(child => mountCodeBlock(child, { viewport$, print$ })),
|
||||||
|
|
||||||
/* Data tables */
|
/* Data tables */
|
||||||
...getElements("table:not([class])", el)
|
...getElements("table:not([class])", el)
|
||||||
|
@ -30,23 +30,33 @@ import {
|
|||||||
of
|
of
|
||||||
} from "rxjs"
|
} from "rxjs"
|
||||||
import {
|
import {
|
||||||
|
combineLatestWith,
|
||||||
distinctUntilKeyChanged,
|
distinctUntilKeyChanged,
|
||||||
finalize,
|
finalize,
|
||||||
map,
|
map,
|
||||||
|
mergeWith,
|
||||||
switchMap,
|
switchMap,
|
||||||
|
take,
|
||||||
|
takeWhile,
|
||||||
tap,
|
tap,
|
||||||
withLatestFrom
|
withLatestFrom
|
||||||
} from "rxjs/operators"
|
} from "rxjs/operators"
|
||||||
|
|
||||||
|
import { feature } from "~/_"
|
||||||
import { resetFocusable, setFocusable } from "~/actions"
|
import { resetFocusable, setFocusable } from "~/actions"
|
||||||
import {
|
import {
|
||||||
Viewport,
|
Viewport,
|
||||||
|
getElement,
|
||||||
getElementContentSize,
|
getElementContentSize,
|
||||||
|
getElementOrThrow,
|
||||||
getElementSize,
|
getElementSize,
|
||||||
getElements,
|
getElements,
|
||||||
watchMedia
|
watchMedia
|
||||||
} from "~/browser"
|
} from "~/browser"
|
||||||
import { renderClipboardButton } from "~/templates"
|
import {
|
||||||
|
renderAnnotation,
|
||||||
|
renderClipboardButton
|
||||||
|
} from "~/templates"
|
||||||
|
|
||||||
import { Component } from "../../_"
|
import { Component } from "../../_"
|
||||||
|
|
||||||
@ -59,6 +69,7 @@ import { Component } from "../../_"
|
|||||||
*/
|
*/
|
||||||
export interface CodeBlock {
|
export interface CodeBlock {
|
||||||
scroll: boolean /* Code block overflows */
|
scroll: boolean /* Code block overflows */
|
||||||
|
annotations?: HTMLElement[] /* Code block annotations */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
@ -70,6 +81,7 @@ export interface CodeBlock {
|
|||||||
*/
|
*/
|
||||||
interface WatchOptions {
|
interface WatchOptions {
|
||||||
viewport$: Observable<Viewport> /* Viewport observable */
|
viewport$: Observable<Viewport> /* Viewport observable */
|
||||||
|
print$: Observable<boolean> /* Print observable */
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -77,6 +89,7 @@ interface WatchOptions {
|
|||||||
*/
|
*/
|
||||||
interface MountOptions {
|
interface MountOptions {
|
||||||
viewport$: Observable<Viewport> /* Viewport observable */
|
viewport$: Observable<Viewport> /* Viewport observable */
|
||||||
|
print$: Observable<boolean> /* Print observable */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
@ -104,7 +117,7 @@ let index = 0
|
|||||||
* @returns Code block observable
|
* @returns Code block observable
|
||||||
*/
|
*/
|
||||||
export function watchCodeBlock(
|
export function watchCodeBlock(
|
||||||
el: HTMLElement, { viewport$ }: WatchOptions
|
el: HTMLElement, { viewport$, print$ }: WatchOptions
|
||||||
): Observable<CodeBlock> {
|
): Observable<CodeBlock> {
|
||||||
const container$ = of(el)
|
const container$ = of(el)
|
||||||
.pipe(
|
.pipe(
|
||||||
@ -112,7 +125,7 @@ export function watchCodeBlock(
|
|||||||
const container = child.closest("[data-tabs]")
|
const container = child.closest("[data-tabs]")
|
||||||
if (container instanceof HTMLElement) {
|
if (container instanceof HTMLElement) {
|
||||||
return merge(
|
return merge(
|
||||||
...getElements("input", container)
|
...getElements(":scope > input", container)
|
||||||
.map(input => fromEvent(input, "change"))
|
.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 */
|
/* Check overflow on resize and tab change */
|
||||||
return merge(
|
return viewport$
|
||||||
viewport$.pipe(distinctUntilKeyChanged("size")),
|
|
||||||
container$
|
|
||||||
)
|
|
||||||
.pipe(
|
.pipe(
|
||||||
|
distinctUntilKeyChanged("size"),
|
||||||
|
mergeWith(container$),
|
||||||
map(() => {
|
map(() => {
|
||||||
const visible = getElementSize(el)
|
const visible = getElementSize(el)
|
||||||
const content = getElementContentSize(el)
|
const content = getElementContentSize(el)
|
||||||
return {
|
return {
|
||||||
scroll: content.width > visible.width
|
scroll: content.width > visible.width,
|
||||||
|
...annotations.length && { annotations }
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
distinctUntilKeyChanged("scroll")
|
distinctUntilKeyChanged("scroll")
|
||||||
@ -163,10 +235,34 @@ export function mountCodeBlock(
|
|||||||
resetFocusable(el)
|
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 */
|
/* Render button for Clipboard.js integration */
|
||||||
if (ClipboardJS.isSupported()) {
|
if (ClipboardJS.isSupported()) {
|
||||||
const parent = el.closest("pre")!
|
const parent = el.closest("pre")!
|
||||||
parent.id = `__code_${index++}`
|
parent.id = `__code_${++index}`
|
||||||
parent.insertBefore(
|
parent.insertBefore(
|
||||||
renderClipboardButton(parent.id),
|
renderClipboardButton(parent.id),
|
||||||
el
|
el
|
||||||
|
@ -20,13 +20,12 @@
|
|||||||
* IN THE SOFTWARE.
|
* IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Observable, Subject } from "rxjs"
|
import { Observable, Subject, merge } from "rxjs"
|
||||||
import {
|
import {
|
||||||
filter,
|
filter,
|
||||||
finalize,
|
finalize,
|
||||||
map,
|
map,
|
||||||
mapTo,
|
mapTo,
|
||||||
mergeWith,
|
|
||||||
tap
|
tap
|
||||||
} from "rxjs/operators"
|
} from "rxjs/operators"
|
||||||
|
|
||||||
@ -40,6 +39,7 @@ import { Component } from "../../_"
|
|||||||
* Details
|
* Details
|
||||||
*/
|
*/
|
||||||
export interface Details {
|
export interface Details {
|
||||||
|
action: "open" | "close" /* Action */
|
||||||
scroll?: boolean /* Scroll into view */
|
scroll?: boolean /* Scroll into view */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ export interface Details {
|
|||||||
*/
|
*/
|
||||||
interface WatchOptions {
|
interface WatchOptions {
|
||||||
target$: Observable<HTMLElement> /* Location target observable */
|
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 {
|
interface MountOptions {
|
||||||
target$: Observable<HTMLElement> /* Location target observable */
|
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(
|
export function watchDetails(
|
||||||
el: HTMLDetailsElement, { target$, print$ }: WatchOptions
|
el: HTMLDetailsElement, { target$, print$ }: WatchOptions
|
||||||
): Observable<Details> {
|
): Observable<Details> {
|
||||||
return target$
|
let open = false
|
||||||
|
return merge(
|
||||||
|
|
||||||
|
/* Open and focus details on location target */
|
||||||
|
target$
|
||||||
.pipe(
|
.pipe(
|
||||||
map(target => target.closest("details:not([open])")!),
|
map(target => target.closest("details:not([open])")!),
|
||||||
filter(details => el === details),
|
filter(details => el === details),
|
||||||
mapTo({ scroll: true }),
|
mapTo<Details>({ action: "open", scroll: true })
|
||||||
mergeWith(print$.pipe(mapTo({})))
|
),
|
||||||
|
|
||||||
|
/* 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
|
el: HTMLDetailsElement, options: MountOptions
|
||||||
): Observable<Component<Details>> {
|
): Observable<Component<Details>> {
|
||||||
const internal$ = new Subject<Details>()
|
const internal$ = new Subject<Details>()
|
||||||
internal$.subscribe(({ scroll }) => {
|
internal$.subscribe(({ action, scroll }) => {
|
||||||
|
if (action === "open")
|
||||||
el.setAttribute("open", "")
|
el.setAttribute("open", "")
|
||||||
|
else
|
||||||
|
el.removeAttribute("open")
|
||||||
if (scroll)
|
if (scroll)
|
||||||
el.scrollIntoView()
|
el.scrollIntoView()
|
||||||
})
|
})
|
||||||
@ -113,6 +130,6 @@ export function mountDetails(
|
|||||||
.pipe(
|
.pipe(
|
||||||
tap(state => internal$.next(state)),
|
tap(state => internal$.next(state)),
|
||||||
finalize(() => internal$.complete()),
|
finalize(() => internal$.complete()),
|
||||||
mapTo({ ref: el })
|
map(state => ({ ref: el, ...state }))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -75,8 +75,7 @@ export interface Palette {
|
|||||||
export function watchPalette(
|
export function watchPalette(
|
||||||
inputs: HTMLInputElement[]
|
inputs: HTMLInputElement[]
|
||||||
): Observable<Palette> {
|
): Observable<Palette> {
|
||||||
const data = localStorage.getItem(__prefix("__palette"))!
|
const current = __md_get<Palette>("__palette") || {
|
||||||
const current = JSON.parse(data) || {
|
|
||||||
index: inputs.findIndex(input => (
|
index: inputs.findIndex(input => (
|
||||||
matchMedia(input.getAttribute("data-md-color-media")!).matches
|
matchMedia(input.getAttribute("data-md-color-media")!).matches
|
||||||
))
|
))
|
||||||
@ -104,7 +103,7 @@ export function watchPalette(
|
|||||||
|
|
||||||
/* Persist preference in local storage */
|
/* Persist preference in local storage */
|
||||||
palette$.subscribe(palette => {
|
palette$.subscribe(palette => {
|
||||||
localStorage.setItem(__prefix("__palette"), JSON.stringify(palette))
|
__md_set("__palette", palette)
|
||||||
})
|
})
|
||||||
|
|
||||||
/* Return palette */
|
/* Return palette */
|
||||||
|
@ -74,22 +74,14 @@ export function watchSource(
|
|||||||
el: HTMLAnchorElement
|
el: HTMLAnchorElement
|
||||||
): Observable<Source> {
|
): Observable<Source> {
|
||||||
return fetch$ ||= defer(() => {
|
return fetch$ ||= defer(() => {
|
||||||
const data = sessionStorage.getItem(__prefix("__source"))
|
const cached = __md_get<SourceFacts>("__source", sessionStorage)
|
||||||
if (data) {
|
if (cached)
|
||||||
return of<SourceFacts>(JSON.parse(data))
|
return of(cached)
|
||||||
} else {
|
else
|
||||||
const value$ = fetchSourceFacts(el.href)
|
return fetchSourceFacts(el.href)
|
||||||
value$.subscribe(value => {
|
.pipe(
|
||||||
try {
|
tap(facts => __md_set("__source", facts, sessionStorage))
|
||||||
sessionStorage.setItem(__prefix("__source"), JSON.stringify(value))
|
)
|
||||||
} catch (err) {
|
|
||||||
/* Uncritical, just swallow */
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
/* Return value */
|
|
||||||
return value$
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(() => NEVER),
|
catchError(() => NEVER),
|
||||||
|
@ -24,7 +24,9 @@ import {
|
|||||||
Observable,
|
Observable,
|
||||||
Subject,
|
Subject,
|
||||||
animationFrameScheduler,
|
animationFrameScheduler,
|
||||||
combineLatest
|
combineLatest,
|
||||||
|
defer,
|
||||||
|
of
|
||||||
} from "rxjs"
|
} from "rxjs"
|
||||||
import {
|
import {
|
||||||
bufferCount,
|
bufferCount,
|
||||||
@ -39,6 +41,7 @@ import {
|
|||||||
tap
|
tap
|
||||||
} from "rxjs/operators"
|
} from "rxjs/operators"
|
||||||
|
|
||||||
|
import { feature } from "~/_"
|
||||||
import {
|
import {
|
||||||
resetAnchorActive,
|
resetAnchorActive,
|
||||||
resetAnchorState,
|
resetAnchorState,
|
||||||
@ -49,6 +52,7 @@ import {
|
|||||||
Viewport,
|
Viewport,
|
||||||
getElement,
|
getElement,
|
||||||
getElements,
|
getElements,
|
||||||
|
getLocation,
|
||||||
watchElementSize
|
watchElementSize
|
||||||
} from "~/browser"
|
} from "~/browser"
|
||||||
|
|
||||||
@ -106,15 +110,18 @@ interface MountOptions {
|
|||||||
*
|
*
|
||||||
* Note that the current anchor is the last item of the `prev` anchor list.
|
* 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
|
* @param options - Options
|
||||||
*
|
*
|
||||||
* @returns Table of contents observable
|
* @returns Table of contents observable
|
||||||
*/
|
*/
|
||||||
export function watchTableOfContents(
|
export function watchTableOfContents(
|
||||||
anchors: HTMLAnchorElement[], { viewport$, header$ }: WatchOptions
|
el: HTMLElement, { viewport$, header$ }: WatchOptions
|
||||||
): Observable<TableOfContents> {
|
): Observable<TableOfContents> {
|
||||||
const table = new Map<HTMLAnchorElement, HTMLElement>()
|
const table = new Map<HTMLAnchorElement, HTMLElement>()
|
||||||
|
|
||||||
|
/* Compute anchor-to-target mapping */
|
||||||
|
const anchors = getElements<HTMLAnchorElement>("[href^=\\#]", el)
|
||||||
for (const anchor of anchors) {
|
for (const anchor of anchors) {
|
||||||
const id = decodeURIComponent(anchor.hash.substring(1))
|
const id = decodeURIComponent(anchor.hash.substring(1))
|
||||||
const target = getElement(`[id="${id}"]`)
|
const target = getElement(`[id="${id}"]`)
|
||||||
@ -134,9 +141,9 @@ export function watchTableOfContents(
|
|||||||
distinctUntilKeyChanged("height"),
|
distinctUntilKeyChanged("height"),
|
||||||
|
|
||||||
/* Build index to map anchor paths to vertical offsets */
|
/* Build index to map anchor paths to vertical offsets */
|
||||||
map(() => {
|
switchMap(body => defer(() => {
|
||||||
let path: HTMLAnchorElement[] = []
|
let path: HTMLAnchorElement[] = []
|
||||||
return [...table].reduce((index, [anchor, target]) => {
|
return of([...table].reduce((index, [anchor, target]) => {
|
||||||
while (path.length) {
|
while (path.length) {
|
||||||
const last = table.get(path[path.length - 1])!
|
const last = table.get(path[path.length - 1])!
|
||||||
if (last.tagName >= target.tagName) {
|
if (last.tagName >= target.tagName) {
|
||||||
@ -158,21 +165,23 @@ export function watchTableOfContents(
|
|||||||
[...path = [...path, anchor]].reverse(),
|
[...path = [...path, anchor]].reverse(),
|
||||||
offset
|
offset
|
||||||
)
|
)
|
||||||
}, new Map<HTMLAnchorElement[], number>())
|
}, new Map<HTMLAnchorElement[], number>()))
|
||||||
}),
|
})
|
||||||
|
.pipe(
|
||||||
|
|
||||||
/* Sort index by vertical offset (see https://bit.ly/30z6QSO) */
|
/* Sort index by vertical offset (see https://bit.ly/30z6QSO) */
|
||||||
map(index => new Map([...index].sort(([, a], [, b]) => a - b))),
|
map(index => new Map([...index].sort(([, a], [, b]) => a - b))),
|
||||||
|
|
||||||
/* Re-compute partition when viewport offset changes */
|
/* Re-compute partition when viewport offset changes */
|
||||||
switchMap(index => combineLatest([adjust$, viewport$])
|
switchMap(index => combineLatest([viewport$, adjust$])
|
||||||
.pipe(
|
.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 */
|
/* Look forward */
|
||||||
while (next.length) {
|
while (next.length) {
|
||||||
const [, offset] = next[0]
|
const [, offset] = next[0]
|
||||||
if (offset - adjust < y) {
|
if (offset - adjust < y || last) {
|
||||||
prev = [...prev, next.shift()!]
|
prev = [...prev, next.shift()!]
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
@ -182,7 +191,7 @@ export function watchTableOfContents(
|
|||||||
/* Look backward */
|
/* Look backward */
|
||||||
while (prev.length) {
|
while (prev.length) {
|
||||||
const [, offset] = prev[prev.length - 1]
|
const [, offset] = prev[prev.length - 1]
|
||||||
if (offset - adjust >= y) {
|
if (offset - adjust >= y && !last) {
|
||||||
next = [prev.pop()!, ...next]
|
next = [prev.pop()!, ...next]
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
@ -199,6 +208,8 @@ export function watchTableOfContents(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
/* Compute and return anchor list migrations */
|
/* Compute and return anchor list migrations */
|
||||||
return partition$
|
return partition$
|
||||||
@ -236,7 +247,7 @@ export function watchTableOfContents(
|
|||||||
/**
|
/**
|
||||||
* Mount table of contents
|
* Mount table of contents
|
||||||
*
|
*
|
||||||
* @param el - Anchor list element
|
* @param el - Table of contents element
|
||||||
* @param options - Options
|
* @param options - Options
|
||||||
*
|
*
|
||||||
* @returns Table of contents component observable
|
* @returns Table of contents component observable
|
||||||
@ -262,11 +273,31 @@ export function mountTableOfContents(
|
|||||||
setAnchorActive(anchor, index === prev.length - 1)
|
setAnchorActive(anchor, index === prev.length - 1)
|
||||||
setAnchorState(anchor, "blur")
|
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 */
|
/* Create and return component */
|
||||||
const anchors = getElements<HTMLAnchorElement>("[href^=\\#]", el)
|
return watchTableOfContents(el, options)
|
||||||
return watchTableOfContents(anchors, options)
|
|
||||||
.pipe(
|
.pipe(
|
||||||
tap(state => internal$.next(state)),
|
tap(state => internal$.next(state)),
|
||||||
finalize(() => internal$.complete()),
|
finalize(() => internal$.complete()),
|
||||||
|
@ -24,6 +24,10 @@ import ClipboardJS from "clipboard"
|
|||||||
import { Observable, Subject } from "rxjs"
|
import { Observable, Subject } from "rxjs"
|
||||||
|
|
||||||
import { translation } from "~/_"
|
import { translation } from "~/_"
|
||||||
|
import {
|
||||||
|
getElementOrThrow,
|
||||||
|
getElements
|
||||||
|
} from "~/browser"
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
* Helper types
|
* Helper types
|
||||||
@ -36,6 +40,34 @@ interface SetupOptions {
|
|||||||
alert$: Subject<string> /* Alert subject */
|
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
|
* Functions
|
||||||
* ------------------------------------------------------------------------- */
|
* ------------------------------------------------------------------------- */
|
||||||
@ -50,7 +82,14 @@ export function setupClipboardJS(
|
|||||||
): void {
|
): void {
|
||||||
if (ClipboardJS.isSupported()) {
|
if (ClipboardJS.isSupported()) {
|
||||||
new Observable<ClipboardJS.Event>(subscriber => {
|
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))
|
.on("success", ev => subscriber.next(ev))
|
||||||
})
|
})
|
||||||
.subscribe(() => alert$.next(translation("clipboard.copied")))
|
.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.
|
* IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { combineLatest } from "rxjs"
|
||||||
|
import { map } from "rxjs/operators"
|
||||||
|
|
||||||
import { configuration } from "~/_"
|
import { configuration } from "~/_"
|
||||||
import { getElementOrThrow, requestJSON } from "~/browser"
|
import {
|
||||||
import { Version, renderVersionSelector } from "~/templates"
|
getElementOrThrow,
|
||||||
|
requestJSON,
|
||||||
|
} from "~/browser"
|
||||||
|
import { getComponentElements } from "~/components"
|
||||||
|
import {
|
||||||
|
Version,
|
||||||
|
renderVersionSelector
|
||||||
|
} from "~/templates"
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
* Functions
|
* Functions
|
||||||
@ -33,9 +43,37 @@ import { Version, renderVersionSelector } from "~/templates"
|
|||||||
*/
|
*/
|
||||||
export function setupVersionSelector(): void {
|
export function setupVersionSelector(): void {
|
||||||
const config = configuration()
|
const config = configuration()
|
||||||
requestJSON<Version[]>(new URL("../versions.json", config.base))
|
const versions$ = requestJSON<Version[]>(
|
||||||
.subscribe(versions => {
|
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")
|
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 "./clipboard"
|
||||||
|
export * from "./code"
|
||||||
export * from "./search"
|
export * from "./search"
|
||||||
export * from "./source"
|
export * from "./source"
|
||||||
export * from "./table"
|
export * from "./table"
|
||||||
|
@ -69,20 +69,13 @@ function renderVersion(version: Version): HTMLElement {
|
|||||||
* Render a version selector
|
* Render a version selector
|
||||||
*
|
*
|
||||||
* @param versions - Versions
|
* @param versions - Versions
|
||||||
|
* @param active - Active version
|
||||||
*
|
*
|
||||||
* @returns Element
|
* @returns Element
|
||||||
*/
|
*/
|
||||||
export function renderVersionSelector(versions: Version[]): HTMLElement {
|
export function renderVersionSelector(
|
||||||
const config = configuration()
|
versions: Version[], active: Version
|
||||||
|
): HTMLElement {
|
||||||
/* Determine active version */
|
|
||||||
const [, current] = config.base.match(/([^/]+)\/?$/)!
|
|
||||||
const active =
|
|
||||||
versions.find(({ version, aliases }) => (
|
|
||||||
version === current || aliases.includes(current)
|
|
||||||
)) || versions[0]
|
|
||||||
|
|
||||||
/* Render version selector */
|
|
||||||
return (
|
return (
|
||||||
<div class="md-version">
|
<div class="md-version">
|
||||||
<button
|
<button
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
@import "main/typeset";
|
@import "main/typeset";
|
||||||
|
|
||||||
@import "main/layout/base";
|
@import "main/layout/base";
|
||||||
@import "main/layout/announce";
|
@import "main/layout/banner";
|
||||||
@import "main/layout/clipboard";
|
@import "main/layout/clipboard";
|
||||||
@import "main/layout/content";
|
@import "main/layout/content";
|
||||||
@import "main/layout/dialog";
|
@import "main/layout/dialog";
|
||||||
@ -55,6 +55,7 @@
|
|||||||
@import "main/layout/sidebar";
|
@import "main/layout/sidebar";
|
||||||
@import "main/layout/source";
|
@import "main/layout/source";
|
||||||
@import "main/layout/tabs";
|
@import "main/layout/tabs";
|
||||||
|
@import "main/layout/tooltip";
|
||||||
@import "main/layout/top";
|
@import "main/layout/top";
|
||||||
@import "main/layout/version";
|
@import "main/layout/version";
|
||||||
|
|
||||||
|
@ -62,9 +62,9 @@ $admonitions: (
|
|||||||
|
|
||||||
// Admonition
|
// Admonition
|
||||||
.admonition {
|
.admonition {
|
||||||
|
display: flow-root;
|
||||||
margin: px2em(20px, 12.8px) 0;
|
margin: px2em(20px, 12.8px) 0;
|
||||||
padding: 0 px2rem(12px);
|
padding: 0 px2rem(12px);
|
||||||
overflow: hidden;
|
|
||||||
color: var(--md-admonition-fg-color);
|
color: var(--md-admonition-fg-color);
|
||||||
font-size: px2rem(12.8px);
|
font-size: px2rem(12.8px);
|
||||||
page-break-inside: avoid;
|
page-break-inside: avoid;
|
||||||
@ -121,6 +121,7 @@ $admonitions: (
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
background-color: color.adjust($clr-blue-a200, $alpha: -0.9);
|
background-color: color.adjust($clr-blue-a200, $alpha: -0.9);
|
||||||
border-left: px2rem(4px) solid $clr-blue-a200;
|
border-left: px2rem(4px) solid $clr-blue-a200;
|
||||||
|
border-top-left-radius: px2rem(2px);
|
||||||
|
|
||||||
// Adjust for right-to-left languages
|
// Adjust for right-to-left languages
|
||||||
[dir="rtl"] & {
|
[dir="rtl"] & {
|
||||||
|
@ -60,12 +60,6 @@
|
|||||||
border-radius: px2rem(2px);
|
border-radius: px2rem(2px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hack: omit margin collapse
|
|
||||||
&::after {
|
|
||||||
display: table;
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Details title
|
// Details title
|
||||||
|
@ -173,6 +173,9 @@
|
|||||||
[data-linenos]::before {
|
[data-linenos]::before {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
left: px2em(-16px, 13.6px);
|
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;
|
float: left;
|
||||||
margin-right: px2em(16px, 13.6px);
|
margin-right: px2em(16px, 13.6px);
|
||||||
margin-left: px2em(-16px, 13.6px);
|
margin-left: px2em(-16px, 13.6px);
|
||||||
@ -192,7 +195,6 @@
|
|||||||
// Code block with line numbers
|
// Code block with line numbers
|
||||||
.highlighttable {
|
.highlighttable {
|
||||||
display: flow-root;
|
display: flow-root;
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
// Set table elements to block layout, because otherwise the whole flexbox
|
// Set table elements to block layout, because otherwise the whole flexbox
|
||||||
// hacking won't work correctly
|
// hacking won't work correctly
|
||||||
@ -246,7 +248,7 @@
|
|||||||
// Code block container - stretch to remaining space
|
// Code block container - stretch to remaining space
|
||||||
.code {
|
.code {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,11 +40,11 @@
|
|||||||
order: initial;
|
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
|
// previous (now deprecated) SuperFences code block grouping behavior
|
||||||
> pre:only-child,
|
> pre:first-child,
|
||||||
> .highlight:only-child pre,
|
> .highlight:first-child pre,
|
||||||
> .highlighttable:only-child {
|
> .highlighttable:first-child {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
// Omit rounded borders
|
// Omit rounded borders
|
||||||
@ -114,6 +114,12 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: color 250ms;
|
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
|
// Tab label on hover
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--md-accent-fg-color);
|
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
|
// previous (now deprecated) SuperFences code block grouping behavior
|
||||||
> pre:only-child,
|
> pre:first-child,
|
||||||
> .highlight:only-child pre,
|
> .highlight:first-child pre,
|
||||||
> .highlighttable:only-child {
|
> .highlighttable:first-child {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
// Omit rounded borders
|
// Omit rounded borders
|
||||||
|
@ -24,21 +24,27 @@
|
|||||||
// Rules
|
// Rules
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
// Announcement bar
|
// Banner for announcements and warnings
|
||||||
.md-announce {
|
.md-banner {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
color: var(--md-footer-fg-color);
|
||||||
background-color: var(--md-footer-bg-color);
|
background-color: var(--md-footer-bg-color);
|
||||||
|
|
||||||
// [print]: Hide announcement bar
|
// [print]: Hide banner
|
||||||
@media print {
|
@media print {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Announcement wrapper
|
// Banner with warning
|
||||||
|
&--warning {
|
||||||
|
color: var(--md-default-fg-color);
|
||||||
|
background: var(--md-typeset-mark-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Banner wrapper
|
||||||
&__inner {
|
&__inner {
|
||||||
margin: px2rem(12px) auto;
|
margin: px2rem(12px) auto;
|
||||||
padding: 0 px2rem(16px);
|
padding: 0 px2rem(16px);
|
||||||
color: var(--md-footer-fg-color);
|
|
||||||
font-size: px2rem(14px);
|
font-size: px2rem(14px);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -27,13 +27,10 @@
|
|||||||
// Content area
|
// Content area
|
||||||
.md-content {
|
.md-content {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
// Hack: we must use `overflow: hidden`, so the content area is capped by
|
// Hack: we must use `min-width: 0`, so the content area is capped by the
|
||||||
// the dimensions of its parent. Otherwise, long code blocks might lead to
|
// dimensions of its parent. Otherwise, long code blocks might lead to a
|
||||||
// a wider content area which will break everything. This, however, induces
|
// wider content area which will overflow. See https://bit.ly/3bP3f8k
|
||||||
// margin collapse, which will break scroll margins. Adding a large enough
|
min-width: 0;
|
||||||
// scroll padding seems to do the trick, at least in Chrome and Firefox.
|
|
||||||
overflow: hidden;
|
|
||||||
scroll-padding-top: px2rem(1024px);
|
|
||||||
|
|
||||||
// Content wrapper
|
// Content wrapper
|
||||||
&__inner {
|
&__inner {
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
right: px2rem(16px);
|
right: px2rem(16px);
|
||||||
bottom: px2rem(16px);
|
bottom: px2rem(16px);
|
||||||
left: initial;
|
left: initial;
|
||||||
z-index: 3;
|
z-index: 4;
|
||||||
min-width: px2rem(222px);
|
min-width: px2rem(222px);
|
||||||
padding: px2rem(8px) px2rem(12px);
|
padding: px2rem(8px) px2rem(12px);
|
||||||
background-color: var(--md-default-fg-color);
|
background-color: var(--md-default-fg-color);
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 3;
|
z-index: 4;
|
||||||
color: var(--md-primary-bg-color);
|
color: var(--md-primary-bg-color);
|
||||||
background-color: var(--md-primary-fg-color);
|
background-color: var(--md-primary-fg-color);
|
||||||
// Hack: reduce jitter by adding a transparent box shadow of the same size
|
// Hack: reduce jitter by adding a transparent box shadow of the same size
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: px2rem(-242px);
|
left: px2rem(-242px);
|
||||||
z-index: 4;
|
z-index: 5;
|
||||||
display: block;
|
display: block;
|
||||||
width: px2rem(242px);
|
width: px2rem(242px);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -163,11 +163,11 @@
|
|||||||
// [tablet -]: Show overlay on active drawer
|
// [tablet -]: Show overlay on active drawer
|
||||||
@include break-to-device(tablet) {
|
@include break-to-device(tablet) {
|
||||||
|
|
||||||
// Sidebar overlay
|
// Drawer overlay
|
||||||
.md-overlay {
|
.md-overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 4;
|
z-index: 5;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
background-color: hsla(0, 0%, 0%, 0.54);
|
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 }}" />
|
<link rel="stylesheet" href="{{ path | url }}" />
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
<!-- Helper functions for inline scripts -->
|
||||||
|
{% include "partials/javascripts/base.html" %}
|
||||||
|
|
||||||
<!-- Analytics -->
|
<!-- Analytics -->
|
||||||
{% block analytics %}
|
{% block analytics %}
|
||||||
{% include "partials/integrations/analytics.html" %}
|
{% include "partials/integrations/analytics.html" %}
|
||||||
@ -156,7 +159,6 @@
|
|||||||
|
|
||||||
<!-- Retrieve features from configuration -->
|
<!-- Retrieve features from configuration -->
|
||||||
{% set features = config.theme.features or [] %}
|
{% set features = config.theme.features or [] %}
|
||||||
{% include "partials/javascripts/base.html" %}
|
|
||||||
|
|
||||||
<!-- User preference: color palette -->
|
<!-- User preference: color palette -->
|
||||||
{% if not config.theme.palette is mapping %}
|
{% if not config.theme.palette is mapping %}
|
||||||
@ -206,6 +208,20 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</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 -->
|
<!-- Header -->
|
||||||
{% block header %}
|
{% block header %}
|
||||||
{% include "partials/header.html" %}
|
{% include "partials/header.html" %}
|
||||||
@ -303,13 +319,12 @@
|
|||||||
{{ page.content }}
|
{{ page.content }}
|
||||||
|
|
||||||
<!-- Last update of source file -->
|
<!-- Last update of source file -->
|
||||||
{% if page and page.meta %}
|
{% if page and page.meta and (
|
||||||
{% if page.meta.git_revision_date_localized or
|
page.meta.git_revision_date_localized or
|
||||||
page.meta.revision_date
|
page.meta.revision_date
|
||||||
%}
|
) %}
|
||||||
{% include "partials/source-file.html" %}
|
{% include "partials/source-file.html" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
<!-- Disqus integration -->
|
<!-- Disqus integration -->
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
@import "main/typeset";
|
@import "main/typeset";
|
||||||
|
|
||||||
@import "main/layout/announce";
|
@import "main/layout/banner";
|
||||||
@import "main/layout/hero";
|
@import "main/layout/hero";
|
||||||
@import "main/layout/iconsearch";
|
@import "main/layout/iconsearch";
|
||||||
@import "main/layout/sponsorship";
|
@import "main/layout/sponsorship";
|
||||||
|
@ -24,8 +24,8 @@
|
|||||||
// Rules
|
// Rules
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
// Announcement bar
|
// Banner for announcements and warnings
|
||||||
.md-announce {
|
.md-banner {
|
||||||
|
|
||||||
// Text link, also on focus/hover
|
// Text link, also on focus/hover
|
||||||
a,
|
a,
|
@ -27,13 +27,26 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
/* Prepend the base path to the given key to ensure uniqueness */
|
/* Prepend the base path to the given key to ensure uniqueness */
|
||||||
function __prefix(key) {
|
function __md_scope(key, storage, base) {
|
||||||
var prefix = new URL("{{ base_url }}", location)
|
var prefix = new URL(base || (
|
||||||
|
storage === localStorage
|
||||||
|
? "{{ config.extra.scope | d(base_url) }}"
|
||||||
|
: "{{ base_url }}"
|
||||||
|
), location)
|
||||||
return prefix.pathname + "." + key
|
return prefix.pathname + "." + key
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fetch the given key from the given storage */
|
/* Fetch the value for a key from the given storage */
|
||||||
function __get(key, storage = localStorage) {
|
function __md_get(key, storage = localStorage, base) {
|
||||||
return JSON.parse(storage.getItem(__prefix(key)))
|
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>
|
</script>
|
||||||
|
@ -22,8 +22,8 @@
|
|||||||
|
|
||||||
<!-- User preference: color palette -->
|
<!-- User preference: color palette -->
|
||||||
<script>
|
<script>
|
||||||
var palette = __get("__palette")
|
var palette = __md_get("__palette")
|
||||||
if (palette !== null && typeof palette.color === "object")
|
if (palette && typeof palette.color === "object")
|
||||||
for (var key in palette.color)
|
for (var [key, value] of Object.entries(palette.color))
|
||||||
document.body.setAttribute("data-md-color-" + key, palette.color[key])
|
document.body.setAttribute("data-md-color-" + key, value)
|
||||||
</script>
|
</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 %}
|
{% import "partials/language.html" as lang with context %}
|
||||||
|
|
||||||
<!-- Last updated date -->
|
<!-- Last updated date -->
|
||||||
{% set label = lang.t("source.file.date.updated") %}
|
|
||||||
<hr />
|
<hr />
|
||||||
<div class="md-source-date">
|
<div class="md-source-date">
|
||||||
<small>
|
<small>
|
||||||
|
|
||||||
<!-- mkdocs-git-revision-date-localized-plugin -->
|
<!-- mkdocs-git-revision-date-localized-plugin -->
|
||||||
{% if page.meta.git_revision_date_localized %}
|
{% 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 %}
|
{% 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 %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- mkdocs-git-revision-date-plugin -->
|
<!-- mkdocs-git-revision-date-plugin -->
|
||||||
{% elif page.meta.revision_date %}
|
{% elif page.meta.revision_date %}
|
||||||
{{ label }}: {{ page.meta.revision_date }}
|
{{ lang.t("source.file.date.updated") }}:
|
||||||
|
{{ page.meta.revision_date }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
40
typings/_/index.d.ts
vendored
40
typings/_/index.d.ts
vendored
@ -52,13 +52,47 @@ declare global {
|
|||||||
const __search: GlobalSearchConfig | undefined
|
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
|
* Google Analytics
|
||||||
*/
|
*/
|
||||||
|
declare global {
|
||||||
function ga(...args: string[]): void
|
function ga(...args: string[]): void
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +108,7 @@ declare global {
|
|||||||
var viewport$: Observable<Viewport> /* Viewport obsevable */
|
var viewport$: Observable<Viewport> /* Viewport obsevable */
|
||||||
var tablet$: Observable<boolean> /* Tablet breakpoint observable */
|
var tablet$: Observable<boolean> /* Tablet breakpoint observable */
|
||||||
var screen$: Observable<boolean> /* Screen 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 alert$: Subject<string> /* Alert subject */
|
||||||
var component$: Observable<Component>/* Component observable */
|
var component$: Observable<Component>/* Component observable */
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user