Public
Edited
Mar 21, 2023
Importers
5 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
multi_cell_template = notebook(`
x = 10

// y = x * 10

y = x * 10 * 70
`)
Insert cell
multi_cell_template()
Insert cell
multi_cell_template({ x: 90 })
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
with_object
Insert cell
Insert cell
Insert cell
Insert cell
viewof notebook_using_value = notebook({ value_used_in_notebook })`
y = 10 + value_used_in_notebook
`('notebook_using_value')
Insert cell
Insert cell
child_notebook = notebook(`
x = 99

y = x * 10
`)
Insert cell
viewof notebook_using_child = notebook({ child_notebook })`
viewof x = child_notebook({})

y = 10 + x.y
`('notebook_using_child')
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof notebook_using_child_with_value = notebook({ childnotebook_with_value })`
viewof x = childnotebook_with_value({})

y = 10 + x.y
`('notebook_using_child_with_value')
Insert cell
Insert cell
Insert cell
clean_imports_notebook = notebook({ clean_imports_importable })`
y = clean_imports_importable + 20

x = y + 10
`
Insert cell
viewof clean_imports = clean_imports_notebook('clean_imports')
Insert cell
clean_imports
Insert cell
Insert cell
Insert cell
Insert cell
viewof viewofs_in_notebook = notebook`
viewof HEIGHT = html\`<input type="range" value="10" min="0" max="50" />\`

result = HEIGHT * 10
`('viewofs_in_notebook')
Insert cell
Insert cell
how_does_it_stack_up_child = notebook({ parse_stack })`
parsed_stack = parse_stack()
full_stack = new Error()
`
Insert cell
viewof how_does_it_stack_up = notebook({ how_does_it_stack_up_child })`
stack = new Error()
viewof child = how_does_it_stack_up_child()
`('how_does_it_stack_up')
Insert cell
Insert cell
viewof Unnamed_cells = notebook`
md\`This cell has text in front of it\`

result = 10

md\`And text after it!!!\`
`('Unnamed_cells')
Insert cell
Insert cell
// viewof import_notebook = notebook(`
// import { x } from "@dralletje/simple-importable-example";
// `)()
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
MONAD_TYPES = ['VALUE', 'ASYNC-VALUE', 'GENERATOR', 'ASYNC-GENERATOR'];
Insert cell
Oh_No_It_Is_Monads = (args, fn) => {
let has_promises = args.some(x => x && x.then != null);
if (has_promises) {
return Promise.all(args).then(args => fn(...args));
}

let result = fn(...args);
}
Insert cell
EXPAND = () => {
}
Insert cell
generator = {
yield 10;
yield 20;
yield 30;
yield 40;
yield 50;
}
Insert cell
Insert cell
notebook({})`
viewof number = html\`<input type="range" value="10" \>\`

result = number * 20
`({}, false)["result"]
Insert cell
notebook({})`
viewof number = html\`<input type="range" value="10" \>\`

result = number * 20
`({ number: 30 }, false)["result"]
Insert cell
child_notebook({}, false)
Insert cell
promise_returning_notebook = notebook({})`
promise = new Promise(resolve => setTimeout(() => resolve(100), 1000))

promise_result = promise * 20

result = 200
`
Insert cell
promise_returning_notebook()
Insert cell
promise_returning_notebook({}, false).result * 30
Insert cell
promise_returning_notebook({}, false).promise_result
Insert cell
Insert cell
direct_library = ({
html: html,
md: md,
Generators: {
input: x => valueof(x)
}
})
Insert cell
execute_notebook_direct = (definitions, imports, replacements) => {
let cells = new Map();

for (let [key, value] of Object.entries(direct_library)) {
cells.set(key, {
using: [],
derive: () => value
});
}

for (let [key, value] of Object.entries(imports)) {
cells.set(key, {
using: [],
derive: () => value
});
}

for (let definition of definitions) {
cells.set(definition.name, {
using: definition.using,
derive: definition.derive,
export: true
});

for (let export_cell of definition.exports || []) {
cells.set(export_cell.name, {
using: export_cell.using,
derive: export_cell.derive,
export: true
});
}
}

for (let [key, value] of Object.entries(replacements)) {
cells.set(key, {
using: [],
derive: () => value
});
}

let results = {};
let cached_results = new Map();
for (let [key, value] of cells.entries()) {
Object.defineProperty(results, key, {
enumerable: Boolean(value.export),
get: () => {
if (cached_results.has(key)) {
return cached_results.get(key);
}

let args = value.using.map(x => results[x]);

let has_promises = args.some(x => x && x.then != null);
let result = has_promises
? Promise.all(args).then(args => value.derive(...args))
: value.derive(...args);
cached_results.set(key, result);

return cached_results.get(key);
}
});
}

return results;
}
Insert cell
parser.parseCell(`
x = 10
`)
Insert cell
function notebook(cells, imports = {}) {
// Just a string
if (typeof cells === 'string') {
let cell_asts = parser.parseModule(cells).cells;
return notebook(cell_asts, imports);
}
// Template string
if (cells.raw) {
let string_parts = cells;
if (string_parts.length > 1) {
// prettier-ignore
throw new Error(`Don't use interpolations in your template, use the imports object instead`);
}

let source = string_parts[0];
let cell_asts = parser.parseModule(source).cells;
return notebook(cell_asts, imports);
}
// Imports objects followed by template
if (!Array.isArray(cells)) {
let imports = cells;
return (string_parts, interpolations) => {
// We don't have to check out interpolations at all,
// because if we have them (thus string_parts will have multiple strings),
// the template handler will throw
return notebook(string_parts, imports);
};
}

let definitions = cells.map(cell => cell_to_definition(cell));

return (name, replacements = {}, visual = true) => {
if (visual === false || replacements === false) {
if (typeof name !== 'string') {
visual = replacements;
replacements = name;
name = null;
}
return execute_notebook_direct(definitions, imports, replacements);
}

// I'm using this to determine whether or not to start collapsed
let parsed_cell_name = null;
try {
parsed_cell_name = parse_stack();
} catch (error) {}

if (typeof name === 'object' || name == null) {
replacements = name || {};

try {
name = parse_stack() || null;
} catch (err) {
name = null;
}
}

const runtime = new ObservableRuntime();
const main = runtime.module();

// Create "hidden" cells for values imported from parent notebook
for (let [key, value] of Object.entries(imports)) {
main.define(key, [], () => value);
}

let inputs = [];
for (let [key, value] of Object.entries(replacements)) {
let output_container = document.createElement('div');
main
.variable(new Inspector(output_container))
.define(key, [], () => value);

inputs.push(dedent(html)`
<div class="cell">
<div class="line">${output_container}</div>
</div>
`);
}

let outputs = [];
for (let cell of definitions) {
if (replacements[cell.name]) {
continue;
}

let output_container = document.createElement('div');

let variable = main.variable(new Inspector(output_container));
let clear_exports = activate_cells_on_module({
module: main,
variable: variable,
cell: cell
});

if (cell.code) {
let textarea = CodeEditor({
cell: cell,
onChange: new_cell => {
clear_exports();
clear_exports = activate_cells_on_module({
module: main,
variable: variable,
cell: new_cell
});
cell_fragment.classList.toggle(
"changed",
!ast_equal(new_cell.ast, cell.ast)
);
}
});

let cell_fragment = dedent(html)`
<div class="cell collapsed">
<div
class="toggle"
onClick=${event => {
event.target.closest('.cell').classList.toggle("collapsed");
}}
></div>
<div class="line">${output_container}</div>
<code class="line">${textarea}</code>
</div>
`;

outputs.push(cell_fragment);
}
}

// let START_EXPANDED = !Boolean(parsed_cell_name);
let START_EXPANDED = true;

let notebook_element = dedent(html)`
<div class="sub-notebook ${START_EXPANDED ? "" : "collapsed-notebook"}">
${
inputs.length > 0
? html.fragment`
${inputs}
<div class="delimiter"></div>
${outputs}
`
: html.fragment`
${outputs}
`
}
</div>
`;

let names = definitions
.filter(x => x.name)
.flatMap(x => [x.name, ...(x.exports || []).map(x => x.name)]);

let inspecting_element = document.createElement('div');
let small_inspect_variable = main
.variable(new Inspector(inspecting_element))
.define(names, (...args) => Object.fromEntries(_.zip(names, args)));

let downsized_caret = dedent(html)`
<svg width="8" height="8" class="observablehq--caret">
<path d="M4 7L0 1h8z" fill="currentColor"></path>
</svg>
`;
let inspecting_element_container = dedent(html)`
<div style="pointer-events: none">
${inspecting_element}
</div>
`;

let container = dedent(html)`
<div>
${
name
? dedent(html)`
<div
class="small-inspect"
onClick=${event => {
if (
event.currentTarget.contains(inspecting_element_container)
) {
inspecting_element_container.replaceWith(downsized_caret);
notebook_element.classList.toggle(
"collapsed-notebook",
false
);
} else {
downsized_caret.replaceWith(inspecting_element_container);
notebook_element.classList.toggle(
"collapsed-notebook",
true
);
}
}}
>
<span class="observablehq--cellname observablehq--inspect">${name} = </span>
${
START_EXPANDED
? downsized_caret
: inspecting_element_container
}
</div>
`
: html`<div class="notebook-spacer"></div>`
}
${notebook_element}
<div class="notebook-spacer"></div>
</div>
`;

let variable = main.variable({
fulfilled: result => {
container.value = result;
container.dispatchEvent(new CustomEvent('input'));
},
rejected: error => {
console.log('Error:', error);
container.value = error;
container.dispatchEvent(new CustomEvent('input'));
}
});
variable.define(names, (...args) => Object.fromEntries(_.zip(names, args)));

return container;
};
}
Insert cell
activate_cells_on_module = ({ module, variable, cell }) => {
let derive_with_name = wrap_with_name(cell.name, cell.derive);
variable.define(cell.name, cell.using, derive_with_name);

let exports = [];
for (let export_cell of cell.exports || []) {
let exported_variable = module.define(
export_cell.name,
export_cell.using,
export_cell.derive
);
exports.push(exported_variable);
}
return () => {
for (let exported of exports) {
exported.delete();
}
};
}
Insert cell
Insert cell
Insert cell
import {
PROPERTY_parse_stack as parse_stack,
PROPERTY_wrap_with_name as wrap_with_name
} from "@dralletje/cell-name"
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more