Skip to main content
Urbicon UI
Back to Recipes

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}