Skip to content

Commit

Permalink
feat: Spark Charts (#36)
Browse files Browse the repository at this point in the history
* First draft

* update spark chart stops

* update packages
  • Loading branch information
severinlandolt authored Jun 17, 2024
1 parent 3031e65 commit a3de51b
Show file tree
Hide file tree
Showing 8 changed files with 2,776 additions and 1,369 deletions.
36 changes: 18 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@
"@react-aria/datepicker": "^3.10.1",
"@react-stately/datepicker": "^3.9.4",
"@remixicon/react": "^4.2.0",
"@storybook/addon-a11y": "^8.1.5",
"@storybook/theming": "^8.1.5",
"@storybook/addon-a11y": "^8.1.10",
"@storybook/theming": "^8.1.10",
"clsx": "^2.1.1",
"date-fns": "^3.6.0",
"prettier-plugin-tailwindcss": "^0.6.1",
"prettier-plugin-tailwindcss": "^0.6.5",
"react": "^18.3.1",
"react-day-picker": "^8.10.1",
"react-dom": "^18.3.1",
Expand All @@ -49,30 +49,30 @@
"devDependencies": {
"@chromatic-com/storybook": "^1.5.0",
"@playwright/test": "^1.44.1",
"@storybook/addon-essentials": "^8.1.5",
"@storybook/addon-interactions": "^8.1.5",
"@storybook/addon-links": "^8.1.5",
"@storybook/blocks": "^8.1.5",
"@storybook/react": "^8.1.5",
"@storybook/react-vite": "^8.1.5",
"@storybook/test": "^8.1.5",
"@types/node": "^20.13.0",
"@storybook/addon-essentials": "^8.1.10",
"@storybook/addon-interactions": "^8.1.10",
"@storybook/addon-links": "^8.1.10",
"@storybook/blocks": "^8.1.10",
"@storybook/react": "^8.1.10",
"@storybook/react-vite": "^8.1.10",
"@storybook/test": "^8.1.10",
"@types/node": "^20.14.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^7.11.0",
"@typescript-eslint/parser": "^7.11.0",
"@vitejs/plugin-react": "^4.3.0",
"@typescript-eslint/eslint-plugin": "^7.13.1",
"@typescript-eslint/parser": "^7.13.1",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.19",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.7",
"eslint-plugin-storybook": "^0.8.0",
"postcss": "^8.4.38",
"prettier": "3.2.5",
"storybook": "^8.1.5",
"tailwindcss": "^3.4.3",
"prettier": "3.3.2",
"storybook": "^8.1.10",
"tailwindcss": "^3.4.4",
"typescript": "^5.4.5",
"vite": "^5.2.12",
"vite": "^5.3.1",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^1.6.0"
}
Expand Down
3,004 changes: 1,653 additions & 1,351 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

311 changes: 311 additions & 0 deletions src/components/SparkChart/SparkChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
// Tremor Raw Spark Chart [v0.0.0]

"use client"

import React from "react"
import {
Area,
Bar,
Line,
AreaChart as RechartsAreaChart,
BarChart as RechartsBarChart,
LineChart as RechartsLineChart,
ResponsiveContainer,
XAxis,
YAxis,
} from "recharts"
import { AxisDomain } from "recharts/types/util/types"

import {
AvailableChartColors,
AvailableChartColorsKeys,
constructCategoryColors,
getColorClassName,
} from "../../utils/chartColors"
import { cx } from "../../utils/cx"
import { getYAxisDomain } from "../../utils/getYAxisDomain"

//#region SparkAreaChart

interface SparkAreaChartProps extends React.HTMLAttributes<HTMLDivElement> {
data: Record<string, any>[]
categories: string[]
index: string
colors?: AvailableChartColorsKeys[]
autoMinValue?: boolean
minValue?: number
maxValue?: number
connectNulls?: boolean
type?: "default" | "stacked" | "percent"
fill?: "gradient" | "solid" | "none"
}
const SparkAreaChart = React.forwardRef<HTMLDivElement, SparkAreaChartProps>(
(props, forwardedRef) => {
const {
data = [],
categories = [],
index,
colors = AvailableChartColors,
autoMinValue = false,
minValue,
maxValue,
connectNulls = false,
type = "default",
className,
fill = "gradient",
...other
} = props

const categoryColors = constructCategoryColors(categories, colors)
const yAxisDomain = getYAxisDomain(autoMinValue, minValue, maxValue)
const stacked = type === "stacked" || type === "percent"
const areaId = React.useId()

const getFillContent = (fillType: SparkAreaChartProps["fill"]) => {
switch (fillType) {
case "none":
return <stop stopColor="currentColor" stopOpacity={0} />
case "gradient":
return (
<>
<stop offset="5%" stopColor="currentColor" stopOpacity={0.4} />
<stop offset="95%" stopColor="currentColor" stopOpacity={0} />
</>
)
case "solid":
return <stop stopColor="currentColor" stopOpacity={0.3} />
default:
return <stop stopColor="currentColor" stopOpacity={0.3} />
}
}

return (
<div ref={forwardedRef} className={cx("h-12 w-28", className)} {...other}>
<ResponsiveContainer>
<RechartsAreaChart
data={data}
margin={{
bottom: 1,
left: 1,
right: 1,
top: 1,
}}
stackOffset={type === "percent" ? "expand" : undefined}
>
<XAxis hide dataKey={index} />
<YAxis hide={true} domain={yAxisDomain as AxisDomain} />

{categories.map((category) => {
const categoryId = `${areaId}-${category}`
return (
<React.Fragment key={category}>
<defs>
<linearGradient
key={category}
className={cx(
getColorClassName(
categoryColors.get(
category,
) as AvailableChartColorsKeys,
"text",
),
)}
id={categoryId}
x1="0"
y1="0"
x2="0"
y2="1"
>
{getFillContent(fill)}
</linearGradient>
</defs>
<Area
className={cx(
getColorClassName(
categoryColors.get(
category,
) as AvailableChartColorsKeys,
"stroke",
),
)}
dot={false}
strokeOpacity={1}
name={category}
type="linear"
dataKey={category}
stroke=""
strokeWidth={2}
strokeLinejoin="round"
strokeLinecap="round"
isAnimationActive={false}
connectNulls={connectNulls}
stackId={stacked ? "stack" : undefined}
fill={`url(#${categoryId})`}
/>
</React.Fragment>
)
})}
</RechartsAreaChart>
</ResponsiveContainer>
</div>
)
},
)

SparkAreaChart.displayName = "SparkAreaChart"

//#region SparkLineChart

interface SparkLineChartProps extends React.HTMLAttributes<HTMLDivElement> {
data: Record<string, any>[]
categories: string[]
index: string
colors?: AvailableChartColorsKeys[]
autoMinValue?: boolean
minValue?: number
maxValue?: number
connectNulls?: boolean
}

const SparkLineChart = React.forwardRef<HTMLDivElement, SparkLineChartProps>(
(props, forwardedRef) => {
const {
data = [],
categories = [],
index,
colors = AvailableChartColors,
autoMinValue = false,
minValue,
maxValue,
connectNulls = false,
className,
...other
} = props

const categoryColors = constructCategoryColors(categories, colors)
const yAxisDomain = getYAxisDomain(autoMinValue, minValue, maxValue)

return (
<div ref={forwardedRef} className={cx("h-12 w-28", className)} {...other}>
<ResponsiveContainer>
<RechartsLineChart
data={data}
margin={{
bottom: 1,
left: 1,
right: 1,
top: 1,
}}
>
<XAxis hide dataKey={index} />
<YAxis hide={true} domain={yAxisDomain as AxisDomain} />
{categories.map((category) => (
<Line
className={cx(
getColorClassName(
categoryColors.get(category) as AvailableChartColorsKeys,
"stroke",
),
)}
dot={false}
strokeOpacity={1}
key={category}
name={category}
type="linear"
dataKey={category}
stroke=""
strokeWidth={2}
strokeLinejoin="round"
strokeLinecap="round"
isAnimationActive={false}
connectNulls={connectNulls}
/>
))}
</RechartsLineChart>
</ResponsiveContainer>
</div>
)
},
)

SparkLineChart.displayName = "SparkLineChart"

//#region SparkBarChart

interface BarChartProps extends React.HTMLAttributes<HTMLDivElement> {
data: Record<string, any>[]
index: string
categories: string[]
colors?: AvailableChartColorsKeys[]
autoMinValue?: boolean
minValue?: number
maxValue?: number
barCategoryGap?: string | number
type?: "default" | "stacked" | "percent"
}

const SparkBarChart = React.forwardRef<HTMLDivElement, BarChartProps>(
(props, forwardedRef) => {
const {
data = [],
categories = [],
index,
colors = AvailableChartColors,
autoMinValue = false,
minValue,
maxValue,
barCategoryGap,
type = "default",
className,
...other
} = props

const categoryColors = constructCategoryColors(categories, colors)

const yAxisDomain = getYAxisDomain(autoMinValue, minValue, maxValue)
const stacked = type === "stacked" || type === "percent"

return (
<div ref={forwardedRef} className={cx("h-12 w-28", className)} {...other}>
<ResponsiveContainer>
<RechartsBarChart
data={data}
margin={{
bottom: 1,
left: 1,
right: 1,
top: 1,
}}
stackOffset={type === "percent" ? "expand" : undefined}
barCategoryGap={barCategoryGap}
>
<XAxis hide dataKey={index} />
<YAxis hide={true} domain={yAxisDomain as AxisDomain} />

{categories.map((category) => (
<Bar
className={cx(
getColorClassName(
categoryColors.get(category) as AvailableChartColorsKeys,
"fill",
),
)}
key={category}
name={category}
type="linear"
dataKey={category}
stackId={stacked ? "stack" : undefined}
isAnimationActive={false}
fill=""
/>
))}
</RechartsBarChart>
</ResponsiveContainer>
</div>
)
},
)

SparkBarChart.displayName = "SparkBarChart"

export { SparkAreaChart, SparkLineChart, SparkBarChart }
7 changes: 7 additions & 0 deletions src/components/SparkChart/changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Tremor Raw SparkChart Changelog

## 0.1.0

### Changes

Feat: Add legendPosition prop
Loading

0 comments on commit a3de51b

Please sign in to comment.