Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cascading and Var everywhere #495

Merged
merged 20 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
## 0.59.0
- [BREAKING] Change entry point module `CSS` (from `CssJs`) on `styled-ppx.melange`, `styled-ppx.native` and `styled-ppx.rescript` (#490) (@davesnx)
- [FEATURE] Add support and interpolation for `zoom`, `will-change` and `user-select` properties (#489) (@davesnx)
- [FEATURE] Support content with interpolation #494 (@davesnx)
- [FEATURE] Support define CSS variables in global and use CSS variables in properties #492 (@davesnx)
- [FEATURE] Support overflow with 2 values
- [FEATURE] Make animation-name abstract (@davesnx)
- [FIX] Add 100 unsupported properties, which will render properly (#489) (@davesnx)
- [FIX] Inline all CSS.Var and CSS.Cascading in properties (#495) (@davesnx)
- [FIX] Color with support for rgba/hsla and others with calc/min and max (#495) (@davesnx)
- [FIX] Warning of kebab-case on emotion client side (#493) (@davesnx)

## 0.58.1
- [BREAKING] FontFamilyName.t is now a string (@davesnx)
Expand Down
46 changes: 46 additions & 0 deletions e2e/melange/src/ui/ui.re
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,49 @@ let _ = [%css
)
|}
];

type calc_value = CSS.Types.Length.calc_value;

let rec modify =
(
fn_float,
fn_int,
fn_calc: calc_value => calc_value,
value: CSS.length,
)
: CSS.length =>
switch (value) {
| `calc(c) => `calc(fn_calc(c))
| `cqmax(f) => `cqmax(fn_float(f))
| `cqh(f) => `cqmax(fn_float(f))
| `cqw(f) => `cqmax(fn_float(f))
| `cqmin(f) => `cqmax(fn_float(f))
| `cqb(f) => `cqmax(fn_float(f))
| `min(m) => `min(Array.map(modify(fn_float, fn_int, fn_calc), m))
| `max(m) => `max(Array.map(modify(fn_float, fn_int, fn_calc), m))
| `cqi(f) => `cqi(fn_float(f))
| `ch(f) => `ch(fn_float(f))
| `cm(f) => `cm(fn_float(f))
| `em(f) => `em(fn_float(f))
| `ex(f) => `ex(fn_float(f))
| `inch(f) => `inch(fn_float(f))
| `mm(f) => `mm(fn_float(f))
| `pc(f) => `pc(fn_float(f))
| `percent(f) => `percent(fn_float(f))
| `pt(f) => `pt(fn_int(f))
| `px(i) => `px(fn_int(i))
| `pxFloat(i) => `pxFloat(fn_float(i))
| `rem(f) => `rem(fn_float(f))
| `vh(f) => `vh(fn_float(f))
| `vmax(f) => `vmax(fn_float(f))
| `vmin(f) => `vmin(fn_float(f))
| `vw(f) => `vw(fn_float(f))
| `zero => `zero
};

let negate_in_calc = (v: calc_value): calc_value =>
`calc(`mult((v, `num(-1.))));
let half_in_calc = (v: calc_value): calc_value => `calc(`div((v, 2.)));

let negative = value => modify((~-.), (~-), negate_in_calc, value);
let half = value => modify(x => x /. 2., x => x / 2, half_in_calc, value);
5 changes: 3 additions & 2 deletions packages/css-property-parser/lib/Parser.re
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module StringMap = Map.Make(String);

let (let.ok) = Result.bind;

/* https://developer.mozilla.org/en-US/docs/Web/CSS/gradient */
let rec _legacy_gradient = [%value.rec
"<-webkit-gradient()> | <-legacy-linear-gradient> | <-legacy-repeating-linear-gradient> | <-legacy-radial-gradient> | <-legacy-repeating-radial-gradient>"
]
Expand Down Expand Up @@ -166,7 +167,7 @@ and color_interpolation_method = [%value.rec
]
and function_color_mix = [%value.rec
// TODO: Use <extended-percentage>
"color-mix(<color-interpolation-method> ',' [ <color> && <percentage>? ]#{2})"
"color-mix(<color-interpolation-method> ',' [ <color> && <percentage>? ] ',' [ <color> && <percentage>? ])"
]
and combinator = [%value.rec "'>' | '+' | '~' | '||'"]
and common_lig_values = [%value.rec
Expand Down Expand Up @@ -829,7 +830,7 @@ and property_animation_fill_mode = [%value.rec
and property_animation_iteration_count = [%value.rec
"[ <single-animation-iteration-count> ]#"
]
and property_animation_name = [%value.rec "[ <keyframes-name> | 'none' ]#"]
and property_animation_name = [%value.rec "[ <keyframes-name> | 'none' | <interpolation> ]#"]
and property_animation_play_state = [%value.rec
"[ <single-animation-play-state> ]#"
]
Expand Down
129 changes: 55 additions & 74 deletions packages/ppx/src/Property_to_runtime.re
Original file line number Diff line number Diff line change
Expand Up @@ -1090,72 +1090,38 @@ and render_function_color_mix = (~loc, value: Types.function_color_mix) => {
| `Increasing => [%expr `increasing]
| `Decreasing => [%expr `decreasing];

switch (value) {
| (x, (), colors: list((Types.color, option(Types.percentage)))) =>
let ((color_one, percentage_one), (color_two, percentage_two)) =
switch (colors) {
| [(c1, p1), (c2, p2)] => ((c1, p1), (c2, p2))
| _ => failwith("unreachable")
};

/*
https://drafts.csswg.org/css-color-5/#color-mix-percent-norm

- If p1 + p2 ≠ 100%, then p1' = p1 / (p1 + p2) and p2' = p2 / (p1 + p2),
where p1' and p2' are the normalization results.
- If p1 = p2 = 0%, the function is invalid
- If both percentage, p1 and p2 are ommited, then p1 = p2 = 50%.
- If p1 is omitted, then p1 = 100% - p2.
- If p2 is omitted, then p2 = 100% - p1 */

let render_percentage = (p1, p2) => {
switch (p1, p2) {
| (Some(p1'), Some(p2')) when p1' == 0. && p2' == 0. =>
raise(Invalid_value("Both percentages can not be 0!"))
| (Some(p1'), Some(p2')) when p1' +. p2' != 100. =>
render_percentage(~loc, p1' /. (p1' +. p2'))
| (Some(p1'), Some(_p2')) => render_percentage(~loc, p1')
| (Some(p1'), None) => render_percentage(~loc, p1')
| (None, Some(p2')) => render_percentage(~loc, 100. -. p2')
| (None, None) => render_percentage(~loc, 50.)
};
};
let (color_interpolation_method, (), color_x, (), color_y) = value;

let render_percentage_one =
render_percentage(percentage_one, percentage_two);
let render_percentage_two =
render_percentage(percentage_two, percentage_one);

let render_color_one = render_color(~loc, color_one);
let render_color_two = render_color(~loc, color_two);

switch (x) {
| ((), `Rectangular_color_space(x)) =>
[%expr
`colorMix((
`in1([%e render_rectangular_color_space(x)]),
([%e render_color_one], [%e render_percentage_one]),
([%e render_color_two], [%e render_percentage_two]),
))]
| ((), `Static(pcs, None)) =>
[%expr
`colorMix((
`in1([%e render_polar_color_space(pcs)]),
([%e render_color_one], [%e render_percentage_one]),
([%e render_color_two], [%e render_percentage_two]),
))]
let color_interpolation_method_expr =
switch (color_interpolation_method) {
| ((), `Rectangular_color_space(x)) => render_rectangular_color_space(x)
| ((), `Static(pcs, None)) => render_polar_color_space(pcs)
| ((), `Static(pcs, Some((size, ())))) =>
[%expr
`colorMix((
`in2((
[%e render_polar_color_space(pcs)],
[%e render_hue_size((), size)],
)),
([%e render_color_one], [%e render_percentage_one]),
([%e render_color_two], [%e render_percentage_two]),
`polar_with_hue((
[%e render_polar_color_space(pcs)],
[%e render_hue_size((), size)],
))]
};

let render_color_with_percentage = (~loc, (color, percentage)) => {
switch (percentage) {
| Some(percentage) =>
[%expr
(
[%e render_color(~loc, color)],
Some([%e render_percentage(~loc, percentage)]),
)]
| None => [%expr ([%e render_color(~loc, color)], None)]
};
};

[%expr
`colorMix((
[%e color_interpolation_method_expr],
[%e render_color_with_percentage(~loc, color_x)],
[%e render_color_with_percentage(~loc, color_y)],
))];
};

let color =
Expand Down Expand Up @@ -3231,18 +3197,30 @@ let render_keyframes_name = (~loc) =>

let render_animation_name = (~loc) =>
fun
| `None => render_string(~loc, "none")
| `Keyframes_name(name) => render_keyframes_name(~loc, name);
| `None => [%expr
CSS.Types.AnimationName.make([%e render_string(~loc, "none")])
]
| `Keyframes_name(name) => {
[%expr
CSS.Types.AnimationName.make([%e render_keyframes_name(~loc, name)])];
}
| `Interpolation(v) => render_variable(~loc, v);

// css-animation-1
let animation_name =
monomorphic(
Property_parser.property_animation_name,
(~loc) => [%expr CSS.animationName],
(~loc) =>
fun
| [one] => render_animation_name(~loc, one)
| _ => raise(Unsupported_feature),
polymorphic(Property_parser.property_animation_name, (~loc) =>
fun
| [one] => {
let value = render_animation_name(~loc, one);
[[%expr CSS.animationName([%e value])]];
}
| many => {
let values =
many
|> List.map(render_animation_name(~loc))
|> Builder.pexp_array(~loc);
[[%expr CSS.animationNames([%e values])]];
}
);

let animation_duration =
Expand Down Expand Up @@ -3371,16 +3349,20 @@ let render_single_animation =
~iterationCount=?[%e
render_option(~loc, render_animation_iteration_count, iterationCount)
],
[%e render_animation_name(~loc, Option.value(name, ~default=`None))],
~name=[%e
render_animation_name(~loc, Option.value(name, ~default=`None))
],
(),
)];
};

let animation =
polymorphic(Property_parser.property_animation, (~loc) =>
polymorphic(Property_parser.property_animation, (~loc) => {
fun
| [one] => [render_single_animation(~loc, one)]
/* TODO: Support multiple animations */
| _ => raise(Unsupported_feature)
);
});

let render_ratio = (~loc, value: Types.ratio) => {
switch (value) {
Expand Down Expand Up @@ -4675,8 +4657,7 @@ let render_content_string = (~loc, str) => {
switch (first, last) {
| ('\'', '\'') => [%expr [%e render_string(~loc, str)]]
| ('"', '"') => [%expr [%e render_string(~loc, str)]]
| _ =>
[%expr [%e render_string(~loc, str)]];
| _ => [%expr [%e render_string(~loc, str)]]
};
};
[%expr `text([%e str])];
Expand Down
10 changes: 7 additions & 3 deletions packages/ppx/test/css-support/animations.t/input.re
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
let foo = [%keyframe {|0% { opacity: 0.0 } 100% { opacity: 1.0 }|}];
let bar = [%keyframe {|0% { opacity: 0.0 } 100% { opacity: 1.0 }|}];

/* CSS Animations Level 1 */
[%css {|animation-name: foo|}];
[%css {|animation-name: foo, bar|}];
[%css {|animation-name: random|}];
[%css {|animation-name: $(foo)|}];
[%css {|animation-name: $(foo), $(bar)|}];
[%css {|animation-duration: 0s|}];
[%css {|animation-duration: 1s|}];
[%css {|animation-duration: 100ms|}];
Expand Down Expand Up @@ -32,4 +36,4 @@
[%css {|animation-fill-mode: both|}];
[%css {|animation: foo 1s 2s infinite linear alternate both|}];
[%css "animation: 4s ease-in 1s infinite reverse both paused"];
[%css "animation: a 300ms linear 400ms infinite reverse forwards running"];
[%css "animation: a 300ms linear 400ms infinite reverse forwards running"];
24 changes: 19 additions & 5 deletions packages/ppx/test/css-support/animations.t/run.t
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,19 @@ If this test fail means that the module is not in sync with the ppx
cookies: [],
}
];
CSS.animationName({js|foo|js});
CSS.unsafe({js|animationName|js}, {js|foo, bar|js});
let foo =
CSS.keyframes([|
(0, [|CSS.opacity(0.)|]),
(100, [|CSS.opacity(1.)|]),
|]);
let bar =
CSS.keyframes([|
(0, [|CSS.opacity(0.)|]),
(100, [|CSS.opacity(1.)|]),
|]);
CSS.animationName(CSS.Types.AnimationName.make({js|random|js}));
CSS.animationName(foo);
CSS.animationNames([|foo, bar|]);
CSS.animationDuration(`s(0));
CSS.animationDuration(`s(1));
CSS.animationDuration(`ms(100));
Expand Down Expand Up @@ -72,7 +83,8 @@ If this test fail means that the module is not in sync with the ppx
~fillMode=?Some(`both),
~playState=?None,
~iterationCount=?Some(`infinite),
{js|foo|js},
~name=CSS.Types.AnimationName.make({js|foo|js}),
(),
);
CSS.animation(
~duration=?Some(`s(4)),
Expand All @@ -82,7 +94,8 @@ If this test fail means that the module is not in sync with the ppx
~fillMode=?Some(`both),
~playState=?Some(`paused),
~iterationCount=?Some(`infinite),
{js|none|js},
~name=CSS.Types.AnimationName.make({js|none|js}),
(),
);
CSS.animation(
~duration=?Some(`ms(300)),
Expand All @@ -92,5 +105,6 @@ If this test fail means that the module is not in sync with the ppx
~fillMode=?Some(`forwards),
~playState=?Some(`running),
~iterationCount=?Some(`infinite),
{js|a|js},
~name=CSS.Types.AnimationName.make({js|a|js}),
(),
);
11 changes: 11 additions & 0 deletions packages/ppx/test/css-support/calc.t/input.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[%css {|width: calc(50% + 4px)|}];
[%css {|width: calc(20px - 10px)|}];
[%css {|width: calc(100vh - calc(2rem + 120px))|}];
[%css {|width: calc(100vh * 2)|}];
[%css {|width: calc(2 * 120px)|}];
[%css
{|width: calc(100vh - calc(2rem + calc(2rem + calc(2rem + calc(2rem + 120px)))))|}
];
[%css
{|width: calc(100vh * calc(2rem - calc(2rem * calc(2rem * calc(2rem / 4)))))|}
];
Loading
Loading