wandering-eye/src/lib/Accordion/AccordionItem.svelte

98 lines
1.8 KiB
Svelte

<script lang="ts">
import { slide } from 'svelte/transition'
export let id: string
export let name: string
export let open = false
let loaded = open
$: {
if (open) loaded = true
}
export let noPadding = false
</script>
<div class="accordion-item">
<button
aria-expanded={open}
aria-controls="{id}-contents"
on:click={() => (open = !open)}
>
<span class="accordion-name">{name}</span>
<span class="open-indicator material-icons" aria-hidden="true">
{open ? 'remove' : 'add'}
</span>
</button>
<div
id="{id}-contents"
class="accordion-contents-wrapper"
class:closed={!open && loaded}
>
{#if loaded}
<div
class="accordion-contents"
class:no-padding={noPadding}
transition:slide|local={{ duration: 100 }}
>
<slot />
</div>
{/if}
</div>
</div>
<style lang="postcss">
.accordion-item {
container: accordion-item / inline-size;
display: grid;
grid-template-rows: min-content 1fr;
}
button {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.25rem;
user-select: none;
cursor: pointer;
font-size: 1.5rem;
font-weight: 500;
padding: 0.25rem 0.5rem;
min-height: 3rem;
}
button:focus-visible {
outline: none;
background-color: var(--lighten-10);
}
.accordion-name {
word-break: break-word;
}
.open-indicator {
display: grid;
place-items: center;
width: 2rem;
height: 2rem;
}
.accordion-contents-wrapper {
display: grid;
grid-template-rows: 1fr;
transition: grid-template-rows 100ms;
}
.accordion-contents {
overflow: hidden;
padding: 1rem 0.5rem;
background-color: rgb(0 0 0 / 0.25);
min-height: 0;
transition: padding 100ms;
}
.closed {
grid-template-rows: 0fr;
}
.closed .accordion-contents {
visibility: hidden;
padding-block: 0;
transition:
padding 100ms,
visibility 0ms 100ms;
}
.no-padding {
padding: 0;
}
</style>