Search code examples
javascriptcssvue.jsvue-componentvuejs3

How to change classes' property for each component in Vue.js?


I've added to my website a reusable component "LightBox" that provides a picture with bigger resolution. This LightBox is based on the fact that each element has thumbnail. What is my issue? I have several elements where I want to change properties of class "thumbnail" for several elements. But I don't how to do it. I've tried using v-bind, but it didn't work for me

My code:

<template>
    <div>
        <a href="" @click.prevent="show">
            <img class="thumbnail" :src="images[thumbnailIndex]" loading="lazy">
        </a>
        <div class="lightbox" v-if="visible" @click="hide">
            <div class="flex">
                <div class="cursor left" @click.stop="prev" :class="{ 'invisible': !hasPrev() }">
                    <svg fill="#ffff" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg"
                        xmlns:xlink="http://www.w3.org/1999/xlink" width="100px" height="100px"
                        viewBox="0 0 537.66 537.66" xml:space="preserve"  stroke="#ffff">

                        <g id="SVGRepo_bgCarrier" stroke-width="0" />

                        <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" />

                        <g id="SVGRepo_iconCarrier">
                            <g>
                                <g>
                                    <path
                                        d="M526.375,175.442H249.458l101.781-115.23c2.939-5.875-0.006-10.64-6.574-10.64H194.735 c-15.012,0.003-34.74,30.233-44.538,41.188C132.96,110.028,3.146,254.326,2.204,256.208c-2.938,5.875-2.938,15.404,0,21.279 L177.52,477.449c2.938,5.875,10.646,10.64,17.215,10.64h149.931c6.57,0,9.514-4.765,6.576-10.64l-98.746-114.347h273.879 c6.234,0,11.285-5.052,11.285-11.285v-165.09C537.66,180.494,532.609,175.442,526.375,175.442z" />
                                </g>
                            </g>
                        </g>

                    </svg>
                 
                </div>
                <div class="lightbox-image" @click.stop="">
                    <img :src="images[index]" loading="lazy">
                </div>
                <div class="cursor right" @click.stop="next" :class="{ 'invisible': !hasNext() }">
                    <svg class="size" fill="#ffff" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg"
                        xmlns:xlink="http://www.w3.org/1999/xlink" width="100px" height="100px"
                        viewBox="0 0 537.66 537.66" xml:space="preserve" transform="rotate(180)" stroke="#ffff">

                        <g id="SVGRepo_bgCarrier" stroke-width="0" />

                        <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" />

                        <g id="SVGRepo_iconCarrier">
                            <g>
                                <g>
                                    <path
                                        d="M526.375,175.442H249.458l101.781-115.23c2.939-5.875-0.006-10.64-6.574-10.64H194.735 c-15.012,0.003-34.74,30.233-44.538,41.188C132.96,110.028,3.146,254.326,2.204,256.208c-2.938,5.875-2.938,15.404,0,21.279 L177.52,477.449c2.938,5.875,10.646,10.64,17.215,10.64h149.931c6.57,0,9.514-4.765,6.576-10.64l-98.746-114.347h273.879 c6.234,0,11.285-5.052,11.285-11.285v-165.09C537.66,180.494,532.609,175.442,526.375,175.442z" />
                                </g>
                            </g>
                        </g>

                    </svg>
                </div>


            </div>


        </div>
    </div>


</template>
<script>
export default {
    props: {
        thumbnailIndex: {
            type: Array,
            required: true,
        },
        images: {
            type: Array,
            default: () => [],
        },

    },
    data() {
        return {
            visible: false,
            index: 1,
        };
    },
    methods: {
        Index(){
            let thumbnailIndex = images.findIndex(element => element === thumbnail);
        },
        show() {
            this.visible = true;
            this.index=this.thumbnailIndex
           
        },
        hide() {
            this.visible = false;
            this.index = 0;
        },
        hasNext() {
            return this.index + 1 < this.images.length;
        },
        hasPrev() {
            return this.index - 1 >= 0
        },
        prev() {
            if (this.hasPrev()) {
                this.index -= 1;
            }
        },
        next() {
            if (this.hasNext()) {
                this.index += 1
            }
        }
    }
}

</script>
<style>
.thumbnail {
    width: 29vh;
}
.lightbox {
    position: fixed;
    z-index: 500;
    display: flex;
    justify-content: center;
    align-content: center;
    background: rgba(0, 0, 0, 0.8);
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 100%;
    height: 100%;
}
.lightbox-image img {
    width: auto;
    height: auto;
    max-width:50%;
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}
.cursor {
    cursor: pointer;
    display: block;
    position: absolute;
    z-index: 1000;
    top: 50%;
    visibility: visible;
}
.cursor.right {
    right: 10%;
}
.cursor.left {
    left: 10%;
}
.invisible {
    visibility: hidden;
}
@media screen and (max-width: 1024px) {
    .thumbnail{
        width: 100%;
    }
    .lightbox-image img{
        max-width: 80%;
        max-height: 80%;;
    }
    .cursor{
        top: 70%;
    }
}
</style>

This is how I've tried to change classes for each thubmnail

<LightBox :thumbnail-index="0" :images="images" :style="{width: "25vh"}"></LightBox>
<LightBox :thumbnail-index="1" :images="images" :style="{width: "25vh"}"></LightBox>
<LightBox :thumbnail-index="2" :images="images" :style="{width: "25vh"}"></LightBox>
<LightBox :thumbnail-index="3" :images="images" :style="{width: "25vh"}"></LightBox>

