From 941577154c43456099034a7cdf28969e0a3add16 Mon Sep 17 00:00:00 2001 From: pyshx Date: Mon, 15 May 2023 19:17:37 +0530 Subject: [PATCH 1/3] feat(web): support nested style expression object in style evaluator --- .../mantle/evaluator/simple/index.test.ts | 47 ++++++++++++++ web/src/core/mantle/evaluator/simple/index.ts | 65 ++++++++++++------- web/src/core/mantle/types/appearance.ts | 2 +- 3 files changed, 90 insertions(+), 24 deletions(-) diff --git a/web/src/core/mantle/evaluator/simple/index.test.ts b/web/src/core/mantle/evaluator/simple/index.test.ts index 101cb4a9a..e1ee8094b 100644 --- a/web/src/core/mantle/evaluator/simple/index.test.ts +++ b/web/src/core/mantle/evaluator/simple/index.test.ts @@ -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", + }, + }, + }); + }); }); diff --git a/web/src/core/mantle/evaluator/simple/index.ts b/web/src/core/mantle/evaluator/simple/index.ts index 5552129f2..551c1ccbd 100644 --- a/web/src/core/mantle/evaluator/simple/index.ts +++ b/web/src/core/mantle/evaluator/simple/index.ts @@ -59,15 +59,23 @@ 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 + // console.log("K: ", k, "V: ", v); + 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)]; + }), ); } @@ -76,23 +84,30 @@ export function clearAllExpressionCaches( feature: Feature | undefined, ) { const appearances: Partial = 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); } - }); + } }); } @@ -100,6 +115,10 @@ 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 !== null && !("expression" in v); +} + function evalExpression( expressionContainer: any, layer: LayerSimple, diff --git a/web/src/core/mantle/types/appearance.ts b/web/src/core/mantle/types/appearance.ts index 1dfafb951..1824860aa 100644 --- a/web/src/core/mantle/types/appearance.ts +++ b/web/src/core/mantle/types/appearance.ts @@ -10,7 +10,7 @@ import type { } from "./value"; export type LayerAppearance = { - [K in keyof T]?: T[K] | ExpressionContainer; + [K in keyof T]?: T[K] | LayerAppearance | ExpressionContainer; }; export type LayerAppearanceTypes = { From 0a299b6437479dc9c890ed7ec8e1e7752edc6cf7 Mon Sep 17 00:00:00 2001 From: Piyush Chauhan <42397980+pyshx@users.noreply.github.com> Date: Wed, 17 May 2023 12:10:04 +0530 Subject: [PATCH 2/3] chore: refactor checking Co-authored-by: keiya sasaki --- web/src/core/mantle/evaluator/simple/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/core/mantle/evaluator/simple/index.ts b/web/src/core/mantle/evaluator/simple/index.ts index 551c1ccbd..d1be68cb2 100644 --- a/web/src/core/mantle/evaluator/simple/index.ts +++ b/web/src/core/mantle/evaluator/simple/index.ts @@ -116,7 +116,7 @@ function hasExpression(e: any): e is ExpressionContainer { } function hasNonExpressionObject(v: any): boolean { - return typeof v === "object" && v !== null && !("expression" in v); + return typeof v === "object" && v && !("expression" in v); } function evalExpression( From 1eca06ef8fe3e75335b99d458b55d8d457a3ea64 Mon Sep 17 00:00:00 2001 From: Piyush Chauhan <42397980+pyshx@users.noreply.github.com> Date: Wed, 17 May 2023 12:42:21 +0530 Subject: [PATCH 3/3] chore: remove log statement --- web/src/core/mantle/evaluator/simple/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/web/src/core/mantle/evaluator/simple/index.ts b/web/src/core/mantle/evaluator/simple/index.ts index d1be68cb2..657256fd1 100644 --- a/web/src/core/mantle/evaluator/simple/index.ts +++ b/web/src/core/mantle/evaluator/simple/index.ts @@ -69,7 +69,6 @@ function recursiveValEval(obj: any, layer: LayerSimple, feature?: Feature): any return Object.fromEntries( Object.entries(obj).map(([k, v]) => { // if v is an object itself and not a null, recurse deeper - // console.log("K: ", k, "V: ", v); if (hasNonExpressionObject(v)) { return [k, recursiveValEval(v, layer, feature)]; }