diff --git a/e2e/melange/src/ui/ui.re b/e2e/melange/src/ui/ui.re index 4ccc7f59..48e32955 100644 --- a/e2e/melange/src/ui/ui.re +++ b/e2e/melange/src/ui/ui.re @@ -22,6 +22,7 @@ module Cositas = [%styled.div display: flex; flex-direction: column; gap: $(lola); + height: calc(1px * (2 + 3 / 3)); |} ]; diff --git a/packages/css-property-parser/lib/Parser.re b/packages/css-property-parser/lib/Parser.re index c3b101d1..ad75066c 100644 --- a/packages/css-property-parser/lib/Parser.re +++ b/packages/css-property-parser/lib/Parser.re @@ -131,7 +131,7 @@ and calc_product = [%value.rec and calc_sum = [%value.rec " [ [ '+' | '-' ] ]*"] /* and calc_value = [%value.rec " | | | "] */ and calc_value = [%value.rec - " | | | | " + " | | | | | '(' ')'" ] and cf_final_image = [%value.rec " | "] and cf_mixing_image = [%value.rec "[ ]? && "] diff --git a/packages/css-property-parser/lib/Standard.re b/packages/css-property-parser/lib/Standard.re index 029c9992..1e8214ff 100644 --- a/packages/css-property-parser/lib/Standard.re +++ b/packages/css-property-parser/lib/Standard.re @@ -4,7 +4,15 @@ open Rule.Pattern; let keyword = string => expect(IDENT(string)); let comma = expect(COMMA); -let delim = string => expect(DELIM(string)); +let delim = + fun + | "(" => expect(LEFT_PAREN) + | ")" => expect(RIGHT_PAREN) + | "[" => expect(LEFT_BRACKET) + | "]" => expect(RIGHT_BRACKET) + | ":" => expect(COLON) + | ";" => expect(SEMI_COLON) + | s => expect(DELIM(s)); let function_call = (name, rule) => { let.bind_match () = token( diff --git a/packages/ppx/src/Property_to_runtime.re b/packages/ppx/src/Property_to_runtime.re index 5e996775..c7faa616 100644 --- a/packages/ppx/src/Property_to_runtime.re +++ b/packages/ppx/src/Property_to_runtime.re @@ -320,28 +320,33 @@ let render_length = (~loc) => | `Zero => [%expr `zero]; let rec render_function_calc = (~loc, calc_sum) => { - render_calc_sum(~loc, calc_sum); + [%expr `calc([%e render_calc_sum(~loc, calc_sum)])]; } -and render_calc_sum = (~loc, calc_sum) => { - switch (calc_sum) { - | (product, []) => render_product(~loc, product) - | (product, list_of_sums) => - /* This isn't a great design of the types, but we need to know the operation - which is in the first position of the array, we ensure that there's one value - since we are on this branch of the switch */ - let op = pick_operation(List.hd(list_of_sums)); - switch (op) { - | `Dash () => - let first = render_product(~loc, product); - let second = render_list_of_sums(~loc, list_of_sums); - [%expr `calc(`sub(([%e first], [%e second])))]; - | `Cross () => - let first = render_product(~loc, product); - let second = render_list_of_sums(~loc, list_of_sums); - [%expr `calc(`add(([%e first], [%e second])))]; +and render_calc_sum = (~loc, (product, sums): Types.calc_sum) => { + let rec go = (left, rest) => { + switch (rest) { + | [] => left + | [x, ...xs] => + switch (x) { + | (`Cross (), calc_product) => + go( + [%expr + `add(([%e left], [%e render_calc_product(~loc, calc_product)])) + ], + xs, + ) + | (`Dash (), calc_product) => + go( + [%expr + `sub(([%e left], [%e render_calc_product(~loc, calc_product)])) + ], + xs, + ) + } }; }; + go(render_calc_product(~loc, product), sums); } and render_function_min_or_max = (~loc, calc_sums: list(Types.calc_sum)) => { switch (calc_sums) { @@ -357,33 +362,26 @@ and render_function_min = (~loc, calc_sums) => { and render_function_max = (~loc, calc_sums) => { [%expr `max([%e render_function_min_or_max(~loc, calc_sums)])]; } -and pick_operation = ((op, _)) => op -and render_list_of_products = (~loc, list_of_products) => { - switch (list_of_products) { - | [one] => render_product_op(~loc, one) - | list => render_list_of_products(~loc, list) - }; -} -and render_list_of_sums = (~loc, list_of_sums) => { - switch (list_of_sums) { - | [(_, one)] => render_product(~loc, one) - | list => render_list_of_sums(~loc, list) - }; -} -and render_product = (~loc, product) => { - switch (product) { - | (calc_value, []) => render_calc_value(~loc, calc_value) - | (calc_value, list_of_products) => - let first = render_calc_value(~loc, calc_value); - let second = render_list_of_products(~loc, list_of_products); - [%expr `calc(`mult(([%e first], [%e second])))]; - }; -} -and render_product_op = (~loc, op) => { - switch (op) { - | `Static_0((), calc_value) => render_calc_value(~loc, calc_value) - | `Static_1((), float) => [%expr `num([%e render_float(~loc, float)])] +and render_calc_product = (~loc, (value, products): Types.calc_product) => { + let rec go = (left, rest) => { + switch (rest) { + | [] => left + | [x, ...xs] => + switch (x) { + | `Static_0(_, value) => + go( + [%expr `mult(([%e left], [%e render_calc_value(~loc, value)]))], + xs, + ) + | `Static_1(_, float_value) => + go( + [%expr `div(([%e left], [%e render_float(~loc, float_value)]))], + xs, + ) + } + }; }; + go(render_calc_value(~loc, value), products); } and render_angle = (~loc) => fun @@ -434,6 +432,7 @@ and render_calc_value = (~loc, calc_value) => { | `Extended_percentage(p) => render_extended_percentage(~loc, p) | `Extended_angle(a) => render_extended_angle(~loc, a) | `Extended_time(t) => render_extended_time(~loc, t) + | `Static(_, calc_sum, _) => render_calc_sum(~loc, calc_sum) }; } and render_extended_length = (~loc) => diff --git a/packages/ppx/src/Property_to_string.re b/packages/ppx/src/Property_to_string.re index a018d816..0e902e45 100644 --- a/packages/ppx/src/Property_to_string.re +++ b/packages/ppx/src/Property_to_string.re @@ -133,21 +133,31 @@ let render_length = | `Vw(n) => render_number(n, "vw") | `Zero => render_string("0"); -let rec render_function_calc = calc_sum => { - render_calc_sum(calc_sum); +let rec render_function_calc = + (calc_sum: Css_property_parser.Parser.Types.calc_sum) => { + [%expr "calc(" ++ [%e render_calc_sum(calc_sum)] ++ ")"]; +} +and render_calc_sum = ((product, sums)) => { + let rec go = (left, rest) => { + switch (rest) { + | [] => left + | [x, ...xs] => + switch (x) { + | (`Cross (), calc_product) => + go( + [%expr [%e left] ++ " + " ++ [%e render_product(calc_product)]], + xs, + ) + | (`Dash (), calc_product) => + go( + [%expr [%e left] ++ " - " ++ [%e render_product(calc_product)]], + xs, + ) + } + }; + }; + go(render_product(product), sums); } -and render_calc_sum = calc_sum => - switch (calc_sum) { - | (product, []) => render_product(product) - | (product, list_of_sums) => - /* This isn't a great design of the types, but we need to know the operation - which is in the first position of the array, we ensure that there's one value - since we are on this branch of the switch */ - let op = pick_operation(List.hd(list_of_sums)); - let first = render_product(product); - let second = render_list_of_sums(list_of_sums); - [%expr "calc(" ++ [%e first] ++ [%e op] ++ [%e second] ++ ")"]; - } and render_function_min_or_max = calc_sums => { switch (calc_sums) { | [] => raise(Invalid_value("expected at least one argument")) @@ -162,39 +172,23 @@ and render_function_min = calc_sums => { and render_function_max = calc_sums => { render_function_min_or_max(calc_sums); } -and render_sum_op = op => { - switch (op) { - | `Dash () => [%expr " - "] - | `Cross () => [%expr " + "] - }; -} -and pick_operation = ((op, _)) => render_sum_op(op) -and render_list_of_products = list_of_products => { - switch (list_of_products) { - | [one] => render_product_op(one) - | list => render_list_of_products(list) - }; -} -and render_list_of_sums = list_of_sums => { - switch (list_of_sums) { - | [(_, one)] => render_product(one) - | list => render_list_of_sums(list) - }; -} -and render_product = product => { - switch (product) { - | (calc_value, []) => render_calc_value(calc_value) - | (calc_value, list_of_products) => - let first = render_calc_value(calc_value); - let second = render_list_of_products(list_of_products); - [%expr "calc(" ++ [%e first] ++ " * " ++ [%e second] ++ ")"]; - }; -} -and render_product_op = op => { - switch (op) { - | `Static_0((), calc_value) => render_calc_value(calc_value) - | `Static_1((), float) => render_number(float, "") +and render_product = ((value, products)) => { + let rec go = (left, rest) => { + switch (rest) { + | [] => left + | [x, ...xs] => + switch (x) { + | `Static_0(_, value) => + go([%expr [%e left] ++ " * " ++ [%e render_calc_value(value)]], xs) + | `Static_1(_, float_value) => + go( + [%expr [%e left] ++ " / " ++ [%e render_number(float_value, "")]], + xs, + ) + } + }; }; + go(render_calc_value(value), products); } and render_calc_value = calc_value => { switch (calc_value) { @@ -203,6 +197,7 @@ and render_calc_value = calc_value => { | `Extended_percentage(p) => render_extended_percentage(p) | `Extended_angle(a) => render_extended_angle(a) | `Extended_time(t) => render_extended_time(t) + | `Static(_, calc_sum, _) => render_calc_sum(calc_sum) }; } and render_time_as_int = diff --git a/packages/ppx/test/css-support/calc.t/run.t b/packages/ppx/test/css-support/calc.t/run.t index 75d73040..b126f10c 100644 --- a/packages/ppx/test/css-support/calc.t/run.t +++ b/packages/ppx/test/css-support/calc.t/run.t @@ -53,9 +53,7 @@ If this test fail means that the module is not in sync with the ppx `calc( `mult(( `rem(2.), - `calc( - `mult((`rem(2.), `calc(`mult((`rem(2.), `num(4.)))))), - ), + `calc(`mult((`rem(2.), `calc(`div((`rem(2.), 4.)))))), )), ), )), diff --git a/packages/ppx/test/css-support/color-module.t/run.t b/packages/ppx/test/css-support/color-module.t/run.t index 91dfa012..8d0b55e1 100644 --- a/packages/ppx/test/css-support/color-module.t/run.t +++ b/packages/ppx/test/css-support/color-module.t/run.t @@ -350,5 +350,5 @@ If this test fail means that the module is not in sync with the ppx {js|color-mix(in lab, teal 65%, olive)|js}, ); - CSS.color(`rgba((0, 0, 0, `num(1.)))); + CSS.color(`rgba((0, 0, 0, `calc(`num(1.))))); CSS.color(`rgba((0, 0, 0, `calc(`sub((`num(10.), `num(1.))))))); diff --git a/packages/ppx/test/native/Static_test.re b/packages/ppx/test/native/Static_test.re index 3b419c03..6672f5a8 100644 --- a/packages/ppx/test/native/Static_test.re +++ b/packages/ppx/test/native/Static_test.re @@ -668,11 +668,10 @@ let properties_static_css_tests = [ [%expr [%css "flex: none"]], [%expr CSS.flex1(`none)], ), - /* Since calc(x) -> x */ ( [%css "width: calc(100px)"], [%expr [%css "width: calc(100px)"]], - [%expr CSS.width(`pxFloat(100.))], + [%expr CSS.width(`calc(`pxFloat(100.)))], ), ( [%css "width: calc(100% + 32px)"], @@ -1166,12 +1165,12 @@ let properties_static_css_tests = [ ( [%css "transition-duration: max(3s, calc(1ms))"], [%expr [%css "transition-duration: max(3s, calc(1ms))"]], - [%expr CSS.transitionDuration(`max([|`s(3), `ms(1)|]))], + [%expr CSS.transitionDuration(`max([|`s(3), `calc(`ms(1))|]))], ), ( [%css "transition-duration: max(+3s, calc(-0ms))"], [%expr [%css "transition-duration: max(+3s, calc(-0ms))"]], - [%expr CSS.transitionDuration(`max([|`s(3), `ms(0)|]))], + [%expr CSS.transitionDuration(`max([|`s(3), `calc(`ms(0))|]))], ), ( [%css "animation: 3s"], diff --git a/packages/runtime/native/shared/Css_types.ml b/packages/runtime/native/shared/Css_types.ml index e21e71e6..c4e3aa40 100644 --- a/packages/runtime/native/shared/Css_types.ml +++ b/packages/runtime/native/shared/Css_types.ml @@ -70,6 +70,7 @@ module Calc = struct {js|calc(|js} ^ fn a ^ {js| * |js} ^ fn b ^ {js|)|js} | `calc (`div (a, b)) -> {js|calc(|js} ^ fn a ^ {js| / |js} ^ Kloth.Float.to_string b ^ {js|)|js} + | `calc (`num a) -> {js|calc(|js} ^ Kloth.Float.to_string a ^ {js|)|js} | `num n -> Kloth.Float.to_string n | `min xs -> {js|min(|js} ^ max_or_min_values fn xs ^ {js|)|js} | `max xs -> {js|max(|js} ^ max_or_min_values fn xs ^ {js|)|js} @@ -1314,6 +1315,7 @@ module Color = struct | `sub of 'a * 'a | `mult of 'a * 'a | `div of 'a * float + | `num of float ] | `min of 'a array | `max of 'a array