fix(map): restore saved styles as inactive drafts

This commit is contained in:
2026-07-17 15:28:44 +08:00
parent 589cf45aa7
commit 08152ff978
3 changed files with 73 additions and 25 deletions
@@ -3,8 +3,10 @@ import {
buildDynamicStyleTemplate,
buildStyleVariables,
getDefaultCustomBreaks,
hydrateStoredLayerStyleStates,
resolveLayerStyle,
requiresStyleApply,
selectStoredLayerStyles,
validateStyleConfig,
} from "./styleEditorUtils";
@@ -104,4 +106,25 @@ describe("styleEditorUtils", () => {
}),
).toBe(true);
});
it("hydrates workspace settings as inactive drafts", () => {
const storedPipeStyle = {
...createDefaultLayerStyleState("pipes").styleConfig,
property: "flow",
showLabels: false,
};
const restored = hydrateStoredLayerStyleStates(
{
version: 2,
layers: { pipes: storedPipeStyle },
},
2,
);
expect(restored.find((state) => state.layerId === "pipes")?.styleConfig).toEqual(
storedPipeStyle,
);
expect(restored.every((state) => !state.isActive)).toBe(true);
expect(selectStoredLayerStyles(restored).pipes).toEqual(storedPipeStyle);
});
});
@@ -4,11 +4,14 @@ import { calculateClassification } from "@utils/breaksClassification";
import { parseColor } from "@utils/parseColor";
import {
createDefaultLayerStyleStates,
GRADIENT_PALETTES,
RAINBOW_PALETTES,
SINGLE_COLOR_PALETTES,
} from "./styleEditorPresets";
import type {
DefaultLayerStyleId,
LayerStyleState,
ResolvedLayerStyle,
StyleConfig,
StyleValidationResult,
@@ -232,6 +235,44 @@ export const validateStyleConfig = (styleConfig: StyleConfig): StyleValidationRe
return { valid: errors.length === 0, errors };
};
export const hydrateStoredLayerStyleStates = (
document: unknown,
expectedVersion: number,
): LayerStyleState[] => {
const defaults = createDefaultLayerStyleStates();
if (!document || typeof document !== "object") return defaults;
const storedDocument = document as {
version?: number;
layers?: Partial<Record<DefaultLayerStyleId, StyleConfig>>;
};
if (storedDocument.version !== expectedVersion || !storedDocument.layers) {
return defaults;
}
return defaults.map((state) => {
const stored = storedDocument.layers?.[state.layerId as DefaultLayerStyleId];
if (!stored || !validateStyleConfig(stored).valid) return state;
return {
...state,
styleConfig: {
...stored,
customBreaks: [...(stored.customBreaks || [])],
customColors: [...(stored.customColors || [])],
},
legendConfig: { ...state.legendConfig, property: stored.property },
isActive: false,
};
});
};
export const selectStoredLayerStyles = (states: LayerStyleState[]) =>
Object.fromEntries(
states
.filter((state) => state.layerId === "junctions" || state.layerId === "pipes")
.map((state) => [state.layerId, state.styleConfig]),
) as Partial<Record<DefaultLayerStyleId, StyleConfig>>;
export const requiresStyleApply = (
applied: StyleConfig | undefined,
draft: StyleConfig,
@@ -20,11 +20,13 @@ import {
buildStyleVariables,
getDefaultCustomBreaks,
getDefaultCustomColors,
hydrateStoredLayerStyleStates,
normalizeCustomBreaks,
requiresStyleApply,
resolveDimensions,
resolveLayerStyle,
resolveStyleColors,
selectStoredLayerStyles,
validateStyleConfig,
} from "./styleEditorUtils";
import type {
@@ -885,23 +887,10 @@ export const useStyleEditor = ({
try {
const raw = window.localStorage.getItem(storageKey);
if (raw) {
const document = JSON.parse(raw) as {
version?: number;
layers?: Partial<Record<DefaultLayerStyleId, StyleConfig>>;
};
if (document.version === STYLE_STORAGE_VERSION && document.layers) {
restored = createDefaultLayerStyleStates().map((state) => {
if (!isDefaultLayerId(state.layerId)) return state;
const stored = document.layers?.[state.layerId];
if (!stored || !validateStyleConfig(stored).valid) return state;
return {
...state,
styleConfig: cloneStyleConfig(stored),
legendConfig: { ...state.legendConfig, property: stored.property },
isActive: true,
};
});
}
restored = hydrateStoredLayerStyleStates(
JSON.parse(raw),
STYLE_STORAGE_VERSION,
);
}
} catch (error) {
console.warn("Restore layer styles failed", error);
@@ -910,19 +899,14 @@ export const useStyleEditor = ({
setLayerStyleStates(restored);
const restoredSelection = restored.find((state) => state.layerId === "junctions");
if (restoredSelection) setStyleConfig(cloneStyleConfig(restoredSelection.styleConfig));
restored
.filter((state) => state.isActive && isDefaultLayerId(state.layerId))
.forEach((state) => syncAuxiliaryLayers(state.layerId as DefaultLayerStyleId, state.styleConfig));
syncAuxiliaryLayers("junctions", null);
syncAuxiliaryLayers("pipes", null);
setPersistenceReady(true);
}, [setLayerStyleStates, storageKey, syncAuxiliaryLayers]);
useEffect(() => {
if (!persistenceReady) return;
const layers = Object.fromEntries(
layerStyleStates
.filter((state) => state.isActive && isDefaultLayerId(state.layerId))
.map((state) => [state.layerId, state.styleConfig]),
);
const layers = selectStoredLayerStyles(layerStyleStates);
try {
window.localStorage.setItem(
storageKey,