Skip to content

Commit

Permalink
feature: render chart blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
getnashty committed Dec 4, 2024
1 parent 7db722b commit d5688e1
Show file tree
Hide file tree
Showing 12 changed files with 786 additions and 77 deletions.
572 changes: 566 additions & 6 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
"react-idle-timer": "5.1.3",
"react-phone-number-input": "3.3.12",
"react-text-mask": "5.5.0",
"recharts": "^2.14.1",
"shaka-player": "4.3.5",
"tailwindcss-interaction-variants": "^5.0.0",
"timeago.js": "4.0.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react';
import { Area, AreaChart, CartesianGrid, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
import { COLORS, ChartBlock, ChartMetaBase, ChartSeries, ChartType } from '..';

export interface AreaChartMeta extends ChartMetaBase {
chartType: ChartType.area;
series: ChartSeries[];
stackedSeries?: boolean;
}

export function AreaChartBlock({ data, meta }: ChartBlock) {
const { series, stackedSeries, xAxis, yAxis } = meta as AreaChartMeta;
return (
<ResponsiveContainer width="100%" height={400}>
<AreaChart data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" label={xAxis ? { value: xAxis, position: 'bottom' } : undefined} />
<YAxis label={yAxis ? { value: yAxis, angle: -90, position: 'left' } : undefined} />
<Tooltip />
<Legend />
{series.map((s, index) => (
<Area
key={s.key}
type="monotone"
dataKey={s.key}
name={s.label}
stackId={stackedSeries ? 'stack' : undefined}
fill={s.color || COLORS[index % COLORS.length]}
stroke={s.color || COLORS[index % COLORS.length]}
/>
))}
</AreaChart>
</ResponsiveContainer>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import { Bar, BarChart, CartesianGrid, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
import { COLORS, ChartBlock, ChartMetaBase, ChartSeries, ChartType } from '..';

export interface BarChartMeta extends ChartMetaBase {
chartType: ChartType.bar;
series: ChartSeries[];
stackedBars?: boolean;
}

export function BarChartBlock({ data, meta }: ChartBlock) {
const { series, stackedBars, xAxis, yAxis } = meta as BarChartMeta;
return (
<ResponsiveContainer width="100%" height={400}>
<BarChart data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" label={xAxis ? { value: xAxis, position: 'bottom' } : undefined} />
<YAxis label={yAxis ? { value: yAxis, angle: -90, position: 'left' } : undefined} />
<Tooltip />
<Legend />
{series.map((s, index) => (
<Bar
key={s.key}
dataKey={s.key}
name={s.label}
stackId={stackedBars ? 'stack' : undefined}
fill={s.color || COLORS[index % COLORS.length]}
/>
))}
</BarChart>
</ResponsiveContainer>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { CartesianGrid, Legend, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
import { ChartMetaBase, ChartType, ChartSeries, ChartBlock, COLORS } from '..';

export interface LineChartMeta extends ChartMetaBase {
chartType: ChartType.line;
series: ChartSeries[];
}

export function LineChartBlock({ data, meta }: ChartBlock) {
const { series, xAxis, yAxis } = meta as LineChartMeta;
return (
<ResponsiveContainer width="100%" height={400}>
<LineChart data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" label={xAxis ? { value: xAxis, position: 'bottom' } : undefined} />
<YAxis label={yAxis ? { value: yAxis, angle: -90, position: 'left' } : undefined} />
<Tooltip />
<Legend />
{series.map((s, index) => (
<Line
key={s.key}
type="monotone"
dataKey={s.key}
name={s.label}
stroke={s.color || COLORS[index % COLORS.length]}
/>
))}
</LineChart>
</ResponsiveContainer>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import { Cell, Legend, Pie, PieChart, ResponsiveContainer, Tooltip } from 'recharts';
import { COLORS, ChartBlock, ChartMetaBase, ChartType } from '..';

export interface PieChartMeta extends ChartMetaBase {
chartType: ChartType.pie;
valueKey: string; // Which field to use for values
nameKey: string; // Which field to use for segment names
colors?: string[]; // Optional array of colors for segments
}

export function PieChartBlock({ data, meta }: ChartBlock) {
const { valueKey, nameKey, colors = COLORS, title } = meta as PieChartMeta;
return (
<ResponsiveContainer width="100%" height={400}>
<PieChart>
<Pie data={data} dataKey={valueKey} nameKey={nameKey} cx="50%" cy="50%" label>
{data.map((_, index) => (
<Cell key={`cell-${index}`} fill={colors[index % colors.length]} />
))}
</Pie>
<Tooltip />
<Legend />
</PieChart>
</ResponsiveContainer>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import { CartesianGrid, Legend, ResponsiveContainer, Scatter, ScatterChart, Tooltip, XAxis, YAxis } from 'recharts';
import { ChartMetaBase, ChartType, ChartBlock, COLORS } from '..';

export interface ScatterChartMeta extends ChartMetaBase {
chartType: ChartType.scatter;
xKey: string; // Field for X coordinates
yKey: string; // Field for Y coordinates
sizeKey?: string; // Optional field for point sizes
nameKey: string; // Field for point labels
colors?: string[]; // Optional array of colors
}

export function ScatterChartBlock({ data, meta }: ChartBlock) {
const { xKey, yKey, sizeKey, nameKey, colors = COLORS, xAxis, yAxis } = meta as ScatterChartMeta;
return (
<ResponsiveContainer width="100%" height={400}>
<ScatterChart>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey={xKey} name={xAxis} />
<YAxis dataKey={yKey} name={yAxis} />
<Tooltip cursor={{ strokeDasharray: '3 3' }} />
<Legend />
<Scatter name={nameKey} data={data} fill={colors[0]} />
</ScatterChart>
</ResponsiveContainer>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import { Cell, ResponsiveContainer, Treemap } from 'recharts';
import { COLORS, ChartBlock, ChartMetaBase, ChartType } from '..';

export interface TreeMapMeta extends ChartMetaBase {
chartType: ChartType.treemap;
valueKey: string; // Field for box sizes
nameKey: string; // Field for box labels
colors?: string[]; // Optional array of colors
}

export function TreeMapBlock({ data, meta }: ChartBlock) {
const { valueKey, nameKey, colors = COLORS } = meta as TreeMapMeta;
return (
<ResponsiveContainer width="100%" height={400}>
<Treemap data={data} dataKey={valueKey} nameKey={nameKey} aspectRatio={4 / 3}>
{data.map((_, index) => (
<Cell key={`cell-${index}`} fill={colors[index % colors.length]} />
))}
</Treemap>
</ResponsiveContainer>
);
}
70 changes: 19 additions & 51 deletions src/modules/AieraChat/Messages/MessageFactory/Block/Chart/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import React from 'react';
import { BaseBlock, BlockType } from '..';
import { match } from 'ts-pattern';
import { BaseBlock, BlockType } from '..';
import { AreaChartBlock, AreaChartMeta } from './Area';
import { BarChartBlock, BarChartMeta } from './Bar';
import { LineChartBlock, LineChartMeta } from './Line';
import { PieChartBlock, PieChartMeta } from './Pie';
import { ScatterChartBlock, ScatterChartMeta } from './Scatter';
import { TreeMapBlock, TreeMapMeta } from './Tree';

export const COLORS = ['#8884d8', '#82ca9d', '#ffc658', '#ff7300', '#00C49F', '#FFBB28', '#FF8042'];

// Chart series configuration
interface ChartSeries {
export interface ChartSeries {
key: string;
label: string;
color?: string;
}

// Updated chart metadata types
type ChartMetaBase = {
export type ChartMetaBase = {
title?: string;
xAxis?: string;
yAxis?: string;
Expand All @@ -25,46 +33,6 @@ export enum ChartType {
treemap = 'treemap',
}

interface AreaChartMeta extends ChartMetaBase {
chartType: ChartType.area;
series: ChartSeries[];
stackedSeries?: boolean;
}

interface LineChartMeta extends ChartMetaBase {
chartType: ChartType.line;
series: ChartSeries[];
}

interface BarChartMeta extends ChartMetaBase {
chartType: ChartType.bar;
series: ChartSeries[];
stackedBars?: boolean;
}

interface PieChartMeta extends ChartMetaBase {
chartType: ChartType.pie;
valueKey: string; // Which field to use for values
nameKey: string; // Which field to use for segment names
colors?: string[]; // Optional array of colors for segments
}

interface ScatterChartMeta extends ChartMetaBase {
chartType: ChartType.scatter;
xKey: string; // Field for X coordinates
yKey: string; // Field for Y coordinates
sizeKey?: string; // Optional field for point sizes
nameKey: string; // Field for point labels
colors?: string[]; // Optional array of colors
}

interface TreeMapMeta extends ChartMetaBase {
chartType: ChartType.treemap;
valueKey: string; // Field for box sizes
nameKey: string; // Field for box labels
colors?: string[]; // Optional array of colors
}

// Common chart data type
type ChartData = {
name: string;
Expand All @@ -81,13 +49,13 @@ export interface ChartBlock extends BaseBlock {
meta: ChartMeta;
}

export function Chart({ meta }: ChartBlock) {
return match(meta.chartType)
.with(ChartType.area, () => <div>Area</div>)
.with(ChartType.bar, () => <div>Bar</div>)
.with(ChartType.line, () => <div>Line</div>)
.with(ChartType.pie, () => <div>Pie</div>)
.with(ChartType.scatter, () => <div>Scatter</div>)
.with(ChartType.treemap, () => <div>Treemap</div>)
export function Chart(props: ChartBlock) {
return match(props.meta.chartType)
.with(ChartType.area, () => <AreaChartBlock {...props} />)
.with(ChartType.bar, () => <BarChartBlock {...props} />)
.with(ChartType.line, () => <LineChartBlock {...props} />)
.with(ChartType.pie, () => <PieChartBlock {...props} />)
.with(ChartType.scatter, () => <ScatterChartBlock {...props} />)
.with(ChartType.treemap, () => <TreeMapBlock {...props} />)
.exhaustive();
}
32 changes: 17 additions & 15 deletions src/modules/AieraChat/Messages/MessageFactory/Block/Image/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,24 @@ export function Image({ url, meta }: ImageBlock) {

if (meta.title) {
return (
<div>
<div className="border border-gray-300 rounded overflow-hidden text-sm my-2">
{content}
{meta.date ? (
<p>
<SearchableText text={meta.title} />, <SearchableText text={meta.date} />
</p>
) : (
<p>
<SearchableText text={meta.title} />
</p>
)}
{meta.source && (
<p>
<SearchableText text={meta.source} />
</p>
)}
<div className="bg-gray-100 px-3 py-2 leading-4">
{meta.date ? (
<p className="font-bold">
<SearchableText text={meta.title} />, <SearchableText text={meta.date} />
</p>
) : (
<p className="font-bold">
<SearchableText text={meta.title} />
</p>
)}
{meta.source && (
<p className="text-gray-600">
<SearchableText text={meta.source} />
</p>
)}
</div>
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,20 @@ export function Table({ headers, rows }: TableBlock) {
useEffect(() => {
if (tableRef.current) {
const { height } = tableRef.current.getBoundingClientRect();
setTableHeight(height + 12);
setTableHeight(height + 16);
}
}, []);

return (
<div
style={{ height: tableHeight }}
className="relative w-full max-w-full overflow-x-auto overflow-y-hidden border bg-slate-400/10 rounded-md border-slate-500/30 px-2 py-1.5"
className="relative w-full max-w-full overflow-x-auto overflow-y-hidden border bg-slate-400/10 rounded-md border-slate-500/30 px-3.5 py-2"
>
<table ref={tableRef} className="absolute text-base antialiased w-full">
{headers.length > 0 && (
<thead>
{headers.map((header, headerIndex) => (
<th key={`header-${headerIndex}`} className="text-nowrap">
<th key={`header-${headerIndex}`} className="text-nowrap pr-4">
<SearchableText text={header} />
</th>
))}
Expand All @@ -54,7 +54,7 @@ export function Table({ headers, rows }: TableBlock) {
<tr key={`row-${rowIndex}`}>
{cells.map((content, cellIndex) => {
return (
<td key={`cell-${cellIndex}`} className="text-nowrap">
<td key={`cell-${cellIndex}`} className="text-nowrap pr-4 font-mono">
{content.map((c, contentIndex) =>
typeof c === 'string' ? (
<SearchableText
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const MessageResponse = ({ data, onReRun }: { onReRun: (k: string) => voi
<Loading>Thinking...</Loading>
) : (
<div className="pb-10 flex flex-col">
<div className="flex flex-col pl-4 pr-2">
<div className="flex flex-col px-4 pb-2">
{data.blocks?.map((block) => (
<Block {...block} />
))}
Expand Down

0 comments on commit d5688e1

Please sign in to comment.