Solution

  • If I understood you correctly, please take a look at following snippet: (pass styles you need and bind it)

    const app = Vue.createApp({
        data() {
          return {
            images: ['https://picsum.photos/500', 'https://picsum.photos/501', 'https://picsum.photos/499', 'https://picsum.photos/502', 'https://picsum.photos/500', 'https://picsum.photos/501', 'https://picsum.photos/499', 'https://picsum.photos/502'],
          };
        },
      });
      app.component("lightBox", {
        template: `
        <div>
          <a href="" @click.prevent="show">
            <img :style="styles" :src="images[thumbnailIndex]" loading="lazy">
          </a>
          <div class="lightbox" v-if="visible" @click="hide">
            <div class="flex">
              <div class="cursor left" @click.stop="prev" :class="{ 'invisible': !hasPrev() }">
                <svg fill="#ffff" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100px" height="100px" viewBox="0 0 537.66 537.66" xml:space="preserve"  stroke="#ffff">
                  <g id="SVGRepo_bgCarrier" stroke-width="0" />
                  <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" />
                  <g id="SVGRepo_iconCarrier">
                    <g>
                      <g>
                        <path d="M526.375,175.442H249.458l101.781-115.23c2.939-5.875-0.006-10.64-6.574-10.64H194.735 c-15.012,0.003-34.74,30.233-44.538,41.188C132.96,110.028,3.146,254.326,2.204,256.208c-2.938,5.875-2.938,15.404,0,21.279 L177.52,477.449c2.938,5.875,10.646,10.64,17.215,10.64h149.931c6.57,0,9.514-4.765,6.576-10.64l-98.746-114.347h273.879 c6.234,0,11.285-5.052,11.285-11.285v-165.09C537.66,180.494,532.609,175.442,526.375,175.442z" />
                      </g>
                    </g>
                  </g>
                </svg>
              </div>
              <div class="lightbox-image" @click.stop="">
                <img :src="images[index]" loading="lazy">
              </div>
              <div class="cursor right" @click.stop="next" :class="{ 'invisible': !hasNext() }">
                <svg class="size" fill="#ffff" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100px" height="100px" viewBox="0 0 537.66 537.66" xml:space="preserve" transform="rotate(180)" stroke="#ffff">
                  <g id="SVGRepo_bgCarrier" stroke-width="0" />
                  <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" />
                  <g id="SVGRepo_iconCarrier">
                    <g>
                      <g>
                        <path d="M526.375,175.442H249.458l101.781-115.23c2.939-5.875-0.006-10.64-6.574-10.64H194.735 c-15.012,0.003-34.74,30.233-44.538,41.188C132.96,110.028,3.146,254.326,2.204,256.208c-2.938,5.875-2.938,15.404,0,21.279 L177.52,477.449c2.938,5.875,10.646,10.64,17.215,10.64h149.931c6.57,0,9.514-4.765,6.576-10.64l-98.746-114.347h273.879 c6.234,0,11.285-5.052,11.285-11.285v-165.09C537.66,180.494,532.609,175.442,526.375,175.442z" />
                      </g>
                    </g>
                  </g>
                </svg>
              </div>
            </div>
          </div>
        </div>`,
        props: {
          thumbnailIndex: {
            type: Array,
            required: true,
          },
          images: {
            type: Array,
            default: () => [],
          },
          styles: {
            type: String,
            deafult: '29vh'
          }
        },
        data() {
          return {
            visible: false,
            index: 1,
          };
        },
        methods: {
          Index() {
            let thumbnailIndex = images.findIndex(
              (element) => element === thumbnail
            );
          },
          show() {
            this.visible = true;
            this.index = this.thumbnailIndex;
          },
          hide() {
            this.visible = false;
            this.index = 0;
          },
          hasNext() {
            return this.index + 1 < this.images.length;
          },
          hasPrev() {
            return this.index - 1 >= 0;
          },
          prev() {
            if (this.hasPrev()) {
              this.index -= 1;
            }
          },
          next() {
            if (this.hasNext()) {
              this.index += 1;
            }
          },
        },
      });
      app.mount("#demo");
    .lightbox {
        position: fixed;
        z-index: 500;
        display: flex;
        justify-content: center;
        align-content: center;
        background: rgba(0, 0, 0, 0.8);
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 100%;
        height: 100%;
    
    }
    .lightbox-image img {
        width: auto;
        height: auto;
        max-width:50%;
        position: fixed;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }
    .cursor {
        cursor: pointer;
        display: block;
        position: absolute;
        z-index: 1000;
        top: 50%;
        visibility: visible;
    }
    .cursor.right {
        right: 10%;
    }
    .cursor.left {
        left: 10%;
    }
    .invisible {
        visibility: hidden;
    }
    @media screen and (max-width: 1024px) {
      .lightbox-image img{
        max-width: 80%;
        max-height: 80%;;
      }
      .cursor{
        top: 70%;
      }
    }
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
    <div id="demo" style="display: flex; flex-wrap: wrap">
        <light-box :thumbnail-index="0" :images="images" :styles="{width: '25vh'}"></light-box>
        <light-box :thumbnail-index="1" :images="images" :styles="{width: '30vh'}"></light-box>
        <light-box :thumbnail-index="2" :images="images" :styles="{width: '45vh'}"></light-box>
        <light-box :thumbnail-index="3" :images="images" :styles="{width: '25vh'}"></light-box>
    </div>