Demo of Perspective.
Example of live market info from IEX Cloud.
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 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://unpkg.com/@finos/perspective/dist/umd/perspective.js"></script> | |
<script src="https://unpkg.com/@finos/perspective-workspace"></script> | |
<script src="https://unpkg.com/@finos/perspective-viewer-datagrid"></script> | |
<script src="https://unpkg.com/@finos/perspective-viewer-d3fc"></script> | |
<link rel='stylesheet' href="index.css"> | |
<link rel='stylesheet' href="https://unpkg.com/@finos/perspective-workspace/dist/umd/material.css" is="custom-style"> | |
<script src="index.js"></script> | |
</head> | |
<body> | |
<input id="ticker" type="text" value="JPM"></input> | |
<div id="container"> | |
<perspective-workspace id="workspace"> | |
<perspective-viewer disable-virtual-datagrid table="query1"></perspective-viewer> | |
<perspective-viewer disable-virtual-datagrid table="query2"></perspective-viewer> | |
<perspective-viewer disable-virtual-datagrid 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")); | |
const to_span = x => `<span style='color:#666'>${x}</span>`; | |
workspace.addEventListener("perspective-datagrid-after-update", event => { | |
const form = new Intl.NumberFormat("en-us", {}); | |
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": "d3_candlestick", | |
"columns": [ | |
"open", | |
"close", | |
"high", | |
"low" | |
], | |
"row-pivots": [ | |
"date" | |
], | |
"plugin_config": { | |
"realValues": [ | |
"open", | |
"close", | |
"high", | |
"low" | |
] | |
}, | |
"master": false, | |
"name": "Px", | |
"table": "query1", | |
"linked": false | |
}, | |
"PERSPECTIVE_GENERATED_ID_3": { | |
"plugin": "d3_y_bar", | |
"columns": [ | |
"uVolume" | |
], | |
"plugin_config": { | |
"realValues": [ | |
"uVolume" | |
] | |
}, | |
"master": false, | |
"name": "Volume", | |
"table": "query1", | |
"linked": false | |
} | |
} | |
} |