Add untracked chart and schema files
This commit is contained in:
71
components/charts/renderers/candlestick-chart-view.tsx
Normal file
71
components/charts/renderers/candlestick-chart-view.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
112
components/charts/renderers/combination-chart-view.tsx
Normal file
112
components/charts/renderers/combination-chart-view.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
100
components/charts/renderers/line-chart-view.tsx
Normal file
100
components/charts/renderers/line-chart-view.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user