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, buildDynamicStyleTemplate,
buildStyleVariables, buildStyleVariables,
getDefaultCustomBreaks, getDefaultCustomBreaks,
hydrateStoredLayerStyleStates,
resolveLayerStyle, resolveLayerStyle,
requiresStyleApply, requiresStyleApply,
selectStoredLayerStyles,
validateStyleConfig, validateStyleConfig,
} from "./styleEditorUtils"; } from "./styleEditorUtils";
@@ -104,4 +106,25 @@ describe("styleEditorUtils", () => {
}), }),
).toBe(true); ).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 { parseColor } from "@utils/parseColor";
import { import {
createDefaultLayerStyleStates,
GRADIENT_PALETTES, GRADIENT_PALETTES,
RAINBOW_PALETTES, RAINBOW_PALETTES,
SINGLE_COLOR_PALETTES, SINGLE_COLOR_PALETTES,
} from "./styleEditorPresets"; } from "./styleEditorPresets";
import type { import type {
DefaultLayerStyleId,
LayerStyleState,
ResolvedLayerStyle, ResolvedLayerStyle,
StyleConfig, StyleConfig,
StyleValidationResult, StyleValidationResult,
@@ -232,6 +235,44 @@ export const validateStyleConfig = (styleConfig: StyleConfig): StyleValidationRe
return { valid: errors.length === 0, errors }; 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 = ( export const requiresStyleApply = (
applied: StyleConfig | undefined, applied: StyleConfig | undefined,
draft: StyleConfig, draft: StyleConfig,
@@ -20,11 +20,13 @@ import {
buildStyleVariables, buildStyleVariables,
getDefaultCustomBreaks, getDefaultCustomBreaks,
getDefaultCustomColors, getDefaultCustomColors,
hydrateStoredLayerStyleStates,
normalizeCustomBreaks, normalizeCustomBreaks,
requiresStyleApply, requiresStyleApply,
resolveDimensions, resolveDimensions,
resolveLayerStyle, resolveLayerStyle,
resolveStyleColors, resolveStyleColors,
selectStoredLayerStyles,
validateStyleConfig, validateStyleConfig,
} from "./styleEditorUtils"; } from "./styleEditorUtils";
import type { import type {
@@ -885,23 +887,10 @@ export const useStyleEditor = ({
try { try {
const raw = window.localStorage.getItem(storageKey); const raw = window.localStorage.getItem(storageKey);
if (raw) { if (raw) {
const document = JSON.parse(raw) as { restored = hydrateStoredLayerStyleStates(
version?: number; JSON.parse(raw),
layers?: Partial<Record<DefaultLayerStyleId, StyleConfig>>; STYLE_STORAGE_VERSION,
}; );
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,
};
});
}
} }
} catch (error) { } catch (error) {
console.warn("Restore layer styles failed", error); console.warn("Restore layer styles failed", error);
@@ -910,19 +899,14 @@ export const useStyleEditor = ({
setLayerStyleStates(restored); setLayerStyleStates(restored);
const restoredSelection = restored.find((state) => state.layerId === "junctions"); const restoredSelection = restored.find((state) => state.layerId === "junctions");
if (restoredSelection) setStyleConfig(cloneStyleConfig(restoredSelection.styleConfig)); if (restoredSelection) setStyleConfig(cloneStyleConfig(restoredSelection.styleConfig));
restored syncAuxiliaryLayers("junctions", null);
.filter((state) => state.isActive && isDefaultLayerId(state.layerId)) syncAuxiliaryLayers("pipes", null);
.forEach((state) => syncAuxiliaryLayers(state.layerId as DefaultLayerStyleId, state.styleConfig));
setPersistenceReady(true); setPersistenceReady(true);
}, [setLayerStyleStates, storageKey, syncAuxiliaryLayers]); }, [setLayerStyleStates, storageKey, syncAuxiliaryLayers]);
useEffect(() => { useEffect(() => {
if (!persistenceReady) return; if (!persistenceReady) return;
const layers = Object.fromEntries( const layers = selectStoredLayerStyles(layerStyleStates);
layerStyleStates
.filter((state) => state.isActive && isDefaultLayerId(state.layerId))
.map((state) => [state.layerId, state.styleConfig]),
);
try { try {
window.localStorage.setItem( window.localStorage.setItem(
storageKey, storageKey,