Files
Website/src/lib/components/tui/TuiAccordion.svelte
2025-11-28 02:40:12 +00:00

120 lines
2.6 KiB
Svelte

<script lang="ts">
import Icon from '@iconify/svelte';
import { getButtonStyle, parseColorText, getSegmentStyle } from './utils';
import type { TerminalLine } from './types';
export let line: TerminalLine;
let openItems: Set<number> = new Set(line.accordionOpen ? [0] : []);
function toggleItem(index: number) {
if (openItems.has(index)) {
openItems.delete(index);
} else {
openItems.add(index);
}
openItems = openItems; // trigger reactivity
}
$: items = line.accordionItems || [{ title: line.content, content: '' }];
</script>
<div class="tui-accordion" style="--accordion-accent: {getButtonStyle(line.style)}">
{#each items as item, i}
{@const contentSegments = parseColorText(item.content)}
<div class="accordion-item" class:open={openItems.has(i)}>
<button class="accordion-header" on:click={() => toggleItem(i)}>
<Icon
icon={openItems.has(i) ? 'mdi:chevron-down' : 'mdi:chevron-right'}
width="16"
class="accordion-icon"
/>
<span class="accordion-title">{item.title}</span>
</button>
{#if openItems.has(i)}
<div class="accordion-content">
{#each contentSegments as segment}
{#if getSegmentStyle(segment)}
<span style={getSegmentStyle(segment)}>{segment.text}</span>
{:else}
{segment.text}
{/if}
{/each}
</div>
{/if}
</div>
{/each}
</div>
<style>
.tui-accordion {
margin: 0.5rem 0;
border: 1px solid var(--terminal-border);
border-radius: 6px;
overflow: hidden;
}
.accordion-item {
border-bottom: 1px solid var(--terminal-border);
}
.accordion-item:last-child {
border-bottom: none;
}
.accordion-header {
display: flex;
align-items: center;
gap: 0.5rem;
width: 100%;
padding: 0.6rem 0.75rem;
background: transparent;
border: none;
color: var(--terminal-text);
font-family: inherit;
font-size: 0.85rem;
font-weight: 500;
text-align: left;
cursor: pointer;
transition: background 0.15s ease;
}
.accordion-header:hover {
background: rgba(255, 255, 255, 0.03);
}
.accordion-item.open .accordion-header {
background: rgba(0, 0, 0, 0.1);
border-bottom: 1px solid var(--terminal-border);
}
:global(.accordion-icon) {
color: var(--accordion-accent);
transition: transform 0.2s ease;
}
.accordion-title {
flex: 1;
}
.accordion-content {
padding: 0.75rem;
color: var(--terminal-muted);
font-size: 0.85rem;
line-height: 1.6;
white-space: pre-wrap;
animation: slideDown 0.2s ease-out;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-5px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>