changeset 2816:c02cebff3f16

client: SPUC7/8: fix tooltip size and positioning The size is now dynamically calculated based on the content and the tooltip is now guaranteed to be visible instead of reaching out of the viewport.
author Markus Kottlaender <markus@intevation.de>
date Tue, 26 Mar 2019 19:37:55 +0100
parents 12f053763be2
children ff58440ce809
files client/src/components/gauge/Waterlevel.vue
diffstat 1 files changed, 59 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/components/gauge/Waterlevel.vue	Tue Mar 26 16:36:48 2019 +0100
+++ b/client/src/components/gauge/Waterlevel.vue	Tue Mar 26 19:37:55 2019 +0100
@@ -639,41 +639,40 @@
       eventRect.call(zoom);
     },
     createTooltips(eventRect) {
-      let dots = this.diagram.append("g").attr("class", "chart-dots");
-      dots
+      // create clippable container for the dot
+      this.diagram
+        .append("g")
+        .attr("class", "chart-dots")
         .append("circle")
         .attr("class", "chart-dot")
         .attr("r", 4);
-      let tooltips = this.diagram.append("g").attr("class", "chart-tooltip");
-      tooltips
-        .append("rect")
-        .attr("x", -25)
-        .attr("y", -25)
-        .attr("rx", 4)
-        .attr("ry", 4)
-        .attr("width", 105)
-        .attr("height", 40);
-      let tooltipText = tooltips.append("text");
+
+      // create container for the tooltip
+      const tooltip = this.diagram.append("g").attr("class", "chart-tooltip");
+      tooltip.append("rect");
+
+      const padding = 5;
+
+      // create container for multiple text rows
+      const tooltipText = tooltip.append("text").attr("text-anchor", "middle");
+      tooltipText.append("tspan").attr("alignment-baseline", "hanging");
       tooltipText
         .append("tspan")
-        .attr("x", -15)
-        .attr("y", -8);
-      tooltipText
-        .append("tspan")
-        .attr("x", 8)
-        .attr("y", 8);
+        .attr("dy", 18)
+        .attr("alignment-baseline", "hanging");
 
       eventRect
         .on("mouseover", () => {
-          this.svg.select(".chart-dot").style("opacity", 1);
-          this.svg.select(".chart-tooltip").style("opacity", 1);
+          this.diagram.select(".chart-dot").style("opacity", 1);
+          this.diagram.select(".chart-tooltip").style("opacity", 1);
         })
         .on("mouseout", () => {
-          this.svg.select(".chart-dot").style("opacity", 0);
-          this.svg.select(".chart-tooltip").style("opacity", 0);
+          this.diagram.select(".chart-dot").style("opacity", 0);
+          this.diagram.select(".chart-tooltip").style("opacity", 0);
         })
         .on("mousemove", () => {
-          let x0 = this.scale.x.invert(
+          // find data point closest to mouse
+          const x0 = this.scale.x.invert(
               d3.mouse(document.querySelector(".zoom"))[0]
             ),
             i = d3.bisector(d => d.date).left(this.waterlevels, x0, 1),
@@ -681,25 +680,19 @@
             d1 = this.waterlevels[i] || d0,
             d = x0 - d0.date > d1.date - x0 ? d1 : d0;
 
-          this.svg
+          const coords = {
+            x: this.scale.x(d.date),
+            y: this.scale.y(d.waterlevel)
+          };
+
+          // position the dot
+          this.diagram
             .select(".chart-dot")
             .style("opacity", 1)
-            .attr(
-              "transform",
-              `translate(${this.scale.x(d.date)}, ${this.scale.y(
-                d.waterlevel
-              )})`
-            );
-          this.svg
-            .select(".chart-tooltip")
-            .style("opacity", 1)
-            .attr(
-              "transform",
-              `translate(${this.scale.x(d.date) - 25}, ${this.scale.y(
-                d.waterlevel
-              ) - 25})`
-            );
-          this.svg.select(".chart-tooltip text tspan:first-child").text(
+            .attr("transform", `translate(${coords.x}, ${coords.y})`);
+
+          // write date
+          this.diagram.select(".chart-tooltip text tspan:first-child").text(
             d.date.toLocaleString([], {
               year: "2-digit",
               month: "2-digit",
@@ -708,9 +701,34 @@
               minute: "2-digit"
             })
           );
-          this.svg
+          // write waterlevel
+          this.diagram
             .select(".chart-tooltip text tspan:last-child")
             .text(d.waterlevel + " cm");
+
+          // get text dimensions
+          const textBBox = this.diagram
+            .select(".chart-tooltip text")
+            .node()
+            .getBBox();
+
+          this.diagram
+            .selectAll(".chart-tooltip text tspan")
+            .attr("x", textBBox.width / 2 + padding)
+            .attr("y", padding);
+
+          // position and scale tooltip box
+
+          let xMax = this.dimensions.width - textBBox.width;
+          let tooltipX = Math.min(coords.x - textBBox.width / 2, xMax);
+          let tooltipY = coords.y - textBBox.height * 2 + padding * 2;
+          this.diagram
+            .select(".chart-tooltip")
+            .style("opacity", 1)
+            .attr("transform", `translate(${tooltipX}, ${tooltipY})`)
+            .select("rect")
+            .attr("width", textBBox.width + padding * 2)
+            .attr("height", textBBox.height + padding * 2);
         });
     },
     isNext(seconds) {