livebook/assets/js/vega_lite/index.js

90 lines
2.3 KiB
JavaScript
Raw Normal View History

import * as vega from "vega";
import vegaEmbed from "vega-embed";
import { getAttributeOrThrow } from "../lib/attribute";
// See https://github.com/vega/vega-lite/blob/b61b13c2cbd4ecde0448544aff6cdaea721fd22a/src/compile/data/assemble.ts#L228-L231
const DEFAULT_DATASET_NAME = "source_0";
/**
* A hook used to render graphics according to the given
* Vega-Lite specification.
*
* The hook expects a `vega_lite:<id>:init` event with `{ spec }` payload,
* where `spec` is the graphic definition as an object.
*
* Later `vega_lite:<id>:push` events may be sent with `{ data, dataset, window }` payload,
* to dynamically update the underlying data.
*
* Configuration:
*
* * `data-id` - plot id
*
*/
const VegaLite = {
mounted() {
this.props = getProps(this);
this.state = {
container: null,
viewPromise: null,
};
this.state.container = document.createElement("div");
this.el.appendChild(this.state.container);
this.handleEvent(`vega_lite:${this.props.id}:init`, ({ spec }) => {
if (!spec.data) {
spec.data = { values: [] };
}
this.state.viewPromise = vegaEmbed(this.state.container, spec, {}).then(
(result) => result.view
);
});
this.handleEvent(
`vega_lite:${this.props.id}:push`,
({ data, dataset, window }) => {
dataset = dataset || DEFAULT_DATASET_NAME;
this.state.viewPromise.then((view) => {
const currentData = view.data(dataset);
const changeset = buildChangeset(currentData, data, window);
view.change(dataset, changeset).run();
});
}
);
},
updated() {
this.props = getProps(this);
},
destroyed() {
if (this.state.viewPromise) {
this.state.viewPromise.then((view) => view.finalize());
}
},
};
function getProps(hook) {
return {
id: getAttributeOrThrow(hook.el, "data-id"),
};
}
function buildChangeset(currentData, newData, window) {
if (window === 0) {
return vega.changeset().remove(currentData);
} else if (window) {
const toInsert = newData.slice(-window);
const freeSpace = Math.max(window - toInsert.length, 0);
const toRemove = currentData.slice(0, -freeSpace);
return vega.changeset().remove(toRemove).insert(toInsert);
} else {
return vega.changeset().insert(newData);
}
}
export default VegaLite;