comparison client/src/components/gauge/Waterlevel.vue @ 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 97cf32cf2562
children 53c2bd009c68
comparison
equal deleted inserted replaced
2815:12f053763be2 2816:c02cebff3f16
637 .call(brush.move, this.scale.x.range()); 637 .call(brush.move, this.scale.x.range());
638 638
639 eventRect.call(zoom); 639 eventRect.call(zoom);
640 }, 640 },
641 createTooltips(eventRect) { 641 createTooltips(eventRect) {
642 let dots = this.diagram.append("g").attr("class", "chart-dots"); 642 // create clippable container for the dot
643 dots 643 this.diagram
644 .append("g")
645 .attr("class", "chart-dots")
644 .append("circle") 646 .append("circle")
645 .attr("class", "chart-dot") 647 .attr("class", "chart-dot")
646 .attr("r", 4); 648 .attr("r", 4);
647 let tooltips = this.diagram.append("g").attr("class", "chart-tooltip"); 649
648 tooltips 650 // create container for the tooltip
649 .append("rect") 651 const tooltip = this.diagram.append("g").attr("class", "chart-tooltip");
650 .attr("x", -25) 652 tooltip.append("rect");
651 .attr("y", -25) 653
652 .attr("rx", 4) 654 const padding = 5;
653 .attr("ry", 4) 655
654 .attr("width", 105) 656 // create container for multiple text rows
655 .attr("height", 40); 657 const tooltipText = tooltip.append("text").attr("text-anchor", "middle");
656 let tooltipText = tooltips.append("text"); 658 tooltipText.append("tspan").attr("alignment-baseline", "hanging");
657 tooltipText 659 tooltipText
658 .append("tspan") 660 .append("tspan")
659 .attr("x", -15) 661 .attr("dy", 18)
660 .attr("y", -8); 662 .attr("alignment-baseline", "hanging");
661 tooltipText
662 .append("tspan")
663 .attr("x", 8)
664 .attr("y", 8);
665 663
666 eventRect 664 eventRect
667 .on("mouseover", () => { 665 .on("mouseover", () => {
668 this.svg.select(".chart-dot").style("opacity", 1); 666 this.diagram.select(".chart-dot").style("opacity", 1);
669 this.svg.select(".chart-tooltip").style("opacity", 1); 667 this.diagram.select(".chart-tooltip").style("opacity", 1);
670 }) 668 })
671 .on("mouseout", () => { 669 .on("mouseout", () => {
672 this.svg.select(".chart-dot").style("opacity", 0); 670 this.diagram.select(".chart-dot").style("opacity", 0);
673 this.svg.select(".chart-tooltip").style("opacity", 0); 671 this.diagram.select(".chart-tooltip").style("opacity", 0);
674 }) 672 })
675 .on("mousemove", () => { 673 .on("mousemove", () => {
676 let x0 = this.scale.x.invert( 674 // find data point closest to mouse
675 const x0 = this.scale.x.invert(
677 d3.mouse(document.querySelector(".zoom"))[0] 676 d3.mouse(document.querySelector(".zoom"))[0]
678 ), 677 ),
679 i = d3.bisector(d => d.date).left(this.waterlevels, x0, 1), 678 i = d3.bisector(d => d.date).left(this.waterlevels, x0, 1),
680 d0 = this.waterlevels[i - 1], 679 d0 = this.waterlevels[i - 1],
681 d1 = this.waterlevels[i] || d0, 680 d1 = this.waterlevels[i] || d0,
682 d = x0 - d0.date > d1.date - x0 ? d1 : d0; 681 d = x0 - d0.date > d1.date - x0 ? d1 : d0;
683 682
684 this.svg 683 const coords = {
684 x: this.scale.x(d.date),
685 y: this.scale.y(d.waterlevel)
686 };
687
688 // position the dot
689 this.diagram
685 .select(".chart-dot") 690 .select(".chart-dot")
686 .style("opacity", 1) 691 .style("opacity", 1)
687 .attr( 692 .attr("transform", `translate(${coords.x}, ${coords.y})`);
688 "transform", 693
689 `translate(${this.scale.x(d.date)}, ${this.scale.y( 694 // write date
690 d.waterlevel 695 this.diagram.select(".chart-tooltip text tspan:first-child").text(
691 )})`
692 );
693 this.svg
694 .select(".chart-tooltip")
695 .style("opacity", 1)
696 .attr(
697 "transform",
698 `translate(${this.scale.x(d.date) - 25}, ${this.scale.y(
699 d.waterlevel
700 ) - 25})`
701 );
702 this.svg.select(".chart-tooltip text tspan:first-child").text(
703 d.date.toLocaleString([], { 696 d.date.toLocaleString([], {
704 year: "2-digit", 697 year: "2-digit",
705 month: "2-digit", 698 month: "2-digit",
706 day: "2-digit", 699 day: "2-digit",
707 hour: "2-digit", 700 hour: "2-digit",
708 minute: "2-digit" 701 minute: "2-digit"
709 }) 702 })
710 ); 703 );
711 this.svg 704 // write waterlevel
705 this.diagram
712 .select(".chart-tooltip text tspan:last-child") 706 .select(".chart-tooltip text tspan:last-child")
713 .text(d.waterlevel + " cm"); 707 .text(d.waterlevel + " cm");
708
709 // get text dimensions
710 const textBBox = this.diagram
711 .select(".chart-tooltip text")
712 .node()
713 .getBBox();
714
715 this.diagram
716 .selectAll(".chart-tooltip text tspan")
717 .attr("x", textBBox.width / 2 + padding)
718 .attr("y", padding);
719
720 // position and scale tooltip box
721
722 let xMax = this.dimensions.width - textBBox.width;
723 let tooltipX = Math.min(coords.x - textBBox.width / 2, xMax);
724 let tooltipY = coords.y - textBBox.height * 2 + padding * 2;
725 this.diagram
726 .select(".chart-tooltip")
727 .style("opacity", 1)
728 .attr("transform", `translate(${tooltipX}, ${tooltipY})`)
729 .select("rect")
730 .attr("width", textBBox.width + padding * 2)
731 .attr("height", textBBox.height + padding * 2);
714 }); 732 });
715 }, 733 },
716 isNext(seconds) { 734 isNext(seconds) {
717 // helper to check whether points in the chart are "next to each other" 735 // helper to check whether points in the chart are "next to each other"
718 // for that they need to be exactly the specified amount of seconds apart. 736 // for that they need to be exactly the specified amount of seconds apart.