Skip to content

Commit

Permalink
feat(web): support nested style expression object in style evaluator (#…
Browse files Browse the repository at this point in the history
…423)

Co-authored-by: keiya sasaki <[email protected]>
  • Loading branch information
pyshx and keiya01 authored May 18, 2023
1 parent c3ab031 commit 60b52c7
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 24 deletions.
47 changes: 47 additions & 0 deletions web/src/core/mantle/evaluator/simple/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -485,4 +485,51 @@ describe("Conditional styling", () => {
},
});
});

test("Nested styling at 2nd degree", () => {
expect(
evalLayerAppearances(
{
marker: {
pointColor: "#FF0000",
pointSize: {
expression: {
conditions: [
["Number('0.32423%') < 1", "200"],
["true", "1"],
],
},
},
labelTypography: {
fontSize: 20,
color: {
expression: {
conditions: [
["${blah}==='value'", "color('#ff0000')"],
["true", "color('#ffffff')"],
],
},
},
},
},
},
{
id: "x",
type: "simple",
properties: {
blah: "value",
},
},
),
).toEqual({
marker: {
pointColor: "#FF0000", // blue
pointSize: 200,
labelTypography: {
fontSize: 20,
color: "#FF0000",
},
},
});
});
});
64 changes: 41 additions & 23 deletions web/src/core/mantle/evaluator/simple/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,22 @@ export function evalLayerAppearances(
properties: layer.properties || {},
};
}

return Object.fromEntries(
Object.entries(appearance).map(([k, v]) => [k, recursiveValEval(v, layer, feature)]),
);
}

function recursiveValEval(obj: any, layer: LayerSimple, feature?: Feature): any {
return Object.fromEntries(
Object.entries(appearance).map(([k, v]) => [
k,
Object.fromEntries(
Object.entries(v).map(([k, v]) => {
return [k, evalExpression(v, layer, feature)];
}),
),
]),
Object.entries(obj).map(([k, v]) => {
// if v is an object itself and not a null, recurse deeper
if (hasNonExpressionObject(v)) {
return [k, recursiveValEval(v, layer, feature)];
}
// if v is not an object, apply the evalExpression function
return [k, evalExpression(v, layer, feature)];
}),
);
}

Expand All @@ -76,30 +83,41 @@ export function clearAllExpressionCaches(
feature: Feature | undefined,
) {
const appearances: Partial<LayerAppearanceTypes> = pick(layer, appearanceKeys);

Object.entries(appearances).forEach(([, v]) => {
Object.entries(v).forEach(([, expressionContainer]) => {
if (hasExpression(expressionContainer)) {
const styleExpression = expressionContainer.expression;
if (typeof styleExpression === "object" && styleExpression.conditions) {
styleExpression.conditions.forEach(([expression1, expression2]) => {
clearExpressionCaches(expression1, feature, layer?.defines);
clearExpressionCaches(expression2, feature, layer?.defines);
});
} else if (typeof styleExpression === "boolean" || typeof styleExpression === "number") {
clearExpressionCaches(String(styleExpression), feature, layer?.defines);
} else if (typeof styleExpression === "string") {
clearExpressionCaches(styleExpression, feature, layer?.defines);
}
recursiveClear(v, layer, feature);
});
}

function recursiveClear(obj: any, layer: LayerSimple | undefined, feature: Feature | undefined) {
Object.entries(obj).forEach(([, v]) => {
// if v is an object itself and not a null, recurse deeper
if (hasNonExpressionObject(v)) {
recursiveClear(v, layer, feature);
} else if (hasExpression(v)) {
// if v is not an object, apply the clearExpressionCaches function
const styleExpression = v.expression;
if (typeof styleExpression === "object" && styleExpression.conditions) {
styleExpression.conditions.forEach(([expression1, expression2]) => {
clearExpressionCaches(expression1, feature, layer?.defines);
clearExpressionCaches(expression2, feature, layer?.defines);
});
} else if (typeof styleExpression === "boolean" || typeof styleExpression === "number") {
clearExpressionCaches(String(styleExpression), feature, layer?.defines);
} else if (typeof styleExpression === "string") {
clearExpressionCaches(styleExpression, feature, layer?.defines);
}
});
}
});
}

function hasExpression(e: any): e is ExpressionContainer {
return typeof e === "object" && e && "expression" in e;
}

function hasNonExpressionObject(v: any): boolean {
return typeof v === "object" && v && !("expression" in v);
}

function evalExpression(
expressionContainer: any,
layer: LayerSimple,
Expand Down
2 changes: 1 addition & 1 deletion web/src/core/mantle/types/appearance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
} from "./value";

export type LayerAppearance<T> = {
[K in keyof T]?: T[K] | ExpressionContainer;
[K in keyof T]?: T[K] | LayerAppearance<T[K]> | ExpressionContainer;
};

export type LayerAppearanceTypes = {
Expand Down

0 comments on commit 60b52c7

Please sign in to comment.