valgeirb / vue3-popper

A Vue.js popover component based on PopperJS
https://valgeirb.github.io/vue3-popper
MIT License
222 stars 66 forks source link

poppers on SVG elements #60

Open simevo opened 2 years ago

simevo commented 2 years ago

We have a use case where we want to display poppers on SVG elements.

The easiest and leaner solution is to have standard tooltips generated with the <title> SVG element, see this proof-of-concept: https://gitlab.com/simevo/vue3-svg-map

Cons: you can't control the tooltip etc.

We have successfully integrated bootstrap5 popovers (based on Popper 2), this works well like this:

<path
  v-for="v in venue_shapes"
  :key="v.id"
  :d="v.d"
  data-bs-toggle="popover"
  data-bs-trigger="hover focus"
  data-bs-content="`<table class='table'><tr><td>Probabilità</td></tr><tr><td>${ probabilita[v.id] }</td></tr></table>`"
/>

You just enable them in the mounted hook and they work. But of course they are not reactive!

vue3-popper looks like the solution to make them reactive. So I have tried using this library in the vue3popper branch of the vue3-svg-map prototype: https://gitlab.com/simevo/vue3-svg-map/-/merge_requests/1

Unfortunately when the <Popper> element is expanded, it generates a bunch of HTML tags which are simply ignored in the SVG, therefore the map disappears.: image

Is there an easy solution to make vue3-popper work with SVG ?

valgeirb commented 2 years ago

Is there an easy solution to make vue3-popper work with SVG ?

Unfortunately for your case, I don't think so since Popper is currently unable to be "renderless" as in not rendering any element except the one that it wraps. I don't know if that's even possible.

SVGs can have HTML in them using <foreignObject/> according to Google 😄 but I don't know anything more about that.

Thanks for the reproduction, I cloned your repo and it's an interesting problem, I just don't see the solution at the moment.

simevo commented 2 years ago

@valgeirb thanks for the quick reply!

We have used foreignObject but as far as I can tell it requires to fix position and size, which is complicated here. The best would be to place the additional HTML elements outside the SVG.

Do you think it would be feasible to create a <PopperDetached> component to be used like this:

<template>
  <PopperDetached
    v-for="(d, name) in districts"
    :key="name"
    :hover="true"
    :trigger="name">
    <template #content>
      ...
    </template>
  </PopperDetached>
  <svg
    ...
  >
    <g>
      <path
        v-for="(d, name) in districts"
        :key="name"
        :id="name"
        ...
      />
    </g>
  </svg>
</template>

(based on the non-working code here: https://gitlab.com/simevo/vue3-svg-map/-/blob/vue3-popper/src/components/MapMaharashtra.vue)

Notice the trigger prop which is used to pass the id of the SVG element that actually triggers the popper.

dgedwards13 commented 1 year ago

I have a similar use case that I'd love an implementation for. Any update here? The PopperDetached option @simevo mentions would work fine for us too.

bertBruynooghe commented 3 months ago

Had a similar issue, and solved it using foreignObject (to allow nested HTML in the svg) and Teleport (to move the tooltip to a place in the DOM outside the svg.