Public
Edited
Apr 25
Paused
Importers
Insert cell
Insert cell
viewof table = PersistentTable("names")
Insert cell
viewof name = Inputs.text({ label: "Add some names" , submit: true})
Insert cell
Insert cell
Insert cell
navioTable
Insert cell
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
async function PersistentTable(
tableName = "data", // the name of the table in the storage
{
value = [],
tableOptions,
storageName = "PersistentTable", // Name of IndexedDB storage
keyName = "data",
label = (widget) =>
`PersistentTable: ${tableName} (${widget.value.length})`, // can return an html element
exportFormat = "csv", // Can also be json
summaryWidget = Inputs.table,
confirm = false // Confirm only works outside observable
} = {}
) {
tableOptions = { select: false, layout: "fixed", ...tableOptions };

// IndexedDB Initialization
const db = IndexedDBStorage({ storageName, tableName });

let table;

const labelHolder = htl.html`<label></label>`;
const tableHolder = htl.html`<div></div>`;
const clearButton = htl.html`<button>Clear</button>`;
const exportButtonHolder = htl.html`<span></span>`;
const importButton = await dataInput({ value: value, label: "Import" });
const target = htl.html`<div>${labelHolder}
<div style="display: flex"> ${importButton} &nbsp; ${exportButtonHolder} &nbsp;&nbsp; ${clearButton}</div>

${tableHolder}</div>`;

// Tries to load the cached value
value = (await db.get(keyName)) || value;

const widget = ReactiveWidget(target, { value, showValue });

// Stores the value and update the widget
async function cacheValue(data) {
await db.set(keyName, data);
widget.setValue(data);
}

// Updates the widget, not calling the event
async function showValue() {
exportButtonHolder.innerHTML = "";
exportButtonHolder.appendChild(
DOM.download(
exportFormat.toLowerCase() === "csv"
? new Blob([d3.csvFormat(widget.value)], { type: "text/csv" })
: new Blob([JSON.stringify(widget.value)], {
type: "application/json"
}),
`${tableName}.${exportFormat}`,
"Export"
)
// DOM.download(
// new Blob([JSON.stringify(widget.value)], { type: "application/json" }),
// `${tableName}.json`,
// "Export"
// )
);
// console.log("PersistentTable showValue");
labelHolder.innerHTML = "";
labelHolder.appendChild(htl.html`${label(widget)}`);
tableHolder.innerHTML = "";

table = await summaryWidget(widget.value, {
...tableOptions,
value: table?.value
});
table.addEventListener("input", (evt) => {
// console.log("PersistentTable widget input", table);
evt.preventDefault();
evt.stopPropagation();
});
tableHolder.appendChild(table);
}

// Clearing
clearButton.addEventListener("click", async () => {
const doClear = async () => {
await db.clear(keyName);
cacheValue([]);
};
// When running withing observable modals are disallowed
try {
if (
confirm &&
!window.confirm("Do you want to delete your whole selection?")
) {
return;
}
doClear();
} catch {
doClear();
}
});

importButton.addEventListener("input", () => {
cacheValue(importButton.value);
});


widget.update = cacheValue;
widget.push = (val) => cacheValue(target.value.concat(val));
widget.value = value;

return target;
}
Insert cell
onNameSubmit = {
if (!name) return;
(viewof table).update([...viewof table.value, { name: name }]);
}
Insert cell
import {IndexedDBStorage} from "@john-guerra/indexeddb-storage"
Insert cell
import {dataInput} from "@john-guerra/data-input"
Insert cell
import { navio } from "@john-guerra/navio"
Insert cell
ReactiveWidget = require("reactive-widget-helper@0.0.3")
Insert cell
isSandboxedIframe()
Insert cell
function isSandboxedIframe() {
if (window.parent === window) return 'no-iframe';
try { var f = window.frameElement; } catch(err) { f = null; }
if(f === null) {
if(document.domain !== '') return 'unkown'; // Probably 'non-sandboxed'
if(location.protocol !== 'data:') return 'sandboxed';
return 'unkown'; // Can be 'sandboxed' on Firefox
}
return f.hasAttribute('sandbox') ? 'sandboxed' : 'non-sandboxed';
}
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