SankeyChart

Data Visualization

visx 기반 생키 다이어그램.

Usage

여러 단계 사이에서 항목이나 양이 어디로 이동하는지 흐름 너비로 표현할 때 사용한다.

import

import

import { SankeyChart } from '@mildang/design-system/Chart';

예제를 복사해 쓸 때 필요한 준비

@mildang/styled-system 은 이 저장소에서 Panda 가 생성하는 산출물이다. 저장소 안에서는 turbo run ship 이후 쓸 수 있고, 패키지 소비자는 자기 Panda 산출물이나 다른 레이아웃 수단으로 바꿔야 한다.

API Reference

SankeyChart Props

Prop

Type

Default

links
필수

SankeyChartLink[]

지정 안 함

nodes
필수

SankeyChartNode[]

지정 안 함

className

string

지정 안 함

colors

string[]

지정 안 함

formatValue

(value: number) => string

(value: number) => value.toLocaleString()

legendPosition

"bottom" | "left" | "right" | "top"

bottom

linkOpacity

number

0.3

margin

Partial<ChartMargin>

지정 안 함

nodePadding

number

12

nodeWidth

number

16

showLabels

boolean

true

showLegend

boolean

false

showTooltip

boolean

false

valueLabel

string

예제

기본

import { SankeyChart } from '@mildang/design-system/Chart';

// 학년 → 계열 → 진로 흐름 (명 단위)
const careerNodes = [
  { id: '고1', label: '고1' },
  { id: '고2', label: '고2' },
  { id: '문과', label: '문과' },
  { id: '이과', label: '이과' },
  { id: '인문대', label: '인문대' },
  { id: '공대', label: '공대' },
  { id: '의대', label: '의대' },
];

const careerLinks = [
  { source: '고1', target: '문과', value: 80 },
  { source: '고1', target: '이과', value: 120 },
  { source: '고2', target: '문과', value: 60 },
  { source: '고2', target: '이과', value: 90 },
  { source: '문과', target: '인문대', value: 140 },
  { source: '이과', target: '공대', value: 150 },
  { source: '이과', target: '의대', value: 60 },
];

const SankeyChartDefaultExample = () => <SankeyChart nodes={careerNodes} links={careerLinks} />;

export default SankeyChartDefaultExample;

Hover Tooltip

import { SankeyChart } from '@mildang/design-system/Chart';

// 학년 → 계열 → 진로 흐름 (명 단위)
const careerNodes = [
  { id: '고1', label: '고1' },
  { id: '고2', label: '고2' },
  { id: '문과', label: '문과' },
  { id: '이과', label: '이과' },
  { id: '인문대', label: '인문대' },
  { id: '공대', label: '공대' },
  { id: '의대', label: '의대' },
];

const careerLinks = [
  { source: '고1', target: '문과', value: 80 },
  { source: '고1', target: '이과', value: 120 },
  { source: '고2', target: '문과', value: 60 },
  { source: '고2', target: '이과', value: 90 },
  { source: '문과', target: '인문대', value: 140 },
  { source: '이과', target: '공대', value: 150 },
  { source: '이과', target: '의대', value: 60 },
];

const SankeyChartHoverTooltipExample = () => <SankeyChart nodes={careerNodes} links={careerLinks} showTooltip valueLabel="학생 수" />;

export default SankeyChartHoverTooltipExample;

라벨 숨김 + 범례

import { SankeyChart } from '@mildang/design-system/Chart';

// 학년 → 계열 → 진로 흐름 (명 단위)
const careerNodes = [
  { id: '고1', label: '고1' },
  { id: '고2', label: '고2' },
  { id: '문과', label: '문과' },
  { id: '이과', label: '이과' },
  { id: '인문대', label: '인문대' },
  { id: '공대', label: '공대' },
  { id: '의대', label: '의대' },
];

const careerLinks = [
  { source: '고1', target: '문과', value: 80 },
  { source: '고1', target: '이과', value: 120 },
  { source: '고2', target: '문과', value: 60 },
  { source: '고2', target: '이과', value: 90 },
  { source: '문과', target: '인문대', value: 140 },
  { source: '이과', target: '공대', value: 150 },
  { source: '이과', target: '의대', value: 60 },
];

