vue-select icon indicating copy to clipboard operation
vue-select copied to clipboard

How to apply css to dropdown list when the prop appendToBody is true

Open eddivalen opened this issue 4 years ago • 16 comments

How can I apply some css to change background of the dropdown list, when I'm using the prop appendToBody, the div is at the end of the body, there is some way to add a class or replace the ID?

I'm trying to applying some dark style...

image

<v-select class="select-x" taggable v-model="value" append-to-body :options="options"></v-select>

eddivalen avatar Aug 17 '20 20:08 eddivalen

Can you please give me an idea to solve this @sagalbot

eddivalen avatar Aug 19 '20 14:08 eddivalen

The .vs__dropdown-menu class is still applied, you can target that for your css 👍🏻

sagalbot avatar Sep 01 '20 16:09 sagalbot

@sagalbot is there a way to add a unique class or ID?

eddivalen avatar Sep 01 '20 21:09 eddivalen

I think the problem is that when setting a class (like it is described here), this class is not applied to .vs__dropdown-menu in the body. So there is no way to style this individually for one rendered component.

heivo avatar Nov 10 '20 09:11 heivo

I also would like to be able to add a dynamic class to the dropdown menu. I have multiple dropdown menu styles set in my wrapper component, and I need appendToBody, but it breaks my custom styles.

edit: My current workaround is to read the VueSelect component uid on mount, and inject global styles with vue-styled-components targetting the id of .vs__dropdown-menu which is #vs1__listbox for example, if the uid is 1. Work-in-progress example:

onMounted(() => {
  if (vueSelect.value !== null) {
    const uid = vueSelect.value.$data.uid
    let menuSpecificStyles = ''

    if (isMenu.value) {
      menuSpecificStyles = `
        border: 0;
        background-color: ${colors.grey.default};
        border-radius: 0 0 0.2rem 0.2rem;
        padding: 0;
        left: auto;
        right: 0.1rem;
        top: calc(100% + 0.1rem);
        width: auto;
        min-width: auto;

        .vs__dropdown-option {
          border-color: ${colors.grey[553]};
          color: ${colors.white};
          text-align: right;
          background-color: ${colors.grey.default};
          margin: 0 1.2rem 0 0.5rem;
          padding-left: 2.8rem !important;

          &:hover {
            color: ${colors.grey[790]};
          }
        }
      `
    }

    injectGlobal`
      #vs${uid}__listbox {
        .vs__dropdown-option--highlight {
          background: ${themeColor};
        }

        ${menuSpecificStyles}
      }
    `
  }
})

edit: I should note that the solutions below that add a classname are better, but in my case I need styled components to enable use of a dynamic theme color.

uturnr avatar Nov 30 '20 22:11 uturnr

@sagalbot Why is this issue closed? Is it mean you do not understand a problem, or you won't to fix it? Custom class applied for dropdown will do the job. Now there is no way to style specific dropdown which is append to body without custom ugly JS like:

mounted() {
        let style = document.createElement('style');
        style.innerText = `#vs${this.$refs.select.$data.uid}__listbox{"some rules"}`;
        document.querySelector('body').appendChild(style);
    },

SchmidtDawid avatar Dec 01 '20 11:12 SchmidtDawid

My workaround is as follows, and prevents me from having to inject global CSS.

mounted() {
  this.$refs.select.$on("open", async () => {
    await this.$nextTick();
    await this.$nextTick();

    this.$refs.select?._vnode.children.forEach(({ elm }) => {
      if (elm instanceof HTMLElement && elm.tagName === "UL") {
        elm.classList.add(this.remoteClassName);
      }
    });
  });
}

It'd be nice to have prop for a custom class-name though, so that we don't have to do hacks like this. The changes I want to do in this scenario are not changes I want to apply to all dropdowns.

EricRabil avatar Dec 04 '20 08:12 EricRabil

One more workaround is to use calculatePosition with Propper.js, the dropdown menu element can be edited when adjusting the position.

<v-select :options="countries" append-to-body :calculate-position="withPopper" />
methods: {
  withPopper (dropdownList, component, {width}) {
    dropdownList.classList.add("style-chooser");
    // ...  adjust the position
  }
}
<style>
  .style-chooser.vs__dropdown-menu {
    background: #dfe5fb;
    border: none;
    color: #394066;
    text-transform: lowercase;
    font-variant: small-caps;
  }
</style>

alantea avatar Dec 23 '20 08:12 alantea

@SchmidtDawid I didn't fully understand the scope of the issue.

@alantea thanks for posting your solution! It's almost perfect, but it leaves you in charge of implementing the positioning yourself. Let me know what you think of the API in #1341. @EricRabil & @uturnr, would this API work for you as well?

<v-select @dropdown:appending="({ el }) => el.classList.add('style-chooser')" />

sagalbot avatar Dec 23 '20 19:12 sagalbot

Looks great. Thank you!

EricRabil avatar Dec 25 '20 16:12 EricRabil

@sagalbot , it looks great.

alantea avatar Dec 25 '20 19:12 alantea

@sagalbot Looks good to me!

uturnr avatar Jan 04 '21 21:01 uturnr

@sagalbot, what do you think about dropdown-class property?

Norserium avatar Mar 22 '21 09:03 Norserium

@sagalbot, what do you think about dropdown-class property?

This would probably be better because classes dynamically added with classList will be removed on rerender.

dorian-marchal avatar Apr 08 '21 22:04 dorian-marchal