1,170 lines · 4 files · 26.8 kB
cases/137-shadcn-area-interactive/example.tsx293 lines · entry
cases/137-shadcn-area-interactive/example.tsx
import { useMemo, useState } from 'react'
import {
areaY,
d3Curve,
defineChart,
stack,
type ChartPoint,
} from '@tanstack/charts'
import { RendererChart } from '@tanstack/charts/react/tooltip'
import { tooltip } from '@tanstack/charts/tooltip'
import { motion } from '@tanstack/charts/motion'
import { scaleLinear, scalePoint } from 'd3-scale'
import { curveNatural } from 'd3-shape'
import {
shadcnColors,
type ShadcnSeriesDatum,
} from '@tanstack/charts-data/shadcn'
import interactiveAreaData from '@tanstack/charts-data/shadcn-area-interactive-data'
import './styles.css'
type InteractiveTimeRange = '90d' | '30d' | '7d'
const twoSeries = ['desktop', 'mobile'] as const
const interactiveAreaRows = interactiveAreaData as readonly {
date: string
desktop: number
mobile: number
}[]
export function createExampleChart(timeRange: InteractiveTimeRange = '90d') {
const filteredRows = filterInteractiveAreaRows(timeRange)
const rows: ShadcnSeriesDatum[] = filteredRows.flatMap((row) => [
{ month: row.date, series: 'mobile', value: row.mobile },
{ month: row.date, series: 'desktop', value: row.desktop },
])
return defineChart(
({ width }) => ({
marks: [
areaY(rows, {
id: 'visitor-areas',
x: 'month',
y: 'value',
z: 'series',
color: 'series',
key: (row) => `${row.month}:${row.series}`,
layout: stack({ order: ['mobile', 'desktop'] }),
curve: d3Curve(curveNatural),
fill: (row) => `url(#shadcn-interactive-${row.series})`,
fillOpacity: 1,
stroke: (row) =>
row.series === 'mobile' ? shadcnColors[1] : shadcnColors[0],
strokeWidth: 1,
}),
],
scales: {
x: {
scale: scalePoint,
axis: {
line: false,
ticks: {
values: interactiveDateTicks(timeRange),
size: 0,
padding: 10,
format: formatMonthDay,
},
},
},
y: {
scale: scaleLinear().domain([0, 1200]),
grid: true,
axis: {
line: false,
ticks: { values: [0, 300, 600, 900, 1200], size: 0 },
tickLabels: false,
},
},
},
color: { domain: twoSeries, range: shadcnColors.slice(0, 2) },
gradients: twoSeries.map((series, index) => ({
id: `shadcn-interactive-${series}`,
x1: 0,
y1: 1,
x2: 0,
y2: 0,
stops: [
{ offset: 0.05, color: shadcnColors[index], opacity: 0.1 },
{ offset: 0.95, color: shadcnColors[index], opacity: 0.8 },
],
})),
margin: {
top: 32,
right: width < 400 ? 14 : 5,
bottom: 35,
left: 5,
},
theme: shadcnTheme(),
}),
{
svgAnimation: false,
focus: 'group-x',
tooltip: {
use: tooltip,
className: 'sc-chart-tooltip',
anchor: 'group-center',
placement: 'auto',
sort: 'color-domain',
content: (points) => shadcnTooltipContent(points),
},
},
)
}
function filterInteractiveAreaRows(timeRange: InteractiveTimeRange) {
const days = timeRange === '30d' ? 30 : timeRange === '7d' ? 7 : 90
const start = new Date('2024-06-30T00:00:00Z')
start.setUTCDate(start.getUTCDate() - days)
const firstDate = start.toISOString().slice(0, 10)
return interactiveAreaRows.filter((row) => row.date >= firstDate)
}
function interactiveDateTicks(timeRange: InteractiveTimeRange) {
if (timeRange === '7d') {
return ['2024-06-23', '2024-06-25', '2024-06-27', '2024-06-29']
}
if (timeRange === '30d') {
return [
'2024-06-01',
'2024-06-08',
'2024-06-15',
'2024-06-22',
'2024-06-29',
]
}
return [
'2024-04-10',
'2024-04-21',
'2024-05-02',
'2024-05-13',
'2024-05-25',
'2024-06-05',
'2024-06-16',
'2024-06-29',
]
}
function formatMonthDay(value: string) {
return new Intl.DateTimeFormat('en-US', {
month: 'short',
day: 'numeric',
timeZone: 'UTC',
}).format(new Date(value))
}
function shadcnTheme() {
return {
foreground: 'var(--muted-foreground, var(--muted))',
grid: 'var(--border)',
background: 'transparent',
}
}
function shadcnTooltipContent<TDatum>(points: readonly ChartPoint<TDatum>[]) {
return {
title: String(points[0]?.xValue ?? ''),
rows: points.map((point) => ({
label: titleCase(
String(
point.group ??
point.markId.replace(
/-?(bars|lines|areas|slices|values|radar)$/u,
'',
),
),
),
value: Number(point.yValue ?? point.xValue ?? 0).toLocaleString('en-US'),
color: point.color,
})),
}
}
function titleCase(value: string) {
return value.charAt(0).toUpperCase() + value.slice(1)
}
export const definition = createExampleChart()
const renderer = motion({
initial: 'always',
transition: { type: 'spring', stiffness: 170, damping: 18, mass: 1 },
})
export interface ExampleProps {
width?: number
height?: number
}
export default function Example({ width = 640, height = 600 }: ExampleProps) {
const [timeRange, setTimeRange] = useState<InteractiveTimeRange>('90d')
const chartDefinition = useMemo(
() => createExampleChart(timeRange),
[timeRange],
)
const contentWidth = Math.max(1, width - 50)
const chartWidth = contentWidth
const chartHeight = 250
return (
<div className="sc-example" style={{ width, height }}>
<article className="sc-card sc-interactive-area" style={{ width }}>
<header className="sc-card-header">
<div className="sc-card-heading">
<h2>Area Chart - Interactive</h2>
<p>Showing total visitors for the last 3 months</p>
</div>
<div className="sc-card-action">
<SelectControl
value={timeRange}
onChange={(value) => setTimeRange(value as InteractiveTimeRange)}
options={[
{ value: '90d', label: 'Last 3 months' },
{ value: '30d', label: 'Last 30 days' },
{ value: '7d', label: 'Last 7 days' },
]}
/>
</div>
</header>
<div className="sc-card-content">
<div
className="sc-chart"
style={{ width: chartWidth, height: chartHeight }}
>
<RendererChart
definition={chartDefinition}
renderer={renderer}
initialWidth={chartWidth}
height={chartHeight}
ariaLabel="Area Chart - Interactive"
/>
</div>
<div className="sc-chart-footer">
<Legend />
</div>
</div>
</article>
</div>
)
}
function Legend() {
return (
<>
{['desktop', 'mobile'].map((label, index) => (
<span className="sc-legend-item" key={label}>
<span
className="sc-legend-dot"
style={{ background: shadcnColors[index] }}
/>
{titleCase(label)}
</span>
))}
</>
)
}
function SelectControl({
value,
options,
onChange,
}: {
value: string
options: readonly {
value: string
label: string
swatch?: string
}[]
onChange: (value: string) => void
}) {
const selected = options.find((option) => option.value === value)
return (
<label className="sc-select-display">
{selected?.swatch ? (
<span
className="sc-select-swatch"
style={{ background: selected.swatch }}
/>
) : null}
<select
aria-label="Select a value"
value={value}
onChange={(event) => onChange(event.currentTarget.value)}
>
{options.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
<svg viewBox="0 0 24 24" aria-hidden="true">
<path
d="m6 9 6 6 6-6"
fill="none"
stroke="currentColor"
strokeWidth="2"
/>
</svg>
</label>
)
}cases/137-shadcn-area-interactive/styles.css202 lines · dependency
cases/137-shadcn-area-interactive/styles.css
.sc-example {
--background: oklch(1 0 0);
--foreground: oklch(0 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--border: oklch(0.922 0 0);
--chart-1: oklch(0.809 0.105 251.813);
--chart-2: oklch(0.623 0.214 259.815);
--chart-3: oklch(0.546 0.245 262.881);
--chart-4: oklch(0.488 0.243 264.376);
--chart-5: oklch(0.424 0.199 265.638);
display: flex;
justify-content: center;
align-items: flex-start;
overflow: hidden;
color: var(--foreground);
background: var(--background);
font-family:
Inter,
ui-sans-serif,
system-ui,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
sans-serif;
text-rendering: geometricPrecision;
}
:root[data-theme='dark'] .sc-example {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--border: oklch(1 0 0 / 10%);
--chart-1: oklch(0.809 0.105 251.813);
--chart-2: oklch(0.623 0.214 259.815);
--chart-3: oklch(0.546 0.245 262.881);
--chart-4: oklch(0.488 0.243 264.376);
--chart-5: oklch(0.424 0.199 265.638);
}
.sc-example,
.sc-example * {
box-sizing: border-box;
}
.sc-card {
display: flex;
flex-direction: column;
gap: 24px;
border: 1px solid var(--border);
border-radius: 14px;
background: var(--card);
color: var(--card-foreground);
padding: 24px 0;
}
.sc-card-header {
display: grid;
gap: 8px;
padding: 0 24px;
}
.sc-card-heading {
display: grid;
gap: 8px;
}
.sc-card-header h2 {
margin: 0;
font-size: 16px;
font-weight: 600;
line-height: 1;
letter-spacing: -0.01em;
}
.sc-card-header p {
margin: 0;
color: var(--muted-foreground);
font-size: 14px;
line-height: 20px;
}
.sc-card-content {
display: flex;
min-height: 0;
flex-direction: column;
padding: 0 24px;
}
.sc-chart {
position: relative;
flex: none;
}
.sc-chart > * {
display: block;
}
.sc-chart-footer {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 12px;
margin-top: 12px;
color: var(--muted-foreground);
font-size: 12px;
}
.sc-legend-item {
display: inline-flex;
align-items: center;
gap: 6px;
}
.sc-legend-dot {
width: 8px;
height: 8px;
border-radius: 2px;
}
.sc-example .ts-chart {
color: var(--muted-foreground);
}
.sc-example .ts-chart__grid line,
.sc-example .ts-chart__polar-grid path,
.sc-example .ts-chart__polar-grid line {
stroke: var(--border);
}
.sc-example .ts-chart__axes text,
.sc-example .ts-chart__polar-grid text {
fill: var(--muted-foreground);
font-size: 12px;
}
.sc-example .ts-chart-tooltip {
min-width: 128px;
padding: 7px 10px !important;
border: 1px solid var(--border) !important;
border-radius: 8px !important;
background: var(--card) !important;
color: var(--card-foreground) !important;
box-shadow: 0 2px 6px rgb(0 0 0 / 8%);
font-size: 12px;
}
.sc-interactive-area {
gap: 0;
padding-top: 0;
}
.sc-interactive-area .sc-card-header {
display: flex;
align-items: center;
gap: 16px;
padding-top: 20px;
padding-bottom: 20px;
border-bottom: 1px solid var(--border);
}
.sc-interactive-area .sc-card-heading {
flex: 1;
gap: 4px;
}
.sc-interactive-area .sc-card-content {
padding-top: 24px;
}
.sc-interactive-area .sc-chart-footer {
margin-top: 8px;
}
.sc-card-action {
margin-left: auto;
}
.sc-select-display {
position: relative;
display: flex;
min-width: 130px;
height: 36px;
align-items: center;
gap: 8px;
padding: 0 12px;
border: 1px solid var(--border);
border-radius: 10px;
background: var(--background);
color: var(--foreground);
font-size: 14px;
box-shadow: 0 1px 2px rgb(0 0 0 / 4%);
}
.sc-select-display:focus-within {
outline: 2px solid var(--foreground);
outline-offset: 2px;
}
.sc-select-display select {
min-width: 0;
flex: 1;
appearance: none;
border: 0;
outline: 0;
background: transparent;
color: inherit;
font: inherit;
cursor: pointer;
}
.sc-select-display svg {
width: 14px;
height: 14px;
flex: none;
color: var(--muted-foreground);
pointer-events: none;
}
.sc-select-swatch {
width: 12px;
height: 12px;
flex: none;
border-radius: 3px;
}packages/charts-demo-data/src/shadcn-area-interactive-data.ts459 lines · dependency
packages/charts-demo-data/src/shadcn-area-interactive-data.ts
const interactiveAreaData = [
{
"date": "2024-04-01",
"desktop": 222,
"mobile": 150
},
{
"date": "2024-04-02",
"desktop": 97,
"mobile": 180
},
{
"date": "2024-04-03",
"desktop": 167,
"mobile": 120
},
{
"date": "2024-04-04",
"desktop": 242,
"mobile": 260
},
{
"date": "2024-04-05",
"desktop": 373,
"mobile": 290
},
{
"date": "2024-04-06",
"desktop": 301,
"mobile": 340
},
{
"date": "2024-04-07",
"desktop": 245,
"mobile": 180
},
{
"date": "2024-04-08",
"desktop": 409,
"mobile": 320
},
{
"date": "2024-04-09",
"desktop": 59,
"mobile": 110
},
{
"date": "2024-04-10",
"desktop": 261,
"mobile": 190
},
{
"date": "2024-04-11",
"desktop": 327,
"mobile": 350
},
{
"date": "2024-04-12",
"desktop": 292,
"mobile": 210
},
{
"date": "2024-04-13",
"desktop": 342,
"mobile": 380
},
{
"date": "2024-04-14",
"desktop": 137,
"mobile": 220
},
{
"date": "2024-04-15",
"desktop": 120,
"mobile": 170
},
{
"date": "2024-04-16",
"desktop": 138,
"mobile": 190
},
{
"date": "2024-04-17",
"desktop": 446,
"mobile": 360
},
{
"date": "2024-04-18",
"desktop": 364,
"mobile": 410
},
{
"date": "2024-04-19",
"desktop": 243,
"mobile": 180
},
{
"date": "2024-04-20",
"desktop": 89,
"mobile": 150
},
{
"date": "2024-04-21",
"desktop": 137,
"mobile": 200
},
{
"date": "2024-04-22",
"desktop": 224,
"mobile": 170
},
{
"date": "2024-04-23",
"desktop": 138,
"mobile": 230
},
{
"date": "2024-04-24",
"desktop": 387,
"mobile": 290
},
{
"date": "2024-04-25",
"desktop": 215,
"mobile": 250
},
{
"date": "2024-04-26",
"desktop": 75,
"mobile": 130
},
{
"date": "2024-04-27",
"desktop": 383,
"mobile": 420
},
{
"date": "2024-04-28",
"desktop": 122,
"mobile": 180
},
{
"date": "2024-04-29",
"desktop": 315,
"mobile": 240
},
{
"date": "2024-04-30",
"desktop": 454,
"mobile": 380
},
{
"date": "2024-05-01",
"desktop": 165,
"mobile": 220
},
{
"date": "2024-05-02",
"desktop": 293,
"mobile": 310
},
{
"date": "2024-05-03",
"desktop": 247,
"mobile": 190
},
{
"date": "2024-05-04",
"desktop": 385,
"mobile": 420
},
{
"date": "2024-05-05",
"desktop": 481,
"mobile": 390
},
{
"date": "2024-05-06",
"desktop": 498,
"mobile": 520
},
{
"date": "2024-05-07",
"desktop": 388,
"mobile": 300
},
{
"date": "2024-05-08",
"desktop": 149,
"mobile": 210
},
{
"date": "2024-05-09",
"desktop": 227,
"mobile": 180
},
{
"date": "2024-05-10",
"desktop": 293,
"mobile": 330
},
{
"date": "2024-05-11",
"desktop": 335,
"mobile": 270
},
{
"date": "2024-05-12",
"desktop": 197,
"mobile": 240
},
{
"date": "2024-05-13",
"desktop": 197,
"mobile": 160
},
{
"date": "2024-05-14",
"desktop": 448,
"mobile": 490
},
{
"date": "2024-05-15",
"desktop": 473,
"mobile": 380
},
{
"date": "2024-05-16",
"desktop": 338,
"mobile": 400
},
{
"date": "2024-05-17",
"desktop": 499,
"mobile": 420
},
{
"date": "2024-05-18",
"desktop": 315,
"mobile": 350
},
{
"date": "2024-05-19",
"desktop": 235,
"mobile": 180
},
{
"date": "2024-05-20",
"desktop": 177,
"mobile": 230
},
{
"date": "2024-05-21",
"desktop": 82,
"mobile": 140
},
{
"date": "2024-05-22",
"desktop": 81,
"mobile": 120
},
{
"date": "2024-05-23",
"desktop": 252,
"mobile": 290
},
{
"date": "2024-05-24",
"desktop": 294,
"mobile": 220
},
{
"date": "2024-05-25",
"desktop": 201,
"mobile": 250
},
{
"date": "2024-05-26",
"desktop": 213,
"mobile": 170
},
{
"date": "2024-05-27",
"desktop": 420,
"mobile": 460
},
{
"date": "2024-05-28",
"desktop": 233,
"mobile": 190
},
{
"date": "2024-05-29",
"desktop": 78,
"mobile": 130
},
{
"date": "2024-05-30",
"desktop": 340,
"mobile": 280
},
{
"date": "2024-05-31",
"desktop": 178,
"mobile": 230
},
{
"date": "2024-06-01",
"desktop": 178,
"mobile": 200
},
{
"date": "2024-06-02",
"desktop": 470,
"mobile": 410
},
{
"date": "2024-06-03",
"desktop": 103,
"mobile": 160
},
{
"date": "2024-06-04",
"desktop": 439,
"mobile": 380
},
{
"date": "2024-06-05",
"desktop": 88,
"mobile": 140
},
{
"date": "2024-06-06",
"desktop": 294,
"mobile": 250
},
{
"date": "2024-06-07",
"desktop": 323,
"mobile": 370
},
{
"date": "2024-06-08",
"desktop": 385,
"mobile": 320
},
{
"date": "2024-06-09",
"desktop": 438,
"mobile": 480
},
{
"date": "2024-06-10",
"desktop": 155,
"mobile": 200
},
{
"date": "2024-06-11",
"desktop": 92,
"mobile": 150
},
{
"date": "2024-06-12",
"desktop": 492,
"mobile": 420
},
{
"date": "2024-06-13",
"desktop": 81,
"mobile": 130
},
{
"date": "2024-06-14",
"desktop": 426,
"mobile": 380
},
{
"date": "2024-06-15",
"desktop": 307,
"mobile": 350
},
{
"date": "2024-06-16",
"desktop": 371,
"mobile": 310
},
{
"date": "2024-06-17",
"desktop": 475,
"mobile": 520
},
{
"date": "2024-06-18",
"desktop": 107,
"mobile": 170
},
{
"date": "2024-06-19",
"desktop": 341,
"mobile": 290
},
{
"date": "2024-06-20",
"desktop": 408,
"mobile": 450
},
{
"date": "2024-06-21",
"desktop": 169,
"mobile": 210
},
{
"date": "2024-06-22",
"desktop": 317,
"mobile": 270
},
{
"date": "2024-06-23",
"desktop": 480,
"mobile": 530
},
{
"date": "2024-06-24",
"desktop": 132,
"mobile": 180
},
{
"date": "2024-06-25",
"desktop": 141,
"mobile": 190
},
{
"date": "2024-06-26",
"desktop": 434,
"mobile": 380
},
{
"date": "2024-06-27",
"desktop": 448,
"mobile": 490
},
{
"date": "2024-06-28",
"desktop": 149,
"mobile": 200
},
{
"date": "2024-06-29",
"desktop": 103,
"mobile": 160
},
{
"date": "2024-06-30",
"desktop": 446,
"mobile": 400
}
] as const
export default interactiveAreaDatapackages/charts-demo-data/src/shadcn.ts216 lines · dependency
packages/charts-demo-data/src/shadcn.ts
export type ShadcnChartFamily =
'area' | 'bar' | 'line' | 'pie' | 'radar' | 'radial' | 'tooltip'
export interface ShadcnCatalogSpec {
name: string
family: ShadcnChartFamily
variant: string
title: string
description: string
footerNote: string
square: boolean
legend: boolean
}
export interface ShadcnMonthDatum {
month: string
desktop: number
mobile: number
tablet: number
}
export interface ShadcnSeriesDatum {
month: string
series: 'desktop' | 'mobile' | 'tablet' | 'other'
value: number
}
export interface ShadcnBrowserDatum {
browser: string
visitors: number
}
export interface ShadcnRadarDatum {
month: string
desktop: number
mobile?: number
}
export interface ShadcnActivityDatum {
date: string
activity: 'running' | 'swimming'
value: number
}
export const shadcnMonths: readonly ShadcnMonthDatum[] = [
{ month: 'January', desktop: 186, mobile: 80, tablet: 44 },
{ month: 'February', desktop: 305, mobile: 200, tablet: 72 },
{ month: 'March', desktop: 237, mobile: 120, tablet: 58 },
{ month: 'April', desktop: 73, mobile: 190, tablet: 91 },
{ month: 'May', desktop: 209, mobile: 130, tablet: 67 },
{ month: 'June', desktop: 214, mobile: 140, tablet: 82 },
]
export const shadcnSeriesRows: readonly ShadcnSeriesDatum[] =
shadcnMonths.flatMap((row) => [
{ month: row.month, series: 'desktop', value: row.desktop },
{ month: row.month, series: 'mobile', value: row.mobile },
{ month: row.month, series: 'tablet', value: row.tablet },
])
export const shadcnBrowsers: readonly ShadcnBrowserDatum[] = [
{ browser: 'chrome', visitors: 275 },
{ browser: 'safari', visitors: 200 },
{ browser: 'firefox', visitors: 187 },
{ browser: 'edge', visitors: 173 },
{ browser: 'other', visitors: 90 },
]
export const shadcnRadarDefault: readonly ShadcnRadarDatum[] = [
{ month: 'January', desktop: 186 },
{ month: 'February', desktop: 305 },
{ month: 'March', desktop: 237 },
{ month: 'April', desktop: 273 },
{ month: 'May', desktop: 209 },
{ month: 'June', desktop: 214 },
]
export const shadcnRadarFilled: readonly ShadcnRadarDatum[] = [
{ month: 'January', desktop: 186 },
{ month: 'February', desktop: 285 },
{ month: 'March', desktop: 237 },
{ month: 'April', desktop: 203 },
{ month: 'May', desktop: 209 },
{ month: 'June', desktop: 264 },
]
export const shadcnRadarMultiple: readonly ShadcnRadarDatum[] =
shadcnMonths.map(({ month, desktop, mobile }) => ({
month,
desktop,
mobile,
}))
export const shadcnRadarLines: readonly ShadcnRadarDatum[] = [
{ month: 'January', desktop: 186, mobile: 160 },
{ month: 'February', desktop: 185, mobile: 170 },
{ month: 'March', desktop: 207, mobile: 180 },
{ month: 'April', desktop: 173, mobile: 160 },
{ month: 'May', desktop: 160, mobile: 190 },
{ month: 'June', desktop: 174, mobile: 204 },
]
export const shadcnActivities: readonly ShadcnActivityDatum[] = [
{ date: '2024-07-15', activity: 'running', value: 450 },
{ date: '2024-07-15', activity: 'swimming', value: 300 },
{ date: '2024-07-16', activity: 'running', value: 380 },
{ date: '2024-07-16', activity: 'swimming', value: 420 },
{ date: '2024-07-17', activity: 'running', value: 520 },
{ date: '2024-07-17', activity: 'swimming', value: 120 },
{ date: '2024-07-18', activity: 'running', value: 140 },
{ date: '2024-07-18', activity: 'swimming', value: 550 },
{ date: '2024-07-19', activity: 'running', value: 600 },
{ date: '2024-07-19', activity: 'swimming', value: 350 },
{ date: '2024-07-20', activity: 'running', value: 480 },
{ date: '2024-07-20', activity: 'swimming', value: 400 },
]
export const shadcnColors = [
'var(--chart-1, var(--ts-chart-1))',
'var(--chart-2, var(--ts-chart-2))',
'var(--chart-3, var(--ts-chart-3))',
'var(--chart-4, var(--ts-chart-4))',
'var(--chart-5, var(--ts-chart-5))',
] as const
const titleOverrides: Record<string, string> = {
'chart-area-default': 'Area Chart',
'chart-area-stacked-expand': 'Area Chart - Stacked Expanded',
'chart-bar-default': 'Bar Chart',
'chart-bar-label-custom': 'Bar Chart - Custom Label',
'chart-bar-stacked': 'Bar Chart - Stacked + Legend',
'chart-line-default': 'Line Chart',
'chart-line-dots-custom': 'Line Chart - Custom Dots',
'chart-line-label-custom': 'Line Chart - Custom Label',
'chart-pie-simple': 'Pie Chart',
'chart-pie-donut-text': 'Pie Chart - Donut with Text',
'chart-pie-label-custom': 'Pie Chart - Custom Label',
'chart-radar-default': 'Radar Chart',
'chart-radar-grid-circle-fill': 'Radar Chart - Grid Circle Filled',
'chart-radar-grid-circle-no-lines': 'Radar Chart - Grid Circle - No lines',
'chart-radar-grid-fill': 'Radar Chart - Grid Filled',
'chart-radar-label-custom': 'Radar Chart - Custom Label',
'chart-radar-radius': 'Radar Chart - Radius Axis',
'chart-radial-simple': 'Radial Chart',
'chart-tooltip-indicator-line': 'Tooltip - Line Indicator',
'chart-tooltip-indicator-none': 'Tooltip - No Indicator',
'chart-tooltip-label-custom': 'Tooltip - Custom label',
'chart-tooltip-label-none': 'Tooltip - No Label',
}
export function getShadcnCatalogSpec(name: string): ShadcnCatalogSpec {
const parts = name.split('-')
const family = parts[1]
if (!isShadcnFamily(family)) {
throw new TypeError(`Unknown shadcn chart family in ${name}`)
}
const variant = parts.slice(2).join('-')
const title =
titleOverrides[name] ??
`${family === 'tooltip' ? 'Tooltip' : `${titleCase(family)} Chart`} - ${variant.split('-').map(titleCase).join(' ')}`
return {
name,
family,
variant,
title,
description:
(family === 'area' || family === 'bar' || family === 'line') &&
variant === 'interactive'
? 'Showing total visitors for the last 3 months'
: family === 'area' || family === 'radar'
? 'Showing total visitors for the last 6 months'
: family === 'tooltip'
? tooltipDescription(variant)
: 'January - June 2024',
footerNote:
family === 'area' || family === 'radar'
? 'January - June 2024'
: 'Showing total visitors for the last 6 months',
square: family === 'pie' || family === 'radar' || family === 'radial',
legend:
variant.includes('legend') ||
variant === 'icons' ||
(variant === 'stacked' && family === 'bar') ||
(family === 'area' && variant === 'interactive'),
}
}
function tooltipDescription(variant: string): string {
if (variant === 'advanced') return 'Tooltip with custom formatter and total.'
if (variant === 'default') return 'Default tooltip with ChartTooltipContent.'
if (variant === 'formatter') return 'Tooltip with custom formatter.'
if (variant === 'icons') return 'Tooltip with icons.'
if (variant === 'indicator-line') return 'Tooltip with line indicator.'
if (variant === 'indicator-none') return 'Tooltip with no indicator.'
if (variant === 'label-custom')
return 'Tooltip with custom label from chartConfig.'
if (variant === 'label-formatter') return 'Tooltip with label formatter.'
if (variant === 'label-none') return 'Tooltip with no label.'
return 'A chart tooltip.'
}
function titleCase(value: string): string {
return value.charAt(0).toUpperCase() + value.slice(1)
}
function isShadcnFamily(value: string | undefined): value is ShadcnChartFamily {
return (
value === 'area' ||
value === 'bar' ||
value === 'line' ||
value === 'pie' ||
value === 'radar' ||
value === 'radial' ||
value === 'tooltip'
)
}