const SankeyChartWithLegendExample = () => <SankeyChart nodes={careerNodes} links={careerLinks} showLabels={false} showLegend />;

export default SankeyChartWithLegendExample;

노드 두께·간격 커스텀

import { SankeyChart } from '@mildang/design-system/Chart';

// 학년 → 계열 → 진로 흐름 (명 단위)
const careerNodes = [
  { id: '고1', label: '고1' },
  { id: '고2', label: '고2' },
  { id: '문과', label: '문과' },
  { id: '이과', label: '이과' },
  { id: '인문대', label: '인문대' },
  { id: '공대', label: '공대' },
  { id: '의대', label: '의대' },
];

const careerLinks = [
  { source: '고1', target: '문과', value: 80 },
  { source: '고1', target: '이과', value: 120 },
  { source: '고2', target: '문과', value: 60 },
  { source: '고2', target: '이과', value: 90 },
  { source: '문과', target: '인문대', value: 140 },
  { source: '이과', target: '공대', value: 150 },
  { source: '이과', target: '의대', value: 60 },
];

const SankeyChartCustomNodeStyleExample = () => (
    <SankeyChart nodes={careerNodes} links={careerLinks} nodeWidth={28} nodePadding={24} linkOpacity={0.5} />
  );

export default SankeyChartCustomNodeStyleExample;

커스텀 색상 (순환)

import { SankeyChart } from '@mildang/design-system/Chart';
import { token } from '@mildang/styled-system/tokens';

// 학년 → 계열 → 진로 흐름 (명 단위)
const careerNodes = [
  { id: '고1', label: '고1' },
  { id: '고2', label: '고2' },
  { id: '문과', label: '문과' },
  { id: '이과', label: '이과' },
  { id: '인문대', label: '인문대' },
  { id: '공대', label: '공대' },
  { id: '의대', label: '의대' },
];

const careerLinks = [
  { source: '고1', target: '문과', value: 80 },
  { source: '고1', target: '이과', value: 120 },
  { source: '고2', target: '문과', value: 60 },
  { source: '고2', target: '이과', value: 90 },
  { source: '문과', target: '인문대', value: 140 },
  { source: '이과', target: '공대', value: 150 },
  { source: '이과', target: '의대', value: 60 },
];

const SankeyChartCustomColorsExample = () => (
    <SankeyChart
      nodes={careerNodes}
      links={careerLinks}
      colors={[
        token('colors.mildangPT.light.400'),
        token('colors.blue.light.400'),
        token('colors.green.light.400'),
      ]}
    />
  );

export default SankeyChartCustomColorsExample;

빈 데이터

import { SankeyChart } from '@mildang/design-system/Chart';

const SankeyChartEmptyExample = () => <SankeyChart nodes={[]} links={[]} />;

export default SankeyChartEmptyExample;

반응형 (너비 자동 조정)

import { token } from '@mildang/styled-system/tokens';
import { SankeyChart } from '@mildang/design-system/Chart';

// 학년 → 계열 → 진로 흐름 (명 단위)
const careerNodes = [
  { id: '고1', label: '고1' },
  { id: '고2', label: '고2' },
  { id: '문과', label: '문과' },
  { id: '이과', label: '이과' },
  { id: '인문대', label: '인문대' },
  { id: '공대', label: '공대' },
  { id: '의대', label: '의대' },
];

const careerLinks = [
  { source: '고1', target: '문과', value: 80 },
  { source: '고1', target: '이과', value: 120 },
  { source: '고2', target: '문과', value: 60 },
  { source: '고2', target: '이과', value: 90 },
  { source: '문과', target: '인문대', value: 140 },
  { source: '이과', target: '공대', value: 150 },
  { source: '이과', target: '의대', value: 60 },
];

const SankeyChartResponsiveExample = () => (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 32, width: '100%' }}>
      {([320, 520, 720] as const).map((w) => (
        <div key={w} style={{ width: w }}>
          <div style={{ marginBottom: 4, fontSize: 11, color: token('colors.neutral.text.lowest') }}>{w}px</div>
          <div style={{ width: w, height: 260 }}>
            <SankeyChart nodes={careerNodes} links={careerLinks} />
          </div>
        </div>
      ))}
    </div>
  );

export default SankeyChartResponsiveExample;