Skip to content

Instantly share code, notes, and snippets.

@texodus

texodus/.block

Last active Apr 20, 2021
Embed
What would you like to do?
Perspective Datagrid / Custom Style Example
license: apache-2.0
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Orbitron">
<link rel='stylesheet' href="https://unpkg.com/@finos/perspective-viewer/dist/umd/material-dense.dark.css">
<script src="https://unpkg.com/@finos/perspective-viewer/dist/umd/perspective-viewer.js"></script>
<script src="https://unpkg.com/@finos/perspective-viewer-datagrid"></script>
<script src="https://unpkg.com/@finos/perspective-viewer-d3fc"></script>
<script src="https://unpkg.com/@finos/perspective/dist/umd/perspective.js"></script>
</head>
<body>
<perspective-viewer
plugin="datagrid"
columns='["Profit","Sub-Category","State","Sales","Category","Order Date","Quantity"]'>
</perspective-viewer>
<script>
function hue(value, min, max) {
const norm = "0" + Math.abs(Math.round(255 * (Math.min(Math.max(value, min), max) / (max - min)))).toString(16);
return norm.slice(norm.length - 2, norm.length);
}
function getBase64Image(img) {
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
return canvas.toDataURL("image/png");
}
function make_led_cell(td, metadata) {
if (metadata.value < 0) {
const fg = hue(Math.min(metadata.value, -50), -100, 0);
td.style.color = `#${fg}${fg}${fg}`;
td.style.background = `radial-gradient(#${hue(Math.min(metadata.value, -20), -100, 0)}3136, #243136`;
td.style.border = `1px solid #${hue(Math.min(metadata.value / 3, -20), -100, 0)}3136`;
} else if (metadata.value > 0) {
const fg = hue(Math.max(metadata.value, 50), 0, 100);
td.style.color = `#${fg}${fg}${fg}`;
td.style.background = `radial-gradient(#24${hue(Math.max(20, metadata.value), 0, 100)}36, #243136`;
td.style.border = `1px solid #24${hue(Math.max(metadata.value / 3, 20), 0, 100)}36`;
} else {
td.style.color = "#000";
td.style.background = "";
td.style.border = ``;
}
}
function make_bar_chart(td, metadata, max) {
td.style.background = "";
td.style.border = ``;;
if (!metadata.value) {
} else {
max = Math.max(max, metadata.value || 0);
let v = (metadata.value || 0) / max
td.innerHTML = `<div style="height:13px;width:${v * 100}%;background:linear-gradient(90deg, #2bb0af 0, #23a7d7);"><div>`;
}
return max;
}
function make_flag(td, metadata, cache, clean_name) {
td.style.background = "";
td.style.border = ``;;
if (cache[clean_name] && cache[clean_name].length > 0) {
const name = metadata.value;
td.textContent = "";
td.appendChild(cache[name].pop());
} else {
const name = metadata.value;
const span = document.createElement("span")
const img = document.createElement("img");
img.onload = () => {
img.onload = undefined;
const data = getBase64Image(img);
img.src = data;
}
img.setAttribute("crossorigin", "anonymous")
img.setAttribute("src", `http://perspective.finos.org/img/flags/${states[clean_name].toLowerCase()}.png`);
td.textContent = "";
span.appendChild(img)
td.appendChild(span);
CACHE[name] = CACHE[name] || [];
CACHE[name].push(img);
}
}
function make_clear(td) {
td.style.border = ``;
td.style.background = "";
td.style.color = "";
}
const CACHE = {}
function clone_img_cache() {
return Object.keys(CACHE).reduce((obj, key) => {
obj[key] = CACHE[key].slice();
return obj;
}, {});
}
window.addEventListener('DOMContentLoaded', async function () {
const viewer = document.getElementsByTagName('perspective-viewer')[0];
const data = await fetch("https://unpkg.com/superstore-arrow/superstore.arrow");
const arr = await data.arrayBuffer();
const table = await perspective.worker().table(arr.slice());
await viewer.load(table);
viewer.toggleConfig();
let max = -Infinity;
let config = {}
const table_schema = await viewer.table.schema();
let dirty = false;
let column_paths = await viewer.view.column_paths();
let row_pivots = await viewer.view.get_config()["row_pivots"];
let schema = await viewer.view.schema();
viewer.addEventListener("perspective-config-update", async () => {
max = -Infinity;
dirty = true;
});
const datagrid = viewer.querySelector("regular-table");
datagrid.addStyleListener(async () => {
if (dirty) {
column_paths = await viewer.view.column_paths();
row_pivots = await viewer.view.get_config()["row_pivots"];
schema = await viewer.view.schema();
dirty = false;
}
const cache = clone_img_cache();
for (const td of datagrid.querySelectorAll("td")) {
const metadata = datagrid.getMeta(td);
let type;
if (metadata.x >= 0) {
const column_path = column_paths[metadata.x];
const column_path_parts = column_path.split("|");
type = schema[column_path_parts[column_path_parts.length - 1]];
} else {
const column_path = row_pivots[metadata.row_header_x - 1];
type = table_schema[column_path];
}
const clean_name = metadata.value && metadata.value.trim && metadata.value.trim();
td.classList.toggle("orbitron", type === "integer" || type === "float");
if (type === "float") {
make_led_cell(td, metadata);
} else if (type === "integer") {
max = make_bar_chart(td, metadata, max);
} else if (clean_name in states) {
make_flag(td, metadata, cache, clean_name);
} else {
make_clear(td);
}
}
});
datagrid.draw();
});
const states = {
"Alabama": "AL",
"Alaska": "AK",
"Arizona": "AZ",
"Arkansas": "AR",
"California": "CA",
"Colorado": "CO",
"Connecticut": "CT",
"District of Columbia": "DC",
"Delaware": "DE",
"Florida": "FL",
"Georgia": "GA",
"Hawaii": "HI",
"Idaho": "ID",
"Illinois": "IL",
"Indiana": "IN",
"Iowa": "IA",
"Kansas": "KS",
"Kentucky": "KY",
"Louisiana": "LA",
"Maine": "ME",
"Maryland": "MD",
"Massachusetts": "MA",
"Michigan": "MI",
"Minnesota": "MN",
"Mississippi": "MS",
"Missouri": "MO",
"Montana": "MT",
"Nebraska": "NE",
"Nevada": "NV",
"New Hampshire": "NH",
"New Jersey": "NJ",
"New Mexico": "NM",
"New York": "NY",
"North Carolina": "NC",
"North Dakota": "ND",
"Ohio": "OH",
"Oklahoma": "OK",
"Oregon": "OR",
"Pennsylvania": "PA",
"Rhode Island": "RI",
"South Carolina": "SC",
"South Dakota": "SD",
"Tennessee": "TN",
"Texas": "TX",
"Utah": "UT",
"Vermont": "VT",
"Virginia": "VA",
"Washington": "WA",
"West Virginia": "WV",
"Wisconsin": "WI",
"Wyoming": "WY"
};
</script>
<style>
perspective-viewer td {
height: 24px;
border-color: #1d1d1d !important;
}
perspective-viewer table, perspective-viewer table tr:hover {
color: #cfd8dc;
}
perspective-viewer tr td.pd-group-header,
perspective-viewer tr th.pd-group-header {
border-right: 1px solid #666 !important;
}
perspective-viewer tbody:hover tr {
opacity: 0.5;
transition: opacity 0.2s ease-in;
}
perspective-viewer tbody:hover tr:hover td div {
transform: scaleY(1.3);
transform-origin: 0;
transition: transform 0.2s ease-in;
}
perspective-viewer tbody:hover tr:hover {
box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.4);
opacity: 1;
transition: none;
}
/* perspective-viewer tr:last-child th {
border-bottom: 1px solid #666;
} */
perspective-viewer tr th {
border-right: 1px solid #666;
}
perspective-viewer tbody:hover tr:hover td {
background: none;
opacity: 1;
}
perspective-viewer tbody:hover tr:nth-child(even):hover td,
perspective-viewer tbody tr:nth-child(even) td {
background: rgba(0, 0, 0, 0.1);
}
perspective-viewer td.orbitron {
text-align: center !important;
font-family: Orbitron;
}
perspective-viewer th.integer,
perspective-viewer td.integer {
width: 100px;
}
perspective-viewer {
flex: 1;
}
body {
display: flex;
flex-direction: column;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: 0;
padding: 0;
}
input {
margin: 24px;
max-width: 300px;
}
@media (max-width: 600px) {
html {
overflow: hidden;
}
body {
position: fixed;
height: 100%;
width: 100%;
margin: 0;
overflow: hidden;
touch-action: none;
}
}
</style>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment