lib/spoom/coverage/d3/timeline.rb in spoom-1.1.0 vs lib/spoom/coverage/d3/timeline.rb in spoom-1.1.1
- old
+ new
@@ -34,21 +34,43 @@
fill: #333;
text-anchor: right;
pointer-events: none;
}
+ .area {
+ fill-opacity: 0.5;
+ }
+
+ .line {
+ stroke-width: 2;
+ fill: transparent;
+ }
+
+ .dot {
+ r: 2;
+ fill: #888;
+ }
+
.inverted .grid line {
stroke: #777;
}
+ .inverted .area {
+ fill-opacity: 0.9;
+ }
+
.inverted .axis text {
fill: #fff;
}
.inverted .axis line {
stroke: #fff;
}
+
+ .inverted .dot {
+ fill: #fff;
+ }
CSS
end
sig { returns(String) }
def self.header_script
@@ -168,11 +190,10 @@
.x((d) => xScale_#{id}(parseDate(d.timestamp)))
.y0(yScale_#{id}(0))
.y1((d) => yScale_#{id}(#{y}))
.curve(d3.#{curve}))
.attr("fill", "#{color}")
- .attr("fill-opacity", 0.5)
HTML
end
sig { params(y: String, color: String, curve: String).returns(String) }
def line(y:, color: "#ccc", curve: "curveCatmullRom.alpha(1)")
@@ -183,12 +204,10 @@
.attr("d", d3.line()
.x((d) => xScale_#{id}(parseDate(d.timestamp)))
.y((d) => yScale_#{id}(#{y}))
.curve(d3.#{curve}))
.attr("stroke", "#{color}")
- .attr("stroke-width", 3)
- .attr("fill", "transparent")
HTML
end
sig { params(y: String).returns(String) }
def points(y:)
@@ -196,14 +215,12 @@
svg_#{id}.selectAll("circle")
.data(data_#{id})
.enter()
.append("circle")
.attr("class", "dot")
- .attr("r", 3)
.attr("cx", (d) => xScale_#{id}(parseDate(d.timestamp)))
.attr("cy", (d, i) => yScale_#{id}(#{y}))
- .attr("fill", "#aaa")
.on("mouseover", (d) => tooltip.style("opacity", 1))
.on("mousemove", tooltip_#{id})
.on("mouseleave", (d) => tooltip.style("opacity", 0));
HTML
end
@@ -379,22 +396,19 @@
.attr("fill", (d, i) => #{color})
layer.append("path")
.attr("class", "area")
.attr("d", area_#{id})
- .attr("fill", (d) => strictnessColor(d.key))
- .attr("fill-opacity", 0.9)
+ .attr("fill", (d) => #{color})
svg_#{id}.selectAll("circle")
.data(points_#{id})
.enter()
.append("circle")
.attr("class", "dot")
- .attr("r", 2)
.attr("cx", (d) => xScale_#{id}(parseDate(#{y})))
.attr("cy", (d, i) => yScale_#{id}(d[1]))
- .attr("fill", "#fff")
.on("mouseover", (d) => tooltip.style("opacity", 1))
.on("mousemove", tooltip_#{id})
.on("mouseleave", (d) => tooltip.style("opacity", 0));
JS
end
@@ -475,9 +489,132 @@
def tooltip
<<~JS
function tooltip_#{id}(d) {
tooltipTimeline(d, "methods");
}
+ JS
+ end
+ end
+
+ class RBIs < Stacked
+ extend T::Sig
+
+ sig { params(id: String, snapshots: T::Array[Snapshot]).void }
+ def initialize(id, snapshots)
+ keys = ['rbis', 'files']
+ data = snapshots.map do |snapshot|
+ {
+ timestamp: snapshot.commit_timestamp,
+ commit: snapshot.commit_sha,
+ total: snapshot.files,
+ values: { files: snapshot.files - snapshot.rbi_files, rbis: snapshot.rbi_files },
+ }
+ end
+ super(id, data, keys)
+ end
+
+ sig { override.returns(String) }
+ def tooltip
+ <<~JS
+ function tooltip_#{id}(d) {
+ moveTooltip(d)
+ .html("commit <b>" + d.data.commit + "</b><br>"
+ + d3.timeFormat("%y/%m/%d")(parseDate(d.data.timestamp)) + "<br><br>"
+ + "Files: <b>" + d.data.values.files + "</b><br>"
+ + "RBIs: <b>" + d.data.values.rbis + "</b><br><br>"
+ + "Total: <b>" + d.data.total + "</b>")
+ }
+ JS
+ end
+
+ sig { override.returns(String) }
+ def script
+ <<~JS
+ #{tooltip}
+
+ var data_#{id} = #{@data.to_json};
+ var keys_#{id} = #{T.unsafe(@keys).to_json};
+
+ var stack_#{id} = d3.stack()
+ .keys(keys_#{id})
+ .value((d, key) => d.values[key]);
+
+ var layers_#{id} = stack_#{id}(data_#{id});
+
+ var points_#{id} = []
+ layers_#{id}.forEach(function(d) {
+ d.forEach(function(p) {
+ p.key = d.key
+ points_#{id}.push(p);
+ });
+ })
+
+ function draw_#{id}() {
+ var width_#{id} = document.getElementById("#{id}").clientWidth;
+ var height_#{id} = 200;
+
+ d3.select("##{id}").selectAll("*").remove()
+
+ var svg_#{id} = d3.select("##{id}")
+ .attr("width", width_#{id})
+ .attr("height", height_#{id});
+
+ #{plot}
+ }
+
+ draw_#{id}();
+ window.addEventListener("resize", draw_#{id});
+ JS
+ end
+
+ sig { override.params(y: String, color: String, curve: String).returns(String) }
+ def line(y:, color: 'strictnessColor(d.key)', curve: 'curveCatmullRom.alpha(1)')
+ <<~JS
+ var area_#{id} = d3.area()
+ .x((d) => xScale_#{id}(parseDate(#{y})))
+ .y0((d) => yScale_#{id}(d[0]))
+ .y1((d) => yScale_#{id}(d[1]))
+ .curve(d3.#{curve});
+
+ var layer = svg_#{id}.selectAll(".layer")
+ .data(layers_#{id})
+ .enter().append("g")
+ .attr("class", "layer")
+
+ layer.append("path")
+ .attr("class", "area")
+ .attr("d", area_#{id})
+ .attr("fill", (d) => #{color})
+
+ layer.append("path")
+ .attr("class", "line")
+ .attr("d", d3.line()
+ .x((d) => xScale_#{id}(parseDate(#{y})))
+ .y((d, i) => yScale_#{id}(d[1]))
+ .curve(d3.#{curve}))
+ .attr("stroke", (d) => #{color})
+
+ svg_#{id}.selectAll("circle")
+ .data(points_#{id})
+ .enter()
+ .append("circle")
+ .attr("class", "dot")
+ .attr("cx", (d) => xScale_#{id}(parseDate(#{y})))
+ .attr("cy", (d, i) => yScale_#{id}(d[1]))
+ .on("mouseover", (d) => tooltip.style("opacity", 1))
+ .on("mousemove", tooltip_#{id})
+ .on("mouseleave", (d) => tooltip.style("opacity", 0));
+ JS
+ end
+
+ sig { override.returns(String) }
+ def plot
+ <<~JS
+ #{x_scale}
+ #{y_scale(min: '0', max: "d3.max(data_#{id}, (d) => d.total + 10)", ticks: 'tickValues([0, 25, 50, 75, 100])')}
+ #{line(y: 'd.data.timestamp', color: "d.key == 'rbis' ? '#8673ff' : '#007bff'")}
+ #{x_ticks}
+ #{y_ticks(ticks: 'tickValues([25, 50, 75])', format: 'd', padding: 20)}
JS
end
end
end
end