fix: graphe labels visibles + tooltip/cursor adaptatifs au thème clair/sombre
- SlideChart reçoit isDark et adapte tooltip, tick, grid, cursor, légende - margin bottom 40px pour que les labels X-axis ne soient plus coupés - cursor et tooltip blancs en thème clair (fini le carré noir) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -19,30 +19,44 @@ const MermaidDiagram = dynamic(
|
|||||||
|
|
||||||
// ── Constants ──────────────────────────────────────────────────────────────────
|
// ── Constants ──────────────────────────────────────────────────────────────────
|
||||||
const CHART_COLORS = ['#6366f1', '#22d3ee', '#f59e0b', '#ef4444', '#10b981', '#a78bfa', '#fb923c', '#14b8a6']
|
const CHART_COLORS = ['#6366f1', '#22d3ee', '#f59e0b', '#ef4444', '#10b981', '#a78bfa', '#fb923c', '#14b8a6']
|
||||||
const TT_STYLE: CSSProperties = { background: '#1C1C1C', border: '1px solid rgba(255,255,255,0.08)', borderRadius: 8, fontSize: 12 }
|
|
||||||
const TICK = { fill: 'rgba(255,255,255,0.55)', fontSize: 11 }
|
function chartTheme(isDark: boolean) {
|
||||||
const GRID_STROKE = 'rgba(255,255,255,0.07)'
|
return {
|
||||||
|
ttStyle: {
|
||||||
|
background: isDark ? '#1C1C1C' : '#ffffff',
|
||||||
|
border: `1px solid ${isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.1)'}`,
|
||||||
|
borderRadius: 8,
|
||||||
|
fontSize: 12,
|
||||||
|
color: isDark ? '#fff' : '#111',
|
||||||
|
} as CSSProperties,
|
||||||
|
tick: { fill: isDark ? 'rgba(255,255,255,0.55)' : 'rgba(0,0,0,0.55)', fontSize: 11 },
|
||||||
|
grid: isDark ? 'rgba(255,255,255,0.07)' : 'rgba(0,0,0,0.08)',
|
||||||
|
cursor: isDark ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.04)',
|
||||||
|
legColor: isDark ? 'rgba(255,255,255,0.6)' : 'rgba(0,0,0,0.6)',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── Chart ──────────────────────────────────────────────────────────────────────
|
// ── Chart ──────────────────────────────────────────────────────────────────────
|
||||||
function SlideChart({ slide }: { slide: SlideSpec }) {
|
function SlideChart({ slide, isDark }: { slide: SlideSpec; isDark: boolean }) {
|
||||||
const chart = slide.chart
|
const chart = slide.chart
|
||||||
if (!chart?.data?.length) return null
|
if (!chart?.data?.length) return null
|
||||||
|
|
||||||
|
const { ttStyle, tick, grid, cursor, legColor } = chartTheme(isDark)
|
||||||
const colors = (chart as any).colors ?? CHART_COLORS
|
const colors = (chart as any).colors ?? CHART_COLORS
|
||||||
const xKey = chart.xKey ?? 'name'
|
const xKey = chart.xKey ?? 'name'
|
||||||
const yKeys = chart.yKeys ?? Object.keys(chart.data[0]).filter(k => k !== xKey)
|
const yKeys = chart.yKeys ?? Object.keys(chart.data[0]).filter(k => k !== xKey)
|
||||||
const legend = chart.showLegend !== false && yKeys.length > 1
|
const legend = chart.showLegend !== false && yKeys.length > 1
|
||||||
const legSt = { fontSize: 12, color: 'rgba(255,255,255,0.6)' }
|
const legSt = { fontSize: 12, color: legColor }
|
||||||
|
|
||||||
switch (chart.type) {
|
switch (chart.type) {
|
||||||
case 'bar':
|
case 'bar':
|
||||||
return (
|
return (
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
<BarChart data={chart.data} margin={{ top: 8, right: 24, left: 0, bottom: 4 }}>
|
<BarChart data={chart.data} margin={{ top: 8, right: 24, left: 0, bottom: 40 }}>
|
||||||
{chart.showGrid !== false && <CartesianGrid strokeDasharray="3 3" stroke={GRID_STROKE} />}
|
{chart.showGrid !== false && <CartesianGrid strokeDasharray="3 3" stroke={grid} />}
|
||||||
<XAxis dataKey={xKey} tick={TICK} axisLine={false} tickLine={false} />
|
<XAxis dataKey={xKey} tick={tick} axisLine={false} tickLine={false} />
|
||||||
<YAxis tick={TICK} axisLine={false} tickLine={false} width={40} />
|
<YAxis tick={tick} axisLine={false} tickLine={false} width={40} />
|
||||||
<Tooltip contentStyle={TT_STYLE} cursor={{ fill: 'rgba(255,255,255,0.04)' }} />
|
<Tooltip contentStyle={ttStyle} cursor={{ fill: cursor }} />
|
||||||
{legend && <Legend wrapperStyle={legSt} />}
|
{legend && <Legend wrapperStyle={legSt} />}
|
||||||
{yKeys.map((k, i) => <Bar key={k} dataKey={k} fill={colors[i % colors.length]} radius={[4, 4, 0, 0]} maxBarSize={56} />)}
|
{yKeys.map((k, i) => <Bar key={k} dataKey={k} fill={colors[i % colors.length]} radius={[4, 4, 0, 0]} maxBarSize={56} />)}
|
||||||
</BarChart>
|
</BarChart>
|
||||||
@@ -51,11 +65,11 @@ function SlideChart({ slide }: { slide: SlideSpec }) {
|
|||||||
case 'line':
|
case 'line':
|
||||||
return (
|
return (
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
<LineChart data={chart.data} margin={{ top: 8, right: 24, left: 0, bottom: 4 }}>
|
<LineChart data={chart.data} margin={{ top: 8, right: 24, left: 0, bottom: 40 }}>
|
||||||
{chart.showGrid !== false && <CartesianGrid strokeDasharray="3 3" stroke={GRID_STROKE} />}
|
{chart.showGrid !== false && <CartesianGrid strokeDasharray="3 3" stroke={grid} />}
|
||||||
<XAxis dataKey={xKey} tick={TICK} axisLine={false} tickLine={false} />
|
<XAxis dataKey={xKey} tick={tick} axisLine={false} tickLine={false} />
|
||||||
<YAxis tick={TICK} axisLine={false} tickLine={false} width={40} />
|
<YAxis tick={tick} axisLine={false} tickLine={false} width={40} />
|
||||||
<Tooltip contentStyle={TT_STYLE} />
|
<Tooltip contentStyle={ttStyle} />
|
||||||
{legend && <Legend wrapperStyle={legSt} />}
|
{legend && <Legend wrapperStyle={legSt} />}
|
||||||
{yKeys.map((k, i) => <Line key={k} type="monotone" dataKey={k} stroke={colors[i % colors.length]} strokeWidth={2.5} dot={{ r: 4, strokeWidth: 0 }} activeDot={{ r: 6 }} />)}
|
{yKeys.map((k, i) => <Line key={k} type="monotone" dataKey={k} stroke={colors[i % colors.length]} strokeWidth={2.5} dot={{ r: 4, strokeWidth: 0 }} activeDot={{ r: 6 }} />)}
|
||||||
</LineChart>
|
</LineChart>
|
||||||
@@ -64,11 +78,11 @@ function SlideChart({ slide }: { slide: SlideSpec }) {
|
|||||||
case 'area':
|
case 'area':
|
||||||
return (
|
return (
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
<AreaChart data={chart.data} margin={{ top: 8, right: 24, left: 0, bottom: 4 }}>
|
<AreaChart data={chart.data} margin={{ top: 8, right: 24, left: 0, bottom: 40 }}>
|
||||||
{chart.showGrid !== false && <CartesianGrid strokeDasharray="3 3" stroke={GRID_STROKE} />}
|
{chart.showGrid !== false && <CartesianGrid strokeDasharray="3 3" stroke={grid} />}
|
||||||
<XAxis dataKey={xKey} tick={TICK} axisLine={false} tickLine={false} />
|
<XAxis dataKey={xKey} tick={tick} axisLine={false} tickLine={false} />
|
||||||
<YAxis tick={TICK} axisLine={false} tickLine={false} width={40} />
|
<YAxis tick={tick} axisLine={false} tickLine={false} width={40} />
|
||||||
<Tooltip contentStyle={TT_STYLE} />
|
<Tooltip contentStyle={ttStyle} />
|
||||||
{legend && <Legend wrapperStyle={legSt} />}
|
{legend && <Legend wrapperStyle={legSt} />}
|
||||||
{yKeys.map((k, i) => <Area key={k} type="monotone" dataKey={k} stroke={colors[i % colors.length]} fill={`${colors[i % colors.length]}28`} strokeWidth={2.5} />)}
|
{yKeys.map((k, i) => <Area key={k} type="monotone" dataKey={k} stroke={colors[i % colors.length]} fill={`${colors[i % colors.length]}28`} strokeWidth={2.5} />)}
|
||||||
</AreaChart>
|
</AreaChart>
|
||||||
@@ -81,7 +95,7 @@ function SlideChart({ slide }: { slide: SlideSpec }) {
|
|||||||
<Pie data={chart.data} dataKey={yKeys[0] ?? 'value'} nameKey={xKey} cx="50%" cy="50%" outerRadius="70%" innerRadius="35%" paddingAngle={2}>
|
<Pie data={chart.data} dataKey={yKeys[0] ?? 'value'} nameKey={xKey} cx="50%" cy="50%" outerRadius="70%" innerRadius="35%" paddingAngle={2}>
|
||||||
{chart.data.map((_, i) => <Cell key={i} fill={colors[i % colors.length]} stroke="transparent" />)}
|
{chart.data.map((_, i) => <Cell key={i} fill={colors[i % colors.length]} stroke="transparent" />)}
|
||||||
</Pie>
|
</Pie>
|
||||||
<Tooltip contentStyle={TT_STYLE} />
|
<Tooltip contentStyle={ttStyle} />
|
||||||
{chart.showLegend !== false && <Legend wrapperStyle={legSt} />}
|
{chart.showLegend !== false && <Legend wrapperStyle={legSt} />}
|
||||||
</PieChart>
|
</PieChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
@@ -93,7 +107,7 @@ function SlideChart({ slide }: { slide: SlideSpec }) {
|
|||||||
<PolarGrid stroke="rgba(255,255,255,0.1)" />
|
<PolarGrid stroke="rgba(255,255,255,0.1)" />
|
||||||
<PolarAngleAxis dataKey={xKey} tick={{ fill: 'rgba(255,255,255,0.65)', fontSize: 11 }} />
|
<PolarAngleAxis dataKey={xKey} tick={{ fill: 'rgba(255,255,255,0.65)', fontSize: 11 }} />
|
||||||
{yKeys.map((k, i) => <Radar key={k} name={k} dataKey={k} stroke={colors[i % colors.length]} fill={colors[i % colors.length]} fillOpacity={0.15} />)}
|
{yKeys.map((k, i) => <Radar key={k} name={k} dataKey={k} stroke={colors[i % colors.length]} fill={colors[i % colors.length]} fillOpacity={0.15} />)}
|
||||||
<Tooltip contentStyle={TT_STYLE} />
|
<Tooltip contentStyle={ttStyle} />
|
||||||
{legend && <Legend wrapperStyle={legSt} />}
|
{legend && <Legend wrapperStyle={legSt} />}
|
||||||
</RadarChart>
|
</RadarChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
@@ -101,11 +115,11 @@ function SlideChart({ slide }: { slide: SlideSpec }) {
|
|||||||
case 'stacked-bar':
|
case 'stacked-bar':
|
||||||
return (
|
return (
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
<BarChart data={chart.data} margin={{ top: 8, right: 24, left: 0, bottom: 4 }}>
|
<BarChart data={chart.data} margin={{ top: 8, right: 24, left: 0, bottom: 40 }}>
|
||||||
{chart.showGrid !== false && <CartesianGrid strokeDasharray="3 3" stroke={GRID_STROKE} />}
|
{chart.showGrid !== false && <CartesianGrid strokeDasharray="3 3" stroke={grid} />}
|
||||||
<XAxis dataKey={xKey} tick={TICK} axisLine={false} tickLine={false} />
|
<XAxis dataKey={xKey} tick={tick} axisLine={false} tickLine={false} />
|
||||||
<YAxis tick={TICK} axisLine={false} tickLine={false} width={40} />
|
<YAxis tick={tick} axisLine={false} tickLine={false} width={40} />
|
||||||
<Tooltip contentStyle={TT_STYLE} cursor={{ fill: 'rgba(255,255,255,0.04)' }} />
|
<Tooltip contentStyle={ttStyle} cursor={{ fill: cursor }} />
|
||||||
<Legend wrapperStyle={legSt} />
|
<Legend wrapperStyle={legSt} />
|
||||||
{yKeys.map((k, i) => <Bar key={k} dataKey={k} stackId="a" fill={colors[i % colors.length]} radius={i === yKeys.length - 1 ? [4, 4, 0, 0] : [0, 0, 0, 0]} />)}
|
{yKeys.map((k, i) => <Bar key={k} dataKey={k} stackId="a" fill={colors[i % colors.length]} radius={i === yKeys.length - 1 ? [4, 4, 0, 0] : [0, 0, 0, 0]} />)}
|
||||||
</BarChart>
|
</BarChart>
|
||||||
@@ -116,11 +130,11 @@ function SlideChart({ slide }: { slide: SlideSpec }) {
|
|||||||
const barKeys = yKeys.filter(k => !lineKeys.includes(k))
|
const barKeys = yKeys.filter(k => !lineKeys.includes(k))
|
||||||
return (
|
return (
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
<BarChart data={chart.data} margin={{ top: 8, right: 24, left: 0, bottom: 4 }}>
|
<BarChart data={chart.data} margin={{ top: 8, right: 24, left: 0, bottom: 40 }}>
|
||||||
{chart.showGrid !== false && <CartesianGrid strokeDasharray="3 3" stroke={GRID_STROKE} />}
|
{chart.showGrid !== false && <CartesianGrid strokeDasharray="3 3" stroke={grid} />}
|
||||||
<XAxis dataKey={xKey} tick={TICK} axisLine={false} tickLine={false} />
|
<XAxis dataKey={xKey} tick={tick} axisLine={false} tickLine={false} />
|
||||||
<YAxis tick={TICK} axisLine={false} tickLine={false} width={40} />
|
<YAxis tick={tick} axisLine={false} tickLine={false} width={40} />
|
||||||
<Tooltip contentStyle={TT_STYLE} cursor={{ fill: 'rgba(255,255,255,0.04)' }} />
|
<Tooltip contentStyle={ttStyle} cursor={{ fill: cursor }} />
|
||||||
<Legend wrapperStyle={legSt} />
|
<Legend wrapperStyle={legSt} />
|
||||||
{barKeys.map((k, i) => <Bar key={k} dataKey={k} fill={colors[i % colors.length]} radius={[4, 4, 0, 0]} maxBarSize={56} />)}
|
{barKeys.map((k, i) => <Bar key={k} dataKey={k} fill={colors[i % colors.length]} radius={[4, 4, 0, 0]} maxBarSize={56} />)}
|
||||||
{lineKeys.map((k, i) => <Line key={k} type="monotone" dataKey={k} stroke={colors[(barKeys.length + i) % colors.length]} strokeWidth={2.5} dot={{ r: 4, strokeWidth: 0 }} />)}
|
{lineKeys.map((k, i) => <Line key={k} type="monotone" dataKey={k} stroke={colors[(barKeys.length + i) % colors.length]} strokeWidth={2.5} dot={{ r: 4, strokeWidth: 0 }} />)}
|
||||||
@@ -132,7 +146,7 @@ function SlideChart({ slide }: { slide: SlideSpec }) {
|
|||||||
return (
|
return (
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
<FunnelChart>
|
<FunnelChart>
|
||||||
<Tooltip contentStyle={TT_STYLE} />
|
<Tooltip contentStyle={ttStyle} />
|
||||||
<Funnel dataKey={yKeys[0] ?? 'value'} data={chart.data.map((d, i) => ({ ...d, fill: colors[i % colors.length] }))} isAnimationActive>
|
<Funnel dataKey={yKeys[0] ?? 'value'} data={chart.data.map((d, i) => ({ ...d, fill: colors[i % colors.length] }))} isAnimationActive>
|
||||||
<LabelList position="center" fill="#fff" fontSize={12} fontWeight={700} dataKey={xKey} />
|
<LabelList position="center" fill="#fff" fontSize={12} fontWeight={700} dataKey={xKey} />
|
||||||
</Funnel>
|
</Funnel>
|
||||||
@@ -168,11 +182,11 @@ function SlideChart({ slide }: { slide: SlideSpec }) {
|
|||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
<BarChart data={waterfallData} margin={{ top: 8, right: 24, left: 0, bottom: 4 }}>
|
<BarChart data={waterfallData} margin={{ top: 8, right: 24, left: 0, bottom: 40 }}>
|
||||||
{chart.showGrid !== false && <CartesianGrid strokeDasharray="3 3" stroke={GRID_STROKE} />}
|
{chart.showGrid !== false && <CartesianGrid strokeDasharray="3 3" stroke={grid} />}
|
||||||
<XAxis dataKey={xKey} tick={TICK} axisLine={false} tickLine={false} />
|
<XAxis dataKey={xKey} tick={tick} axisLine={false} tickLine={false} />
|
||||||
<YAxis tick={TICK} axisLine={false} tickLine={false} width={40} />
|
<YAxis tick={tick} axisLine={false} tickLine={false} width={40} />
|
||||||
<Tooltip contentStyle={TT_STYLE} />
|
<Tooltip contentStyle={ttStyle} />
|
||||||
<Bar dataKey="__start" stackId="w" fill="transparent" />
|
<Bar dataKey="__start" stackId="w" fill="transparent" />
|
||||||
<Bar dataKey="__delta" stackId="w" radius={[4, 4, 0, 0]} maxBarSize={56}>
|
<Bar dataKey="__delta" stackId="w" radius={[4, 4, 0, 0]} maxBarSize={56}>
|
||||||
{waterfallData.map((d, i) => <Cell key={i} fill={d.__isTotal ? colors[2] : d.__isPositive ? colors[4] : colors[3]} />)}
|
{waterfallData.map((d, i) => <Cell key={i} fill={d.__isTotal ? colors[2] : d.__isPositive ? colors[4] : colors[3]} />)}
|
||||||
@@ -395,7 +409,7 @@ function SlideContent({ slide, index, palette, radius }: { slide: SlideSpec; ind
|
|||||||
{slide.subtitle && <p style={{ margin: '6px 0 0', fontSize: 15, color: muted }}>{slide.subtitle}</p>}
|
{slide.subtitle && <p style={{ margin: '6px 0 0', fontSize: 15, color: muted }}>{slide.subtitle}</p>}
|
||||||
<div style={accentBar} />
|
<div style={accentBar} />
|
||||||
<div style={{ flex: 1 }}>
|
<div style={{ flex: 1 }}>
|
||||||
<SlideChart slide={slide} />
|
<SlideChart slide={slide} isDark={isDark} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user