From 919532295db2a4779516b16bf49eead9a0fb906d Mon Sep 17 00:00:00 2001
From: Darius Williams <dswilliams@contractor.usgs.gov>
Date: Fri, 29 Jul 2022 15:59:43 -0500
Subject: [PATCH] Moving graph controls within the legend component

---
 .../components/hydrograph/index.js            |  24 ++-
 .../hydrograph/vue-components/legend.vue      | 140 +++++++++++++++---
 2 files changed, 130 insertions(+), 34 deletions(-)

diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/index.js b/assets/src/scripts/monitoring-location/components/hydrograph/index.js
index b3c046270..314c6a777 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/index.js
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/index.js
@@ -29,7 +29,6 @@ import {getPreferredIVMethodID} from './selectors/time-series-data';
 
 
 import {showDataIndicators} from './data-indicator';
-import {drawTimeSeriesLegend} from './legend';
 
 import {initializeTimeSeriesGraph, drawTimeSeriesGraphData} from './time-series-graph';
 
@@ -156,21 +155,20 @@ export const attachToNode = function(store,
             mapStateToPropsFactory: createStructuredSelector
         });
         hydrographApp.provide('store', store);
+        hydrographApp.provide('siteno', siteno);
         hydrographApp.provide('initialLoadingComplete', initialLoadingComplete);
         hydrographApp.mount('#hydrograph-brush-container');
     }
