fix(map): restore saved styles as inactive drafts
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user