|
import embed from "https://cdn.jsdelivr.net/npm/vega-embed@6/+esm"; |
|
import debounce from "https://cdn.jsdelivr.net/npm/[email protected]/debounce/+esm"; |
|
|
|
export async function render({ model, el }) { |
|
let finalize; |
|
|
|
function showError(error){ |
|
el.innerHTML = ( |
|
'<div style="color:red;">' |
|
+ '<p>JavaScript Error: ' + error.message + '</p>' |
|
+ "<p>This usually means there's a typo in your chart specification. " |
|
+ "See the javascript console for the full traceback.</p>" |
|
+ '</div>' |
|
); |
|
} |
|
|
|
const reembed = async () => { |
|
if (finalize != null) { |
|
finalize(); |
|
} |
|
|
|
let spec = model.get("spec"); |
|
let api; |
|
try { |
|
api = await embed(el, spec); |
|
} catch (error) { |
|
showError(error) |
|
return; |
|
} |
|
|
|
finalize = api.finalize; |
|
|
|
|
|
const wait = model.get("debounce_wait") ?? 10; |
|
const maxWait = wait; |
|
|
|
const initialSelections = {}; |
|
for (const selectionName of Object.keys(model.get("_vl_selections"))) { |
|
const storeName = `${selectionName}_store`; |
|
const selectionHandler = (_, value) => { |
|
const newSelections = cleanJson(model.get("_vl_selections") ?? {}); |
|
const store = cleanJson(api.view.data(storeName) ?? []); |
|
|
|
newSelections[selectionName] = {value, store}; |
|
model.set("_vl_selections", newSelections); |
|
model.save_changes(); |
|
}; |
|
api.view.addSignalListener(selectionName, debounce(selectionHandler, wait, {maxWait})); |
|
|
|
initialSelections[selectionName] = { |
|
value: cleanJson(api.view.signal(selectionName) ?? {}), |
|
store: cleanJson(api.view.data(storeName) ?? []) |
|
} |
|
} |
|
model.set("_vl_selections", initialSelections); |
|
|
|
const initialParams = {}; |
|
for (const paramName of Object.keys(model.get("_params"))) { |
|
const paramHandler = (_, value) => { |
|
const newParams = JSON.parse(JSON.stringify(model.get("_params"))) || {}; |
|
newParams[paramName] = value; |
|
model.set("_params", newParams); |
|
model.save_changes(); |
|
}; |
|
api.view.addSignalListener(paramName, debounce(paramHandler, wait, {maxWait})); |
|
|
|
initialParams[paramName] = api.view.signal(paramName) ?? null |
|
} |
|
model.set("_params", initialParams); |
|
model.save_changes(); |
|
|
|
|
|
model.on('change:_params', async (new_params) => { |
|
for (const [param, value] of Object.entries(new_params.changed._params)) { |
|
api.view.signal(param, value); |
|
} |
|
await api.view.runAsync(); |
|
}); |
|
} |
|
|
|
model.on('change:spec', reembed); |
|
model.on('change:debounce_wait', reembed); |
|
await reembed(); |
|
} |
|
|
|
function cleanJson(data) { |
|
return JSON.parse(JSON.stringify(data)) |
|
} |