-    const legendControlsContainer = graphContainer.append('div')
-        .classed('ts-legend-controls-container', true);
     if (!showOnlyGraph) {
-        const graphControlsApp = createApp(GraphControlsApp, {});
-        graphControlsApp.use(ReduxConnectVue, {
-            store,
-            mapDispatchToPropsFactory: (actionCreators) => (dispatch) => bindActionCreators(actionCreators, dispatch),
-            mapStateToPropsFactory: createStructuredSelector
-        });
-        graphControlsApp.provide('store', store);
-        graphControlsApp.provide('siteno', siteno);
-        graphControlsApp.mount('.ts-legend-controls-container');
+        // const graphControlsApp = createApp(GraphControlsApp, {});
+        // graphControlsApp.use(ReduxConnectVue, {
+        //     store,
+        //     mapDispatchToPropsFactory: (actionCreators) => (dispatch) => bindActionCreators(actionCreators, dispatch),
+        //     mapStateToPropsFactory: createStructuredSelector
+        // });
+        // graphControlsApp.provide('store', store);
+        // graphControlsApp.provide('siteno', siteno);
+        // graphControlsApp.mount('.ts-legend-controls-container');
 
         const parameterSelectionApp = createApp(ParameterSelectionApp, {});
         parameterSelectionApp.use(ReduxConnectVue, {
@@ -195,8 +193,6 @@ export const attachToNode = function(store,
         showDataIndicators(false, store);
         graphContainer.call(drawTimeSeriesGraphData, store, !thisShowOnlyGraph);
 
-        legendControlsContainer.call(drawTimeSeriesLegend, store);
-
         if (!thisShowOnlyGraph) {
             // eslint-disable-next-line vue/one-component-per-file
             const dataTablesApp = createApp(DataTablesApp, {});
diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/vue-components/legend.vue b/assets/src/scripts/monitoring-location/components/hydrograph/vue-components/legend.vue
index cf365ceea..00006944a 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/vue-components/legend.vue
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/vue-components/legend.vue
@@ -1,51 +1,151 @@
 <template>
-  <div class="hydrograph-container">
-      <svg 
+  <div class="ts-legend-controls-container">
+    <div class="hydrograph-container">
+      <svg
         v-if="legendMarkerRows.length"
         class="legend-svg"
+        :viewBox="svgViewBox"
       >
         <g
+          ref="legend"
           class="legend"
           :transform="legendTransform"
-        >
-          
-        </g>
+        />
       </svg>
+    </div>
+    <div class="graph-controls-container">
+      <GraphControls />
+    </div>
   </div>
 </template>
 
 <script>
-import {useActions, useState} from 'redux-connect-vue';
-import {computed, inject, ref, watchEffect} from 'vue';
-
-import {createStructuredSelector} from 'reselect';
+import GraphControls from './graph-controls.vue';
 
-import {drawSimpleLegend} from 'd3render/legend';
+import {useState} from 'redux-connect-vue';
+import {computed, ref, watchEffect} from 'vue';
+import {select} from 'd3-selection';
 
-import {getMainLayout} from './selectors/layout';
-import {getLegendMarkerRows} from './selectors/legend-data';
+import {getMainLayout} from '../selectors/layout';
+import {getLegendMarkerRows} from '../selectors/legend-data';
 import {mediaQuery} from 'ui/utils';
+import config from 'ui/config';
 
 export default {
   name: 'Legend',
+  components: {
+    GraphControls
+  },
   setup() {
+    const legend = ref(null);
+
+    const RECTANGLE_MARKER_WIDTH = 20;
+    const RECTANGLE_MARKER_HEIGHT = 10;
+    const LINE_MARKER_WIDTH = 20;
+    const MARKER_GROUP_X_OFFSET = 15;
+    const VERTICAL_ROW_OFFSET = 18;
+
+    let yPosition = VERTICAL_ROW_OFFSET;
+
     const state = useState({
       legendMarkerRows: getLegendMarkerRows,
       layout: getMainLayout
     });
 
+    // Set the size of the containing svg node to the size of the legend.
+    const bBox = computed(() => {
+      try {
+        return select(legend.value).node().getBBox();
+      } catch (error) {
+        return null;
+      }
+    })
+
     const legendTransform = computed(() => {
-      return `translate(${mediaQuery(config.USWDS_MEDIUM_SCREEN) ? layout.margin.left : 0}, 0)`
+      return `translate(${mediaQuery(config.USWDS_MEDIUM_SCREEN) ? state.layout.value.margin.left : 0}, 0)`;
+    });
+
+    const svgViewBox = computed(() => {
+      if (bBox.value) {
+        return `0 0 ${state.layout.value.width} ${bBox.value.height + 10}`;
+      }
+      return ''
+    });
+
+    watchEffect(() => {
+      state.legendMarkerRows.value.forEach((rowMarkers) => {
+        let xPosition = 0;
+        let markerArgs;
+        let markerGroup;
+        let lastMarker;
+
+        const getNewXPosition = function(markerGroup, lastXPosition) {
+            // Long story short, firefox is unable to get the bounding box if
+            // the svg element isn't actually taking up space and visible. Folks on the
+            // internet seem to have gotten around this by setting `visibility:hidden`
+            // to hide things, but that would still mean the elements will take up space.
+            // which we don't want. So, here's some error handling for getBBox failures.
+            // This handling ends up not creating the legend, but that's okay because the
+            // graph is being shown anyway. A more detailed discussion of this can be found at:
+            // https://bugzilla.mozilla.org/show_bug.cgi?id=612118 and
+            // https://stackoverflow.com/questions/28282295/getbbox-of-svg-when-hidden.
+            try {
+                const markerGroupBBox = markerGroup.node().getBBox();
+                return markerGroupBBox.x + markerGroupBBox.width + MARKER_GROUP_X_OFFSET;
+            } catch (error) {
+                // See above explanation
+                return lastXPosition;
+            }
+        };
+
+        const repositionLastMarkerWhenNeeded = function() {
+            if (xPosition - MARKER_GROUP_X_OFFSET > state.layout.value.width && legend.value) {
+                // Need to remove the last marker and draw it on the next row.
+                markerGroup.remove();
+                xPosition = 0;
+                yPosition = yPosition + VERTICAL_ROW_OFFSET;
+                markerArgs.x = xPosition;
+                markerArgs.y = yPosition;
+                markerGroup = lastMarker.type(legend.value, markerArgs);
+                xPosition = getNewXPosition(markerGroup, xPosition);
+            }
+        };
+
+        rowMarkers.forEach((marker) => {
+          if (legend.value) {
+            repositionLastMarkerWhenNeeded();
+
+            markerArgs = {
+                x: xPosition,
+                y: yPosition,
+                text: marker.text,
+                domId: marker.domId,
+                domClass: marker.domClass,
+                width: RECTANGLE_MARKER_WIDTH,
+                height: RECTANGLE_MARKER_HEIGHT,
+                length: LINE_MARKER_WIDTH,
+                r: marker.radius,
+                fill: marker.fill
+            };
+
+            lastMarker = marker;
+            markerGroup = marker.type(select(legend.value), markerArgs);
+            xPosition = getNewXPosition(markerGroup, xPosition);
+          }
+          
+        });
+        repositionLastMarkerWhenNeeded();
+        //start new row
+        yPosition = yPosition = yPosition + VERTICAL_ROW_OFFSET;
+      });
     });
 
     return {
       ...state,
-      legendTransform
-    }
+      legendTransform,
+      svgViewBox,
+      legend
+    };
   }
 }
-</script>
-
-<style>
-
-</style>
\ No newline at end of file
+</script>
\ No newline at end of file
-- 
GitLab