observablehq / plot

A concise API for exploratory data visualization implementing a layered grammar of graphics
https://observablehq.com/plot/
ISC License
4.25k stars 173 forks source link

Event and pointer interaction in Vue.js context #2008

Closed dawadam closed 6 months ago

dawadam commented 6 months ago

Hi, I'm trying to use Plot on my Vue.js app, using this official code.

It works very well as of now.

Now I want to add pointer interaction, but nothing appears. I think the problem comes from event implementation because in PlotRender.js, the comment regarding the event part is:

  addEventListener() {
    // ignored; interaction needs real DOM
  }
  removeEventListener() {
    // ignored; interaction needs real DOM
  }
  dispatchEvent() {
    // ignored; interaction needs real DOM
  }

Is it realy impossible to use events in a Vue.js context?

mbostock commented 6 months ago

You need to use client-side rendering as described in the Getting started guide. If you use server-side rendering, the generated plot is serialized to HTML and loses all its interaction.

import * as Plot from "@observablehq/plot";
import {h, withDirectives} from "vue";

export default {
  props: ["options"],
  render() {
    const {options} = this;
    return withDirectives(h("div"), [
      [
        {
          mounted(el) {
            el.append(Plot.plot(options));
          }
        }
      ]
    ]);
  }
};
mbostock commented 6 months ago

Also, the PlotRender component you linked to isn’t “official” in the sense that it’s not supported — it’s just what we use internally to build the Plot documentation. You’re free to use it but it isn’t guaranteed to work or be documented.

The way that we handle interactive plots in our documentation is the defer directive. For example:

https://github.com/observablehq/plot/blob/41a63e372453d2f95e7a046839dfd245d21e7660/docs/interactions/pointer.md?plain=1#L37-L46

Note the :::plot defer instead of the normal :::plot as you see with non-interactive plots.

The defer directive triggers client-side rendering. During server-side rendering an empty <div> is rendered, and then using an IntersectionObserver on the client, the plot is rendered on the client just before it becomes visible.

https://github.com/observablehq/plot/blob/41a63e372453d2f95e7a046839dfd245d21e7660/docs/components/PlotRender.js#L154-L217

This is useful not just for interactive plots, but for plots that would otherwise generate many megabytes of serialized SVG, such as maps. I’m sure there’s a way to do traditional full server-side rendering with client-side hydration of event listeners in Vue, but we didn’t want to do the typical thing of rendering the charts twice.

dawadam commented 6 months ago

Ok thanks, I didn't realize I was using the server version, especially since I don't need it. So I'm going to work on a client version. Is there a fully ready version, sample client-side render file, to save me time?

edit: ok it works with the client version, thank you very much ! :)