leeoniya / uPlot

📈 A small, fast chart for time series, lines, areas, ohlc & bars
MIT License
8.83k stars 385 forks source link

Is there a way to extract data on the graph via context menu? #989

Closed jduncanRadBlue closed 3 months ago

jduncanRadBlue commented 3 months ago

I'm creating a context menu (right click on an element/line on the graph) I am having a hard time figuring out how to access the data. I would like to show something in a dialog that users can use to 'see more' data about a certain feature.
Anything you can offer on how to access the elements would be greatly appreciated!

<template>
<div @contextmenu.prevent="showContextMenu($event)">
  <ContextMenu
      v-model="showMenu"
      :x="menuX"
      :y="menuY"
      :actions="contextMenuActions"
      @action-clicked="handleMenuAction"
    />
<v-navigation-drawer
      v-model="timeline"
      location="bottom"
      width="100%"
      style="transition: none;"
      disable-resize-watcher

    >
          <v-toolbar
            density="compact"
          >

         <v-toolbar-title>Timeline</v-toolbar-title>

        <v-spacer></v-spacer>

        <v-btn icon @click="closeTimeline">
          <v-icon>mdi-close</v-icon>
        </v-btn>
      </v-toolbar>

      <v-main class="pa-0">
        <v-container fluid >
              <v-card >
                <div id="uplot-Container" style="min-height: 350px; max-height: 400px;">
                <!-- <UplotVue ref="timelineContainer" :options="options" :data="plotData" :key="graphKey" /> -->
                <UplotVue v-show="displayAIS" ref="timelineContainer" :options="options" :data="plotData" :key="graphKey" />
                <div v-show="!displayAIS" >
                  <div class="myCol">
                      <span id="mySpan">
                        <v-icon >mdi-information-outline</v-icon>
                        <h4 >Data not displayed.  Select Data in Layers Manager to display Data.</h4>
                      </span>
                  </div>
                </div>
              </div>
              </v-card>
        </v-container>
      </v-main>
        </v-navigation-drawer>
</div>
  </template>

<script setup>
import {onMounted, onUpdated, ref, watch} from 'vue'
import "uplot/dist/uPlot.min.css";

import UplotVue from 'uplot-vue';
import ContextMenu from '@/components/ContextMenu.vue';
import {useFishState} from "@/store/fishState"
const stateStore = useStateStore()
import { useLayerState } from '@/store/lmState';
const layerState = useLayerState();
import {storeToRefs} from 'pinia'

const {timeline} = storeToRefs(stateStore)
const graphKey = ref(0)
const timelineContainer = ref(null)
const height = ref(null)
const width = ref(null)

const showMenu = ref(false)
const menuX = ref(0)
const menuY = ref(0)

const contextMenuActions = ref([
  { label: 'Get Info', action: 'getInfo', icon: 'mdi-information-box-outline', disabled: false },
  { label: 'Sync', action: 'sync', icon: 'mdi-autorenew', disabled: false },
]);

const plotData = stateStore.plotData

const showContextMenu = (event) => {
  //the context menu adds pixels to the x and y to show it offset, so get the correct pixel that was actually right clicked to find feature
  event.preventDefault()
  console.log(event)

  //this is the pixel location for the menu based on where the user right clicked and what gets passed to the contextMenu component for placement on screen.
  menuX.value = event.clientX
  menuY.value = event.clientY
  showMenu.value = true
}

function handleMenuAction(action){
  console.log(action);
  console.log('x: '+menuX.value + ' ' +'y: '+menuY.value);
}

const options = ref({
  Title: "AIS Data",
    drawOrder: ["series", "axes"],

    axes: [
      {
        side:0,
        stroke: "white",
        ticks: {
          stroke: "#808080"
        },
        grid: {
          stroke: "rgba(236, 236, 236, 0.5)"
        }
      },
      {
        stroke: "white",
        ticks: {
          stroke: "#808080"
        },
        grid: {
          stroke: "rgba(236, 236, 236, 0.5)"
        },

      }
    ],
    series: [
      {
        label: 'Timstamp'
      },
      {
        label: 'CM',
        stroke: "red",
        width: 4
        //fill: "rgba(255,0,0,0.1)",
        // value: fmtVal,

      },
      {
        label: "NC",
        stroke: "green",
        width: 4
        // fill: "rgba(0,255,0,0.1)",

      },
      {
        label: "OE",
        stroke: "blue",
        width: 4
        //fill: "rgba(0,0,255,0.1)",
        //value: fmtVal,
      },
      {
        label: "SB",
        stroke: "orange",
        width: 4
        //fill: "rgba(0,0,255,0.1)",
        //value: fmtVal,
      },
      {
        label: "ST",
        stroke: "yellow",
        width: 4
        //fill: "rgba(0,0,255,0.1)",
        //value: fmtVal,
      },
    ],
    scales: { x: { time: true } },
  },)

const closeTimeline = () => {
    fishState.setTimeline(false)
}

const {displayAIS} = storeToRefs(layerState);
layerState.$subscribe((mutation, state)=>{
})

onUpdated(() => {
  graphKey.value++
  const element = document.getElementById('uplot-Container')
  const positionInfo = element.getBoundingClientRect()
  const height = positionInfo.height
  const width = positionInfo.width

  options.value = {
    ...options.value,
    height: height - 50,
    width: width - 30
  }
})

onMounted(() => {
 console.log(plotData.value)

})
leeoniya commented 3 months ago

sorry, i have no experience with Vue.

your data comes from outside of uPlot, so it should be accessible outside of uPlot.

you may want to ask on StackOverflow or Reddit

jduncanRadBlue commented 3 months ago

Ok, thanks. The function to get the data from the right click I would think would be javascript and not specific to vue. That is just the framework I am using. Has anyone ever needed to right click on the graph and extract element data that you know of?

leeoniya commented 3 months ago

@jduncanRadBlue take a look at:

https://github.com/leeoniya/uPlot/issues/239#issuecomment-2299294298

https://leeoniya.github.io/uPlot/demos/tooltips-closest.html