Skip to content

Commit

Permalink
sidebar sections
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock committed Nov 13, 2023
1 parent 0e6ae72 commit 1e700cb
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 30 deletions.
45 changes: 27 additions & 18 deletions docs/.observablehq/config.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
export default {
title: "Observable CLI",
pages: [
{path: "/index", name: "Overview"},
{path: "/getting-started", name: "Getting started"},
{path: "/markdown", name: "Markdown"},
{path: "/html", name: "HTML"},
{path: "/javascript", name: "JavaScript"},
{path: "/javascript/reactivity", name: "Reactivity"},
{path: "/javascript/display", name: "Display"},
{path: "/javascript/files", name: "Files"},
{path: "/javascript/promises", name: "Promises"},
{path: "/javascript/generators", name: "Generators"},
{path: "/javascript/mutables", name: "Mutables"},
{path: "/javascript/imports", name: "Imports"},
{path: "/javascript/inputs", name: "Inputs"},
{path: "/dot", name: "DOT"},
{path: "/mermaid", name: "Mermaid"},
{path: "/tex", name: "TeX"},
{path: "/loaders", name: "Data loaders"},
{path: "/contributing", name: "Contributing"},
{name: "Getting started", path: "/getting-started"},
{name: "Markdown", path: "/markdown"},
{name: "JavaScript", path: "/javascript"},
{name: "Data loaders", path: "/loaders"},
{
name: "JavaScript",
pages: [
{name: "Reactivity", path: "/javascript/reactivity"},
{name: "Display", path: "/javascript/display"},
{name: "Files", path: "/javascript/files"},
{name: "Promises", path: "/javascript/promises"},
{name: "Generators", path: "/javascript/generators"},
{name: "Mutables", path: "/javascript/mutables"},
{name: "Imports", path: "/javascript/imports"},
{name: "Inputs", path: "/javascript/inputs"}
]
},
{
name: "Features",
pages: [
{name: "HTML", path: "/html"},
{name: "DOT", path: "/dot"},
{name: "Mermaid", path: "/mermaid"},
{name: "TeX", path: "/tex"}
]
},
{name: "Contributing", path: "/contributing"}
]
};
60 changes: 56 additions & 4 deletions public/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ body {
#observablehq-sidebar {
position: fixed;
background: var(--theme-background-alt);
color: var(--theme-foreground);
color: var(--theme-foreground-muted);
font: 14px var(--sans-serif);
font-weight: 600;
font-weight: 500;
width: 240px;
z-index: 1;
top: 0;
Expand All @@ -85,13 +85,59 @@ body {
}

#observablehq-sidebar ol {
list-style: none;
margin: 0;
padding: 0;
}

#observablehq-sidebar ol li {
display: block;
#observablehq-sidebar > ol,
#observablehq-sidebar > details {
position: relative;
padding: 0.5rem 0 0;
margin: 0.5rem 0;
}

#observablehq-sidebar ol {
padding-bottom: 1rem;
}

#observablehq-sidebar > ol,
#observablehq-sidebar > details {
position: relative;
border-top: solid 1px var(--theme-foreground-faintest);
}

#observablehq-sidebar > ol:first-child {
border-top: none;
font-weight: 700;
padding: 0;
margin: 0;
color: var(--theme-foreground);
}

#observablehq-sidebar summary {
list-style: none;
font-weight: 700;
color: var(--theme-foreground);
}

#observablehq-sidebar summary::marker {
display: none;
}

#observablehq-sidebar summary::after {
position: absolute;
right: 1rem;
margin-top: -0.25rem;
font-weight: 700;
font-size: 20px;
content: "⟩";
transition: transform 250ms ease;
transform-origin: 50% 50%;
}

#observablehq-sidebar details[open] summary:after {
transform: rotate(90deg);
}

#observablehq-sidebar-toggle {
Expand Down Expand Up @@ -122,15 +168,21 @@ body {
content: "⟨";
}

#observablehq-sidebar summary,
.observablehq-link a {
display: block;
padding: 0.5rem 1rem;
}

#observablehq-sidebar summary:hover,
.observablehq-link a:hover {
background: var(--theme-background);
}

.observablehq-link a:hover {
color: var(--theme-foreground-focus);
}

.observablehq-link a[href] {
color: inherit;
}
Expand Down
13 changes: 12 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import {stat} from "node:fs/promises";
import {join} from "node:path";

export interface Page {
name: string;
path: string;
}

export interface Section {
name: string;
open?: boolean;
pages: Page[];
}

export interface Config {
title?: string;
pages?: {path: string; name: string}[];
pages?: (Page | Section)[]; // TODO rename to sidebar?
}

export async function readConfig(root: string): Promise<Config | undefined> {
Expand Down
51 changes: 44 additions & 7 deletions src/render.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {dirname, join} from "node:path";
import {type Config} from "./config.js";
import {type Config, type Page} from "./config.js";
import {computeHash} from "./hash.js";
import {resolveImport} from "./javascript/imports.js";
import {type FileReference, type ImportReference} from "./javascript.js";
Expand Down Expand Up @@ -87,13 +87,44 @@ ${JSON.stringify(parseResult.data)}
${
showSidebar
? `<input id="observablehq-sidebar-toggle" type="checkbox">
<nav id="observablehq-sidebar">
<nav id="observablehq-sidebar">${
title
? `
<ol>
<li class="observablehq-link">
<a href="/">${escapeData(title)}</a>
</li>
</ol>`
: ""
}
<ol>${pages
?.map(
(p) => `
<li class="observablehq-link${p.path === path ? " observablehq-link-active" : ""}"><a href="${escapeDoubleQuoted(
p.path.replace(/\/index$/, "") || "/"
)}">${escapeData(p.name)}</a></li>`
?.map((p, i) =>
"pages" in p
? `${i > 0 && "path" in pages[i - 1] ? "</ol>" : ""}
<details${p.open === undefined || p.open ? " open" : ""}>
<summary>${escapeData(p.name)}</summary>
<ol>${p.pages
.map(
(p) => `
${renderListItem(p, path)}`
)
.join("")}
</ol>
</details>`
: "path" in p
? `${
i === 0
? `
`
: !("path" in pages[i - 1])
? `
</ol>
<ol>
`
: `
`
}${renderListItem(p, path)}`
: null
)
.join("")}
</ol>
Expand All @@ -114,6 +145,12 @@ ${parseResult.html}</main>
`;
}

function renderListItem(p: Page, path: string): string {
return `<li class="observablehq-link${
p.path === path ? " observablehq-link-active" : ""
}"><a href="${escapeDoubleQuoted(p.path.replace(/\/index$/, "") || "/")}">${escapeData(p.name)}</a></li>`;
}

function getImportPreloads(parseResult: ParseResult, path: string): Iterable<string> {
const specifiers = new Set<string>(["npm:@observablehq/runtime"]);
for (const {name, type} of parseResult.imports) {
Expand Down

0 comments on commit 1e700cb

Please sign in to comment.