mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-17 10:19:44 -07:00
docs: update generated docs
This commit is contained in:
@@ -9,20 +9,27 @@ function createContentsItem(section, path) {
|
||||
const anchor = document.createElement("a");
|
||||
const children = section.children ?? [];
|
||||
const sectionPath = [...path, section.title];
|
||||
const id = createPathId(sectionPath);
|
||||
|
||||
anchor.href = `#${createPathId(sectionPath)}`;
|
||||
anchor.href = `#${id}`;
|
||||
anchor.append(section.title);
|
||||
|
||||
if (children.length) {
|
||||
const details = document.createElement("details");
|
||||
const summary = document.createElement("summary");
|
||||
const list = document.createElement("ol");
|
||||
|
||||
for (const child of children) {
|
||||
list.append(createContentsItem(child, sectionPath));
|
||||
}
|
||||
item.append(list);
|
||||
|
||||
summary.append(anchor);
|
||||
details.append(summary, list);
|
||||
item.append(details);
|
||||
} else {
|
||||
item.append(anchor);
|
||||
}
|
||||
|
||||
item.prepend(anchor);
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,13 +21,19 @@ main.learn {
|
||||
text-transform: uppercase;
|
||||
|
||||
ol {
|
||||
display: grid;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li + li {
|
||||
margin-block-start: 0.25rem;
|
||||
summary {
|
||||
display: block;
|
||||
list-style: none;
|
||||
|
||||
&::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -40,8 +46,7 @@ main.learn {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
border-radius: 0.25rem;
|
||||
margin-block: -0.25rem;
|
||||
margin-inline: calc(-1 * var(--line-indent)) 1rem;
|
||||
margin-inline: 0 1rem;
|
||||
padding: 0.25rem;
|
||||
padding-inline-start: calc(var(--line-width) + var(--line-gap));
|
||||
|
||||
@@ -74,9 +79,6 @@ main.learn {
|
||||
|
||||
ol ol {
|
||||
--line-indent: var(--line-step);
|
||||
|
||||
margin-block-start: 0.25rem;
|
||||
margin-inline-start: var(--line-step);
|
||||
}
|
||||
|
||||
ol ol ol {
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
const storagePrefix = "bitview:learn-section";
|
||||
|
||||
/** @param {string} id */
|
||||
function getStorageKey(id) {
|
||||
return `${storagePrefix}:${id}`;
|
||||
}
|
||||
|
||||
/** @param {HTMLElement} section */
|
||||
function getDetails(section) {
|
||||
return /** @type {HTMLDetailsElement} */ (
|
||||
section.querySelector(":scope > details")
|
||||
);
|
||||
}
|
||||
|
||||
/** @param {HTMLElement} section */
|
||||
function getHash(section) {
|
||||
return `#${section.id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} main
|
||||
* @param {HTMLElement} section
|
||||
*/
|
||||
function getNavDetails(main, section) {
|
||||
const anchor = main.querySelector(`nav a[href="${getHash(section)}"]`);
|
||||
const summary = anchor?.parentElement;
|
||||
|
||||
return summary?.localName === "summary" ? summary.parentElement : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} main
|
||||
* @param {HTMLElement} section
|
||||
*/
|
||||
function syncNavSection(main, section) {
|
||||
const details = getNavDetails(main, section);
|
||||
|
||||
if (details) details.toggleAttribute("open", getDetails(section).open);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} main
|
||||
* @param {HTMLElement} section
|
||||
*/
|
||||
function openSection(main, section) {
|
||||
let current = section;
|
||||
|
||||
while (main.contains(current)) {
|
||||
const details = current.querySelector(":scope > details");
|
||||
if (details) {
|
||||
/** @type {HTMLDetailsElement} */ (details).open = true;
|
||||
syncNavSection(main, current);
|
||||
}
|
||||
|
||||
const parent = current.parentElement?.closest("section[id]");
|
||||
if (!parent) break;
|
||||
current = /** @type {HTMLElement} */ (parent);
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {HTMLElement} main */
|
||||
export function initSectionDetails(main) {
|
||||
const sections = [
|
||||
...main.querySelectorAll("section[id]:not([data-numbered='false'])"),
|
||||
].map((section) => /** @type {HTMLElement} */ (section));
|
||||
|
||||
for (const section of sections) {
|
||||
const saved = localStorage.getItem(getStorageKey(section.id));
|
||||
|
||||
getDetails(section).open = saved === "1";
|
||||
syncNavSection(main, section);
|
||||
}
|
||||
|
||||
main.addEventListener("toggle", (event) => {
|
||||
const details = /** @type {HTMLDetailsElement} */ (event.target);
|
||||
const section = /** @type {HTMLElement} */ (details.parentElement);
|
||||
if (!section.matches("article section[id]")) return;
|
||||
|
||||
localStorage.setItem(getStorageKey(section.id), details.open ? "1" : "0");
|
||||
syncNavSection(main, section);
|
||||
main.dispatchEvent(new Event("sectiontoggle"));
|
||||
}, { capture: true });
|
||||
|
||||
/** @param {string} hash */
|
||||
function openHash(hash) {
|
||||
const target = document.getElementById(hash.slice(1));
|
||||
|
||||
if (target && main.contains(target)) openSection(main, target);
|
||||
}
|
||||
|
||||
/** @param {string} hash */
|
||||
function toggleHash(hash) {
|
||||
const target = document.getElementById(hash.slice(1));
|
||||
if (!target || !main.contains(target)) return false;
|
||||
|
||||
const section = /** @type {HTMLElement} */ (target);
|
||||
const details = getDetails(section);
|
||||
|
||||
if (details.open) {
|
||||
details.open = false;
|
||||
syncNavSection(main, section);
|
||||
return false;
|
||||
}
|
||||
|
||||
openSection(main, section);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (window.location.hash) openHash(window.location.hash);
|
||||
|
||||
return {
|
||||
openHash,
|
||||
toggleHash,
|
||||
};
|
||||
}
|
||||
@@ -21,18 +21,24 @@ function scrollToTarget(target, behavior) {
|
||||
/**
|
||||
* @param {HTMLElement} main
|
||||
* @param {ScrollBehavior} behavior
|
||||
* @param {LearnDetails} details
|
||||
*/
|
||||
function scrollToCurrentHash(main, behavior) {
|
||||
const target = getHashTarget(main, window.location.hash);
|
||||
function scrollToCurrentHash(main, behavior, details) {
|
||||
const hash = window.location.hash;
|
||||
const target = getHashTarget(main, hash);
|
||||
|
||||
if (target) scrollToTarget(target, behavior);
|
||||
if (target) {
|
||||
details.openHash(hash);
|
||||
scrollToTarget(target, behavior);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} main
|
||||
* @param {(hash: string) => void} onHashNavigate
|
||||
* @param {LearnDetails} details
|
||||
*/
|
||||
export function initHashLinks(main, onHashNavigate) {
|
||||
export function initHashLinks(main, onHashNavigate, details) {
|
||||
main.addEventListener("click", (event) => {
|
||||
if (!isPlainLeftClick(event)) return;
|
||||
|
||||
@@ -47,6 +53,9 @@ export function initHashLinks(main, onHashNavigate) {
|
||||
if (!target) return;
|
||||
|
||||
event.preventDefault();
|
||||
const open = details.toggleHash(url.hash);
|
||||
if (!open) return;
|
||||
|
||||
onHashNavigate(url.hash);
|
||||
scrollToTarget(target, "smooth");
|
||||
|
||||
@@ -57,8 +66,10 @@ export function initHashLinks(main, onHashNavigate) {
|
||||
|
||||
window.addEventListener("popstate", () => {
|
||||
if (main.hidden) return;
|
||||
scrollToCurrentHash(main, "auto");
|
||||
scrollToCurrentHash(main, "auto", details);
|
||||
});
|
||||
|
||||
main.addEventListener("pageactive", () => scrollToCurrentHash(main, "auto"));
|
||||
main.addEventListener("pageactive", () => {
|
||||
scrollToCurrentHash(main, "auto", details);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createContents } from "./contents/index.js";
|
||||
import { sections } from "./data/index.js";
|
||||
import { createChart } from "./charts/index.js";
|
||||
import { initSectionDetails } from "./details.js";
|
||||
import { initHashLinks } from "./hash-links.js";
|
||||
import { initScrollSpy } from "./scroll-spy.js";
|
||||
import { createPathId } from "./path.js";
|
||||
@@ -18,6 +19,8 @@ function createSection(section, path = []) {
|
||||
const description = document.createElement("p");
|
||||
const children = section.children ?? [];
|
||||
const id = createPathId(sectionPath);
|
||||
/** @type {HTMLElement[]} */
|
||||
const content = [description];
|
||||
|
||||
element.id = id;
|
||||
if (section.numbered === false) element.dataset.numbered = "false";
|
||||
@@ -25,11 +28,21 @@ function createSection(section, path = []) {
|
||||
anchor.append(section.title);
|
||||
heading.append(anchor);
|
||||
description.append(section.description);
|
||||
element.append(heading, description);
|
||||
if (section.chart) element.append(createChart(section.chart, id));
|
||||
if (section.chart) content.push(createChart(section.chart, id));
|
||||
|
||||
for (const child of children) {
|
||||
element.append(createSection(child, sectionPath));
|
||||
content.push(createSection(child, sectionPath));
|
||||
}
|
||||
|
||||
if (section.numbered === false) {
|
||||
element.append(heading, ...content);
|
||||
} else {
|
||||
const details = document.createElement("details");
|
||||
const summary = document.createElement("summary");
|
||||
|
||||
summary.append(heading);
|
||||
details.append(summary, ...content);
|
||||
element.append(details);
|
||||
}
|
||||
|
||||
return element;
|
||||
@@ -45,7 +58,8 @@ export function createLearnPage() {
|
||||
}
|
||||
|
||||
main.append(createContents(sections), article);
|
||||
const details = initSectionDetails(main);
|
||||
const navigateToHash = initScrollSpy(main);
|
||||
initHashLinks(main, navigateToHash);
|
||||
initHashLinks(main, navigateToHash, details);
|
||||
return main;
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ export function initScrollSpy(main) {
|
||||
const viewportTop = getViewportTop();
|
||||
|
||||
for (const section of sections) {
|
||||
if (!section.getClientRects().length) continue;
|
||||
if (section.getBoundingClientRect().top > viewportTop) break;
|
||||
|
||||
currentSection = section;
|
||||
@@ -149,6 +150,7 @@ export function initScrollSpy(main) {
|
||||
alignNavToTop = true;
|
||||
scheduleUpdate();
|
||||
});
|
||||
main.addEventListener("sectiontoggle", scheduleUpdate);
|
||||
scheduleUpdate();
|
||||
return navigateToHash;
|
||||
}
|
||||
|
||||
+116
-42
@@ -23,6 +23,19 @@ main.learn {
|
||||
grid-template-columns: 14rem minmax(0, 1fr);
|
||||
padding: 0 var(--page-x);
|
||||
scroll-padding-top: var(--offset);
|
||||
interpolate-size: allow-keywords;
|
||||
|
||||
details::details-content {
|
||||
height: 0;
|
||||
overflow: clip;
|
||||
transition:
|
||||
height 1s ease,
|
||||
content-visibility 1s ease allow-discrete;
|
||||
}
|
||||
|
||||
details[open]::details-content {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
article {
|
||||
counter-reset: theme;
|
||||
@@ -79,8 +92,13 @@ main.learn {
|
||||
margin-top: 8rem;
|
||||
}
|
||||
|
||||
> section:has(> details:not([open])) + section {
|
||||
margin-top: var(--offset);
|
||||
}
|
||||
|
||||
section[id] {
|
||||
> :is(h1, h2, h3, h4) {
|
||||
> :is(h1, h2, h3, h4),
|
||||
> details > summary > :is(h1, h2, h3, h4) {
|
||||
a {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
@@ -115,17 +133,17 @@ main.learn {
|
||||
}
|
||||
|
||||
> section {
|
||||
> section {
|
||||
> details > section {
|
||||
counter-increment: topic;
|
||||
counter-reset: detail;
|
||||
scroll-margin-top: var(--offset);
|
||||
|
||||
> section {
|
||||
> details > section {
|
||||
counter-increment: detail;
|
||||
counter-reset: subtopic;
|
||||
scroll-margin-top: var(--offset);
|
||||
|
||||
> section {
|
||||
> details > section {
|
||||
counter-increment: subtopic;
|
||||
scroll-margin-top: var(--offset);
|
||||
}
|
||||
@@ -134,52 +152,108 @@ main.learn {
|
||||
}
|
||||
|
||||
section[id]:not([data-numbered="false"]) {
|
||||
> :is(h1, h2, h3, h4) {
|
||||
position: sticky;
|
||||
top: var(--offset);
|
||||
line-height: 1;
|
||||
background: var(--black);
|
||||
> details {
|
||||
> summary {
|
||||
position: relative;
|
||||
line-height: 1;
|
||||
list-style: none;
|
||||
background: var(--black);
|
||||
cursor: pointer;
|
||||
|
||||
&::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "+";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
translate: 0 -50%;
|
||||
color: var(--gray);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--font-size-sm);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
> :is(h1, h2, h3, h4) {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
&:has(> h1) {
|
||||
z-index: 8;
|
||||
|
||||
> h1 {
|
||||
border-bottom: 1px solid var(--gray);
|
||||
font-size: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
&:has(> h2) {
|
||||
z-index: 7;
|
||||
|
||||
> h2 {
|
||||
border-bottom: 1px dashed var(--gray);
|
||||
font-size: var(--topic-font-size);
|
||||
}
|
||||
}
|
||||
|
||||
&:has(> h3) {
|
||||
z-index: 6;
|
||||
|
||||
> h3 {
|
||||
border-bottom: 1px dotted var(--gray);
|
||||
font-size: var(--detail-font-size);
|
||||
}
|
||||
}
|
||||
|
||||
&:has(> h4) {
|
||||
z-index: 5;
|
||||
|
||||
> h4 {
|
||||
border-bottom: 1px dotted var(--gray);
|
||||
font-size: var(--subtopic-font-size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&[open] > summary::after {
|
||||
content: "-";
|
||||
}
|
||||
|
||||
&[open] > summary {
|
||||
position: sticky;
|
||||
top: var(--offset);
|
||||
|
||||
> h1 {
|
||||
padding-bottom: var(--heading-padding-bottom);
|
||||
}
|
||||
|
||||
> h2 {
|
||||
padding-top: var(--topic-padding-top);
|
||||
padding-bottom: var(--heading-padding-bottom);
|
||||
}
|
||||
|
||||
> h3 {
|
||||
padding-top: var(--detail-padding-top);
|
||||
padding-bottom: var(--detail-padding-bottom);
|
||||
}
|
||||
|
||||
> h4 {
|
||||
padding-top: var(--subtopic-padding-top);
|
||||
padding-bottom: var(--subtopic-padding-bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> h1 {
|
||||
z-index: 8;
|
||||
padding-bottom: var(--heading-padding-bottom);
|
||||
border-bottom: 1px solid var(--gray);
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
> h2 {
|
||||
z-index: 7;
|
||||
padding-top: var(--topic-padding-top);
|
||||
padding-bottom: var(--heading-padding-bottom);
|
||||
border-bottom: 1px dashed var(--gray);
|
||||
font-size: var(--topic-font-size);
|
||||
}
|
||||
|
||||
> h3 {
|
||||
z-index: 6;
|
||||
padding-top: var(--detail-padding-top);
|
||||
padding-bottom: var(--detail-padding-bottom);
|
||||
border-bottom: 1px dotted var(--gray);
|
||||
font-size: var(--detail-font-size);
|
||||
}
|
||||
|
||||
> h4 {
|
||||
z-index: 5;
|
||||
padding-top: var(--subtopic-padding-top);
|
||||
padding-bottom: var(--subtopic-padding-bottom);
|
||||
border-bottom: 1px dotted var(--gray);
|
||||
font-size: var(--subtopic-font-size);
|
||||
}
|
||||
|
||||
> p {
|
||||
> details > p {
|
||||
margin-top: 1rem;
|
||||
color: var(--white);
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: var(--line-height-sm);
|
||||
}
|
||||
|
||||
> figure {
|
||||
> details > figure {
|
||||
margin-top: 2rem;
|
||||
color: var(--gray);
|
||||
font-size: var(--font-size-xs);
|
||||
|
||||
Vendored
+5
@@ -6,6 +6,11 @@ declare global {
|
||||
numbered?: boolean;
|
||||
children?: LearnSection[];
|
||||
};
|
||||
|
||||
type LearnDetails = {
|
||||
openHash(hash: string): void;
|
||||
toggleHash(hash: string): boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export {};
|
||||
|
||||
Reference in New Issue
Block a user