Skip to content
Snippets Groups Projects
BeeswarmChart.vue 18.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • Cee Nell's avatar
    Cee Nell committed
    <template>
    
    Cee Nell's avatar
    Cee Nell committed
      <section id="beeswarm">
        <div id="text1" class="text-container">
          <p>
    
            Everyone needs access to clean water. The gap between water security and <span class="tooltip-group"><span class="tooltip-span"> water insecurity </span><span class="tooltiptext"> Populations cannot maintain access to adequate quantities of water at an acceptable quality to sustain livelihoods, development, and human and ecosystem health.</span></span> can be the difference between a daydream and a nightmare. People may be more or less vulnerable to water insecurity due to  
    
    Cee Nell's avatar
    Cee Nell committed
            <span 
              :class="['highlight', 'Demographiccharacteristics', { checked: isChecked.Demographiccharacteristics }]" 
              @click="toggleCategory('Demographiccharacteristics')"
            >
              demographic characteristics
            </span>,
            <span 
              :class="['highlight', 'Health', { checked: isChecked.Health }]" 
              @click="toggleCategory('Health')"
            >
              health
            </span>, 
            <span 
              :class="['highlight', 'Livingconditions', { checked: isChecked.Livingconditions }]" 
              @click="toggleCategory('Livingconditions')"
            >
              living conditions
            </span>, 
            <span 
              :class="['highlight', 'Socioeconomicstatus', { checked: isChecked.Socioeconomicstatus }]" 
              @click="toggleCategory('Socioeconomicstatus')"
            >
              socioeconomic status
            </span>,
            <span 
              :class="['highlight', 'Riskperception', { checked: isChecked.Riskperception }]" 
              @click="toggleCategory('Riskperception')"
            >
              risk perception
            </span>,
            <span 
              :class="['highlight', 'Landtenure', { checked: isChecked.Landtenure }]" 
              @click="toggleCategory('Landtenure')"
            >
              land tenure
            </span>, and 
            <span 
              :class="['highlight', 'Exposure', { checked: isChecked.Exposure }]" 
              @click="toggleCategory('Exposure')"
            >
              exposure to stressors
    
            </span>  (like drought or pollution). Below is a chart that displays how certain social vulnerability determinants relate to water insecurity.
    
    Cee Nell's avatar
    Cee Nell committed
          </p>
        </div>
    
        <div id="text2" class="text-container tooltip-width">
          <div id="tooltip" class="tooltip">Interact with the chart to explore evidence<br>for social vulnerability determinants.</div>
          <em>Many social vulnerability determinants have been studied. Some show positive <span class="legend-box positive"></span> relationships with water insecurity, some negative <span class="legend-box negative"></span>, and others unknown <span class="legend-box unknown"></span> <a href='https://www.sciencebase.gov/catalog/item/63f79d49d34e4f7eda456572' target='_blank'>(Hines and others, 2023)</a>.</em>
        </div>
    
    Cee Nell's avatar
    Cee Nell committed
        <div id="beeswarm-chart-container">
        </div>
        <div id="text2" class="text-container tooltip-width">
    
          <em>The y-axis of the chart represents the level of agreement among studies where increased <b>consensus</b> indicates a majority of studies using the selected determinant recorded the same direction of influence on conditions of water insecurity and <b>inconclusive</b> indicates studies using the selected determinant did not record the same direction of influence on conditions of water insecurity. The size of the bubbles on the chart represents the number of studies, with larger bubbles indicating that a particular determinant has been studied more frequently.</em>
    
    Cee Nell's avatar
    Cee Nell committed
        </div>
    
    Cee Nell's avatar
    Cee Nell committed
      </section>
    </template>
    
    <script setup>
    import { onMounted, ref } from "vue";
    import * as d3 from 'd3';
    
    Cee Nell's avatar
    Cee Nell committed
    import { isMobile } from 'mobile-device-detect';
    
    Cee Nell's avatar
    Cee Nell committed
    
    // Global variables 
    const publicPath = import.meta.env.BASE_URL;
    const dataSet1 = ref([]); 
    const dataSet2 = ref([]); 
    const selectedDataSet = ref('dataSet1');
    const data = ref([]);
    let simulation;
    
    // Set up SVG
    let svg;
    const height = 600;
    const width = 800;
    
    Cee Nell's avatar
    Cee Nell committed
    const margin = { top: 40, right: 20, bottom: 40, left: 40 };
    
    Cee Nell's avatar
    Cee Nell committed
    
    const isChecked = ref({
      Demographiccharacteristics: true,
      Health: true,
      Livingconditions: true,
      Socioeconomicstatus: true,
      Riskperception: true,
      Landtenure: true,
      Exposure: true
    });
    
    // Set colors for bubble charts
    const dimensionColors = {
      Demographiccharacteristics: "#092836",
      Landtenure: "#1b695e",
      Livingconditions: "#7a5195",
      Socioeconomicstatus: "#2a468f",
      Health: "#ef5675",
      Riskperception: "#ff764a",
      Exposure: "#ffa600"
    };
    
    const patternId = 'pattern-stripe';
    
    // bar chart patterning and fills
    const legendData = [
    
    Cee Nell's avatar
    Cee Nell committed
      { name: 'Positive', color: dimensionColors['Demographiccharacteristics'] },
      { name: 'Negative', color: 'white', stroke: dimensionColors['Demographiccharacteristics'] },
      { name: 'Unknown', pattern: true, color: `url(#pattern-stripe)`, stroke: dimensionColors['Demographiccharacteristics'] }
    ];
    
    // create legend for stack bar charts
    function createPattern() {
      const svgDefs = d3.select('body')
    
    Cee Nell's avatar
    Cee Nell committed
        .append('svg')
    
    Cee Nell's avatar
    Cee Nell committed
        .attr('width', 0)
        .attr('height', 0)
        .append('defs');
    
    Cee Nell's avatar
    Cee Nell committed
    
    
    Cee Nell's avatar
    Cee Nell committed
      const pattern = svgDefs.append('pattern')
    
        .attr('id', patternId)
    
    Cee Nell's avatar
    Cee Nell committed
        .attr('patternUnits', 'userSpaceOnUse')
        .attr('width', 8)
        .attr('height', 8);
    
      pattern.append('rect')
        .attr('width', 8)
        .attr('height', 8)
        .attr('fill', 'white');
    
      pattern.append('path')
        .attr('d', 'M-2,2 l4,-4 M0,8 l8,-8 M6,10 l4,-4')
        .attr('stroke', dimensionColors['Demographiccharacteristics'])
        .attr('stroke-width', 3);
    
    }
    function createLegend() {
      const keyW = 65;
      const keyH = 12;
    
      createPattern();
    
    Cee Nell's avatar
    Cee Nell committed
    
      legendData.forEach(d => {
        const legend = d3.select(`.${d.name.toLowerCase()}`)
          .append('svg')
          .attr('width', keyW + 5)
          .attr('height', keyH);
    
        legend.append('rect')
          .attr('x', 4)
          .attr('y', 0)
          .attr('width', keyW)
          .attr('height', keyH)
          .style('fill', d.pattern ? d.color : d.color)
          .style('stroke', d.stroke ? d.stroke : 'none')
    
          .style('stroke-width', d.stroke ? 2 : 0);
    
    Cee Nell's avatar
    Cee Nell committed
      });
    
    Cee Nell's avatar
    Cee Nell committed
    }
    
    Cee Nell's avatar
    Cee Nell committed
    
    // Load data and then make chart
    onMounted(async () => {
      try {
        await loadDatasets();
        data.value = selectedDataSet.value === 'dataSet1' ? dataSet1.value : dataSet2.value;
        if (data.value.length > 0) {
          createBeeswarmChart();
    
          createLegend();
    
    Cee Nell's avatar
    Cee Nell committed
        } else {
          console.error('Error loading data');
    
    Cee Nell's avatar
    Cee Nell committed
        }
    
    Cee Nell's avatar
    Cee Nell committed
      } catch (error) {
        console.error('Error during component mounting', error);
    
    Cee Nell's avatar
    Cee Nell committed
    });
    
    async function loadDatasets() {
      try {
        dataSet1.value = await loadData('determinant_uncertainty.csv');
        dataSet2.value = await loadData('indicator_uncertainty.csv');
        console.log('data in');
      } catch (error) {
        console.error('Error loading datasets', error);
    
    Cee Nell's avatar
    Cee Nell committed
    }
    
    async function loadData(fileName) {
      try {
        const data = await d3.csv(publicPath + fileName, d => { 
          d.level_agreement = +(+d.level_agreement).toFixed(2); 
          d.evidence_val = +d.evidence_val; 
          d.sig_value = +d.sig_value; 
          return d;
        });
        return data;
      } catch (error) {
        console.error(`Error loading data from ${fileName}`, error);
        return [];
      }
    }
    
    function createBeeswarmChart() {
    
    Cee Nell's avatar
    Cee Nell committed
      // Remove any existing SVG element
      d3.select('#beeswarm-chart-container').select('svg').remove();
      
      // Get dynamic dimensions to draw chart
      const containerWidth = document.getElementById('beeswarm-chart-container').offsetWidth;
    
    Cee Nell's avatar
    Cee Nell committed
      const containerHeight = window.innerWidth <= 700 ? window.innerHeight * 0.5 : 600;
    
      const margin = window.innerWidth <= 700 ? { top: 40, right: 10, bottom: 20, left: 10 } : { top: 40, right: 20, bottom: 40, left: 40 };
    
    Cee Nell's avatar
    Cee Nell committed
      const width = containerWidth - margin.left - margin.right;
      const height = containerHeight - margin.top - margin.bottom;
      
    
    Cee Nell's avatar
    Cee Nell committed
      svg = d3
        .select('#beeswarm-chart-container')
        .append('svg')
        .attr('class', 'beeswarmSvg')
    
    Cee Nell's avatar
    Cee Nell committed
        .attr('viewBox', `0 0 ${containerWidth} ${containerHeight}`)
        .attr('preserveAspectRatio', 'xMidYMid meet')
    
        .style('width', '80%')
    
    Cee Nell's avatar
    Cee Nell committed
        .style('height', 'auto');
    
    Cee Nell's avatar
    Cee Nell committed
    
      const yScale = d3.scaleLinear()
        .domain([40, d3.max(data.value, d => d.level_agreement)])
    
    Cee Nell's avatar
    Cee Nell committed
        .range([containerHeight - margin.bottom, margin.top]);
    
    Cee Nell's avatar
    Cee Nell committed
    
      // Set radius based on evidence value
      const radiusScale = d3.scaleLinear()
        .domain([d3.min(data.value, d => d.evidence_val), d3.max(data.value, d => d.evidence_val)])
    
        .range(window.innerWidth <= 700 ? [8, 40] : [10, 70]);
    
    Cee Nell's avatar
    Cee Nell committed
    
      const yAxis = svg.append('g')
    
      .attr("transform", "translate(50, 0)")
      .call(d3.axisLeft(yScale)
        .ticks(5)
        .tickFormat(d => d + '%'))
      .attr("stroke-width", 2)
      .attr("font-size", 18);
    
    Cee Nell's avatar
    Cee Nell committed
    
      // Add label to y axis
      svg.append('text')
    
        .attr("class", "yLabel")
        .attr("text-anchor", "left")
    
    Cee Nell's avatar
    Cee Nell committed
        .attr("font-weight", 700)
    
    Cee Nell's avatar
    Cee Nell committed
        .attr("transform", `translate(${margin.left}, ${margin.top / 2})`)
    
    Cee Nell's avatar
    Cee Nell committed
        .text("Consensus");
    
    Cee Nell's avatar
    Cee Nell committed
      svg.append('text')
        .attr("class", "yLabel")
        .attr("text-anchor", "left")
        .attr("font-weight", 700)
    
    Cee Nell's avatar
    Cee Nell committed
        .attr("transform", `translate(${margin.left}, ${containerHeight - (margin.bottom / 2) + 10})`)
    
    Cee Nell's avatar
    Cee Nell committed
        .text("Inconclusive");
    
    Cee Nell's avatar
    Cee Nell committed
      // Set forces
    
    Cee Nell's avatar
    Cee Nell committed
      const forceY = d3.forceY(d => yScale(d.level_agreement)).strength(0.9);
      const forceX = d3.forceX(margin.left + (width / 2)).strength(0.1);
    
    Cee Nell's avatar
    Cee Nell committed
      const forceCollide = d3.forceCollide(d => radiusScale(d.evidence_val) + 2).iterations(20);
      const forceManyBody = d3.forceManyBody().strength(1);
    
      const bubbles = svg
    
    Cee Nell's avatar
    Cee Nell committed
        .selectAll('.bubble')
        .data(data.value)
        .enter()
        .append('circle')
        .attr('class', 'bubble')
        .attr('r', d => radiusScale(d.evidence_val))
        .style('fill', d => dimensionColors[d.dimension.replace(' ', '')])
        .on('mouseover', handleMouseOver)
        .on('mouseout', handleMouseOut);
    
    Cee Nell's avatar
    Cee Nell committed
    
      // Run simulation
      simulation = d3.forceSimulation()
        .force('x', forceX)
        .force('y', forceY)
        .force('collide', forceCollide)
        .force('charge', forceManyBody)
        .nodes(data.value)
        .on('tick', ticked)
        .alpha(0.75)
        .alphaDecay(0.03);
    
      function ticked() {
        bubbles
          .attr("cx", d => Math.max(margin.left + radiusScale(d.evidence_val), Math.min(width - margin.right - radiusScale(d.evidence_val), d.x)))
    
    Cee Nell's avatar
    Cee Nell committed
          .attr("cy", d => Math.max(radiusScale(d.evidence_val), Math.min(containerHeight - radiusScale(d.evidence_val), d.y)));
    
      //createLegend(); // add legend to caption
    
    Cee Nell's avatar
    Cee Nell committed
    }
    
    function toggleCategory(category) {
      isChecked.value[category] = !isChecked.value[category];
      console.log(`Category toggled: ${category}, new value: ${isChecked.value[category]}`);
      updateChart();
    }
    
    
    Cee Nell's avatar
    Cee Nell committed
    function updateChart() {
    
    Cee Nell's avatar
    Cee Nell committed
      // Adjust height based on screen size
      const isMobile = window.innerWidth <= 700;
      const containerHeight = isMobile ? window.innerHeight * 0.50 : height;
    
    Cee Nell's avatar
    Cee Nell committed
      // Set the y scale
      const yScale = d3.scaleLinear()
        .domain([40, d3.max(data.value, d => d.level_agreement)])
    
    Cee Nell's avatar
    Cee Nell committed
        .range([containerHeight - margin.bottom, margin.top]);
    
    Cee Nell's avatar
    Cee Nell committed
      // Set the radius scale based on evidence value
      const radiusScale = d3.scaleLinear()
        .domain([d3.min(data.value, d => d.evidence_val), d3.max(data.value, d => d.evidence_val)])
    
        .range(window.innerWidth <= 700 ? [8, 40] : [10, 70]);
    
    Cee Nell's avatar
    Cee Nell committed
      // Filter data based on active categories
      const activeCategories = Object.keys(isChecked.value).filter(category => isChecked.value[category]);
      console.log('Active categories:', activeCategories);
    
      // Select all bubbles and bind data
      const bubbles = svg.selectAll(".bubble")
        .data(data.value, d => d.id);
    
      // Update existing bubbles
      bubbles
        .attr('r', d => radiusScale(d.evidence_val))
    
        .style('fill', d => activeCategories.includes(d.dimension.replace(' ', '')) ? dimensionColors[d.dimension.replace(' ', '')] : 'rgba(217, 217, 217, 0.95)');
    
    Cee Nell's avatar
    Cee Nell committed
    
      // Add new bubbles
      bubbles.enter()
        .append('circle')
        .attr('class', 'bubble')
        .attr('r', d => radiusScale(d.evidence_val))
    
        .style('fill', d => activeCategories.includes(d.dimension.replace(' ', '')) ? dimensionColors[d.dimension.replace(' ', '')] : 'rgba(217, 217, 217, 0.95)')
    
    Cee Nell's avatar
    Cee Nell committed
        .attr('cx', d => d.x)  // Use existing x position
        .attr('cy', d => d.y)
    
    Cee Nell's avatar
    Cee Nell committed
        .on('mouseover', handleMouseOver)
        .on('mouseout', handleMouseOut)
    
    Cee Nell's avatar
    Cee Nell committed
        .merge(bubbles);
    
      // Ensure all bubbles are handled correctly
      bubbles.exit().remove();
    }
    
    Cee Nell's avatar
    Cee Nell committed
    
    
    function updatePatternColor(color) {
      d3.select(`#${patternId} path`)
        .attr('stroke', color);
    }
    
    
    Cee Nell's avatar
    Cee Nell committed
    // Tooltip functions
    
    Cee Nell's avatar
    Cee Nell committed
    function handleMouseOver(event, d) {
    
      const activeCategories = Object.keys(isChecked.value).filter(category => isChecked.value[category]);
    
      // Check if the category of the data point is toggled
      if (!activeCategories.includes(d.dimension.replace(' ', ''))) {
        return;  // Do nothing if the category is untoggled
      }
    
    
      // Add text to tooltip
    
    Cee Nell's avatar
    Cee Nell committed
      const tooltip = d3.select('#tooltip');
      tooltip.html('')
        .append('div')
    
        .html(`<strong>${d.determinant_wrapped}</strong> appeared in ${d.evidence_val} ${d.evidence_val === 1 ? 'study' : 'studies'}.`);
    
    Cee Nell's avatar
    Cee Nell committed
    
    
      // Define categories in stacked bar chart
    
    Cee Nell's avatar
    Cee Nell committed
      const barData = [
        { name: 'positive', value: d.pos_related_total, stroke: dimensionColors[d.dimension.replace(' ', '')], fill: dimensionColors[d.dimension.replace(' ', '')] },
        { name: 'negative', value: d.neg_related_total, stroke: dimensionColors[d.dimension.replace(' ', '')], fill: 'white' },
    
    Cee Nell's avatar
    Cee Nell committed
        { name: 'unknown', value: d.unk_direction_total, pattern: true, stroke: dimensionColors[d.dimension.replace(' ', '')], fill: `url(#pattern-stripe)` }
    
    Cee Nell's avatar
    Cee Nell committed
      ];
    
      // Set dimensions for the bar chart
    
    Cee Nell's avatar
    Cee Nell committed
      const barWidth = 200;
      const barHeight = 15;
    
    Cee Nell's avatar
    Cee Nell committed
    
      // Create an SVG element for the bar chart
      const svgBar = tooltip.append('svg')
        .attr('width', barWidth + 10)
        .attr('height', barHeight + 10);
    
      const g = svgBar.append('g')
        .attr('transform', 'translate(8, 8)');
    
      // Create a scale for the x-axis
      const xBar = d3.scaleLinear()
        .domain([0, d3.sum(barData, d => d.value)])
        .range([0, barWidth]);
    
      // Create groups for each bar segment
      const barGroups = g.selectAll('g')
        .data(barData)
        .enter()
        .append('g');
    
    
      // Add the rectangles for the bars
    
    Cee Nell's avatar
    Cee Nell committed
      barGroups.append('rect')
        .attr('x', (d, i) => i > 0 ? xBar(d3.sum(barData.slice(0, i), d => d.value)) : 0)
        .attr('y', 0)
        .attr('width', d => xBar(d.value))
        .attr('height', barHeight)
    
    Cee Nell's avatar
    Cee Nell committed
        .style('fill', d => d.pattern ? `url(#${patternId})` : d.fill)
    
    Cee Nell's avatar
    Cee Nell committed
        .style('stroke', d => d.stroke ? d.stroke : 'none');
    
    
      updatePatternColor(dimensionColors[d.dimension.replace(' ', '')]);
    
      // add emphasis to bubbles
      d3.select(this)
        .attr('stroke', dimensionColors[d.dimension.replace(' ', '')])
        .attr('stroke-width', 8);
      
      // update colors in legend
      d3.select('#text2').select('.positive').select('rect')
    
    Cee Nell's avatar
    Cee Nell committed
        .style('fill', dimensionColors[d.dimension.replace(' ', '')]);
    
    
    Cee Nell's avatar
    Cee Nell committed
      d3.select('#text2').select('.negative').select('rect')
        .style('fill', 'white')
    
    Cee Nell's avatar
    Cee Nell committed
        .style('stroke', dimensionColors[d.dimension.replace(' ', '')]);
    
    
    Cee Nell's avatar
    Cee Nell committed
      d3.select('#text2').select('.unknown').select('rect')
        .style('fill', `url(#pattern-stripe)`)
    
    Cee Nell's avatar
    Cee Nell committed
        .style('stroke', dimensionColors[d.dimension.replace(' ', '')]);
    
    
        d3.select('#text2').select(`#${patternId} path`)
        //.style('fill', `url(#pattern-stripe)`)
        .attr('stroke', dimensionColors[d.dimension.replace(' ', '')]);
    
    Cee Nell's avatar
    Cee Nell committed
    }
    
    function handleMouseOut() {
    
    Cee Nell's avatar
    Cee Nell committed
      const tooltip = d3.select('#tooltip');
    
      
      // add starting text
    
    Cee Nell's avatar
    Cee Nell committed
      tooltip.html('Interact with the chart to explore evidence for<br>social vulnerability determinants.');
    
      
      // remove emphasis on bubbles
    
    Cee Nell's avatar
    Cee Nell committed
      d3.select(this)
        .attr('stroke', null)
        .attr('stroke-width', null);
    
      
      /* d3.select('#text2').select('.positive').select('rect')
        .style('fill', dimensionColors['Demographiccharacteristics']);
    
      d3.select('#text2').select('.negative').select('rect')
        .style('fill', 'white')
        .style('stroke', dimensionColors['Demographiccharacteristics']); */
    
      /* d3.select('#text2').select('.unknown').select('rect')
        .style('fill', `url(#pattern-stripe)`)
        .style('stroke', dimensionColors[d.dimension.replace(' ', '')]); */
    
    Cee Nell's avatar
    Cee Nell committed
    }
    
    Cee Nell's avatar
    Cee Nell committed
    </script>
    
    <style scoped lang="scss">
    $Demographiccharacteristics: #092836;
    $Landtenure: #1b695e;
    $Livingconditions: #7a5195;
    $Socioeconomicstatus: #2a468f;
    $Health: #ef5675;
    $Riskperception: #ff764a;
    $Exposure: #ffa600;
    $ThemeGrey: rgba(217, 217, 217, 0.95);
    
    #beeswarm-chart-container {
    
    Cee Nell's avatar
    Cee Nell committed
      text-align: center;
      position: relative;
    
    Cee Nell's avatar
    Cee Nell committed
      max-width: 800px;
      margin: auto;
    
    }
    
    #beeswarm-chart-container svg {
    
    Cee Nell's avatar
    Cee Nell committed
      //width: 90vw;
      max-width: 700px;
    
    Cee Nell's avatar
    Cee Nell committed
      max-height: 100%;
      height: auto; /* Maintain aspect ratio */
    
    Cee Nell's avatar
    Cee Nell committed
      margin: auto;
    
    Cee Nell's avatar
    Cee Nell committed
      display: inline-block;
    
    Cee Nell's avatar
    Cee Nell committed
      font-weight: bold;
    }
    
    .tooltip-width {
      width: 100%; 
      margin: 0 auto;
      display: flex;
      flex-direction: column;
      align-items: center;
    }
    
    .tooltip {
      opacity: 1;
      background: $ThemeGrey;
      padding: 8px;
      border-radius: 5px;
      pointer-events: none; 
      width: 100%; 
    
    Cee Nell's avatar
    Cee Nell committed
      height: 70px;
    
      text-align: center
    
    Cee Nell's avatar
    Cee Nell committed
    }
    
    .hidden {
      display: none;
    
    }
    
    .highlight {
      color: white;
      padding: 0.25px 5px;
      border-radius: 10px;
      white-space: nowrap;
      font-weight: bold;
      cursor: pointer;
      transition: all 0.1s;
    
      &.Demographiccharacteristics {
        background-color: $Demographiccharacteristics;
    
    
      &.Landtenure {
        background-color: $Landtenure;
    
    
      &.Livingconditions {
        background-color: $Livingconditions;
    
    
      &.Socioeconomicstatus {
        background-color: $Socioeconomicstatus;
    
    
      &.Health {
        background-color: $Health;
    
    
      &.Riskperception {
        background-color: $Riskperception;
    
    
      &.Exposure {
        background-color: $Exposure;
    
    
      &:not(.checked) {
        color: black;
        background-color: $ThemeGrey;
    
    Cee Nell's avatar
    Cee Nell committed
    @media (max-width: 700px) {
        .yLabel {
          font-size: 12px; /* Adjusted font size for mobile devices */
        }
      
        #beeswarm-chart-container {
          width: 100vw; /* Make the chart container take up 100% of the viewport width on mobile */
          height: auto; /* Make the chart container take up the full height of the screen */
        }
      
        #beeswarm-chart-container svg {
          height: 100%; /* Make the SVG fill the container height */
        }
      
        #mobile-tooltip {
          display: block; /* Show the mobile tooltip only on mobile devices */
          margin: 10px;
        }
      }
    
    
    
    Cee Nell's avatar
    Cee Nell committed
    </style>
    
    <style lang="scss">
    .tooltip-span {
      position: relative;
      cursor: pointer;
      display: inline-block;
      border-bottom: 1px dotted rgba(54, 54, 54, 0.8);
      z-index: 10;
    }
    
    .tooltiptext {
      visibility: hidden;
    
      width: 200px;
    
      background-color: #555;
      color: #fff;
      text-align: center;
      border-radius: 6px;
      padding: 5px;
      position: absolute;
      z-index: 1;
    
      margin-left: -170px;
    
      margin-top: 2rem;
      opacity: 0;
      transition: opacity 0.3s;
    }
    
    .tooltip-group:hover .tooltiptext {
      visibility: visible;
      opacity: 1;
    }
    </style>