Skip to content

Instantly share code, notes, and snippets.

@sayhicoelho
Last active February 16, 2023 17:20
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sayhicoelho/f9593e22072ececc469ffbce536ac209 to your computer and use it in GitHub Desktop.
Save sayhicoelho/f9593e22072ececc469ffbce536ac209 to your computer and use it in GitHub Desktop.
Vue.js Accordion base on Bootstrap Collapse.

Usage

<AccordionGroup :multiple="false">
  <AccordionItem class="card">
    <div slot="header" class="p-4">
      <h5>ACCORDION 1</h5>
    </div>
    <div slot="content" class="card-body p-4">
      <p>The content 1</p>
    </div>
  </AccordionItem>
  <AccordionItem class="card">
    <div slot="header" class="p-4">
      <h5>ACCORDION 2</h5>
    </div>
    <div slot="content" class="card-body p-4">
      <p>The content 2</p>
    </div>
  </AccordionItem>
</AccordionGroup>
<template>
<div class="accordion-group">
<slot />
</div>
</template>
<script>
export default {
name: 'AccordionGroup',
props: {
multiple: {
type: Boolean,
default: false,
}
}
}
</script>
<template>
<div class="accordion-item" ref="item">
<div class="accordion-header" @click="toggle" v-if="hasHeader">
<slot name="header" />
</div>
<div :class="['accordion-content', { show, collapsing }]" :style="heightStyle" ref="content">
<div class="accordion-content-inner">
<slot name="content" />
</div>
</div>
</div>
</template>
<script>
export default {
name: 'AccordionItem',
props: {
autoScroll: {
type: Boolean,
default: false
}
},
data() {
return {
show: false,
collapsing: false,
height: null,
}
},
computed: {
hasHeader() {
return !!this.$slots.header
},
heightStyle() {
if (this.height !== null) {
return `height: ${this.height}px;`
}
return ''
}
},
methods: {
collapse() {
this.height = this.$refs.content.scrollHeight
this.collapsing = true
this.show = false
setTimeout(() => {
this.height = null
}, 5)
setTimeout(() => {
this.show = false
this.collapsing = false
}, 200)
},
toggle() {
if (this.collapsing) return
this.$emit('toggle', !this.show)
if (!this.show && this.autoScroll) {
setTimeout(() => {
window.scrollTo({
top: this.$refs.item.offsetTop - 8,
behavior: 'smooth'
})
}, 250)
}
if (!this.show) {
if (!this.$parent.multiple) {
this.$parent.$children.forEach(component => {
if (component !== this) {
component.collapse()
}
})
}
this.collapsing = true
this.$nextTick(() => {
this.height = this.$refs.content.scrollHeight
})
setTimeout(() => {
this.show = true
this.height = null
this.collapsing = false
}, 200)
} else {
this.collapse()
}
}
}
}
</script>
<style scoped>
.accordion-header {
cursor: pointer;
}
.accordion-item.show > .accordion-header,
.accordion-header:hover {
background-color: #f2f2f2;
}
.accordion-content {
transition: height 200ms ease;
}
.accordion-content:not(.show):not(.collapsing) {
display: none;
}
.accordion-content.collapsing {
height: 0;
overflow: hidden;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment