diff --git a/fastn-js/js/dom.js b/fastn-js/js/dom.js index 5fdfdb76c8..4817d48ca2 100644 --- a/fastn-js/js/dom.js +++ b/fastn-js/js/dom.js @@ -659,13 +659,11 @@ fastn_dom.Length = { } } -fastn_dom.MaskImage = { - Src: (value) => { +fastn_dom.Mask = { + Image: (value) => { return [1, value]; }, - LinearGradient: (value) => { - return [2, value]; - }, + // todo: mode, composite, etc. } fastn_dom.Event = { @@ -1066,16 +1064,11 @@ class Node2 { this.attachCss("text-shadow", darkShadowCss, true, `body.dark .${lightClass}`); } } - attachLinearGradientCss(value, property = "background-image") { - if (fastn_utils.isNull(value)) { - this.attachCss(property, value); - return; - } + getLinearGradientString(value) { var lightGradientString = ""; var darkGradientString = ""; let colorsList = value.get("colors").get().getList(); - let direction = fastn_utils.getStaticValue(value.get("direction")); colorsList.map(function (element) { // LinearGradient RecordInstance let lg_color = element.item; @@ -1112,11 +1105,23 @@ class Node2 { lightGradientString = lightGradientString.trim().slice(0, -1); darkGradientString = darkGradientString.trim().slice(0, -1); + return [lightGradientString, darkGradientString]; + } + attachLinearGradientCss(value) { + if (fastn_utils.isNull(value)) { + this.attachCss("background-image", value); + return; + } + + let direction = fastn_utils.getStaticValue(value.get("direction")); + + const [lightGradientString, darkGradientString] = this.getLinearGradientString(value); + if (lightGradientString === darkGradientString) { - this.attachCss(property, `linear-gradient(${direction}, ${lightGradientString})`, false); + this.attachCss("background-image", `linear-gradient(${direction}, ${lightGradientString})`, false); } else { - let lightClass = this.attachCss(property, `linear-gradient(${direction}, ${lightGradientString})`,true); - this.attachCss(property, `linear-gradient(${direction}, ${darkGradientString})`, true, `body.dark .${lightClass}`); + let lightClass = this.attachCss("background-image", `linear-gradient(${direction}, ${lightGradientString})`,true); + this.attachCss("background-image", `linear-gradient(${direction}, ${darkGradientString})`, true, `body.dark .${lightClass}`); } } attachBackgroundImageCss(value) { @@ -1172,20 +1177,50 @@ class Node2 { } } attachMaskImageCss(value, vendorPrefix) { - const propWithPrefix = vendorPrefix ? `${vendorPrefix}-mask-image` : "mask-image"; + const propertyWithPrefix = vendorPrefix ? `${vendorPrefix}-mask-image` : "mask-image"; + if (fastn_utils.isNull(value)) { - this.attachCss(propWithPrefix, value); + this.attachCss(propertyWithPrefix, value); return; } - let lightValue = fastn_utils.getStaticValue(value.get("light")); - let darkValue = fastn_utils.getStaticValue(value.get("dark")); + let src = fastn_utils.getStaticValue(value.get("src")); + let linearGradient = fastn_utils.getStaticValue(value.get("linear-gradient")); - if (lightValue === darkValue) { - this.attachCss(propWithPrefix, `url(${lightValue})`, false); + const maskLightImageValues = []; + const maskDarkImageValues = []; + + if(!fastn_utils.isNull(src)) { + let lightValue = fastn_utils.getStaticValue(src.get("light")); + let darkValue = fastn_utils.getStaticValue(src.get("dark")); + + maskLightImageValues.push(`url(${lightValue})`); + + if (lightValue !== darkValue) { + maskDarkImageValues.push(`url(${darkValue})`); + } + } + + if(!fastn_utils.isNull(linearGradient)) { + let direction = fastn_utils.getStaticValue(value.get("direction")); + + const [lightGradientString, darkGradientString] = this.getLinearGradientString(value); + + maskLightImageValues.push(`linear-gradient(${direction}, ${lightGradientString})`); + + if (lightGradientString !== darkGradientString) { + maskDarkImageValues.push(`linear-gradient(${direction}, ${darkGradientString})`); + } + } + + const maskLightImageString = maskLightImageValues.join(", "); + const maskDarkImageString = maskDarkImageValues.join(", "); + + if(!maskDarkImageString || maskLightImageString === maskDarkImageString) { + this.attachCss(propertyWithPrefix, maskLightImageString, false); } else { - let lightClass = this.attachCss(propWithPrefix, `url(${lightValue})`, true); - this.attachCss(propWithPrefix, `url(${darkValue})`, true, `body.dark .${lightClass}`); + let lightClass = this.attachCss(propertyWithPrefix, maskLightImageString, true); + this.attachCss(propertyWithPrefix, maskDarkImageString, true, `body.dark .${lightClass}`); } } attachExternalCss(css) { @@ -1560,7 +1595,7 @@ class Node2 { this.attachBackdropMultiFilter(staticValue[1]); break; } - } else if (kind === fastn_dom.PropertyKind.MaskImage) { + } else if (kind === fastn_dom.PropertyKind.Mask) { if (fastn_utils.isNull(staticValue)) { this.attachCss("mask-image", staticValue); return; @@ -1569,14 +1604,10 @@ class Node2 { const [backgroundType, value] = staticValue; switch (backgroundType) { - case fastn_dom.MaskImage.Src()[0]: + case fastn_dom.Mask.Image()[0]: this.attachMaskImageCss(value); this.attachMaskImageCss(value, "-webkit"); break; - case fastn_dom.MaskImage.LinearGradient()[0]: - this.attachMaskLinearGradientCss(value, "mask-image"); - this.attachMaskLinearGradientCss(value, "-webkit-mask-image"); - break; } } else if (kind === fastn_dom.PropertyKind.Classes) { fastn_utils.removeNonFastnClasses(this); diff --git a/fastn-js/src/property.rs b/fastn-js/src/property.rs index 328d26a02a..4018fb2613 100644 --- a/fastn-js/src/property.rs +++ b/fastn-js/src/property.rs @@ -427,7 +427,7 @@ pub enum PropertyKind { Favicon, Selectable, BackdropFilter, - MaskImage, + Mask, } impl PropertyKind { @@ -567,7 +567,7 @@ impl PropertyKind { PropertyKind::Favicon => "fastn_dom.PropertyKind.Favicon", PropertyKind::Selectable => "fastn_dom.PropertyKind.Selectable", PropertyKind::BackdropFilter => "fastn_dom.PropertyKind.BackdropFilter", - PropertyKind::MaskImage => "fastn_dom.PropertyKind.MaskImage", + PropertyKind::Mask => "fastn_dom.PropertyKind.Mask", } } } diff --git a/ftd/src/interpreter/constants.rs b/ftd/src/interpreter/constants.rs index ba1259e3ac..0cc32769b6 100644 --- a/ftd/src/interpreter/constants.rs +++ b/ftd/src/interpreter/constants.rs @@ -331,6 +331,9 @@ pub const FTD_BACKDROP_FILTER_SEPIA: &str = "ftd#backdrop-filter.sepia"; pub const FTD_BACKDROP_FILTER_SATURATE: &str = "ftd#backdrop-filter.saturate"; pub const FTD_BACKDROP_FILTER_MULTI: &str = "ftd#backdrop-filter.multi"; -pub const FTD_MASK_IMAGE: &str = "ftd#mask-image"; -pub const FTD_MASK_IMAGE_SRC: &str = "ftd#mask-image.src"; -pub const FTD_MASK_IMAGE_LINEAR_GRADIENT: &str = "ftd#mask-image.linear-gradient"; +pub const FTD_MASK_IMAGE_DATA: &str = "ftd#mask-image"; +pub const FTD_MASK_IMAGE_DATA_SRC: &str = "ftd#mask-image.src"; +pub const FTD_MASK_IMAGE_DATA_LINEAR_GRADIENT: &str = "ftd#mask-image.linear-gradient"; + +pub const FTD_MASK: &str = "ftd#mask"; +pub const FTD_MASK_IMAGE: &str = "ftd#mask.image"; diff --git a/ftd/src/interpreter/things/default.rs b/ftd/src/interpreter/things/default.rs index 2554ee7a7f..d42a37a431 100644 --- a/ftd/src/interpreter/things/default.rs +++ b/ftd/src/interpreter/things/default.rs @@ -9706,26 +9706,47 @@ pub fn default_bag() -> indexmap::IndexMap { }) ), ( - ftd::interpreter::FTD_MASK_IMAGE.to_string(), + ftd::interpreter::FTD_MASK_IMAGE_DATA.to_string(), + ftd::interpreter::Thing::Record(ftd::interpreter::Record { + name: ftd::interpreter::FTD_MASK_IMAGE_DATA.to_string(), + fields: std::iter::IntoIterator::into_iter([ + ftd::interpreter::Field { + name: "src".to_string(), + kind: ftd::interpreter::Kind::record(ftd::interpreter::FTD_IMAGE_SRC) + .into_kind_data().caption().into_optional(), + mutable: false, + value: None, + access_modifier: Default::default(), + line_number: 0, + }, + ftd::interpreter::Field { + name: "linear-gradient".to_string(), + kind: ftd::interpreter::Kind::record(ftd::interpreter::FTD_LINEAR_GRADIENT) + .into_kind_data() + .into_optional(), + mutable: false, + value: None, + access_modifier: Default::default(), + line_number: 0, + }, + ]).collect(), + line_number: 0, + }), + ), + ( + ftd::interpreter::FTD_MASK.to_string(), ftd::interpreter::Thing::OrType(ftd::interpreter::OrType { - name: ftd::interpreter::FTD_MASK_IMAGE.to_string(), + name: ftd::interpreter::FTD_MASK.to_string(), variants: vec![ ftd::interpreter::OrTypeVariant::Regular(ftd::interpreter::Field::new( - ftd::interpreter::FTD_MASK_IMAGE_SRC, - ftd::interpreter::Kind::record(ftd::interpreter::FTD_IMAGE_SRC) - .into_kind_data(), - false, - None, - 0, - )), - ftd::interpreter::OrTypeVariant::Regular(ftd::interpreter::Field::new( - ftd::interpreter::FTD_MASK_IMAGE_LINEAR_GRADIENT, - ftd::interpreter::Kind::record(ftd::interpreter::FTD_LINEAR_GRADIENT) + ftd::interpreter::FTD_MASK_IMAGE, + ftd::interpreter::Kind::record(ftd::interpreter::FTD_MASK_IMAGE_DATA) .into_kind_data(), false, None, 0, )), + // todo: mode, position, composite, etc. ], line_number: 0, }), @@ -10925,8 +10946,8 @@ fn common_arguments() -> Vec { .into_kind_data(), ), ftd::interpreter::Argument::default( - "mask-image", - ftd::interpreter::Kind::or_type(ftd::interpreter::FTD_MASK_IMAGE) + "mask", + ftd::interpreter::Kind::or_type(ftd::interpreter::FTD_MASK) .into_optional() .into_kind_data(), ), diff --git a/ftd/src/js/element.rs b/ftd/src/js/element.rs index 302f63a70a..cdccca909e 100644 --- a/ftd/src/js/element.rs +++ b/ftd/src/js/element.rs @@ -2225,7 +2225,7 @@ pub struct Common { pub js: Option, pub events: Vec, pub selectable: Option, - pub mask_image: Option, + pub mask: Option, } impl Common { @@ -2441,7 +2441,7 @@ impl Common { whitespace: ftd::js::value::get_optional_js_value("white-space", properties, arguments), shadow: ftd::js::value::get_optional_js_value("shadow", properties, arguments), selectable: ftd::js::value::get_optional_js_value("selectable", properties, arguments), - mask_image: ftd::js::value::get_optional_js_value("mask-image", properties, arguments), + mask: ftd::js::value::get_optional_js_value("mask", properties, arguments), events: events.to_vec(), } } @@ -3039,14 +3039,9 @@ impl Common { ), )); } - if let Some(ref mask_image) = self.mask_image { + if let Some(ref mask) = self.mask { component_statements.push(fastn_js::ComponentStatement::SetProperty( - mask_image.to_set_property( - fastn_js::PropertyKind::MaskImage, - doc, - element_name, - rdata, - ), + mask.to_set_property(fastn_js::PropertyKind::Mask, doc, element_name, rdata), )); } component_statements diff --git a/ftd/src/js/value.rs b/ftd/src/js/value.rs index d2831d059b..63cc755f98 100644 --- a/ftd/src/js/value.rs +++ b/ftd/src/js/value.rs @@ -537,9 +537,9 @@ fn ftd_to_js_variant(name: &str, variant: &str) -> (String, bool) { let js_variant = backdrop_filter_variants(variant); (format!("fastn_dom.BackdropFilter.{}", js_variant), true) } - "ftd#mask-image" => { - let js_variant = mask_image_variants(variant); - (format!("fastn_dom.MaskImage.{}", js_variant), true) + "ftd#mask" => { + let js_variant = mask_variants(variant); + (format!("fastn_dom.Mask.{}", js_variant), true) } t => todo!("{} {}", t, variant), } @@ -905,10 +905,9 @@ fn backdrop_filter_variants(name: &str) -> &'static str { } } -fn mask_image_variants(name: &str) -> &'static str { +fn mask_variants(name: &str) -> &'static str { match name { - "src" => "Src", - "linear-gradient" => "LinearGradient", - t => todo!("invalid mask image variant {}", t), + "image" => "Image", + t => todo!("invalid mask variant {}", t), } } diff --git a/ftd/t/js/68-mask.ftd b/ftd/t/js/68-mask.ftd index 98e064b839..bdd797e299 100644 --- a/ftd/t/js/68-mask.ftd +++ b/ftd/t/js/68-mask.ftd @@ -1,5 +1,5 @@ -- ftd.container: background.solid: red -mask-image.src: https://mdn.github.io/css-examples/masking/star.svg +mask.image: https://mdn.github.io/css-examples/masking/star.svg width.fixed.px: 300 height.fixed.px: 300