Accordion
Accordion Example
The accordion presented here is made up of two components. There is an outer accordion
component that acts as a container, with accordion-panel
components as children that can be expanded or collapsed.
Usage might look something like this:
vue
<template>
<basic-accordion>
<basic-accordion-panel title="First">
First panel content
</basic-accordion-panel>
<basic-accordion-panel title="Second">
<template v-for="i in 20">
Second panel content {{ i }}
<br v-if="i !== 20">
</template>
</basic-accordion-panel>
<basic-accordion-panel title="Third">
Third panel content
</basic-accordion-panel>
</basic-accordion>
</template>
<script setup>
import BasicAccordion from './accordion.vue'
import BasicAccordionPanel from './accordion-panel.vue'
</script>
While the 3 children given in this example are static, they could also be created dynamically using v-for
over a suitable array.
Running this example we get:
The code for accordion.vue
:
vue
<template>
<div class="accordion"><slot /></div>
</template>
<script setup>
import { computed, provide, ref } from 'vue'
// Holds the id of the currently expanded panel
const expanded = ref(null)
// Use `provide` to communicate with the child panels
provide('accordion-register', () => {
const id = Symbol()
return {
expanded: computed(() => expanded.value === id),
toggle () {
expanded.value = expanded.value === id ? null: id
},
unregister () {
if (expanded.value === id) {
expanded.value = null
}
}
}
})
</script>
<style scoped>
.accordion {
border: 1px solid #ccc;
display: flex;
flex-direction: column;
height: 300px;
width: 240px;
}
</style>
The corresponding accordion-panel.vue
is:
vue
<template>
<div class="accordion-panel" :class="{ expanded }">
<div class="header" @click="toggle">{{ title }}</div>
<div v-if="expanded" class="body"><slot /></div>
</div>
</template>
<script setup>
import { inject, onUnmounted } from 'vue'
const props = defineProps({
title: {
required: true,
type: String
}
})
const register = inject('accordion-register')
const { expanded, toggle, unregister } = register()
onUnmounted(unregister)
</script>
<style scoped>
.accordion-panel {
display: flex;
flex-direction: column;
}
.accordion-panel + .accordion-panel {
margin-top: 1px;
}
.header {
background-color: #e6f6ff;
border: 1px solid #ccc;
cursor: pointer;
margin: -1px;
padding: 5px;
}
.body {
background-color: #f7fcff;
border: 1px solid #ccc;
flex: auto;
margin: 0 -1px -1px;
overflow: auto;
}
.expanded {
flex: auto;
min-height: 0;
}
</style>
Vue Patterns
See Coupled Components with provide
/inject
.
Libraries
Various libraries include an accordion component, or a component that can achieve a similar effect. These include:
- Vuetify - Expansion Panel
- Element Plus - Collapse
- Quasar - Expansion Item
- Ant Design Vue - Collapse
- Headless UI - Disclosure
- PrimeVue - Accordion
- Naive UI - Collapse
- Oruga - Collapse