Demo of Perspective.
Example of live market info from IEX Cloud.
license: apache-2.0 |
Demo of Perspective.
Example of live market info from IEX Cloud.
perspective-workspace { | |
flex: 1 | |
} | |
#container { | |
flex: 1; | |
display: flex; | |
} | |
input { | |
margin: 12px 8px 4px 8px; | |
padding: 8px; | |
font-size: 24px; | |
flex: 0 1; | |
text-transform: uppercase; | |
max-width: 600px; | |
} | |
perspective-viewer-datagrid regular-table { | |
overflow-x: hidden; | |
} | |
perspective-viewer table { | |
white-space: nowrap; | |
} | |
perspective-viewer tr, | |
perspective-viewer thead, | |
perspective-viewer tbody { | |
display: inline-block; | |
} | |
perspective-viewer th, | |
perspective-viewer td { | |
display: block; | |
} | |
body { | |
background-color: #eee; | |
display: flex; | |
margin: 0; | |
flex-direction: column; | |
position: absolute; | |
top: 0; | |
left:0; | |
right: 0; | |
bottom: 0; | |
} |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no" /> | |
<script src="https://cdn.jsdelivr.net/npm/@finos/perspective@latest"></script> | |
<script src="https://cdn.jsdelivr.net/npm/@finos/perspective-workspace@latest"></script> | |
<script src="https://cdn.jsdelivr.net/npm/@finos/perspective-viewer-datagrid@latest"></script> | |
<script src="https://cdn.jsdelivr.net/npm/@finos/perspective-viewer-d3fc@latest"></script> | |
<link rel='stylesheet' href="index.css"> | |
<link rel='stylesheet' crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/@finos/perspective-workspace/dist/css/material.css"> | |
<script src="index.js"></script> | |
</head> | |
<body> | |
<input id="ticker" type="text" value="JPM"></input> | |
<div id="container"> | |
<perspective-workspace id="workspace"> | |
<perspective-viewer table="query1"></perspective-viewer> | |
<perspective-viewer table="query2"></perspective-viewer> | |
<perspective-viewer table="query3"></perspective-viewer> | |
</perspective-workspace> | |
</div> | |
</body> | |
</html> |
const PUB_TOKEN = "Tpk_ecc89ddf30a611e9958142010a80043c"; | |
const IEX_URL = "https://sandbox.iexapis.com/beta/stock/"; | |
const QUERY1 = "/chart?token=" + PUB_TOKEN; | |
const QUERY2 = "/stats?token=" + PUB_TOKEN; | |
const QUERY3 = "/balance-sheet?token=" + PUB_TOKEN; | |
const worker = perspective.shared_worker(); | |
const TABLES = new Map(); | |
async function run(input, string, wrap = false, index = null) { | |
const req = await fetch(IEX_URL + input.value + string); | |
let data = await req.json(); | |
if (index) { | |
data = data[index]; | |
} | |
if (wrap) { | |
data = [data]; | |
} | |
let table; | |
if (TABLES.has(string)) { | |
table = TABLES.get(string); | |
table.clear(); | |
table.update(data); | |
} else { | |
table = await worker.table(data); | |
TABLES.set(string, table); | |
} | |
return table; | |
} | |
async function get_layout() { | |
const req = await fetch("layout.json"); | |
const json = await req.json(); | |
return json; | |
} | |
window.addEventListener("DOMContentLoaded", async function () { | |
const workspace = document.getElementsByTagName("perspective-workspace")[0]; | |
const input = document.getElementById("ticker"); | |
input.addEventListener("keydown", async (ev) => { | |
input.style.color = "inherit"; | |
if (ev.keyCode === 13) { | |
try { | |
await run(input, QUERY1); | |
await run(input, QUERY2, true); | |
await run(input, QUERY3, false, "balancesheet"); | |
} catch (e) { | |
input.style.color = "red"; | |
} | |
} | |
}); | |
workspace.addTable("query1", run(input, QUERY1)); | |
workspace.addTable("query2", run(input, QUERY2, true)); | |
workspace.addTable("query3", run(input, QUERY3, false, "balancesheet")); | |
customElements.get("perspective-viewer-datagrid").prototype.virtual_mode = | |
"none"; | |
const to_span = (x) => `<span style='color:#666'>${x}</span>`; | |
workspace.addEventListener("perspective-datagrid-after-update", (event) => { | |
const form = new Intl.NumberFormat([], {}); | |
const datagrid = event.detail; | |
for (const td of datagrid.get_tds()) { | |
const metadata = datagrid.get_meta(td); | |
if (metadata.column.endsWith("Percent")) { | |
td.innerHTML = | |
form.format(Math.round(metadata.value * 100)) + | |
to_span(" %"); | |
} else if ( | |
metadata.type === "float" && | |
(metadata.value < -1000000 || metadata.value > 1000000) | |
) { | |
td.innerHTML = | |
form.format(Math.round(metadata.value / 1000000)) + | |
to_span(" mm"); | |
} | |
} | |
}); | |
const json = await get_layout(); | |
workspace.restore(json); | |
}); |
{ | |
"sizes": [1], | |
"detail": { | |
"main": { | |
"type": "split-area", | |
"orientation": "horizontal", | |
"children": [ | |
{ | |
"type": "tab-area", | |
"widgets": [ | |
"PERSPECTIVE_GENERATED_ID_1", | |
"PERSPECTIVE_GENERATED_ID_2" | |
], | |
"currentIndex": 0 | |
}, | |
{ | |
"type": "tab-area", | |
"widgets": [ | |
"PERSPECTIVE_GENERATED_ID_5", | |
"PERSPECTIVE_GENERATED_ID_3" | |
], | |
"currentIndex": 0 | |
} | |
], | |
"sizes": [0.4, 0.6] | |
} | |
}, | |
"mode": "globalFilters", | |
"viewers": { | |
"PERSPECTIVE_GENERATED_ID_1": { | |
"plugin": "datagrid", | |
"columns": [ | |
"companyName", | |
"nextDividendDate", | |
"nextEarningsDate", | |
"exDividendDate", | |
"week52change", | |
"week52high", | |
"week52low", | |
"marketcap", | |
"employees", | |
"day200MovingAvg", | |
"day50MovingAvg", | |
"float", | |
"avg10Volume", | |
"avg30Volume", | |
"ttmEPS", | |
"ttmDividendRate", | |
"sharesOutstanding", | |
"maxChangePercent", | |
"year5ChangePercent", | |
"year2ChangePercent", | |
"year1ChangePercent", | |
"ytdChangePercent", | |
"month6ChangePercent", | |
"month3ChangePercent", | |
"month1ChangePercent", | |
"day30ChangePercent", | |
"day5ChangePercent", | |
"dividendYield", | |
"peRatio", | |
"beta" | |
], | |
"master": false, | |
"name": "Reference", | |
"table": "query2", | |
"linked": false | |
}, | |
"PERSPECTIVE_GENERATED_ID_2": { | |
"plugin": "datagrid", | |
"columns": [ | |
"reportDate", | |
"fiscalDate", | |
"currency", | |
"currentCash", | |
"shortTermInvestments", | |
"receivables", | |
"inventory", | |
"otherCurrentAssets", | |
"currentAssets", | |
"longTermInvestments", | |
"propertyPlantEquipment", | |
"goodwill", | |
"intangibleAssets", | |
"otherAssets", | |
"totalAssets", | |
"accountsPayable", | |
"currentLongTermDebt", | |
"otherCurrentLiabilities", | |
"totalCurrentLiabilities", | |
"longTermDebt", | |
"otherLiabilities", | |
"minorityInterest", | |
"totalLiabilities", | |
"commonStock", | |
"retainedEarnings", | |
"treasuryStock", | |
"capitalSurplus", | |
"shareholderEquity", | |
"netTangibleAssets" | |
], | |
"master": false, | |
"name": "Balance Sheet", | |
"table": "query3", | |
"linked": false | |
}, | |
"PERSPECTIVE_GENERATED_ID_5": { | |
"plugin": "Candlestick", | |
"columns": ["open", "close", "high", "low"], | |
"group_by": ["date"], | |
"plugin_config": { | |
"realValues": ["open", "close", "high", "low"] | |
}, | |
"master": false, | |
"name": "Px", | |
"table": "query1", | |
"linked": false | |
}, | |
"PERSPECTIVE_GENERATED_ID_3": { | |
"plugin": "Y Bar", | |
"columns": ["uVolume"], | |
"plugin_config": { | |
"realValues": ["uVolume"] | |
}, | |
"master": false, | |
"name": "Volume", | |
"table": "query1", | |
"linked": false | |
} | |
} | |
} |