56 lines
1.3 KiB
Svelte
56 lines
1.3 KiB
Svelte
<script lang="ts">
|
||
import type { HTMLAttributes } from 'svelte/elements';
|
||
import type { Snippet } from 'svelte';
|
||
|
||
interface Props extends HTMLAttributes<HTMLDivElement> {
|
||
id?: string;
|
||
label?: string;
|
||
expanded?: boolean;
|
||
children?: Snippet;
|
||
}
|
||
|
||
let {
|
||
id,
|
||
label = '',
|
||
expanded = $bindable(false),
|
||
class: className = '',
|
||
children,
|
||
...rest
|
||
}: Props = $props();
|
||
</script>
|
||
|
||
<div {id} class="spoiler-group {className}" {...rest}>
|
||
<button
|
||
type="button"
|
||
class="btn btn-link p-0 d-flex align-items-center w-100 text-decoration-none spoiler-header"
|
||
onclick={() => (expanded = !expanded)}
|
||
aria-expanded={expanded}>
|
||
<span class="font-monospace fs-5 ms-1 fw-bold text-muted spoiler-icon">{expanded ? '−' : '+'}</span>
|
||
<span class="small text-nowrap ms-1">{label}</span>
|
||
<div class="flex-fill border-top ms-1"></div>
|
||
</button>
|
||
|
||
{#if expanded}
|
||
<div class="p-2 border border-top-0 spoiler-content">
|
||
{@render children?.()}
|
||
</div>
|
||
{:else}
|
||
<div style="padding-top: 0.75em;"></div>
|
||
{/if}
|
||
</div>
|
||
|
||
<style>
|
||
.spoiler-header {
|
||
margin-bottom: -0.75em;
|
||
}
|
||
.spoiler-content {
|
||
padding-top: 0.75em !important;
|
||
}
|
||
.spoiler-icon {
|
||
line-height: 1;
|
||
padding-bottom: 0.1em;
|
||
}
|
||
.btn:hover .spoiler-icon {
|
||
color: var(--bs-dark) !important;
|
||
}
|
||
</style>
|