Files
finos--perspective/examples/blocks/src/nypd/index.js
T
2026-07-13 12:23:53 +08:00

187 lines
7.7 KiB
JavaScript

// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
// ┃ This file is part of the Perspective library, distributed under the terms ┃
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
import "/node_modules/@perspective-dev/viewer/dist/cdn/perspective-viewer.js";
import "/node_modules/@perspective-dev/workspace/dist/cdn/perspective-workspace.js";
import "/node_modules/@perspective-dev/viewer-datagrid/dist/cdn/perspective-viewer-datagrid.js";
import "/node_modules/@perspective-dev/viewer-charts/dist/cdn/perspective-viewer-charts.js";
import perspective from "/node_modules/@perspective-dev/client/dist/cdn/perspective.js";
let DATA_URL = "nypdccrb.arrow";
let LAYOUTS = localStorage.getItem("layouts")
? JSON.parse(localStorage.getItem("layouts"))
: undefined;
const worker = await perspective.worker();
const theme_style_node = document.createElement("style");
document.head.appendChild(theme_style_node);
let DARK_THEME;
let LIGHT_THEME;
function toggle_theme() {
if (theme_style_node.dataset.theme === "Pro Light") {
theme_style_node.textContent = DARK_THEME;
document.body.classList.add("dark");
window.theme.textContent = "Light Theme";
for (const view of document.querySelectorAll("perspective-viewer")) {
view.setAttribute("theme", "Pro Dark");
view.restyleElement();
}
theme_style_node.dataset.theme = "Pro Dark";
} else {
theme_style_node.textContent = LIGHT_THEME;
document.body.classList.remove("dark");
window.theme.textContent = "Dark Theme";
for (const view of document.querySelectorAll("perspective-viewer")) {
view.setAttribute("theme", "Pro Light");
view.restyleElement();
}
theme_style_node.dataset.theme = "Pro Light";
}
}
async function fetch_progress(url) {
window.message.textContent = "Downloading...";
const response = await fetch(url);
const ab = await new Response(await response.blob()).arrayBuffer();
window.message.style.display = "none";
return ab;
}
DARK_THEME = await fetch(
"/node_modules/@perspective-dev/workspace/dist/css/pro-dark.css",
).then((x) => x.text());
LIGHT_THEME = await fetch(
"/node_modules/@perspective-dev/workspace/dist/css/pro.css",
).then((x) => x.text());
document.body.innerHTML = `
<style>
</style>
<div id='buttons'>
<span id="message"></span>
<select id="layouts"></select>
<button id="save_as">Save As</button>
<input id="name_input" style="display: none"></input>
<button id="save" style="display: none">Save</button>
<button id="cancel" style="display: none">Cancel</button>
<button id="theme" style="float: right">Light Theme</button>
<button id="copy" style="float: right">Debug to Clipboard</button>
<button id="reset" style="float: right">Reset LocalStorage</button>
<a href="https://github.com/texodus/nypd-ccrb">NYCLU/CCRB Data</a>
<a href="https://github.com/perspective-dev/perspective">Built With Perspective</a>
</div>
<perspective-workspace id='workspace'></perspective-workspace>
`.trim();
toggle_theme();
window.workspace.addEventListener(
"workspace-new-view",
({ detail: { widget } }) => {
widget.viewer.setAttribute("theme", theme_style_node.dataset.theme);
},
);
window.workspace.addTable(
"ccrb",
(async () => worker.table(await fetch_progress(DATA_URL)))(),
// worker.table(await fetch_progress(DATA_URL))
);
if (LAYOUTS == undefined) {
LAYOUTS = await (await fetch("./layout.json")).json();
}
const layout_names = Object.keys(LAYOUTS);
let selected_layout = LAYOUTS[layout_names[0]];
await window.workspace.restore(selected_layout);
function set_layout_options() {
const layout_names = Object.keys(LAYOUTS);
window.layouts.innerHTML = "";
for (const layout of layout_names) {
window.layouts.innerHTML += `<option${
layout === selected_layout ? " selected='true'" : ""
}>${layout}</option>`;
}
}
set_layout_options();
window.name_input.value = layout_names[0];
window.layouts.addEventListener("change", async () => {
if (window.layouts.value.trim().length === 0) {
return;
}
window.workspace.innerHTML = "";
await window.workspace.restore(LAYOUTS[window.layouts.value]);
window.name_input.value = window.layouts.value;
});
window.save_as.addEventListener("click", async () => {
window.save_as.style.display = "none";
window.save.style.display = "inline-block";
window.cancel.style.display = "inline-block";
window.name_input.style.display = "inline-block";
window.copy.style.display = "none";
window.layouts.style.display = "none";
});
function cancel() {
window.save_as.style.display = "inline-block";
window.save.style.display = "none";
window.cancel.style.display = "none";
window.name_input.style.display = "none";
window.copy.style.display = "inline-block";
window.layouts.style.display = "inline-block";
}
window.cancel.addEventListener("click", cancel);
window.reset.addEventListener("click", () => {
localStorage.clear();
window.reset.innerText = "Reset!";
setTimeout(() => {
window.reset.innerText = "Reset LocalStorage";
}, 1000);
});
window.save.addEventListener("click", async () => {
const token = await window.workspace.save();
const new_name = window.name_input.value;
LAYOUTS[new_name] = token;
set_layout_options();
window.layouts.value = new_name;
window.save_as.innerText = "Saved!";
setTimeout(() => {
window.save_as.innerText = "Save As";
}, 1000);
localStorage.setItem("layouts", JSON.stringify(LAYOUTS));
cancel();
});
window.copy.addEventListener("click", async () => {
await navigator.clipboard.writeText(JSON.stringify(LAYOUTS));
window.copy.innerText = "Copied!";
setTimeout(() => {
window.copy.innerText = "Debug to Clipboard";
}, 1000);
});
window.theme.addEventListener("click", toggle_theme);