Public
Edited
Sep 4, 2021
Fork of Simple D3
1 star
Insert cell
Insert cell
chart = {
let svg = d3
.create("svg")
.attr("viewBox", [0, 0, width, height])
.style("background", "#1B1A40");

svg.append("style").text(`
.tranche--active {
text-decoration: underline
}
`);

var apyToggle = true;
var t = d3.transition().duration(1000);

const dataProps = [
{ line: "netAApy", bar: "trancheAValueUSD", sliceLine: "sliceAApy" },
{ line: "netBApy", bar: "trancheBValueUSD", sliceLine: "sliceBApy" }
];

let selectedData = dataProps[0];
let trancheToggle = true;
let hoveredIndex = -1;

let hoverGroup = svg
.append("g")
.attr("transform", `translate(0,${margin.bottom})`);

let hoverLine = hoverGroup
.append("line")
.attr("x1", 0)
.attr("x2", 0)
.attr("y1", 0)
.attr("y2", 0)
.attr("stroke-dasharray", "4,4")
.attr("stroke", "white")
.attr("stroke-width", 2)
.attr("class", "hover");

let dateText = svg
.append("text")
.attr("opacity", 0)
.attr("fill", "white")
.attr("font-size", 14)
.attr("text-anchor", "middle")
.text("gigi");

let rects = svg
.append("g")
.attr("fill", rectColor)
.attr("transform", `translate(0,${height - rectHeight - margin.bottom})`);

function updateRects() {
rects
.selectAll("rect")
.data(chartData[selectedData.bar])
.join("rect")
.attr("x", (d, i) => x(i))
.attr("class", (d, i) => `rect-${i} bar`)
.attr("width", x.bandwidth())
.transition()
.duration(2000)
.attr("y", (d) => y(d))
.attr("height", (d) => y(0) - y(d));
}

let hoverText = rects
.append("text")
.attr("opacity", 0)
.attr("class", "hover")
.attr("fill", "white")
.attr("text-anchor", "middle")
.text("");

let subtitleHoverText = rects
.append("text")
.attr("opacity", 0)
.attr("class", "hover")
.attr("fill", "white")
.attr("font-size", 10)
.attr("text-anchor", "middle")
.text("TRANCHE VALUE");

let lineGroup = svg
.append("g")
.attr("class", "line-group")
.attr("transform", `translate(0,${rectHeight - 50})`);

var netALine = lineGroup
.append("path")
.attr("stroke", lineColor)
.attr("fill", "none")
.attr("stroke-width", 3)
.attr("opacity", 0.8);

var netBLine = lineGroup
.append("path")
.attr("stroke", lineColor)
.attr("fill", "none")
.attr("stroke-width", 3)
.attr("opacity", 0.2);

function updateLines() {
var aData = apyToggle
? chartData[dataProps[0]["line"]]
: chartData[dataProps[0]["sliceLine"]];

var bData = apyToggle
? chartData[dataProps[1]["line"]]
: chartData[dataProps[1]["sliceLine"]];

yLine.domain([0, d3.max([d3.max(aData), d3.max(bData)])]);

netALine.attr("d", line(aData));
netBLine.attr("d", line(bData));

// var aLabel = apyToggle ? "Tranche A Net APY" : "Tranche A Slice APY";
// var bLabel = apyToggle ? "Tranche B Net APY" : "Tranche B Slice APY";
var aLabel = "Tranche A";
var bLabel = "Tranche B";
netAApyLabel.text(aLabel);
netBApyLabel.text(bLabel);
}

var title = svg
.append("text")
.attr("fill", "white")
.attr("text-anchor", "start")
.attr("x", margin.left)
.attr("y", 40)
.attr("font-size", 22)
.attr("font-weight", 700)
.text("Tranche Performance");

const toggleWidth = 200;
var toggleHeight = 30;
var toggleGroup = svg
.append("g")
.attr("transform", `translate(250,${40 - toggleHeight + 5})`);

let toggleRect = toggleGroup
.append("rect")
.attr("x", margin.left)
.attr("y", 0)
.attr("width", toggleWidth)
.attr("height", toggleHeight)
.attr("fill", "#26254A")
.attr("rx", toggleHeight / 2)
.attr("ry", toggleHeight / 2);

let activeRect = toggleGroup
.append("rect")
.attr("x", margin.left)
.attr("y", 0)
.attr("width", toggleWidth / 2)
.attr("height", toggleHeight)
.attr("fill", "#3B3765")
.attr("rx", toggleHeight / 2)
.attr("ry", toggleHeight / 2);

let netApyRectLabel = toggleGroup
.append("text")
.attr("x", margin.left + toggleWidth / 4)
.attr("y", toggleHeight / 2 + 5)
.attr("fill", "white")
.attr("text-anchor", "middle")
.attr("font-size", 12)
.attr("font-weight", 700)
.text("NET APY");

let sliceApyRectLabel = toggleGroup
.append("text")
.attr("x", margin.left + toggleWidth / 4 + toggleWidth / 2)
.attr("y", toggleHeight / 2 + 5)
.attr("fill", "white")
.attr("opacity", 0.5)
.attr("text-anchor", "middle")
.attr("font-size", 12)
.attr("font-weight", 700)
.text("SLICE APY");

let hoverCircle = lineGroup
.append("circle")
.attr("r", 10)
.attr("opacity", 0)
.attr("stroke", "white")
.attr("stroke-width", 4)
.attr("fill", lineColor);

let netAApyLabel = lineGroup
.append("text")
.attr("fill", "white")
.attr("font-size", 12)
.attr("font-weight", 700)
//.attr("x", width - margin.right - x.bandwidth() / 2 + 10)
.attr("x", width - margin.right + 10)
.attr("y", yLine(chartData.netAApy[chartData.netAApy.length - 1]) + 4)
.style("cursor", "pointer")
.style("opacity", trancheToggle ? 0.8 : 0.2)
.text("Tranche A")
.on("mousedown", function (e) {
trancheToggle = true;
selectedData = dataProps[0];

netALine.transition().duration(1000).style("opacity", 0.8);
netBLine.transition().duration(1000).style("opacity", 0.2);
netAApyLabel.transition().duration(1000).style("opacity", 1);
netBApyLabel.transition().duration(1000).style("opacity", 0.2);

updateRects();
});

let netBApyLabel = lineGroup
.append("text")
.attr("fill", "white")
.attr("font-size", 12)
.attr("font-weight", 700)
//.attr("x", width - margin.right - x.bandwidth() / 2 + 10)
.attr("x", width - margin.right + 10)
.attr("y", yLine(chartData.netBApy[chartData.netBApy.length - 1]) + 4)
.style("cursor", "pointer")
.style("opacity", trancheToggle ? 0.2 : 0.8)
.text("Tranche B")
.on("mousedown", function (e) {
trancheToggle = false;
selectedData = dataProps[1];

netBLine.transition().duration(1000).style("opacity", 0.8);
netALine.transition().duration(1000).style("opacity", 0.2);
netAApyLabel.transition().duration(1000).style("opacity", 0.2);
netBApyLabel.transition().duration(1000).style("opacity", 1);

updateRects();
});

let toggleOverlay = toggleGroup
.append("rect")
.attr("x", margin.left)
.attr("y", 0)
.attr("width", toggleWidth)
.attr("height", toggleHeight)
.attr("opacity", 0)
.attr("rx", toggleHeight / 2)
.attr("ry", toggleHeight / 2)
.style("cursor", "pointer")
.on("mousedown", function (e) {
var offset = margin.left + toggleWidth / 2;
if (!apyToggle) offset = margin.left;
activeRect.transition().attr("x", offset);

var netOpacity = apyToggle ? 0.5 : 1;
var sliceOpacity = apyToggle ? 1 : 0.5;
netApyRectLabel.transition().attr("opacity", netOpacity);
sliceApyRectLabel.transition().attr("opacity", sliceOpacity);

apyToggle = !apyToggle;
updateLines();
});

let lineValueText = lineGroup
.append("text")
.attr("fill", "white")
.attr("font-size", 12)
.attr("text-anchor", "middle")
.attr("opacity", 0);

let lineValueTextSubtitle = lineGroup
.append("text")
.attr("fill", "white")
.attr("font-size", 10)
.attr("text-anchor", "middle")
.attr("opacity", 0);

let hoverRect = svg
.append("rect")
.attr("opacity", 0)
.attr("fill", "white")
.attr("x", margin.left)
.attr("y", margin.top)
.attr("width", width - margin.right - margin.left)
.attr("height", height - margin.bottom)
.on("mousemove", function (event, d) {
var index = Math.floor(hoverScale.invert(event.clientX));
updateHover(index);
})
.on("mouseout", function (event, d) {
clearHover();
});

updateRects();
updateLines();

function updateHover(index) {
if (index == hoveredIndex) return;
else hoveredIndex = index;

d3.selectAll(".bar").attr("fill", rectColor);

var activeProp = apyToggle ? "line" : "sliceLine";

// d3.select(this).transition().attr("fill", lineColor);

// console.log("pokusat cu do ovoga", d3.select(`.rect-${index}`));
// d3.select(`.rect-${index}`).transition().attr("fill", lineColor);
d3.select(`.rect-${index}`).attr("fill", lineColor);

let xOffset = x(index) + x.bandwidth() / 2;
var date = new Date(chartData.date[index]);
var dateString = date ? dateFormat(date) : "";

hoverLine
.attr("opacity", 0.65)
.attr("x1", xOffset)
.attr("x2", xOffset)
.attr("y1", yLine(chartData[selectedData[activeProp]][index]) + 40)
.attr(
"y2",
y(chartData[selectedData.bar][index]) +
height -
rectHeight -
margin.bottom -
115
);

hoverText
.attr("opacity", 1)
.attr("x", xOffset)
.attr("y", y(chartData[selectedData.bar][index]) - 40)
.text(priceFormat(chartData[selectedData.bar][index]))
// .text(priceFormat(100))
.raise();

subtitleHoverText
.attr("opacity", 0.65)
.attr("x", xOffset)
.attr("y", y(chartData[selectedData.bar][index]) - 20)
.raise();

dateText
.attr("opacity", 1)
.attr("x", xOffset)
.attr("y", height - margin.bottom + 20)
.text(dateString);

hoverCircle
.attr("opacity", 1)
.attr("cx", xOffset)
.attr("cy", yLine(chartData[selectedData[activeProp]][index]));

lineValueText
.attr("opacity", 1)
.attr("x", xOffset)
.attr("y", yLine(chartData[selectedData[activeProp]][index]) - 40)
.text(apyFormat(chartData[selectedData[activeProp]][index] / 100));

var apyTitle = apyToggle ? "NET APY" : "SLICE APY";
lineValueTextSubtitle
.attr("opacity", 0.65)
.attr("x", xOffset)
.attr("y", yLine(chartData[selectedData[activeProp]][index]) - 20)
.text(apyTitle);
}

function clearHover() {
hoverLine.attr("opacity", 0);
subtitleHoverText.attr("opacity", 0);
hoverText.attr("opacity", 0).text();
dateText.attr("opacity", 0).text();
hoverCircle.attr("opacity", 0);
lineValueText.attr("opacity", 0).text();
lineValueTextSubtitle.attr("opacity", 0);
d3.selectAll(".bar").attr("fill", rectColor);
}

return svg.node();
}
Insert cell
hoverScale = d3
.scaleLinear()
.domain([0, chartData.trancheAApy.length - 1])
.range([margin.left, width - margin.right])
.clamp(true)
Insert cell
apyFormat = d3.format(".2%")
Insert cell
dateFormat = d3.timeFormat("%b %d")
Insert cell
chartData = apiData.result.chartData
Insert cell
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap');

div, input, button {
font-family: 'Inter', sans-serif;
}
</style>
Insert cell
apiData = d3.json("https://chart-tranche.herokuapp.com/chart")
Insert cell
priceFormat = d3.format("($,.2f")
Insert cell
line = d3
.line()
.x((d, i) => x(i) + x.bandwidth() / 2)
.y((d, i) => yLine(d))
.curve(d3.curveCatmullRom)
Insert cell
x = d3
.scaleBand()
// .domain(d3.range(data.length))
.domain(d3.range(chartData.date.length))
.range([margin.left, width - margin.right])
.padding(0.1)
Insert cell
yLine = d3
.scaleLinear()
.domain([0, d3.max([d3.max(chartData.netAApy), d3.max(chartData.netBApy)])])
.range([rectHeight, 0])
Insert cell
y = d3
.scaleLinear()
.domain([
0,
d3.max([
d3.max(chartData.trancheAValueUSD),
d3.max(chartData.trancheBValueUSD)
])
])
.range([rectHeight, 0])
Insert cell
lineColor = "#7277FF"
Insert cell
rectColor = "#33315F"
Insert cell
rectHeight = 150
Insert cell
height = 600
Insert cell
margin = ({ top: 60, right: 100, bottom: 50, left: 30 })
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more