Trace Drawer
Hierarchical "How was this value calculated?" drawer. Clicking an aggregated result opens a drawer from the right with the full calculation pipeline — input values as leaves, formulas as sublabels.
Live Preview
Heating cost statement unit 4
Billing period 2024
Allocated heating costs €1,855.47
Features
- Drawer layout with header / body / footer
- Nested list structure via a recursive snippet
- Formula display as a secondary label per step
- Source references as Badges with a link indicator
- Export action in the footer for PDF/clipboard
Code
TraceDrawer.svelte
<script lang="ts">
import { Drawer, Card, Button, Badge } from '@urbicon-ui/blocks';
interface TraceNode {
label: string;
value: string;
formula?: string;
children?: TraceNode[];
reference?: string;
}
let { trace, open = $bindable() }: { trace: TraceNode; open: boolean } = $props();
</script>
<Drawer bind:open title={`How is ${trace.value} derived?`} placement="right" size="lg">
{@render TraceNodeRender(trace, 0)}
{#snippet footer()}
<Button intent="neutral" variant="outlined">Export as PDF</Button>
<Button intent="primary" onclick={() => (open = false)}>Close</Button>
{/snippet}
</Drawer>
{#snippet TraceNodeRender(node, depth)}
{@const stacked = depth >= 2}
<Card variant="outlined" padding="sm">
<div class={stacked
? 'flex flex-col items-start gap-1'
: 'flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1'}>
<span class="text-text-primary min-w-0 text-sm font-medium">{node.label}</span>
<span class="text-text-primary tabular-nums font-semibold">{node.value}</span>
</div>
{#if node.formula}
<p class="text-text-tertiary text-xs font-mono mt-1 break-words">{node.formula}</p>
{/if}
{#if node.reference}
<Badge variant="soft" size="xs" class="mt-2">{node.reference}</Badge>
{/if}
{#if node.children}
<ul class="ml-1 mt-3 space-y-2 border-l border-border-subtle pl-3">
{#each node.children as child (child.label)}
<li>{@render TraceNodeRender(child, depth + 1)}</li>
{/each}
</ul>
{/if}
</Card>
{/snippet}