example-viewer
Version:
A presentation tool for code examples.
83 lines (70 loc) • 2.74 kB
JavaScript
import { select, local } from "d3-selection";
import { component } from "d3-component";
import { getLoadedFiles } from "./getFiles";
import magicSandbox from "magic-sandbox";
const iframe = component("iframe", "shadow runner")
.create(function (){
select(this)
.attr("width", "960") // 960 X 500 is standard for bl.ocks.org.
.attr("height", "500")
.attr("marginwidth", "0") // Have no margin to match with bl.ocks.org.
.attr("marginheight", "0")
.attr("frameborder", "0px") // We'll have a shadow instead of a border.
.attr("scrolling", "no"); // Disable scrolling to match with bl.ocks.org.
})
.render(function ({ source, z }){
select(this).style("z-index", z);
if(source){
select(this).attr("srcdoc", source);
}
});
const framesPerSecond = 6; // Seems to be the fastest rate without flicker.
const filesLocal = local();
export default component("div")
.create(function (){
// The notion of double buffering is used to minimize flickering.
// These constants represent z-index values for iframe buffers.
const BACK = 4; // The header z-index is 3; this will be above that.
const FRONT = 5; // This is for the "front buffer"; visible to the user.
let buffers = [{ z: BACK }, { z: FRONT }];
let needsSwap = false;
setInterval(() => {
// Swap the z-index of buffers if needed.
if(needsSwap){
buffers = buffers.reverse();
iframe(this, buffers);
needsSwap = false;
}
// The existence of a value in filesLocal
// indicates that the content has changed.
const files = filesLocal.get(this);
// Set the content of the back buffer
// if the content has changed.
if(files){
// Set the content of the back buffer.
const template = files["index.html"].content;
const source = magicSandbox(template, files);
iframe(this, buffers.map(buffer => Object.assign({}, buffer, {
source: buffer.z === BACK ? source : null
})));
// Signal that buffers should be swapped on the next frame.
needsSwap = true;
// Signal that the content has been rendered into a buffer.
filesLocal.set(this, null);
}
}, 1000 / framesPerSecond);
})
.render(function (state){
const loadedFiles = getLoadedFiles(state);
if(loadedFiles){
// Transform into the format that magicSandbox expects.
// TODO make this the overall format? Seems better, more explicit.
const files = {};
Object.keys(loadedFiles).forEach(function (name){
files[name] = {
content : loadedFiles[name]
};
});
filesLocal.set(this, files);
}
});