patorjk / d3-context-menu

A plugin for d3.js that allows you to easy use context-menus in your visualizations.
MIT License
140 stars 54 forks source link

Vuejs #44

Open kevindesousa opened 6 years ago

kevindesousa commented 6 years ago

How use with ES6 and VueJS ?

Importing import * as d3 from 'd3' import contextMenu from 'd3-context-menu'

Init let d3ContextMenu = contextMenu(d3)

Config .on('contextmenu', d3ContextMenu([ { title: 'Item #1', action: (elm, d, i) => { console.log('Item #1 clicked!') console.log('The data for this circle is: ' + d) } }, { title: 'Item #2', action: (elm, d, i) => { console.log('You have clicked the second item!') console.log('The data for this circle is: ' + d) } } ]))

My graph work, but nothing on click, just the real right click of the browser...

heliomsolivas commented 5 years ago

Have you solved it? I'm with this issue too

SamuelEarl commented 4 years ago

I am using D3 v5 and Vue.js and I could not get this plugin to work in my project. So I created a context menu using HTML and I love it! It is very easy to create with modern frameworks, like Vue, where you can control the component state easily. This is a sample of my Vue component:

<template>
  <div id="chart-component">
    <div id="context-menu" v-if="showContextMenu">
      <div><button class="menu-item" @click="method1('arg')">Option 1</button></div>
      <div><button class="menu-item" @click="method2('arg')">Option 2</button></div>
      <div><button class="menu-item" @click="method3('arg')">Option 3</button></div>
    </div>
    <div id="chart-container"></div>
  </div>
</template>

<script>
import * as d3 from "d3";

export default {
  data() {
    return {
      showContextMenu: false
    }
  },

  beforeDestroy() {
    const chartComponent = document.getElementById("chart-component");
    chartComponent.removeEventListener("contextmenu", this.setContextMenuCoords);
  },

  methods: {
    createChart() {
      const chartComponent = document.getElementById("chart-component");
      chartComponent.addEventListener("contextmenu", this.setContextMenuCoords);
      ...
      this.someGroup.append("rect")
        .attr("width", this.rect.width)
        .attr("height", this.rect.height)
        .on("contextmenu", function(d) {
          // Prevent the default context menu from appearing.
          d3.event.preventDefault();
          // Pass the data object (d) and the DOM element (this) to vm.contextMenu.
          vm.contextMenu(d, this);
        });

      // Hide the context menu when a user left-clicks anywhere outside of the context menu.
      // This will also reset and hide the context menus after a user clicks any option within the context menus.
      d3.select("body")
        .on("click", function() {
          vm.showContextMenu = false;
        });
    },

    contextMenu(d, domEl) {
      // Use d and domEl, if necessary.
      this.showContextMenu = true;
    },

    /**
     * NOTE: The D3 event listeners are fired before the addEventListener functions are fired. 
     * So the context-menu elements will exist in the DOM by the time this method is executed.
     */
    setContextMenuCoords(event) {
      event.preventDefault();

      const ctxMenu = document.getElementById("context-menu");
      // If the user right-clicked somewhere other than on rect element that was appended to someGroup, then the ctxMenu will not exist. The following conditional check is to prevent errors when the user right-clicks outside of the rect element.
      if (ctxMenu) {
        // Get the width and height of the current context menu with the window.getComputedStyle() method.
        const compStyles = window.getComputedStyle(ctxMenu);
        const menuWidth = parseInt(compStyles.width);
        const menuHeight = parseInt(compStyles.height);

        // Get the dimensions of the <svg> element.
        const baseSvgEl = document.getElementById("svg");
        const chartRightEdge = baseSvgEl.getBoundingClientRect().right;
        const chartBottomEdge = baseSvgEl.getBoundingClientRect().bottom;

        // Get the right-click coordinates. (NOTE: The right-click event is called "contextmenu".)
        const mousePosX = event.clientX;
        const mousePosY = event.clientY;

        // If the right-click coordinates are too close to the right edge of the chart, then shift the context menu to the left so it does not overflow to the right of the chart area.
        if ((mousePosX + menuWidth) < chartRightEdge) {
          ctxMenu.style.left = mousePosX + "px";
        }
        else {
          ctxMenu.style.left = mousePosX - menuWidth + "px";
        }
        // If the right-click coordinates are too close to the bottom edge of the chart, then shift the context menu up so it does not overflow below the chart area.
        if ((mousePosY + menuHeight) < chartBottomEdge) {
          ctxMenu.style.top = mousePosY + "px";
        }
        else {
          ctxMenu.style.top = mousePosY - menuHeight + "px";
        }
      }
    },
  }
};
</script>

<style lang="stylus" scoped>
  #context-menu {
    position: absolute;
    width: 200px;
    background-color: white;
    z-index: 100;
    border: 1px solid rgba(0, 0, 0, 0.3);;
    box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);

    .menu-item {
      width: 100%;
      padding: 10px;
      text-align: left;
      background-color: inherit;
      &:hover {
        background-color: lighten(steelblue, 50%);
      }
    }
  }
</style>

I didn't define all of the properties in the Vue data option, but if you are familiar with Vue and D3 then that shouldn't matter.

You basically create a context menu from plain HTML and show or hide it using D3 events. You also attach an event listener onto the component that listens for the "contextmenu" event. When a user right-clicks anywhere on the component, you handle that event with the setContextMenuCoords method, which sets the top and left position properties of the context menu. The comments in the code above should help to clarify.

I hope that helps. Good luck!

DineshKarri443 commented 4 years ago

@SamuelEarl I am trying to implement a context-menu from your solution above, one issue with this solution is when I right click on a node the contextmenu comes up fine, but when I right-click again immediately on an empty area ( where there are no nodes ), the context menu still comes up. It's only when we do a left click that the context-menu goes away.