stephan281094 / vue-drag-select

A Vue component for drag selecting elements. Inspired by react-drag-select.
75 stars 26 forks source link

Select nested child elements #6

Open Grawl opened 7 years ago

Grawl commented 7 years ago

I tried to select <g> inside SVG inside my Vue app, but when I tried to console.log(el) in isItemSelected method, I just see <svg> element in colsole.

stephan281094 commented 7 years ago

Hi there! Have you applied the CSS 'selectorClass' class to the <g> elements? Could you perhaps show me some code?

Grawl commented 7 years ago

I use vue-drag-select like this gist and import it using Rollup:

import dragSelect from './vendor/vue-drag-select'
const app = new Vue({
    el: '#hallApp',
    components: {
        'drag-select-container': dragSelect
    }
})

And I put my SVG right into template, inline:

<div id="hallApp">
    <drag-select-container selectorClass="cls-1">
        <template scope="{ selectedItems }">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="6602.848 -554.785 878.112 543.45">
                <g id="Group_1089" transform="translate(-957.152 -3588.785)">
                    <g id="Rectangle_1128" class="cls-1" data-sector="pew-2" data-row="row-1"
                       data-seat="0" transform="translate(7812.659 3529.023)">
                        <rect class="cls-7" width="10.221" height="10.221"/>
                        <rect class="cls-8" x="0.1" y="0.1" width="10.021" height="10.021"/>
                    </g>
                </g>
                …
            </svg>
        </template>
    </drag-select-container>
</div>

It's a map of a concert hall. I handle clicks on elements with [data-seat] attribute and use data-attributes to do some, it works.

I want to select a multiple <g class="cls-1"> elements using vue-drag-select.

I added console.log(el) to isItemSelected method to debug this and I see that el is an <svg> element when I drag to select some elements.

If I use regular HTML elements into <template>, it works properly:

<template>
    <drag-select-container selectorClass="cls-1">
        <template scope="{ selectedItems }">
            <div class="cls-1">
            …
        </template>
    </drag-select-container>
</template>

In that case, isItemSelected shows me how many <div class="cls-1"> is selected.

stephan281094 commented 7 years ago

Hi there! Thanks for demonstrating me your code. The reason why it's not working is because Vue Drag Select only looks one level deep for children. Since the drag-select-container is defined as a parent of svg, the only child it can find is svg. Therefore this is not a bug related to SVG, but rather to nested children.

A potential solution to this problem is to recursively look for children, though I'm not sure if that's a good idea.

Grawl commented 7 years ago

Why you think it's a bad idea? In that case, performance is due to library user, not on a library itself. You requires CSS class name for child elements, and it's obvious must be equal to CSS selector query. I see it like drag-select-container .cls-1, so if you mean drag-select-container > .cls-1 you must specify it in documentation.

stephan281094 commented 7 years ago

I agree. Let's refactor the code to allow nested child elements.

stephan281094 commented 5 years ago

Unfortunately no, nested child elements are currently not supported. Feel free to send a pull request, though!

JochenLutz commented 5 years ago

I need to select rows in a table build with BootstrapVue's b-table. I have a working solution in my fork. I added a function property that returns the items to work on. It works for me, but I'm not sure it's generic for all use cases. I also added a select event triggered only on onMouseUp. Is this approach worthy a PR? If yes, what is missing (apart from documentation)?

Usage

<drag-select selectorClass="selectable" :selectorFunction="selectableItems">
  <template slot-scope="{ selectedItems }">
    <b-table :items="items" tbody-tr-class="selectable">
    </b-table>
  </template>
</drag-select>

[…]

methods: {
  selectableItems() {
    return this.$el.getElementsByTagName('tr')
  }
}
stephan281094 commented 5 years ago

Hi Jochen, thanks for showing interest in this project!

Having a more composable way of defining the selectable items is definitely a better solution. Doing it this way, we should be able to get rid of the whole selectorClass property and make vue-drag-select smarter when it looks for elements that are 'selected'.

In my opinion, a more intuitive approach to this would be to define a property selectableItems that's simply an array of DOM elements. We can then simply check if the selected elements are in this array, or if they are a child of one of the elements in the array.

If we can get that to work properly, I'm definitely open to pull requests!

JochenLutz commented 5 years ago

Ok, good to know you are open to greater changes. I removed selectorClass. Passing the items as array does not work in my case, because the <b-table> is a child node of <drag-select>, so I also support a function returning the array of items. I kept the usage of the child nodes as default if no items are passed explicitly. This fallback still uses Vue components if available instead of DOM nodes as I'm not sure of that feature's use cases. Still missing documentation. Does this meet your idea of more composable? Should I open a PR?

daliborjelinek commented 5 years ago

Hi, I would really appreciate this functionality. @stephan281094 Are you planning accept this pull request and release new version?

joycelacey commented 3 years ago

@JochenLutz I am also looking to use this with BootstrapVue's b-table and took a look at your fork and I'm interested in using the changes you made, but having a little trouble wrapping my brain around how you get b-table to recognize what was selected. Could you post a codepen of how you got that part to work?