mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2024-11-27 17:00:54 +01:00
Improved rendering of code annotations + keep focus for clipboard button
This commit is contained in:
parent
122be2ec46
commit
fb751c108c
@ -57,6 +57,7 @@
|
|||||||
"declaration-colon-space-after": null,
|
"declaration-colon-space-after": null,
|
||||||
"declaration-no-important": true,
|
"declaration-no-important": true,
|
||||||
"declaration-block-single-line-max-declarations": 0,
|
"declaration-block-single-line-max-declarations": 0,
|
||||||
|
"function-calc-no-unspaced-operator": null,
|
||||||
"function-url-no-scheme-relative": true,
|
"function-url-no-scheme-relative": true,
|
||||||
"function-url-quotes": "always",
|
"function-url-quotes": "always",
|
||||||
"font-family-name-quotes": "always-where-recommended",
|
"font-family-name-quotes": "always-where-recommended",
|
||||||
|
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
@ -34,7 +34,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block styles %}
|
{% block styles %}
|
||||||
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.62128196.min.css' | url }}">
|
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.2a4617e2.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 }}">
|
||||||
@ -213,7 +213,7 @@
|
|||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script src="{{ 'assets/javascripts/bundle.85839339.min.js' | url }}"></script>
|
<script src="{{ 'assets/javascripts/bundle.acd49e06.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
@ -16,5 +16,5 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
<script src="{{ 'overrides/assets/javascripts/bundle.6950b4a3.min.js' | url }}"></script>
|
<script src="{{ 'overrides/assets/javascripts/bundle.7c4664dd.min.js' | url }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -70,13 +70,13 @@ theme:
|
|||||||
primary: indigo
|
primary: indigo
|
||||||
accent: indigo
|
accent: indigo
|
||||||
toggle:
|
toggle:
|
||||||
icon: material/toggle-switch-off-outline
|
icon: material/toggle-switch
|
||||||
name: Switch to dark mode
|
name: Switch to dark mode
|
||||||
- scheme: slate
|
- scheme: slate
|
||||||
primary: red
|
primary: red
|
||||||
accent: red
|
accent: red
|
||||||
toggle:
|
toggle:
|
||||||
icon: material/toggle-switch
|
icon: material/toggle-switch-off-outline
|
||||||
name: Switch to light mode
|
name: Switch to light mode
|
||||||
font:
|
font:
|
||||||
text: Roboto
|
text: Roboto
|
||||||
|
@ -37,6 +37,11 @@ import { getActiveElement } from "../_"
|
|||||||
/**
|
/**
|
||||||
* Watch element focus
|
* Watch element focus
|
||||||
*
|
*
|
||||||
|
* Previously, this function used `focus` and `blur` events to determine whether
|
||||||
|
* an element is focused, but this doesn't work if there are focusable elements
|
||||||
|
* within the elements itself. A better solutions it to use `focusin/out` events
|
||||||
|
* events which bubble up the tree and allow for more fine-grained control.
|
||||||
|
*
|
||||||
* @param el - Element
|
* @param el - Element
|
||||||
*
|
*
|
||||||
* @returns Element focus observable
|
* @returns Element focus observable
|
||||||
@ -45,11 +50,16 @@ export function watchElementFocus(
|
|||||||
el: HTMLElement
|
el: HTMLElement
|
||||||
): Observable<boolean> {
|
): Observable<boolean> {
|
||||||
return merge(
|
return merge(
|
||||||
fromEvent<FocusEvent>(el, "focus"),
|
fromEvent(document.body, "focusin"),
|
||||||
fromEvent<FocusEvent>(el, "blur")
|
fromEvent(document.body, "focusout")
|
||||||
)
|
)
|
||||||
.pipe(
|
.pipe(
|
||||||
map(({ type }) => type === "focus"),
|
map(() => {
|
||||||
|
const active = getActiveElement()
|
||||||
|
return typeof active !== "undefined"
|
||||||
|
? el.contains(active)
|
||||||
|
: false
|
||||||
|
}),
|
||||||
startWith(el === getActiveElement())
|
startWith(el === getActiveElement())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,6 @@ const keyboard$ = watchKeyboard()
|
|||||||
const viewport$ = watchViewport()
|
const viewport$ = watchViewport()
|
||||||
const tablet$ = watchMedia("(min-width: 960px)")
|
const tablet$ = watchMedia("(min-width: 960px)")
|
||||||
const screen$ = watchMedia("(min-width: 1220px)")
|
const screen$ = watchMedia("(min-width: 1220px)")
|
||||||
const hover$ = watchMedia("(hover)")
|
|
||||||
const print$ = watchPrint()
|
const print$ = watchPrint()
|
||||||
|
|
||||||
/* Retrieve search index, if search is enabled */
|
/* Retrieve search index, if search is enabled */
|
||||||
@ -199,7 +198,7 @@ const content$ = defer(() => merge(
|
|||||||
|
|
||||||
/* Content */
|
/* Content */
|
||||||
...getComponentElements("content")
|
...getComponentElements("content")
|
||||||
.map(el => mountContent(el, { target$, hover$, print$ })),
|
.map(el => mountContent(el, { target$, print$ })),
|
||||||
|
|
||||||
/* Search highlighting */
|
/* Search highlighting */
|
||||||
...getComponentElements("content")
|
...getComponentElements("content")
|
||||||
@ -254,7 +253,6 @@ window.keyboard$ = keyboard$ /* Keyboard observable */
|
|||||||
window.viewport$ = viewport$ /* Viewport observable */
|
window.viewport$ = viewport$ /* Viewport observable */
|
||||||
window.tablet$ = tablet$ /* Media tablet observable */
|
window.tablet$ = tablet$ /* Media tablet observable */
|
||||||
window.screen$ = screen$ /* Media screen observable */
|
window.screen$ = screen$ /* Media screen observable */
|
||||||
window.hover$ = hover$ /* Media hover observable */
|
|
||||||
window.print$ = print$ /* Media print observable */
|
window.print$ = print$ /* Media print observable */
|
||||||
window.alert$ = alert$ /* Alert subject */
|
window.alert$ = alert$ /* Alert subject */
|
||||||
window.component$ = component$ /* Component observable */
|
window.component$ = component$ /* Component observable */
|
||||||
|
@ -57,7 +57,6 @@ export type Content =
|
|||||||
*/
|
*/
|
||||||
interface MountOptions {
|
interface MountOptions {
|
||||||
target$: Observable<HTMLElement> /* Location target observable */
|
target$: Observable<HTMLElement> /* Location target observable */
|
||||||
hover$: Observable<boolean> /* Media hover observable */
|
|
||||||
print$: Observable<boolean> /* Media print observable */
|
print$: Observable<boolean> /* Media print observable */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,13 +76,13 @@ interface MountOptions {
|
|||||||
* @returns Content component observable
|
* @returns Content component observable
|
||||||
*/
|
*/
|
||||||
export function mountContent(
|
export function mountContent(
|
||||||
el: HTMLElement, { target$, hover$, print$ }: MountOptions
|
el: HTMLElement, { target$, print$ }: MountOptions
|
||||||
): Observable<Component<Content>> {
|
): Observable<Component<Content>> {
|
||||||
return merge(
|
return merge(
|
||||||
|
|
||||||
/* Code blocks */
|
/* Code blocks */
|
||||||
...getElements("pre > code", el)
|
...getElements("pre > code", el)
|
||||||
.map(child => mountCodeBlock(child, { hover$, print$ })),
|
.map(child => mountCodeBlock(child, { print$ })),
|
||||||
|
|
||||||
/* Data tables */
|
/* Data tables */
|
||||||
...getElements("table:not([class])", el)
|
...getElements("table:not([class])", el)
|
||||||
|
@ -33,8 +33,7 @@ import {
|
|||||||
switchMap,
|
switchMap,
|
||||||
takeLast,
|
takeLast,
|
||||||
takeUntil,
|
takeUntil,
|
||||||
tap,
|
tap
|
||||||
withLatestFrom
|
|
||||||
} from "rxjs"
|
} from "rxjs"
|
||||||
|
|
||||||
import { feature } from "~/_"
|
import { feature } from "~/_"
|
||||||
@ -69,7 +68,6 @@ export interface CodeBlock {
|
|||||||
* Mount options
|
* Mount options
|
||||||
*/
|
*/
|
||||||
interface MountOptions {
|
interface MountOptions {
|
||||||
hover$: Observable<boolean> /* Media hover observable */
|
|
||||||
print$: Observable<boolean> /* Media print observable */
|
print$: Observable<boolean> /* Media print observable */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,13 +85,13 @@ let sequence = 0
|
|||||||
* ------------------------------------------------------------------------- */
|
* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the code annotations belonging to a code block
|
* Find candidate list element directly following a code block
|
||||||
*
|
*
|
||||||
* @param el - Code block element
|
* @param el - Code block element
|
||||||
*
|
*
|
||||||
* @returns Code annotation list or nothing
|
* @returns List element or nothing
|
||||||
*/
|
*/
|
||||||
function findAnnotationList(el: HTMLElement): HTMLElement | undefined {
|
function findCandidateList(el: HTMLElement): HTMLElement | undefined {
|
||||||
if (el.nextElementSibling) {
|
if (el.nextElementSibling) {
|
||||||
const sibling = el.nextElementSibling as HTMLElement
|
const sibling = el.nextElementSibling as HTMLElement
|
||||||
if (sibling.tagName === "OL")
|
if (sibling.tagName === "OL")
|
||||||
@ -101,7 +99,7 @@ function findAnnotationList(el: HTMLElement): HTMLElement | undefined {
|
|||||||
|
|
||||||
/* Skip empty paragraphs - see https://bit.ly/3r4ZJ2O */
|
/* Skip empty paragraphs - see https://bit.ly/3r4ZJ2O */
|
||||||
else if (sibling.tagName === "P" && !sibling.children.length)
|
else if (sibling.tagName === "P" && !sibling.children.length)
|
||||||
return findAnnotationList(sibling)
|
return findCandidateList(sibling)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Everything else */
|
/* Everything else */
|
||||||
@ -151,20 +149,17 @@ export function watchCodeBlock(
|
|||||||
* @returns Code block and annotation component observable
|
* @returns Code block and annotation component observable
|
||||||
*/
|
*/
|
||||||
export function mountCodeBlock(
|
export function mountCodeBlock(
|
||||||
el: HTMLElement, { hover$, ...options }: MountOptions
|
el: HTMLElement, options: MountOptions
|
||||||
): Observable<Component<CodeBlock | Annotation>> {
|
): Observable<Component<CodeBlock | Annotation>> {
|
||||||
|
const { matches: hover } = matchMedia("(hover)")
|
||||||
return defer(() => {
|
return defer(() => {
|
||||||
const push$ = new Subject<CodeBlock>()
|
const push$ = new Subject<CodeBlock>()
|
||||||
push$
|
push$.subscribe(({ scrollable }) => {
|
||||||
.pipe(
|
if (scrollable && hover)
|
||||||
withLatestFrom(hover$)
|
el.setAttribute("tabindex", "0")
|
||||||
)
|
else
|
||||||
.subscribe(([{ scrollable: scroll }, hover]) => {
|
el.removeAttribute("tabindex")
|
||||||
if (scroll && hover)
|
})
|
||||||
el.setAttribute("tabindex", "0")
|
|
||||||
else
|
|
||||||
el.removeAttribute("tabindex")
|
|
||||||
})
|
|
||||||
|
|
||||||
/* Render button for Clipboard.js integration */
|
/* Render button for Clipboard.js integration */
|
||||||
if (ClipboardJS.isSupported()) {
|
if (ClipboardJS.isSupported()) {
|
||||||
@ -177,11 +172,12 @@ export function mountCodeBlock(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Handle code annotations */
|
/* Handle code annotations */
|
||||||
const container =
|
const container = el.closest([
|
||||||
el.closest(".highlighttable") ||
|
":not(td.code) > .highlight", /* Code blocks */
|
||||||
el.closest(".highlight")
|
".highlighttable" /* Code blocks with line numbers */
|
||||||
|
].join(", "))
|
||||||
if (container instanceof HTMLElement) {
|
if (container instanceof HTMLElement) {
|
||||||
const list = findAnnotationList(container)
|
const list = findCandidateList(container)
|
||||||
|
|
||||||
/* Mount code annotations, if enabled */
|
/* Mount code annotations, if enabled */
|
||||||
if (typeof list !== "undefined" && (
|
if (typeof list !== "undefined" && (
|
||||||
|
@ -31,12 +31,14 @@ import {
|
|||||||
map,
|
map,
|
||||||
switchMap,
|
switchMap,
|
||||||
take,
|
take,
|
||||||
tap
|
tap,
|
||||||
|
throttleTime
|
||||||
} from "rxjs"
|
} from "rxjs"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ElementOffset,
|
ElementOffset,
|
||||||
getElement,
|
getElement,
|
||||||
|
getElementSize,
|
||||||
watchElementContentOffset,
|
watchElementContentOffset,
|
||||||
watchElementFocus,
|
watchElementFocus,
|
||||||
watchElementOffset
|
watchElementOffset
|
||||||
@ -76,10 +78,13 @@ export function watchAnnotation(
|
|||||||
watchElementContentOffset(container)
|
watchElementContentOffset(container)
|
||||||
]))
|
]))
|
||||||
.pipe(
|
.pipe(
|
||||||
map(([{ x, y }, scroll]) => ({
|
map(([{ x, y }, scroll]) => {
|
||||||
x: x - scroll.x,
|
const { width } = getElementSize(el)
|
||||||
y: y - scroll.y
|
return ({
|
||||||
}))
|
x: x - scroll.x + width / 2,
|
||||||
|
y: y - scroll.y
|
||||||
|
})
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
/* Actively watch code annotation on focus */
|
/* Actively watch code annotation on focus */
|
||||||
@ -122,7 +127,18 @@ export function mountAnnotation(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/* Blur open annotation on click (= close) */
|
/* Track relative origin of tooltip */
|
||||||
|
push$
|
||||||
|
.pipe(
|
||||||
|
throttleTime(500),
|
||||||
|
map(() => container.getBoundingClientRect()),
|
||||||
|
map(({ x }) => x)
|
||||||
|
)
|
||||||
|
.subscribe(origin => {
|
||||||
|
el.style.setProperty("--md-tooltip-0", `${-origin}px`)
|
||||||
|
})
|
||||||
|
|
||||||
|
/* Close open annotation on click */
|
||||||
const index = getElement(":scope > :last-child", el)
|
const index = getElement(":scope > :last-child", el)
|
||||||
const blur$ = fromEvent(index, "mousedown", { once: true })
|
const blur$ = fromEvent(index, "mousedown", { once: true })
|
||||||
push$
|
push$
|
||||||
|
@ -21,7 +21,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import ClipboardJS from "clipboard"
|
import ClipboardJS from "clipboard"
|
||||||
import { Observable, Subject } from "rxjs"
|
import {
|
||||||
|
Observable,
|
||||||
|
Subject,
|
||||||
|
mapTo,
|
||||||
|
tap
|
||||||
|
} from "rxjs"
|
||||||
|
|
||||||
import { translation } from "~/_"
|
import { translation } from "~/_"
|
||||||
import {
|
import {
|
||||||
@ -92,6 +97,13 @@ export function setupClipboardJS(
|
|||||||
})
|
})
|
||||||
.on("success", ev => subscriber.next(ev))
|
.on("success", ev => subscriber.next(ev))
|
||||||
})
|
})
|
||||||
.subscribe(() => alert$.next(translation("clipboard.copied")))
|
.pipe(
|
||||||
|
tap(ev => {
|
||||||
|
const trigger = ev.trigger as HTMLElement
|
||||||
|
trigger.focus()
|
||||||
|
}),
|
||||||
|
mapTo(translation("clipboard.copied"))
|
||||||
|
)
|
||||||
|
.subscribe(alert$)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,26 +146,23 @@
|
|||||||
top: calc(var(--md-tooltip-y) + 1.2ch);
|
top: calc(var(--md-tooltip-y) + 1.2ch);
|
||||||
left:
|
left:
|
||||||
clamp(
|
clamp(
|
||||||
#{px2rem(0px)},
|
calc(
|
||||||
calc(var(--md-tooltip-x) + #{px2rem(12px)}),
|
var(--md-tooltip-0, 0) +
|
||||||
calc(100vw - var(--md-tooltip-width) - 2 * #{px2rem(16px)})
|
#{px2rem(16px)}
|
||||||
|
),
|
||||||
|
var(--md-tooltip-x),
|
||||||
|
calc(
|
||||||
|
100vw -
|
||||||
|
var(--md-tooltip-width) +
|
||||||
|
calc(
|
||||||
|
var(--md-tooltip-0, 0) +
|
||||||
|
#{px2rem(16px)}
|
||||||
|
) -
|
||||||
|
2 * #{px2rem(16px)}
|
||||||
|
)
|
||||||
);
|
);
|
||||||
font-family: var(--md-text-font-family);
|
font-family: var(--md-text-font-family);
|
||||||
|
|
||||||
// [mobile -]: Align with body copy
|
|
||||||
@include break-to-device(mobile) {
|
|
||||||
|
|
||||||
// Top-level code block
|
|
||||||
.md-content__inner > :is(pre, .highlight) & {
|
|
||||||
left:
|
|
||||||
clamp(
|
|
||||||
#{px2rem(16px)},
|
|
||||||
calc(var(--md-tooltip-x) + #{px2rem(12px)}),
|
|
||||||
calc(100vw - var(--md-tooltip-width) - #{px2rem(16px)})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Code annotation tooltip when not focused
|
// Code annotation tooltip when not focused
|
||||||
:not(:focus-within) > & {
|
:not(:focus-within) > & {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@ -189,8 +186,8 @@
|
|||||||
// alignment of text following a code annotation.
|
// alignment of text following a code annotation.
|
||||||
&::after {
|
&::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0.1ch;
|
top: 0.025em;
|
||||||
left: -0.2ch;
|
left: -0.126em;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
width: max(2.2ch, 100% + 1.2ch);
|
width: max(2.2ch, 100% + 1.2ch);
|
||||||
height: 2.2ch;
|
height: 2.2ch;
|
||||||
|
1
typings/_/index.d.ts
vendored
1
typings/_/index.d.ts
vendored
@ -108,7 +108,6 @@ declare global {
|
|||||||
var viewport$: Observable<Viewport> /* Viewport obsevable */
|
var viewport$: Observable<Viewport> /* Viewport obsevable */
|
||||||
var tablet$: Observable<boolean> /* Media tablet observable */
|
var tablet$: Observable<boolean> /* Media tablet observable */
|
||||||
var screen$: Observable<boolean> /* Media screen observable */
|
var screen$: Observable<boolean> /* Media screen observable */
|
||||||
var hover$: Observable<boolean> /* Media hover observable */
|
|
||||||
var print$: Observable<boolean> /* Media print observable */
|
var print$: Observable<boolean> /* Media 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