<commit_before>/*eslint-env browser*/
import React from "react";
import * as globals from "../../util/globals";
import {dataFont, infoPanelStyles} from "../../globalStyles";
import { prettyString } from "./tipSelectedPanel";

const InfoPanel = ({mutType, tree, hovered, viewer, colorBy}) => {

  /* this is a function - we can bail early */
  if (!(tree && hovered)) {
    return null
  }

  /**
   * This maps tree coordinates to coordinates on the svg.
   * @param  {float} x
   * @param  {float}} y
   * @param  {reactSVGpanZoom} V viewer state
   * @return {[x,y]} point on plane
   */
  const treePosToViewer = (x,y, V) =>{
    const dx = (x*V.a + V.e); //this is the distance form the upper left corner
    const dy = (y*V.d + V.f); //at the current zoome level
    return {x: dx, y: dy};
  }

  /* I have no idea how these should acually be calculated, but apparently
  they are relative to the HTML body */
  const viewerState = viewer.getValue();
  const xOffset = 10;
  const yOffset = 10;

  const width = 200;
  const pos = treePosToViewer(hovered.d.xTip, hovered.d.yTip, viewerState);

  const styles = {
    container: {
      position: "absolute",
      width,
      padding: "10px",
      borderRadius: 10,
      zIndex: 1000,
      pointerEvents: "none",
      fontFamily: infoPanelStyles.panel.fontFamily,
      fontSize: 14,
      lineHeight: 1,
      fontWeight: infoPanelStyles.panel.fontWeight,
      color: infoPanelStyles.panel.color,
      backgroundColor: infoPanelStyles.panel.backgroundColor,
      wordWrap: "break-word",
      wordBreak: "break-word"
    }
  };
  if (pos.x<viewerState.viewerWidth*0.7){
    styles.container.left = pos.x + xOffset;
  }else{
    styles.container.right = viewerState.viewerWidth-pos.x + xOffset;
  }
  if (pos.y<viewerState.viewerHeight*0.7){
    styles.container.top = pos.y + 4*yOffset;
  }else{
    styles.container.bottom = viewerState.viewerHeight-pos.y+yOffset;
  }

  /* getX methods return styled JSX */
  const getMutations = (d) => {
    /* mutations are stored at:
    NUCLEOTIDE: d.muts = list of strings
    AMINO ACID: d.aa_muts = dict of proteins -> list of strings
    */
    if (mutType === "nuc") {
      if (typeof d.muts !== "undefined" && d.muts.length) {
        const nDisplay = 5; // number of mutations to display (max)
        const n = d.muts.length; // number of mutations that exist
        let m = "Nt mutations: " + d.muts.slice(0, Math.min(nDisplay, n)).join(", ");
        if (n > nDisplay) {
          m += (" + " + (n - nDisplay) + " more");
        }
        return (<span>{m}</span>);
      } else {
        return (<span>No nucleotide mutations</span>);
      }
    } else if (typeof d.aa_muts !== "undefined") {
      /* are there any? */
      const prots = Object.keys(d.aa_muts);
      const counts = {};
      for (const prot of prots) {
        counts[prot] = d.aa_muts[prot].length;
      }
      if (prots.map((k) => counts[k]).reduce((a, b) => a + b, 0)) {
        const nDisplay = 3; // number of mutations to display per protein
        const m = [<span key={"init"}>AA mutations:</span>];
        prots.forEach((prot, idx) => {
          if (counts[prot]) {
            let x = prot + ":\u00A0\u00A0" + d.muts.slice(0, Math.min(nDisplay, counts[prot])).join(", ");
            if (counts[prot] > nDisplay) {
              x += " + " + (counts[prot] - nDisplay) + " more";
            }
            m.push(<span key={idx}><br />{x}</span>);
          }
        });
        return m;
      } else {
        return (<span>No amino acid mutations</span>);
      }
    }
    return "Error parsing mutations.";
  };
  const getFrequencies = (d) => {
    if (d.frequency !== undefined) {
      const disp = "Frequency: " + (100 * d.frequency).toFixed(1) + "%";
      return (<p>{disp}</p>);
    }
    return null;
  };
  const getColorByAttr = (d) => {
    const disp = `${prettyString(colorBy)}: ${prettyString(d.attr[colorBy])}`;
    return (<p>{disp}</p>);
  };

  const makeInfoPanel = () => {
    if (hovered.type === ".tip") {
      const tip = hovered.d;
      return (
        <div style={styles.container}>
          <div className={"tooltip"} style={infoPanelStyles.tooltip}>
            <div style={infoPanelStyles.tooltipHeading}>
              {tip.n.attr.strain}
            </div>
            <div>
              {prettyString(tip.n.attr.country)}, {prettyString(tip.n.attr.date)}
            </div>
            <div style={infoPanelStyles.comment}>
              Click on tip to display more info
            </div>
          </div>
        </div>
      );
    } else { /* BRANCH */<commit_msg>show amino acid mutations in tool tip, don't show colorby in tooltip when color by genotype<commit_after>/*eslint-env browser*/
import React from "react";
import * as globals from "../../util/globals";
import {dataFont, infoPanelStyles} from "../../globalStyles";
import { prettyString } from "./tipSelectedPanel";

const InfoPanel = ({mutType, tree, hovered, viewer, colorBy}) => {

  /* this is a function - we can bail early */
  if (!(tree && hovered)) {
    return null
  }

  /**
   * This maps tree coordinates to coordinates on the svg.
   * @param  {float} x
   * @param  {float}} y
   * @param  {reactSVGpanZoom} V viewer state
   * @return {[x,y]} point on plane
   */
  const treePosToViewer = (x,y, V) =>{
    const dx = (x*V.a + V.e); //this is the distance form the upper left corner
    const dy = (y*V.d + V.f); //at the current zoome level
    return {x: dx, y: dy};
  }

  /* I have no idea how these should acually be calculated, but apparently
  they are relative to the HTML body */
  const viewerState = viewer.getValue();
  const xOffset = 10;
  const yOffset = 10;

  const width = 200;
  const pos = treePosToViewer(hovered.d.xTip, hovered.d.yTip, viewerState);

  const styles = {
    container: {
      position: "absolute",
      width,
      padding: "10px",
      borderRadius: 10,
      zIndex: 1000,
      pointerEvents: "none",
      fontFamily: infoPanelStyles.panel.fontFamily,
      fontSize: 14,
      lineHeight: 1,
      fontWeight: infoPanelStyles.panel.fontWeight,
      color: infoPanelStyles.panel.color,
      backgroundColor: infoPanelStyles.panel.backgroundColor,
      wordWrap: "break-word",
      wordBreak: "break-word"
    }
  };
  if (pos.x<viewerState.viewerWidth*0.7){
    styles.container.left = pos.x + xOffset;
  }else{
    styles.container.right = viewerState.viewerWidth-pos.x + xOffset;
  }
  if (pos.y<viewerState.viewerHeight*0.7){
    styles.container.top = pos.y + 4*yOffset;
  }else{
    styles.container.bottom = viewerState.viewerHeight-pos.y+yOffset;
  }

  /* getX methods return styled JSX */
  const getMutations = (d) => {
    /* mutations are stored at:
    NUCLEOTIDE: d.muts = list of strings
    AMINO ACID: d.aa_muts = dict of proteins -> list of strings
    */
    if (mutType === "nuc") {
      if (typeof d.muts !== "undefined" && d.muts.length) {
        const nDisplay = 5; // number of mutations to display (max)
        const n = d.muts.length; // number of mutations that exist
        let m = "Nt mutations: " + d.muts.slice(0, Math.min(nDisplay, n)).join(", ");
        if (n > nDisplay) {
          m += (" + " + (n - nDisplay) + " more");
        }
        return (<span>{m}</span>);
      } else {
        return (<span>No nucleotide mutations</span>);
      }
    } else if (typeof d.aa_muts !== "undefined") {
      /* are there any? */
      const prots = Object.keys(d.aa_muts);
      const counts = {};
      for (const prot of prots) {
        counts[prot] = d.aa_muts[prot].length;
      }
      if (prots.map((k) => counts[k]).reduce((a, b) => a + b, 0)) {
        const nDisplay = 3; // number of mutations to display per protein
        const m = [<span key={"init"}>AA mutations:</span>];
        prots.forEach((prot, idx) => {
          if (counts[prot]) {
            let x = prot + ":\u00A0\u00A0" + d.aa_muts[prot].slice(0, Math.min(nDisplay, counts[prot])).join(", ");
            if (counts[prot] > nDisplay) {
              x += " + " + (counts[prot] - nDisplay) + " more";
            }
            m.push(<span key={idx}><br />{x}</span>);
          }
        });
        return m;
      } else {
        return (<span>No amino acid mutations</span>);
      }
    }
    return "Error parsing mutations.";
  };
  const getFrequencies = (d) => {
    if (d.frequency !== undefined) {
      const disp = "Frequency: " + (100 * d.frequency).toFixed(1) + "%";
      return (<p>{disp}</p>);
    }
    return null;
  };
  const getColorByAttr = (d) => {
    if (colorBy.slice(0,2)!=='gt'){
      const disp = `${prettyString(colorBy)}: ${prettyString(d.attr[colorBy])}`;
      return (<p>{disp}</p>);
    }else{
      return "";
    }
  };

  const makeInfoPanel = () => {
    if (hovered.type === ".tip") {
      const tip = hovered.d;
      return (
        <div style={styles.container}>
          <div className={"tooltip"} style={infoPanelStyles.tooltip}>
            <div style={infoPanelStyles.tooltipHeading}>
              {tip.n.attr.strain}
            </div>
            <div>
              {prettyString(tip.n.attr.country)}, {prettyString(tip.n.attr.date)}
            </div>
            <div style={infoPanelStyles.comment}>
              Click on tip to display more info
            </div>
          </div>
        </div>
      );
    } else { /* BRANCH */
{'fraction_non_alphanumeric': 0.15781066979022468, 'fraction_numerical': 0.008305824725801299, 'mean_word_length': 2.2736144998257233, 'pattern_counts': {'":': 2, '<': 57, '<?xml version=': 0, '>': 77, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2741675', 'n_tokens_mistral': 3177, 'n_tokens_neox': 3066, 'n_words': 791}