Add untracked chart and schema files

This commit is contained in:
2026-03-13 00:11:59 -04:00
parent 8a8c4f7177
commit 01199d489a
16 changed files with 1794 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
import {
ComposedChart,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
Scatter
} from 'recharts';
import type { ChartDataPoint } from '@/lib/types';
import { getChartColors } from '../utils/chart-colors';
import { ChartTooltip } from '../primitives/chart-tooltip';
import { CandlestickShape } from '../utils/candlestick-shapes';
import { isOHLCVData } from '../utils/chart-data-transformers';
type CandlestickChartViewProps = {
data: ChartDataPoint[];
formatters?: {
price?: (value: number) => string;
date?: (value: string) => string;
volume?: (value: number) => string;
};
};
export function CandlestickChartView({
data,
formatters
}: CandlestickChartViewProps) {
const colors = getChartColors();
const ohlcvData = data.filter(isOHLCVData);
if (ohlcvData.length === 0) {
return (
<div className="flex h-full items-center justify-center text-sm text-[color:var(--terminal-muted)]">
Candlestick chart requires OHLCV data
</div>
);
}
return (
<ResponsiveContainer width="100%" height="100%">
<ComposedChart data={ohlcvData}>
<CartesianGrid strokeDasharray="2 2" stroke={colors.grid} />
<XAxis
dataKey="date"
stroke={colors.muted}
fontSize={11}
tickFormatter={formatters?.date}
minTickGap={32}
/>
<YAxis
stroke={colors.muted}
fontSize={11}
tickFormatter={formatters?.price}
width={60}
domain={['auto', 'auto']}
/>
<Tooltip
content={(tooltipProps) => <ChartTooltip {...tooltipProps} formatters={formatters} />}
cursor={{ stroke: colors.muted, strokeDasharray: '3 3' }}
/>
<Scatter
dataKey="close"
shape={<CandlestickShape />}
isAnimationActive={false}
/>
</ComposedChart>
</ResponsiveContainer>
);
}

View File

@@ -0,0 +1,112 @@
import {
ComposedChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
Legend
} from 'recharts';
import type { DataSeries } from '@/lib/types';
import { getChartColors } from '../utils/chart-colors';
import { ChartTooltip } from '../primitives/chart-tooltip';
import { mergeDataSeries } from '../utils/chart-data-transformers';
type CombinationChartViewProps = {
dataSeries: DataSeries[];
formatters?: {
price?: (value: number) => string;
date?: (value: string) => string;
volume?: (value: number) => string;
};
};
export function CombinationChartView({
dataSeries,
formatters
}: CombinationChartViewProps) {
const colors = getChartColors();
if (!dataSeries || dataSeries.length === 0) {
return (
<div className="flex h-full items-center justify-center text-sm text-[color:var(--terminal-muted)]">
No data series provided
</div>
);
}
const mergedData = mergeDataSeries(dataSeries);
const visibleSeries = dataSeries.filter(series => series.visible !== false);
const baseValues = Object.fromEntries(visibleSeries.map((series) => {
const initialPoint = mergedData.find((entry) => typeof entry[series.id] === 'number');
const baseValue = typeof initialPoint?.[series.id] === 'number' ? Number(initialPoint[series.id]) : null;
return [series.id, baseValue];
}));
const normalizedData = mergedData.map((point) => {
const normalizedPoint: Record<string, string | number | null> = { date: point.date };
visibleSeries.forEach((series) => {
const baseValue = baseValues[series.id];
const currentValue = typeof point[series.id] === 'number' ? Number(point[series.id]) : null;
normalizedPoint[series.id] = baseValue && currentValue
? ((currentValue / baseValue) - 1) * 100
: null;
});
return normalizedPoint;
});
return (
<ResponsiveContainer width="100%" height="100%">
<ComposedChart data={normalizedData}>
<CartesianGrid strokeDasharray="2 2" stroke={colors.grid} />
<XAxis
dataKey="date"
stroke={colors.muted}
fontSize={11}
tickFormatter={formatters?.date}
minTickGap={32}
/>
<YAxis
stroke={colors.muted}
fontSize={11}
tickFormatter={(value) => `${Number(value).toFixed(0)}%`}
width={60}
domain={['auto', 'auto']}
/>
<Tooltip
content={(tooltipProps) => (
<ChartTooltip
{...tooltipProps}
formatters={{
...formatters,
price: (value: number) => `${value.toFixed(2)}%`
}}
/>
)}
cursor={{ stroke: colors.muted, strokeDasharray: '3 3' }}
/>
<Legend />
{visibleSeries.map(series => {
const seriesColor = series.color || colors.primary;
return (
<Line
key={series.id}
type="monotone"
dataKey={series.id}
name={series.label}
stroke={seriesColor}
strokeWidth={2}
dot={false}
connectNulls={false}
isAnimationActive={normalizedData.length <= 500}
/>
);
})}
</ComposedChart>
</ResponsiveContainer>
);
}

View File

@@ -0,0 +1,100 @@
import {
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer
} from 'recharts';
import type { ChartDataPoint } from '@/lib/types';
import { getChartColors } from '../utils/chart-colors';
import { ChartTooltip } from '../primitives/chart-tooltip';
import { isPriceData, isOHLCVData } from '../utils/chart-data-transformers';
type LineChartViewProps = {
data: ChartDataPoint[];
formatters?: {
price?: (value: number) => string;
date?: (value: string) => string;
volume?: (value: number) => string;
};
};
export function LineChartView({
data,
formatters
}: LineChartViewProps) {
const colors = getChartColors();
const chartData = data.map(point => {
if (isOHLCVData(point)) {
return {
date: point.date,
price: point.close,
open: point.open,
high: point.high,
low: point.low,
close: point.close,
volume: point.volume
};
} else if (isPriceData(point)) {
return {
date: point.date,
price: point.price
};
}
return point;
});
return (
<ResponsiveContainer width="100%" height="100%">
<LineChart data={chartData} margin={{ top: 5, right: 5, left: 5, bottom: 5 }}>
<CartesianGrid
strokeDasharray="3 3"
stroke={colors.grid}
vertical={false}
/>
<XAxis
dataKey="date"
stroke={colors.muted}
fontSize={11}
tickFormatter={formatters?.date}
minTickGap={50}
axisLine={{ stroke: colors.grid }}
tickLine={false}
/>
<YAxis
stroke={colors.muted}
fontSize={11}
tickFormatter={formatters?.price}
width={65}
domain={['auto', 'auto']}
axisLine={false}
tickLine={false}
orientation="right"
/>
<Tooltip
content={(tooltipProps) => <ChartTooltip {...tooltipProps} formatters={formatters} />}
cursor={{ stroke: colors.muted, strokeWidth: 1, strokeDasharray: '5 5' }}
isAnimationActive={false}
/>
<Line
type="linear"
dataKey="price"
stroke={colors.primary}
strokeWidth={2}
dot={false}
activeDot={{
r: 4,
stroke: colors.primary,
strokeWidth: 2,
fill: colors.tooltipBg
}}
isAnimationActive={false}
connectNulls={true}
/>
</LineChart>
</ResponsiveContainer>
);
}