Skip to content

Instantly share code, notes, and snippets.

@sayhicoelho
Created August 1, 2020 23:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sayhicoelho/5d54cbc233575310215fef28a3334b21 to your computer and use it in GitHub Desktop.
Save sayhicoelho/5d54cbc233575310215fef28a3334b21 to your computer and use it in GitHub Desktop.
Svelte accordion based on Bootstrap Collapse.

Usage

<AccordionGroup multiple={false} header={true}>
  <AccordionItem class="card">
    <div slot="header" class="p-4">
      <h5>Accordion 1</h5>
    </div>
    <div slot="content" class="card-body p-4">
      The content
    </div>
  </AccordionItem>
</AccordionGroup>
<script>
import { setContext } from 'svelte';
export let multiple = false
export let header = true
const items = new Set()
setContext('accordion', {
getMultiple: () => multiple,
getHeader: () => header,
getItems: () => items,
addItem: item => items.add(item),
deleteItem: item => items.delete(item),
})
</script>
<div class="accordion-group">
<slot />
</div>
<script>
import { onMount, getContext, createEventDispatcher, tick } from 'svelte';
export let className = ''
export {className as class}
const {
getMultiple,
getHeader,
getItems,
addItem,
deleteItem
} = getContext('accordion')
const multiple = getMultiple()
const header = getHeader()
const items = getItems()
const dispatch = createEventDispatcher();
let show = false
let collapsing = false
let height = null
let content = null
$: heightStyle = height !== null ? `height: ${height}px;` : ''
function collapse() {
height = content.scrollHeight
collapsing = true
show = false
setTimeout(() => {
height = null
}, 5)
setTimeout(() => {
show = false
collapsing = false
}, 200)
}
async function toggle() {
if (collapsing) return
if (!show) {
if (!multiple) {
items.forEach(item => {
if (item !== collapse) {
item()
}
})
}
collapsing = true
await tick()
height = content.scrollHeight
setTimeout(() => {
show = true
height = null
collapsing = false
}, 200)
} else {
collapse()
}
dispatch('toggle', show)
}
onMount(() => {
addItem(collapse)
return () => {
deleteItem(collapse)
}
})
</script>
<div class={'accordion-item ' + className}>
{#if header}
<div class="accordion-header" on:click={toggle}>
<slot name="header" />
</div>
{/if}
<div class="accordion-content" class:show class:collapsing style={heightStyle} bind:this={content}>
<div class="accordion-content-inner">
<slot name="content" />
</div>
</div>
</div>
<style lang="css">
.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