Public
Edited
Jan 25, 2024
Fork of Slides
Importers
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
slide.md("callout")`
# Draw attention to this point
`
Insert cell
slide.html('small')`
<div style="height:40vw">Code is a code example -- edit the code below to change the display
</div>
`
Insert cell
annotate.md(
[
{ query: "em", type: "underline", color: "#B71C1C" },
{ queryAll: "strong", type: "circle", color: "#1C1CB7" },
{ query: "h1", type: "underline", color: "#1C1CB7" },
{ query: "span", type: "highlight", multiline: true, color: "yellow" },
{ query: ".orz", type: "crossed-off", color: "red" },
{ query: ".graph", type: "underline", color: "blue" },
{ exclude: true, queryAll: "no-annotation" }
],
visibility
)("callout")`
# Annotations

**this ** is a test
*and*

<span>multiline is
also
aupported
</span>
<strong class="orz">this?</strong>
<no-annotation>**exclude** from rough</no-annotation>
<span class='graph'>
${dot`
digraph {
rankdir = LR
node[shape=record]
a -> b -> c
}
`}
${htl.html`
<rough-notation showOnLoad type="crossed-off" color="yellow"><p>forbidden</p></rough-notation>
<rough-notation showOnLoad type="highlight" color="#FF6d00">
<h2>WC Rough Notation</h2>
</rough-notation>
`}


</span>
`
Insert cell
annotate.html([{ query: "div", type: "underline", color: "#B71C1C" }])`


<div>

hello <no-annotation>**world**</no-annotation>

</div>

<rough-notation showOnLoad type="highlight" color="#FF6d00">
<h2>WC Rough Notation</h2>
</rough-notation>

`
Insert cell
Insert cell
Insert cell
Insert cell
// annotation
annotate = ({
md: wrap_annotation(md),
html: wrap_annotation(html),
svg: wrap_annotation(svg)
})
Insert cell
wrap_annotation = (fn) =>
(config, vis) =>
(...args) => {
let ext_fn = wrap(fn, "slide");
let configs = Array.isArray(config) ? config : [config];
let get_candidates = (cfg, e) => {
let candidates = [];
if (cfg.query) {
candidates.push(e.querySelector(cfg.query));
} else if (cfg.queryAll) {
candidates = candidates.concat(
Array.from(e.querySelectorAll(cfg.queryAll))
);
} else {
candidates.push(e);
}
return candidates;
};
let apply_annotation = (e) => {
// find all exclusion elements and it's children
let exclusions = configs
.filter((cfg) => cfg.exclude)
.map((cfg) => {
if (!e) return null;
return get_candidates(cfg, e);
})
.flat()
.filter((e) => !!e)
.map((e) => {
return Array.from(e.querySelectorAll("*"));
})
.flat();
let annotations = configs
.filter((cfg) => !cfg.exclude)
.map((cfg) => {
if (!e) return null;
let candidates = get_candidates(cfg, e);

return candidates
.filter((c) => c && !exclusions.includes(c))
.map((c) => roughNotation.annotate(c, cfg));
});

if (annotations.length > 0) {
// TODO: custom group
const ag = roughNotation.annotationGroup(
annotations.flat().filter((a) => a)
);

ag.show();
}
return e;
};
if (args[0].raw) {
if (vis) return vis().then(() => apply_annotation(ext_fn(...args)));
else return apply_annotation(ext_fn(...args));
} else {
let f = ext_fn(args[0]);

return (...rest) => {
if (vis) return vis().then(() => apply_annotation(f(...rest)));
else return apply_annotation(f(...rest));
};
}
}
Insert cell
slide = ({
md: wrap(md, 'slide'),
html: wrap(html, 'slide')
})
Insert cell
notes = ({
md: wrap(md, 'notes'),
html: wrap(html, 'notes')
})
Insert cell
wrap = (fn, className) =>
(...args) => {
if (args[0].raw)
return html`<div class="${className}">${fn(...args)}</div>`;
else
return (...rest) => html`
<div class="${className} ${args
.map((s) => `${className}--${s}`)
.join(" ")}">${fn(...rest)}</div>
`;
}
Insert cell
// hide li elements (so they can be shown on key events)
hide_li_elements = function (slide) {
const lis = slide.querySelectorAll(".observablehq .slide li");
lis.forEach((li) => li.classList.add("hidden"));
}
Insert cell
keyBindings = {
let currentSlide = 0;
let showNotes = () => {};
let show_slide = (index) => {
const slides = document.querySelectorAll(".observablehq .slide");
let target_index = Math.min(index, slides.length - 1);
const slide = slides[target_index];
slide.scrollIntoView();
showNotes(slide);
};
function keyUp(event) {
const slides = document.querySelectorAll(".observablehq .slide");
switch (event.key) {
case "ArrowUp":
case "ArrowLeft":
currentSlide = Math.max(currentSlide - 1, 0);
break;
case "ArrowDown":
case "ArrowRight":
currentSlide = Math.min(currentSlide + 1, slides.length - 1);

break;
case "Home":
currentSlide = 0;
break;
case "End":
currentSlide = slides.length - 1;

break;
}
show_slide(currentSlide);
}

document.removeEventListener("keyup", keyUp);
document.addEventListener("keyup", keyUp);

const button = html`<button>Notes</button>`;
button.onclick = () => {
const notes = open("about:blank", "notes", "width=300,height=200").document
.body;
showNotes = (slide) => {
notes.innerHTML = "";
let cell = slide.parentNode;
const siblings = Array.from(document.querySelectorAll(".observablehq"));
for (const sibling of siblings.slice(siblings.indexOf(cell))) {
if (sibling.querySelector(".notes")) {
notes.innerHTML = sibling.innerHTML;
break;
}
}
};
};

return button;
}
Insert cell
divider_color = "rgb(128, 207, 156)"
Insert cell
style = html`
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@300&display=swap" rel="stylesheet">
<style>
.slide,
.notes {
padding: 1% 10%;
width: calc(100% + 28px);
box-sizing: border-box;
margin: 0 -14px;
}

.slide {
min-height: 64vw;
display: flex;
align-items: center;
}
.slide{
font-size: 3vw;
}
.slide {
border: solid 0.1em transparent;
border-bottom-color: ${divider_color};
}

.slide.slide--small {
padding-bottom: 1%;
min-height: auto;
}

.slide.slide--bleed {
padding: 0;
min-height: 0;
}

.slide h1,
.slide h2,
.slide p,
.slide pre,
.slide img {
max-width: 100%;
}



.slide small {
color: gray;
}

.slide blockquote,
.slide ol,
.slide ul {
max-width: none;
}

.slide > * {
width: 100%;
}
.slide > iframe {
height: 56vw;
}

.slide ul,
.slide ol {
padding-left: 3vw;
margin-bottom: 0;
}
.slide ul li::before,
.slide ol li::before {
margin-left: -2vw;
}


.slide--intro {
background-color:black;
color:white;
font-family:"Lato", sans-serif;
}
.slide--intro h1 {
border-bottom: 1px solid ${divider_color};
width: 100%;
color:white;
margin-bottom:3px;
}

.slide--intro p {
margin-top:0px;
}

.slide--feature p {
border-top: 1px solid ${divider_color};
font-size:.8em;
position: absolute;
bottom: 0px;
width: 83%;
}
.slide--feature p code {
font-size:.8em;
}

p code, li code {color: #c30771;}

li.hidden {
opacity:0;
}

.slide--bullets {
align-items:flex-start;
}
.slide--bullets h1 {
font-family:"Lato", sans-serif;
font-size:1.75em;
border-bottom: 1px solid ${divider_color};
}

.slide--bullets ul {
list-style-position: outside;
list-style-type: none;
padding:0px;
}

.slide--callout {
background-color:${divider_color};
}

.slide--feature > span {
position:relative;
height:100%;
}
.slide--feature > span > img {
max-height:calc(100% - 210px);
border: 1px solid black;
position: absolute;
top: 43%;
transform: translate(-50%, -50%);
left:50%;
}
.slide--feature > span > p {
bottom:30px;
width:100%;
}
.slide {
height: 64vw;
}
</style>`
Insert cell
roughNotation = import(
"https://unpkg.com/rough-notation@0.5.0/lib/rough-notation.esm.js?module"
)
Insert cell
roughComponent = import(
"https://unpkg.com/vanilla-rough-notation@0.4.6/index.js?module"
)
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more