2026-02-12 19:42:23 +01:00
|
|
|
<script lang="ts">
|
|
|
|
|
import ToggleButton from "layout/ToggleButton.svelte";
|
|
|
|
|
import { onMount } from "svelte";
|
|
|
|
|
|
|
|
|
|
type Expandable = {[key: string]: boolean}
|
|
|
|
|
|
|
|
|
|
let {
|
|
|
|
|
id,
|
|
|
|
|
collapsed = false,
|
|
|
|
|
title,
|
|
|
|
|
body,
|
|
|
|
|
}: {
|
|
|
|
|
id: string
|
|
|
|
|
collapsed: boolean
|
|
|
|
|
title: import('svelte').Snippet
|
|
|
|
|
body: import('svelte').Snippet
|
|
|
|
|
} = $props();
|
|
|
|
|
|
|
|
|
|
let expanded: boolean = $state(true)
|
|
|
|
|
|
|
|
|
|
const get_status = (): Expandable => {
|
|
|
|
|
let exp = localStorage.getItem("menu_expanded")
|
|
|
|
|
if (exp === null) {
|
|
|
|
|
exp = "{}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return JSON.parse(exp) as Expandable
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMount(() => {
|
|
|
|
|
let exp = get_status()
|
|
|
|
|
if (exp[id] !== undefined) {
|
|
|
|
|
expanded = exp[id]
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const toggle = (e: MouseEvent) => {
|
|
|
|
|
let exp = get_status()
|
|
|
|
|
|
|
|
|
|
exp[id] = expanded
|
|
|
|
|
|
|
|
|
|
localStorage.setItem("menu_expanded", JSON.stringify(exp))
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<div class="title">
|
|
|
|
|
<ToggleButton bind:on={expanded} action={toggle} icon_on="arrow_drop_down" icon_off="arrow_drop_up" highlight={false}/>
|
|
|
|
|
|
|
|
|
|
{#if !collapsed}
|
|
|
|
|
{@render title()}
|
|
|
|
|
{/if}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{#if expanded}
|
|
|
|
|
{@render body()}
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
.title {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
align-items: center;
|
2026-02-19 18:15:06 +01:00
|
|
|
border-top: 1px solid var(--separator);
|
2026-02-12 19:42:23 +01:00
|
|
|
}
|
|
|
|
|
</style>
|