fix: normalize style with full defaults when chart enters Redux
All style sub-objects (title, legend, xAxis, yAxis, dataLabel, background, border, animation) are guaranteed to exist in Redux. This fixes: - Style config panels showing blank (returned null for missing fields) - Style changes not applying (updateStyle now deep-merges sub-objects) - Color palette changes not persisting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1ee9d57222
commit
e12f169319
|
|
@ -4,6 +4,33 @@ import { type FieldBinding } from '@/domain/entities/FieldBinding';
|
||||||
import { type StyleConfig } from '@/domain/entities/StyleConfig';
|
import { type StyleConfig } from '@/domain/entities/StyleConfig';
|
||||||
import { type SortConfig } from '@/domain/valueObjects/SortOrder';
|
import { type SortConfig } from '@/domain/valueObjects/SortOrder';
|
||||||
|
|
||||||
|
const DEFAULT_STYLE: StyleConfig = {
|
||||||
|
title: { text: '', visible: false, fontSize: 16, fontWeight: 'bold', color: '#333', align: 'left' },
|
||||||
|
colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4'],
|
||||||
|
legend: { visible: true, position: 'top', orient: 'horizontal', fontSize: 12, color: '#666' },
|
||||||
|
xAxis: { visible: true, labelVisible: true, labelFontSize: 12, labelColor: '#666', labelRotation: 0, titleVisible: false, titleText: '', gridVisible: true, gridColor: '#eee' },
|
||||||
|
yAxis: { visible: true, labelVisible: true, labelFontSize: 12, labelColor: '#666', labelRotation: 0, titleVisible: false, titleText: '', gridVisible: true, gridColor: '#eee' },
|
||||||
|
dataLabel: { visible: false, fontSize: 12, color: '#333', position: 'outside', format: 'value' },
|
||||||
|
background: { color: 'transparent', opacity: 1, borderRadius: 4 },
|
||||||
|
border: { visible: false, color: '#ddd', width: 1, style: 'solid' },
|
||||||
|
animation: { enabled: true, duration: 500, easing: 'ease-out' },
|
||||||
|
};
|
||||||
|
|
||||||
|
function ensureStyle(style: any): StyleConfig {
|
||||||
|
if (!style || typeof style !== 'object') return { ...DEFAULT_STYLE };
|
||||||
|
return {
|
||||||
|
title: { ...DEFAULT_STYLE.title, ...(style.title ?? {}) },
|
||||||
|
colors: (Array.isArray(style.colors) && style.colors.length > 0) ? style.colors : DEFAULT_STYLE.colors,
|
||||||
|
legend: { ...DEFAULT_STYLE.legend, ...(style.legend ?? {}) },
|
||||||
|
xAxis: { ...DEFAULT_STYLE.xAxis, ...(style.xAxis ?? {}) },
|
||||||
|
yAxis: { ...DEFAULT_STYLE.yAxis, ...(style.yAxis ?? {}) },
|
||||||
|
dataLabel: { ...DEFAULT_STYLE.dataLabel, ...(style.dataLabel ?? {}) },
|
||||||
|
background: { ...DEFAULT_STYLE.background, ...(style.background ?? {}) },
|
||||||
|
border: { ...DEFAULT_STYLE.border, ...(style.border ?? {}) },
|
||||||
|
animation: { ...DEFAULT_STYLE.animation, ...(style.animation ?? {}) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface ChartState {
|
interface ChartState {
|
||||||
charts: ChartInstance[];
|
charts: ChartInstance[];
|
||||||
activeChartId: string | null;
|
activeChartId: string | null;
|
||||||
|
|
@ -19,7 +46,8 @@ const chartSlice = createSlice({
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
addChart(state, action: PayloadAction<ChartInstance>) {
|
addChart(state, action: PayloadAction<ChartInstance>) {
|
||||||
state.charts.push(action.payload);
|
const chart = { ...action.payload, style: ensureStyle(action.payload.style) };
|
||||||
|
state.charts.push(chart);
|
||||||
},
|
},
|
||||||
removeChart(state, action: PayloadAction<string>) {
|
removeChart(state, action: PayloadAction<string>) {
|
||||||
state.charts = state.charts.filter((c) => c.id !== action.payload);
|
state.charts = state.charts.filter((c) => c.id !== action.payload);
|
||||||
|
|
@ -46,7 +74,21 @@ const chartSlice = createSlice({
|
||||||
) {
|
) {
|
||||||
const chart = state.charts.find((c) => c.id === action.payload.chartId);
|
const chart = state.charts.find((c) => c.id === action.payload.chartId);
|
||||||
if (chart) {
|
if (chart) {
|
||||||
chart.style = { ...chart.style, ...action.payload.style };
|
// Deep merge each sub-object
|
||||||
|
const incoming = action.payload.style;
|
||||||
|
const current = chart.style as StyleConfig;
|
||||||
|
chart.style = {
|
||||||
|
...current,
|
||||||
|
...incoming,
|
||||||
|
title: incoming.title ? { ...current.title, ...incoming.title } : current.title,
|
||||||
|
legend: incoming.legend ? { ...current.legend, ...incoming.legend } : current.legend,
|
||||||
|
xAxis: incoming.xAxis ? { ...current.xAxis, ...incoming.xAxis } : current.xAxis,
|
||||||
|
yAxis: incoming.yAxis ? { ...current.yAxis, ...incoming.yAxis } : current.yAxis,
|
||||||
|
dataLabel: incoming.dataLabel ? { ...current.dataLabel, ...incoming.dataLabel } : current.dataLabel,
|
||||||
|
background: incoming.background ? { ...current.background, ...incoming.background } : current.background,
|
||||||
|
border: incoming.border ? { ...current.border, ...incoming.border } : current.border,
|
||||||
|
animation: incoming.animation ? { ...current.animation, ...incoming.animation } : current.animation,
|
||||||
|
};
|
||||||
chart.updatedAt = new Date().toISOString();
|
chart.updatedAt = new Date().toISOString();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue