This commit is contained in:
178
node_modules/vitepress/dist/client/theme-default/composables/outline.js
generated
vendored
Normal file
178
node_modules/vitepress/dist/client/theme-default/composables/outline.js
generated
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
import { getScrollOffset } from 'vitepress';
|
||||
import { onMounted, onUnmounted, onUpdated } from 'vue';
|
||||
import { throttleAndDebounce } from '../support/utils';
|
||||
import { useAside } from './aside';
|
||||
const ignoreRE = /\b(?:VPBadge|header-anchor|footnote-ref|ignore-header)\b/;
|
||||
// cached list of anchor elements from resolveHeaders
|
||||
const resolvedHeaders = [];
|
||||
export function resolveTitle(theme) {
|
||||
return ((typeof theme.outline === 'object' &&
|
||||
!Array.isArray(theme.outline) &&
|
||||
theme.outline.label) ||
|
||||
theme.outlineTitle ||
|
||||
'On this page');
|
||||
}
|
||||
export function getHeaders(range) {
|
||||
const headers = [
|
||||
...document.querySelectorAll('.VPDoc :where(h1,h2,h3,h4,h5,h6)')
|
||||
]
|
||||
.filter((el) => el.id && el.hasChildNodes())
|
||||
.map((el) => {
|
||||
const level = Number(el.tagName[1]);
|
||||
return {
|
||||
element: el,
|
||||
title: serializeHeader(el),
|
||||
link: '#' + el.id,
|
||||
level
|
||||
};
|
||||
});
|
||||
return resolveHeaders(headers, range);
|
||||
}
|
||||
function serializeHeader(h) {
|
||||
let ret = '';
|
||||
for (const node of h.childNodes) {
|
||||
if (node.nodeType === 1) {
|
||||
if (ignoreRE.test(node.className))
|
||||
continue;
|
||||
ret += node.textContent;
|
||||
}
|
||||
else if (node.nodeType === 3) {
|
||||
ret += node.textContent;
|
||||
}
|
||||
}
|
||||
return ret.trim();
|
||||
}
|
||||
export function resolveHeaders(headers, range) {
|
||||
if (range === false) {
|
||||
return [];
|
||||
}
|
||||
const levelsRange = (typeof range === 'object' && !Array.isArray(range)
|
||||
? range.level
|
||||
: range) || 2;
|
||||
const [high, low] = typeof levelsRange === 'number'
|
||||
? [levelsRange, levelsRange]
|
||||
: levelsRange === 'deep'
|
||||
? [2, 6]
|
||||
: levelsRange;
|
||||
return buildTree(headers, high, low);
|
||||
}
|
||||
export function useActiveAnchor(container, marker) {
|
||||
const { isAsideEnabled } = useAside();
|
||||
const onScroll = throttleAndDebounce(setActiveLink, 100);
|
||||
let prevActiveLink = null;
|
||||
onMounted(() => {
|
||||
requestAnimationFrame(setActiveLink);
|
||||
window.addEventListener('scroll', onScroll);
|
||||
});
|
||||
onUpdated(() => {
|
||||
// sidebar update means a route change
|
||||
activateLink(location.hash);
|
||||
});
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('scroll', onScroll);
|
||||
});
|
||||
function setActiveLink() {
|
||||
if (!isAsideEnabled.value) {
|
||||
return;
|
||||
}
|
||||
const scrollY = window.scrollY;
|
||||
const innerHeight = window.innerHeight;
|
||||
const offsetHeight = document.body.offsetHeight;
|
||||
const isBottom = Math.abs(scrollY + innerHeight - offsetHeight) < 1;
|
||||
// resolvedHeaders may be repositioned, hidden or fix positioned
|
||||
const headers = resolvedHeaders
|
||||
.map(({ element, link }) => ({
|
||||
link,
|
||||
top: getAbsoluteTop(element)
|
||||
}))
|
||||
.filter(({ top }) => !Number.isNaN(top))
|
||||
.sort((a, b) => a.top - b.top);
|
||||
// no headers available for active link
|
||||
if (!headers.length) {
|
||||
activateLink(null);
|
||||
return;
|
||||
}
|
||||
// page top
|
||||
if (scrollY < 1) {
|
||||
activateLink(null);
|
||||
return;
|
||||
}
|
||||
// page bottom - highlight last link
|
||||
if (isBottom) {
|
||||
activateLink(headers[headers.length - 1].link);
|
||||
return;
|
||||
}
|
||||
// find the last header above the top of viewport
|
||||
let activeLink = null;
|
||||
for (const { link, top } of headers) {
|
||||
if (top > scrollY + getScrollOffset() + 4) {
|
||||
break;
|
||||
}
|
||||
activeLink = link;
|
||||
}
|
||||
activateLink(activeLink);
|
||||
}
|
||||
function activateLink(hash) {
|
||||
if (prevActiveLink) {
|
||||
prevActiveLink.classList.remove('active');
|
||||
}
|
||||
if (hash == null) {
|
||||
prevActiveLink = null;
|
||||
}
|
||||
else {
|
||||
prevActiveLink = container.value.querySelector(`a[href="${decodeURIComponent(hash)}"]`);
|
||||
}
|
||||
const activeLink = prevActiveLink;
|
||||
if (activeLink) {
|
||||
activeLink.classList.add('active');
|
||||
marker.value.style.top = activeLink.offsetTop + 39 + 'px';
|
||||
marker.value.style.opacity = '1';
|
||||
}
|
||||
else {
|
||||
marker.value.style.top = '33px';
|
||||
marker.value.style.opacity = '0';
|
||||
}
|
||||
}
|
||||
}
|
||||
function getAbsoluteTop(element) {
|
||||
let offsetTop = 0;
|
||||
while (element !== document.body) {
|
||||
if (element === null) {
|
||||
// child element is:
|
||||
// - not attached to the DOM (display: none)
|
||||
// - set to fixed position (not scrollable)
|
||||
// - body or html element (null offsetParent)
|
||||
return NaN;
|
||||
}
|
||||
offsetTop += element.offsetTop;
|
||||
element = element.offsetParent;
|
||||
}
|
||||
return offsetTop;
|
||||
}
|
||||
function buildTree(data, min, max) {
|
||||
resolvedHeaders.length = 0;
|
||||
const result = [];
|
||||
const stack = [];
|
||||
data.forEach((item) => {
|
||||
const node = { ...item, children: [] };
|
||||
let parent = stack[stack.length - 1];
|
||||
while (parent && parent.level >= node.level) {
|
||||
stack.pop();
|
||||
parent = stack[stack.length - 1];
|
||||
}
|
||||
if (node.element.classList.contains('ignore-header') ||
|
||||
(parent && 'shouldIgnore' in parent)) {
|
||||
stack.push({ level: node.level, shouldIgnore: true });
|
||||
return;
|
||||
}
|
||||
if (node.level > max || node.level < min)
|
||||
return;
|
||||
resolvedHeaders.push({ element: node.element, link: node.link });
|
||||
if (parent)
|
||||
parent.children.push(node);
|
||||
else
|
||||
result.push(node);
|
||||
stack.push(node);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user