Public
Edited
Jan 17, 2024
5 forks
Importers
40 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
tsla = d3.csvParse(
await FileAttachment("tsla.csv").text(),
d3.autoType
)
Insert cell
Insert cell
Plot.plot({
// Plot general style options
style: { fontFamily: "Roboto", fontSize: 12 },
height: 270,
y: { nice: 5 },
// Inside the marks array we define the plot marks we want to use
marks: [
// line chart
Plot.line(tsla, {
x: "Date",
y: "Close",
stroke: "#34A853",
strokeWidth: 2
}),
// Grid mark
Plot.gridY({ stroke: "#E8EAED", strokeWidth: 1, strokeOpacity: 0.5 }),
// horizontal line at y=0
Plot.ruleY([0], { stroke: "lightgray" }),
]
})
Insert cell
Insert cell
function withGradient({color, id = DOM.uid("gradient").id}, callback) {
return [
callback(`url(#${id})`),
() => svg`<defs>
<linearGradient id=${id} gradientTransform="rotate(90)">
<stop offset=0% stop-color=${color} stop-opacity=0.3 />
<stop offset=30% stop-color=${color} stop-opacity=0.2 />
<stop offset=60% stop-color=${color} stop-opacity=0.1 />
<stop offset=80% stop-color=${color} stop-opacity=0 />
</linearGradient>
</defs>`
];
}
Insert cell
Plot.plot({
style: { fontFamily: "Roboto", fontSize: 12 },
height: 270,
y: { nice: 5 },
marks: [
// Line Chart
Plot.lineY(tsla, {
x: "Date",
y: "Close",
stroke: "#34A853",
strokeWidth: 2
}),
// Grid and horizontal line at y=0
Plot.ruleY([0], { stroke: "lightgray" }),
Plot.gridY({ stroke: "#E8EAED", strokeWidth: 1, strokeOpacity: 0.5 }),
// Area Chart
withGradient({color: "#34A853"}, fill => Plot.areaY(tsla, {
x: "Date",
y: "Close",
fill,
})),
]
})
Insert cell
Insert cell
Insert cell
Insert cell
function plotCustomPointer(data, {x, y, color}) {
return Plot.marks(
Plot.ruleX(
data,
Plot.pointerX({
x: x,
py: y,
stroke: "#202124",
strokeDasharray: "3,4",
opacity: 0.55,
strokeWidth: 2
})
),
Plot.dot(
data,
Plot.pointerX({
x: x,
y: y,
r: 5,
fill: color
})
)
);
}
Insert cell
Insert cell
Plot.plot({
style: { fontFamily: "Roboto", fontSize: 12 },
height: 270,
y: { nice: 5 },
marks: [
// Line Chart
Plot.lineY(tsla, {
x: "Date",
y: "Close",
stroke: "#34A853",
strokeWidth: 2
}),
// Grid and horizontal line at y=0
Plot.ruleY([0], { stroke: "lightgray" }),
Plot.gridY({ stroke: "#E8EAED", strokeWidth: 1, strokeOpacity: 0.5 }),
// Area Chart
withGradient({color: "#34A853"}, fill => Plot.areaY(tsla, {
x: "Date",
y: "Close",
fill,
})),
// Custom Pointer
plotCustomPointer(tsla, {x: "Date", y: "Close", color: "#34A853"}),
]
})
Insert cell
Insert cell
Insert cell
htmlTooltip = ({ price, date, currency, width, height }) =>
`
<foreignObject width=${width} height=${height}>
<div style="width: ${width}px;" class="tooltip">
<span class="tooltip-price">${price} ${currency}</span><span class="tooltip-time">${date}</span>
</div>
<foreignObject/>
`
Insert cell
Insert cell
<style>
.tooltip {
background-color: #fff;
font-weight: 400;
font-size: 12px;
height: 28px;
line-height: 28px;
padding-left: 8px;
padding-right: 8px;
position: absolute;
top: 0px;
left: 0px;
border-radius: 2px;
box-shadow: 0 1px 3px rgba(0,0,0,.2), 0 1px 1px rgba(0,0,0,.14);
z-index: 2;
}

.tooltip-price {
margin-right: 5px;
color: #202124;
}

.tooltip-time {
color: rgba(0,0,0,.67);
}
</style>
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function plotCustomTooltip(
data,
{ tooltipHeight, tooltipWidth, x, y, currency }
) {
return Plot.marks(
Plot.tip(
data,
Plot.pointerX({
x: x,
y: y,
// Extend the tip mark render function
render: (index, scales, values, dimensions, context) => {
const g = d3.select(context.ownerSVGElement).append("g");
const [i] = index;
if (i !== undefined) {
const formatTime = d3.utcFormat("%d %b %Y");
const priceDate = formatTime(values.channels.x.value[i]);
const closePrice = d3.format(".2f")(values.channels.y.value[i]);
// The tooltip starts after the left margin in the x-axis to avoid blocking the y-axis labels
// We start to translate the tooltip when the pointer is halfway the initial tooltip location
// We stop moving the tooltip when there's not enough width available within the container
const currentXPosition = values.x[i];
const tooltipStartPosition = dimensions.marginLeft;
const tooltipDefaultPosition = currentXPosition - tooltipWidth / 2;
const tooltipEndPosition =
dimensions.width - dimensions.marginRight - tooltipWidth;
g.attr(
"transform",
`translate(${Math.min(
currentXPosition <= tooltipWidth / 2 + dimensions.marginLeft
? tooltipStartPosition
: tooltipDefaultPosition,
tooltipEndPosition
)}, 0)`
).append(
() =>
svg`${htmlTooltip({
price: closePrice,
date: priceDate,
currency: currency,
width: tooltipWidth,
height: tooltipHeight
})}`
);
}
return g.node();
}
})
)
);
}
Insert cell
Insert cell
FinalChart = GoogleFinanceChart(tsla, {x: "Date", y: "Close", color: "#34A853" })
Insert cell
GoogleFinanceChart = (data, {x, y, color}) => Plot.plot({
style: { fontFamily: "Roboto", fontSize: 12 },
height: 270,
marginTop: 30,
y: { nice: 5 },
marks: [
// Line Chart and horizontal grid
Plot.ruleY([0], { stroke: "lightgray" }), // draw an horizontal line at 0
Plot.gridY({ stroke: "#E8EAED", strokeWidth: 1, strokeOpacity: 0.5 }),
Plot.lineY(data, {
x: x,
y: y,
stroke: color,
strokeWidth: 2
}),
// Area Chart
withGradient({color: color}, fill => Plot.areaY(data, {
x: x,
y: y,
fill,
})),
// Custom Vertical Pointer
plotCustomPointer(data, {x: x, y: y, color: color}),
// Custom HTML tooltip
plotCustomTooltip(data, {tooltipHeight: 30, tooltipWidth: 146, x: x, y: y, currency: "USD"})
]
})
Insert cell
Insert cell
Insert cell
Insert cell
import {note, details} from "@observablehq/notes"
Insert cell
svg
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