summaryrefslogtreecommitdiff
path: root/frontend/src/components/Sparkline.svelte
blob: 12a3b47bd595cc142d4b5bc6966ab44931f028ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<script lang="ts">
  // 项目行迷你权益缩略图:最近一个回测 equity 的首尾两端点画一条线。
  // 无数据(first/last 缺失)→ 组件整体留空,不渲染任何 svg(leader 约束)。
  interface Props {
    first: number | null;
    last: number | null;
    up?: boolean;
    ariaLabel?: string;
  }
  let { first = null, last = null, up = false, ariaLabel = '最近回测权益缩略' }: Props = $props();

  const H = 24;
  const PAD = 3;
  // 首尾两点:左起 0, 右侧满宽(宽度由父级 flex 决定,viewBox 归一为 100)
  const X1 = PAD + 0;
  const X2 = 100 - 0;
  // 首尾净值常见的取值范围很大,只关心涨跌形态:把两点摆在对角带上,
  // 上行从左下到右上,下行从左上到右下,端点各留 pad。
  const y1 = $derived(up ? H - PAD : PAD);
  const y2 = $derived(up ? PAD : H - PAD);
</script>

{#if first !== null && last !== null}
  <svg
    class="spark"
    viewBox={`0 0 100 ${H}`}
    preserveAspectRatio="none"
    width="72"
    height={H}
    role="img"
    aria-label={ariaLabel}
    data-testid="sparkline"
  >
    <path
      d={`M${X1},${y1} L${X2},${y2}`}
      stroke={up ? 'var(--ok)' : 'var(--danger)'}
      stroke-width="2.5"
      fill="none"
      stroke-linecap="round"
      vector-effect="non-scaling-stroke"
    />
    <circle cx={X1} cy={y1} r="2.5" fill={up ? 'var(--ok)' : 'var(--danger)'} vector-effect="non-scaling-stroke" />
    <circle cx={X2} cy={y2} r="2.5" fill={up ? 'var(--ok)' : 'var(--danger)'} />
  </svg>
{/if}