mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2024-11-23 23:21:00 +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-no-important": true,
|
||||
"declaration-block-single-line-max-declarations": 0,
|
||||
"function-calc-no-unspaced-operator": null,
|
||||
"function-url-no-scheme-relative": true,
|
||||
"function-url-quotes": "always",
|
||||
"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 %}
|
||||
{% endblock %}
|
||||
{% 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 %}
|
||||
{% set palette = config.theme.palette %}
|
||||
<link rel="stylesheet" href="{{ 'assets/stylesheets/palette.9204c3b2.min.css' | url }}">
|
||||
@ -213,7 +213,7 @@
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% 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"] %}
|
||||
<script src="{{ path | url }}"></script>
|
||||
{% endfor %}
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -16,5 +16,5 @@
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
{{ super() }}
|
||||
<script src="{{ 'overrides/assets/javascripts/bundle.6950b4a3.min.js' | url }}"></script>
|
||||
<script src="{{ 'overrides/assets/javascripts/bundle.7c4664dd.min.js' | url }}"></script>
|
||||
{% endblock %}
|
||||
|
@ -70,13 +70,13 @@ theme:
|
||||
primary: indigo
|
||||
accent: indigo
|
||||
toggle:
|
||||
icon: material/toggle-switch-off-outline
|
||||
icon: material/toggle-switch
|
||||
name: Switch to dark mode
|
||||
- scheme: slate
|
||||
primary: red
|
||||
accent: red
|
||||
toggle:
|
||||
icon: material/toggle-switch
|
||||
icon: material/toggle-switch-off-outline
|
||||
name: Switch to light mode
|
||||
font:
|
||||
text: Roboto
|
||||
|
@ -37,6 +37,11 @@ import { getActiveElement } from "../_"
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @returns Element focus observable
|
||||
@ -45,11 +50,16 @@ export function watchElementFocus(
|
||||
el: HTMLElement
|
||||
): Observable<boolean> {
|
||||
return merge(
|
||||
fromEvent<FocusEvent>(el, "focus"),
|
||||
fromEvent<FocusEvent>(el, "blur")
|
||||
fromEvent(document.body, "focusin"),
|
||||
fromEvent(document.body, "focusout")
|
||||
)
|
||||
.pipe(
|
||||
map(({ type }) => type === "focus"),
|
||||
map(() => {
|
||||
const active = getActiveElement()
|
||||
return typeof active !== "undefined"
|
||||
? el.contains(active)
|
||||
: false
|
||||
}),
|
||||
startWith(el === getActiveElement())
|
||||
)
|
||||
}
|
||||
|
@ -97,7 +97,6 @@ const keyboard$ = watchKeyboard()
|
||||
const viewport$ = watchViewport()
|
||||
const tablet$ = watchMedia("(min-width: 960px)")
|
||||
const screen$ = watchMedia("(min-width: 1220px)")
|
||||
const hover$ = watchMedia("(hover)")
|
||||
const print$ = watchPrint()
|
||||
|
||||
/* Retrieve search index, if search is enabled */
|
||||
@ -199,7 +198,7 @@ const content$ = defer(() => merge(
|
||||
|
||||
/* Content */
|
||||
...getComponentElements("content")
|
||||
.map(el => mountContent(el, { target$, hover$, print$ })),
|
||||
.map(el => mountContent(el, { target$, print$ })),
|
||||
|
||||
/* Search highlighting */
|
||||
...getComponentElements("content")
|
||||
@ -254,7 +253,6 @@ window.keyboard$ = keyboard$ /* Keyboard observable */
|
||||
window.viewport$ = viewport$ /* Viewport observable */
|
||||
window.tablet$ = tablet$ /* Media tablet observable */
|
||||
window.screen$ = screen$ /* Media screen observable */
|
||||
window.hover$ = hover$ /* Media hover observable */
|
||||
window.print$ = print$ /* Media print observable */
|
||||
window.alert$ = alert$ /* Alert subject */
|
||||
window.component$ = component$ /* Component observable */
|
||||
|
@ -57,7 +57,6 @@ export type Content =
|
||||
*/
|
||||
interface MountOptions {
|
||||
target$: Observable<HTMLElement> /* Location target observable */
|
||||
hover$: Observable<boolean> /* Media hover observable */
|
||||
print$: Observable<boolean> /* Media print observable */
|
||||
}
|
||||
|
||||
@ -77,13 +76,13 @@ interface MountOptions {
|
||||
* @returns Content component observable
|
||||
*/
|
||||
export function mountContent(
|
||||
el: HTMLElement, { target$, hover$, print$ }: MountOptions
|
||||
el: HTMLElement, { target$, print$ }: MountOptions
|
||||
): Observable<Component<Content>> {
|
||||
return merge(
|
||||
|
||||
/* Code blocks */
|
||||
...getElements("pre > code", el)
|
||||
.map(child => mountCodeBlock(child, { hover$, print$ })),
|
||||
.map(child => mountCodeBlock(child, { print$ })),
|
||||
|
||||
/* Data tables */
|
||||
...getElements("table:not([class])", el)
|
||||
|
@ -33,8 +33,7 @@ import {
|
||||
switchMap,
|
||||
takeLast,
|
||||
takeUntil,
|
||||
tap,
|
||||
withLatestFrom
|
||||
tap
|
||||
} from "rxjs"
|
||||
|
||||
import { feature } from "~/_"
|
||||
@ -69,7 +68,6 @@ export interface CodeBlock {
|
||||
* Mount options
|
||||
*/
|
||||
interface MountOptions {
|
||||
hover$: Observable<boolean> /* Media hover 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
|
||||
*
|
||||
* @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) {
|
||||
const sibling = el.nextElementSibling as HTMLElement
|
||||
if (sibling.tagName === "OL")
|
||||
@ -101,7 +99,7 @@ function findAnnotationList(el: HTMLElement): HTMLElement | undefined {
|
||||
|
||||
/* Skip empty paragraphs - see https://bit.ly/3r4ZJ2O */
|
||||
else if (sibling.tagName === "P" && !sibling.children.length)
|
||||
return findAnnotationList(sibling)
|
||||
return findCandidateList(sibling)
|
||||
}
|
||||
|
||||
/* Everything else */
|
||||
@ -151,20 +149,17 @@ export function watchCodeBlock(
|
||||
* @returns Code block and annotation component observable
|
||||
*/
|
||||
export function mountCodeBlock(
|
||||
el: HTMLElement, { hover$, ...options }: MountOptions
|
||||
el: HTMLElement, options: MountOptions
|
||||
): Observable<Component<CodeBlock | Annotation>> {
|
||||
const { matches: hover } = matchMedia("(hover)")
|
||||
return defer(() => {
|
||||
const push$ = new Subject<CodeBlock>()
|
||||
push$
|
||||
.pipe(
|
||||
withLatestFrom(hover$)
|
||||
)
|
||||
.subscribe(([{ scrollable: scroll }, hover]) => {
|
||||
if (scroll && hover)
|
||||
el.setAttribute("tabindex", "0")
|
||||
else
|
||||
el.removeAttribute("tabindex")
|
||||
})
|
||||
push$.subscribe(({ scrollable }) => {
|
||||
if (scrollable && hover)
|
||||
el.setAttribute("tabindex", "0")
|
||||
else
|
||||
el.removeAttribute("tabindex")
|
||||
})
|
||||
|
||||
/* Render button for Clipboard.js integration */
|
||||
if (ClipboardJS.isSupported()) {
|
||||
@ -177,11 +172,12 @@ export function mountCodeBlock(
|
||||
}
|
||||
|
||||
/* Handle code annotations */
|
||||
const container =
|
||||
el.closest(".highlighttable") ||
|
||||
el.closest(".highlight")
|
||||
const container = el.closest([
|
||||
":not(td.code) > .highlight", /* Code blocks */
|
||||
".highlighttable" /* Code blocks with line numbers */
|
||||
].join(", "))
|
||||
if (container instanceof HTMLElement) {
|
||||
const list = findAnnotationList(container)
|
||||
const list = findCandidateList(container)
|
||||
|
||||
/* Mount code annotations, if enabled */
|
||||
if (typeof list !== "undefined" && (
|
||||
|
@ -31,12 +31,14 @@ import {
|
||||
map,
|
||||
switchMap,
|
||||
take,
|
||||
tap
|
||||
tap,
|
||||
throttleTime
|
||||
} from "rxjs"
|
||||
|
||||
import {
|
||||
ElementOffset,
|
||||
getElement,
|
||||
getElementSize,
|
||||
watchElementContentOffset,
|
||||
watchElementFocus,
|
||||
watchElementOffset
|
||||
@ -76,10 +78,13 @@ export function watchAnnotation(
|
||||
watchElementContentOffset(container)
|
||||
]))
|
||||
.pipe(
|
||||
map(([{ x, y }, scroll]) => ({
|
||||
x: x - scroll.x,
|
||||
y: y - scroll.y
|
||||
}))
|
||||
map(([{ x, y }, scroll]) => {
|
||||
const { width } = getElementSize(el)
|
||||
return ({
|
||||
x: x - scroll.x + width / 2,
|
||||
y: y - scroll.y
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
/* 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 blur$ = fromEvent(index, "mousedown", { once: true })
|
||||
push$
|
||||
|
@ -21,7 +21,12 @@
|
||||
*/
|
||||
|
||||
import ClipboardJS from "clipboard"
|
||||
import { Observable, Subject } from "rxjs"
|
||||
import {
|
||||
Observable,
|
||||
Subject,
|
||||
mapTo,
|
||||
tap
|
||||
} from "rxjs"
|
||||
|
||||
import { translation } from "~/_"
|
||||
import {
|
||||
@ -92,6 +97,13 @@ export function setupClipboardJS(
|
||||
})
|
||||
.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);
|
||||
left:
|
||||
clamp(
|
||||
#{px2rem(0px)},
|
||||
calc(var(--md-tooltip-x) + #{px2rem(12px)}),
|
||||
calc(100vw - var(--md-tooltip-width) - 2 * #{px2rem(16px)})
|
||||
calc(
|
||||
var(--md-tooltip-0, 0) +
|
||||
#{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);
|
||||
|
||||
// [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
|
||||
:not(:focus-within) > & {
|
||||
user-select: none;
|
||||
@ -189,8 +186,8 @@
|
||||
// alignment of text following a code annotation.
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0.1ch;
|
||||
left: -0.2ch;
|
||||
top: 0.025em;
|
||||
left: -0.126em;
|
||||
z-index: -1;
|
||||
width: max(2.2ch, 100% + 1.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 tablet$: Observable<boolean> /* Media tablet observable */
|
||||
var screen$: Observable<boolean> /* Media screen observable */
|
||||
var hover$: Observable<boolean> /* Media hover observable */
|
||||
var print$: Observable<boolean> /* Media print observable */
|
||||
var alert$: Subject<string> /* Alert subject */
|
||||
var component$: Observable<Component>/* Component observable */
|
||||
|
Loading…
Reference in New Issue
Block a user