From 7a94e51ceeadf4d16d1da76363b82c98fa255dca Mon Sep 17 00:00:00 2001 From: Harsh Singh <64768386+harshdoesdev@users.noreply.github.com> Date: Thu, 26 Oct 2023 10:27:34 +0530 Subject: [PATCH] mask-image property added and code refactor (#1396) * mask property added * mask-composite property * mask mode property and test fixes * mask-image property added * mask-image -> mask.image * fixed tests * mask-position, repeat, size properties * fixed tests * fixed stackoverflow issue caused by default_bag --- fastn-js/js/dom.js | 196 +++++++++++- fastn-js/src/property.rs | 2 + ftd/src/executor/test.rs | 2 +- ftd/src/interpreter/constants.rs | 39 +++ ftd/src/interpreter/main.rs | 2 +- ftd/src/interpreter/test.rs | 2 +- ftd/src/interpreter/things/default.rs | 416 +++++++++++++++++++++++++- ftd/src/js/element.rs | 41 +-- ftd/src/js/mod.rs | 10 +- ftd/src/js/value.rs | 68 +++++ ftd/src/node/test.rs | 2 +- ftd/t/js/68-mask.ftd | 39 +++ ftd/t/js/68-mask.html | 137 +++++++++ 13 files changed, 917 insertions(+), 39 deletions(-) create mode 100644 ftd/t/js/68-mask.ftd create mode 100644 ftd/t/js/68-mask.html diff --git a/fastn-js/js/dom.js b/fastn-js/js/dom.js index 9b6d5973bb..c2130a83ee 100644 --- a/fastn-js/js/dom.js +++ b/fastn-js/js/dom.js @@ -101,6 +101,14 @@ fastn_dom.propertyMap = { "-webkit-box-orient": "wbo", "-webkit-line-clamp": "wlc", "backdrop-filter": "bdf", + "mask-image": "mi", + "-webkit-mask-image": "wmi", + "mask-size": "ms", + "-webkit-mask-size": "wms", + "mask-repeat": "mre", + "-webkit-mask-repeat": "wmre", + "mask-position": "mp", + "-webkit-mask-position": "wmp", }; // dynamic-class-css.md @@ -282,6 +290,7 @@ fastn_dom.PropertyKind = { TextShadow: 117, Selectable: 118, BackdropFilter: 119, + Mask: 120, }; @@ -657,7 +666,46 @@ fastn_dom.Length = { } } +fastn_dom.Mask = { + Image: (value) => { + return [1, value]; + }, + Multi: (value) => { + return [2, value] + }, +} +fastn_dom.MaskSize = { + Auto: "auto", + Cover: "cover", + Contain: "contain", + Fixed: (value) => { return value } +} + +fastn_dom.MaskRepeat = { + Repeat: "repeat", + RepeatX: "repeat-x", + RepeatY: "repeat-y", + NoRepeat: "no-repeat", + Space: "space", + Round: "round", +} + +fastn_dom.MaskPosition = { + Left: "left", + Right: "right", + Center: "center", + LeftTop: "left top", + LeftCenter: "left center", + LeftBottom: "left bottom", + CenterTop: "center top", + CenterCenter: "center center", + CenterBottom: "center bottom", + RightTop: "right top", + RightCenter: "right center", + RightBottom: "right bottom", + Length: (value) => { return value; }, +} fastn_dom.Event = { Click: 0, @@ -1055,16 +1103,11 @@ class Node2 { this.attachCss("text-shadow", darkShadowCss, true, `body.dark .${lightClass}`); } } - attachLinearGradientCss(value) { - if (fastn_utils.isNull(value)) { - this.attachCss("background-image", 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; @@ -1101,6 +1144,18 @@ 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("background-image", `linear-gradient(${direction}, ${lightGradientString})`, false); } else { @@ -1160,6 +1215,112 @@ class Node2 { this.attachCss("background-image", `url(${darkValue})`, true, `body.dark .${lightClass}`); } } + attachMaskImageCss(value, vendorPrefix) { + const propertyWithPrefix = vendorPrefix ? `${vendorPrefix}-mask-image` : "mask-image"; + + if (fastn_utils.isNull(value)) { + this.attachCss(propertyWithPrefix, value); + return; + } + + let src = fastn_utils.getStaticValue(value.get("src")); + let linearGradient = fastn_utils.getStaticValue(value.get("linear_gradient")); + let color = fastn_utils.getStaticValue(value.get("color")); + + 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")); + + const lightUrl = `url(${lightValue})`; + const darkUrl = `url(${darkValue})`; + + if(!fastn_utils.isNull(linearGradient)) { + const lightImageValues = [lightUrl]; + const darkImageValues = [darkUrl]; + + if(!fastn_utils.isNull(color)) { + const lightColor = fastn_utils.getStaticValue(color.get("light")); + const darkColor = fastn_utils.getStaticValue(color.get("dark")); + + lightImageValues.push(lightColor); + darkImageValues.push(darkColor); + } + maskLightImageValues.push(`image(${lightImageValues.join(", ")})`); + maskDarkImageValues.push(`image(${darkImageValues.join(", ")})`); + } else { + maskLightImageValues.push(lightUrl); + maskDarkImageValues.push(darkUrl); + } + } + + if(!fastn_utils.isNull(linearGradient)) { + let direction = fastn_utils.getStaticValue(linearGradient.get("direction")); + + const [lightGradientString, darkGradientString] = this.getLinearGradientString(linearGradient); + + maskLightImageValues.push(`linear-gradient(${direction}, ${lightGradientString})`); + maskDarkImageValues.push(`linear-gradient(${direction}, ${darkGradientString})`); + } + + const maskLightImageString = maskLightImageValues.join(", "); + const maskDarkImageString = maskDarkImageValues.join(", "); + + if(maskLightImageString === maskDarkImageString) { + this.attachCss(propertyWithPrefix, maskLightImageString, false); + } else { + let lightClass = this.attachCss(propertyWithPrefix, maskLightImageString, true); + this.attachCss(propertyWithPrefix, maskDarkImageString, true, `body.dark .${lightClass}`); + } + } + attachMaskSizeCss(value, vendorPrefix) { + const propertyNameWithPrefix = vendorPrefix ? `${vendorPrefix}-mask-size` : "mask-size"; + if(fastn_utils.isNull(value)) { + this.attachCss(propertyNameWithPrefix, value); + } + const [size, ...two_values] = ["size", "size_x", "size_y"] + .map(size => fastn_utils.getStaticValue(value.get(size))); + + if(!fastn_utils.isNull(size)) { + this.attachCss(propertyNameWithPrefix, size, true); + } else { + const [size_x, size_y] = two_values.map(value => value || "auto"); + this.attachCss(propertyNameWithPrefix, `${size_x} ${size_y}`, true); + } + } + attachMaskMultiCss(value, vendorPrefix) { + if (fastn_utils.isNull(value)) { + this.attachCss("mask-repeat", value); + this.attachCss("mask-position", value); + this.attachCss("mask-size", value); + this.attachCss("mask-image", value); + return; + } + + const maskImage = fastn_utils.getStaticValue(value.get("image")); + this.attachMaskImageCss(maskImage); + this.attachMaskImageCss(maskImage, vendorPrefix); + this.attachMaskSizeCss(value); + this.attachMaskSizeCss(value, vendorPrefix); + const maskRepeatValue = fastn_utils.getStaticValue(value.get("repeat")); + if(fastn_utils.isNull(maskRepeatValue)) { + this.attachCss("mask-repeat", maskRepeatValue); + this.attachCss("-webkit-mask-repeat", maskRepeatValue); + } else { + this.attachCss("mask-repeat", maskRepeatValue); + this.attachCss("-webkit-mask-repeat", maskRepeatValue); + } + const maskPositionValue = fastn_utils.getStaticValue(value.get("position")); + if(fastn_utils.isNull(maskPositionValue)) { + this.attachCss("mask-position", maskPositionValue); + this.attachCss("-webkit-mask-position", maskPositionValue); + } else { + this.attachCss("mask-position", maskPositionValue); + this.attachCss("-webkit-mask-position", maskPositionValue); + } + } attachExternalCss(css) { if (hydrating) { let css_tag = document.createElement('link'); @@ -1496,8 +1657,8 @@ class Node2 { } else if (kind === fastn_dom.PropertyKind.TextShadow) { this.attachTextShadow(staticValue); } else if (kind === fastn_dom.PropertyKind.BackdropFilter) { - if (fastn_utils.isNull(value)) { - this.attachCss("backdrop-filter", value); + if (fastn_utils.isNull(staticValue)) { + this.attachCss("backdrop-filter", staticValue); return; } @@ -1528,10 +1689,27 @@ class Node2 { this.attachCss("backdrop-filter", `saturate(${fastn_utils.getStaticValue(staticValue[1])})`); break; case 9: - console.log("Here"); this.attachBackdropMultiFilter(staticValue[1]); break; } + } else if (kind === fastn_dom.PropertyKind.Mask) { + if (fastn_utils.isNull(staticValue)) { + this.attachCss("mask-image", staticValue); + return; + } + + const [backgroundType, value] = staticValue; + + switch (backgroundType) { + case fastn_dom.Mask.Image()[0]: + this.attachMaskImageCss(value); + this.attachMaskImageCss(value, "-webkit"); + break; + case fastn_dom.Mask.Multi()[0]: + this.attachMaskMultiCss(value); + this.attachMaskMultiCss(value, "-webkit"); + break; + } } else if (kind === fastn_dom.PropertyKind.Classes) { fastn_utils.removeNonFastnClasses(this); if (!fastn_utils.isNull(staticValue)) { diff --git a/fastn-js/src/property.rs b/fastn-js/src/property.rs index 174d0c52e1..4018fb2613 100644 --- a/fastn-js/src/property.rs +++ b/fastn-js/src/property.rs @@ -427,6 +427,7 @@ pub enum PropertyKind { Favicon, Selectable, BackdropFilter, + Mask, } impl PropertyKind { @@ -566,6 +567,7 @@ impl PropertyKind { PropertyKind::Favicon => "fastn_dom.PropertyKind.Favicon", PropertyKind::Selectable => "fastn_dom.PropertyKind.Selectable", PropertyKind::BackdropFilter => "fastn_dom.PropertyKind.BackdropFilter", + PropertyKind::Mask => "fastn_dom.PropertyKind.Mask", } } } diff --git a/ftd/src/executor/test.rs b/ftd/src/executor/test.rs index e87208f31c..461bd33e40 100644 --- a/ftd/src/executor/test.rs +++ b/ftd/src/executor/test.rs @@ -77,7 +77,7 @@ fn p(s: &str, t: &str, fix: bool, file_location: &std::path::PathBuf) { let doc = interpret_helper("foo", s).unwrap_or_else(|e| panic!("{:?}", e)); let mut executor = ftd::executor::ExecuteDoc::from_interpreter(doc).unwrap_or_else(|e| panic!("{:?}", e)); - for thing in ftd::interpreter::default::default_bag().keys() { + for thing in ftd::interpreter::default::get_default_bag().keys() { executor.bag.remove(thing); } let expected_json = serde_json::to_string_pretty(&executor).unwrap(); diff --git a/ftd/src/interpreter/constants.rs b/ftd/src/interpreter/constants.rs index 2411cec670..a441c3db57 100644 --- a/ftd/src/interpreter/constants.rs +++ b/ftd/src/interpreter/constants.rs @@ -330,3 +330,42 @@ pub const FTD_BACKDROP_FILTER_OPACITY: &str = "ftd#backdrop-filter.opacity"; 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_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"; +pub const FTD_MASK_MULTI: &str = "ftd#mask.multi"; + +pub const FTD_MASK_MULTI_DATA: &str = "ftd#mask-multi"; + +pub const FTD_MASK_SIZE: &str = "ftd#mask-size"; +pub const FTD_MASK_SIZE_FIXED: &str = "ftd#mask-size.fixed"; +pub const FTD_MASK_SIZE_COVER: &str = "ftd#mask-size.cover"; +pub const FTD_MASK_SIZE_CONTAIN: &str = "ftd#mask-size.contain"; +pub const FTD_MASK_SIZE_AUTO: &str = "ftd#mask-size.auto"; + +pub const FTD_MASK_REPEAT: &str = "ftd#mask-repeat"; +pub const FTD_MASK_REPEAT_BOTH_REPEAT: &str = "ftd#mask-repeat.repeat"; +pub const FTD_MASK_REPEAT_X_REPEAT: &str = "ftd#mask-repeat.repeat-x"; +pub const FTD_MASK_REPEAT_Y_REPEAT: &str = "ftd#mask-repeat.repeat-y"; +pub const FTD_MASK_REPEAT_NO_REPEAT: &str = "ftd#mask-repeat.no-repeat"; +pub const FTD_MASK_REPEAT_SPACE: &str = "ftd#mask-repeat.space"; +pub const FTD_MASK_REPEAT_ROUND: &str = "ftd#mask-repeat.round"; + +pub const FTD_MASK_POSITION: &str = "ftd#mask-position"; +pub const FTD_MASK_POSITION_LEFT: &str = "ftd#mask-position.left"; +pub const FTD_MASK_POSITION_CENTER: &str = "ftd#mask-position.center"; +pub const FTD_MASK_POSITION_RIGHT: &str = "ftd#mask-position.right"; +pub const FTD_MASK_POSITION_LEFT_TOP: &str = "ftd#mask-position.left-top"; +pub const FTD_MASK_POSITION_LEFT_CENTER: &str = "ftd#mask-position.left-center"; +pub const FTD_MASK_POSITION_LEFT_BOTTOM: &str = "ftd#mask-position.left-bottom"; +pub const FTD_MASK_POSITION_CENTER_TOP: &str = "ftd#mask-position.center-top"; +pub const FTD_MASK_POSITION_CENTER_CENTER: &str = "ftd#mask-position.center-center"; +pub const FTD_MASK_POSITION_CENTER_BOTTOM: &str = "ftd#mask-position.center-bottom"; +pub const FTD_MASK_POSITION_RIGHT_TOP: &str = "ftd#mask-position.right-top"; +pub const FTD_MASK_POSITION_RIGHT_CENTER: &str = "ftd#mask-position.right-center"; +pub const FTD_MASK_POSITION_RIGHT_BOTTOM: &str = "ftd#mask-position.right-bottom"; +pub const FTD_MASK_POSITION_LENGTH: &str = "ftd#mask-position.length"; diff --git a/ftd/src/interpreter/main.rs b/ftd/src/interpreter/main.rs index ae34cca2c1..c3664a3d1e 100644 --- a/ftd/src/interpreter/main.rs +++ b/ftd/src/interpreter/main.rs @@ -78,7 +78,7 @@ impl InterpreterState { fn new(id: String) -> InterpreterState { InterpreterState { id, - bag: ftd::interpreter::default::default_bag(), + bag: ftd::interpreter::default::get_default_bag().clone(), ..Default::default() } } diff --git a/ftd/src/interpreter/test.rs b/ftd/src/interpreter/test.rs index 1f0de8e030..85ba03bf35 100644 --- a/ftd/src/interpreter/test.rs +++ b/ftd/src/interpreter/test.rs @@ -73,7 +73,7 @@ pub fn interpret_helper( #[track_caller] fn p(s: &str, t: &str, fix: bool, file_location: &std::path::PathBuf) { let mut i = interpret_helper("foo", s).unwrap_or_else(|e| panic!("{:?}", e)); - for thing in ftd::interpreter::default::default_bag().keys() { + for thing in ftd::interpreter::default::get_default_bag().keys() { i.data.remove(thing); } let expected_json = serde_json::to_string_pretty(&i).unwrap(); diff --git a/ftd/src/interpreter/things/default.rs b/ftd/src/interpreter/things/default.rs index 4430afcb77..8aaeb654d3 100644 --- a/ftd/src/interpreter/things/default.rs +++ b/ftd/src/interpreter/things/default.rs @@ -1299,7 +1299,6 @@ pub fn default_bag() -> indexmap::IndexMap { line_number: 0, }), ), - // TODO: Instead of default value, make the integer optional ( ftd::interpreter::FTD_BACKDROP_MULTI.to_string(), ftd::interpreter::Thing::Record(ftd::interpreter::Record { @@ -7598,7 +7597,7 @@ pub fn default_bag() -> indexmap::IndexMap { value: ftd::interpreter::Value::Record { name: ftd::interpreter::FTD_COLOR.to_string(), fields: std::iter::IntoIterator::into_iter([( - "light".to_string(), + "light".to_string().to_string(), ftd::interpreter::PropertyValue::Value { value: ftd::interpreter::Value::String { @@ -7880,7 +7879,7 @@ pub fn default_bag() -> indexmap::IndexMap { line_number: 0, }, ), ( - "dark".to_string(), + "dark".to_string().to_string(), ftd::interpreter::PropertyValue::Value { value: ftd::interpreter::Value::String { text: "#65b693".to_string() @@ -9705,11 +9704,414 @@ pub fn default_bag() -> indexmap::IndexMap { line_number: 0, is_static: false }) - ) + ), + ( + 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, + }, + ftd::interpreter::Field { + name: "color".to_string(), + kind: ftd::interpreter::Kind::record(ftd::interpreter::FTD_COLOR) + .into_kind_data() + .into_optional(), + mutable: false, + value: None, + access_modifier: Default::default(), + line_number: 0, + }, + ]).collect(), + line_number: 0, + }), + ), + ( + ftd::interpreter::FTD_MASK_SIZE.to_string(), + ftd::interpreter::Thing::OrType(ftd::interpreter::OrType { + name: ftd::interpreter::FTD_MASK_SIZE.to_string(), + variants: vec![ + ftd::interpreter::OrTypeVariant::Regular(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_SIZE_FIXED, + ftd::interpreter::Kind::or_type(ftd::interpreter::FTD_LENGTH) + .into_kind_data(), + false, + None, + 0, + )), + ftd::interpreter::OrTypeVariant::new_constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_SIZE_AUTO, + ftd::interpreter::Kind::string().into_kind_data(), + false, + Some( + ftd::interpreter::Value::new_string( + ftd::interpreter::FTD_MASK_SIZE_AUTO, + ) + .into_property_value(false, 0), + ), + 0, + )), + ftd::interpreter::OrTypeVariant::new_constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_SIZE_COVER, + ftd::interpreter::Kind::string().into_kind_data(), + false, + Some( + ftd::interpreter::Value::new_string( + ftd::interpreter::FTD_MASK_SIZE_CONTAIN, + ) + .into_property_value(false, 0), + ), + 0, + )), + ], + line_number: 0, + }), + ), + + ( + ftd::interpreter::FTD_MASK_REPEAT.to_string(), + ftd::interpreter::Thing::OrType(ftd::interpreter::OrType { + name: ftd::interpreter::FTD_MASK_REPEAT.to_string(), + variants: vec![ + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_REPEAT_BOTH_REPEAT, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("repeat") + .into_property_value(false, 0),), + 0, + )), + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_REPEAT_X_REPEAT, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("repeat-x") + .into_property_value(false, 0)), + 0, + )), + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_REPEAT_Y_REPEAT, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("repeat-y") + .into_property_value(false, 0)), + 0, + )), + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_REPEAT_NO_REPEAT, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("no-repeat") + .into_property_value(false, 0)), + 0, + )), + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_REPEAT_SPACE, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("space") + .into_property_value(false, 0)), + 0, + )), + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_REPEAT_ROUND, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("round") + .into_property_value(false, 0)), + 0, + )), + ], + line_number: 0, + }), + ), + ( + ftd::interpreter::FTD_MASK_POSITION.to_string(), + ftd::interpreter::Thing::OrType(ftd::interpreter::OrType { + name: ftd::interpreter::FTD_MASK_POSITION.to_string(), + variants: vec![ + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_POSITION_LEFT, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("left") + .into_property_value(false, 0),), + 0, + )), + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_POSITION_CENTER, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("center") + .into_property_value(false, 0)), + 0, + )), + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_POSITION_RIGHT, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("right") + .into_property_value(false, 0)), + 0, + )), + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_POSITION_LEFT_TOP, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("left-top") + .into_property_value(false, 0)), + 0, + )), + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_POSITION_LEFT_CENTER, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("left-center") + .into_property_value(false, 0)), + 0, + )), + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_POSITION_LEFT_BOTTOM, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("left-bottom") + .into_property_value(false, 0)), + 0, + )), + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_POSITION_CENTER_TOP, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("center-top") + .into_property_value(false, 0)), + 0, + )), + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_POSITION_CENTER_CENTER, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("center-center") + .into_property_value(false, 0)), + 0, + )), + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_POSITION_CENTER_BOTTOM, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("center-bottom") + .into_property_value(false, 0)), + 0, + )), + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_POSITION_RIGHT_TOP, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("right-top") + .into_property_value(false, 0)), + 0, + )), + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_POSITION_RIGHT_CENTER, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("right-center") + .into_property_value(false, 0)), + 0, + )), + ftd::interpreter::OrTypeVariant::Constant(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_POSITION_RIGHT_BOTTOM, + ftd::interpreter::Kind::string() + .into_kind_data() + .caption(), + false, + Some(ftd::interpreter::Value::new_string("right-bottom") + .into_property_value(false, 0)), + 0, + )), + ftd::interpreter::OrTypeVariant::AnonymousRecord(ftd::interpreter::Record { + name: ftd::interpreter::FTD_MASK_POSITION_LENGTH.to_string(), + fields: std::iter::IntoIterator::into_iter([ + ftd::interpreter::Field { + name: "x".to_string(), + kind: ftd::interpreter::Kind::or_type(ftd::interpreter::FTD_LENGTH) + .into_kind_data(), + mutable: false, + value: None, + access_modifier: Default::default(), + line_number: 0, + }, + ftd::interpreter::Field { + name: "y".to_string(), + kind: ftd::interpreter::Kind::or_type(ftd::interpreter::FTD_LENGTH) + .into_kind_data(), + mutable: false, + value: None, + access_modifier: Default::default(), + line_number: 0, + }, + ]).collect(), + line_number: 0, + }), + ], + line_number: 0, + }), + ), + ( + ftd::interpreter::FTD_MASK_MULTI_DATA.to_string(), + ftd::interpreter::Thing::Record(ftd::interpreter::Record { + name: ftd::interpreter::FTD_MASK_MULTI_DATA.to_string(), + fields: std::iter::IntoIterator::into_iter([ + ftd::interpreter::Field { + name: "image".to_string(), + kind: ftd::interpreter::Kind::record(ftd::interpreter::FTD_MASK_IMAGE_DATA) + .into_kind_data(), + mutable: false, + value: None, + access_modifier: Default::default(), + line_number: 0, + }, + ftd::interpreter::Field { + name: "size".to_string(), + kind: ftd::interpreter::Kind::or_type(ftd::interpreter::FTD_MASK_SIZE) + .into_kind_data() + .into_optional(), + mutable: false, + value: None, + access_modifier: Default::default(), + line_number: 0, + }, + ftd::interpreter::Field { + name: "size-x".to_string(), + kind: ftd::interpreter::Kind::or_type(ftd::interpreter::FTD_MASK_SIZE) + .into_kind_data() + .into_optional(), + mutable: false, + value: None, + access_modifier: Default::default(), + line_number: 0, + }, + ftd::interpreter::Field { + name: "size-y".to_string(), + kind: ftd::interpreter::Kind::or_type(ftd::interpreter::FTD_MASK_SIZE) + .into_kind_data() + .into_optional(), + mutable: false, + value: None, + access_modifier: Default::default(), + line_number: 0, + }, + ftd::interpreter::Field { + name: "repeat".to_string(), + kind: ftd::interpreter::Kind::or_type(ftd::interpreter::FTD_MASK_REPEAT) + .into_kind_data() + .into_optional(), + mutable: false, + value: None, + access_modifier: Default::default(), + line_number: 0, + }, + ftd::interpreter::Field { + name: "position".to_string(), + kind: ftd::interpreter::Kind::or_type(ftd::interpreter::FTD_MASK_POSITION) + .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.to_string(), + variants: vec![ + ftd::interpreter::OrTypeVariant::Regular(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_IMAGE, + ftd::interpreter::Kind::record(ftd::interpreter::FTD_MASK_IMAGE_DATA) + .into_kind_data(), + false, + None, + 0, + )), + ftd::interpreter::OrTypeVariant::Regular(ftd::interpreter::Field::new( + ftd::interpreter::FTD_MASK_MULTI, + ftd::interpreter::Kind::record(ftd::interpreter::FTD_MASK_MULTI_DATA) + .into_kind_data(), + false, + None, + 0, + )), + ], + line_number: 0, + }), + ), ]) .collect() } +pub static DEFAULT_BAG: once_cell::sync::OnceCell< + indexmap::IndexMap, +> = once_cell::sync::OnceCell::new(); + +pub fn get_default_bag() -> &'static indexmap::IndexMap { + DEFAULT_BAG.get_or_init(ftd::interpreter::things::default::default_bag) +} + pub fn image_function() -> ftd::interpreter::ComponentDefinition { ftd::interpreter::ComponentDefinition { name: "ftd#image".to_string(), @@ -10900,6 +11302,12 @@ fn common_arguments() -> Vec { .into_optional() .into_kind_data(), ), + ftd::interpreter::Argument::default( + "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 8d83125c60..fbd3b0d535 100644 --- a/ftd/src/js/element.rs +++ b/ftd/src/js/element.rs @@ -147,7 +147,7 @@ pub struct CheckBox { impl CheckBox { pub fn from(component: &ftd::interpreter::Component) -> CheckBox { - let component_definition = ftd::interpreter::default::default_bag() + let component_definition = ftd::interpreter::default::get_default_bag() .get("ftd#checkbox") .unwrap() .clone() @@ -233,7 +233,7 @@ pub struct TextInput { impl TextInput { pub fn from(component: &ftd::interpreter::Component) -> TextInput { - let component_definition = ftd::interpreter::default::default_bag() + let component_definition = ftd::interpreter::default::get_default_bag() .get("ftd#text-input") .unwrap() .clone() @@ -377,7 +377,7 @@ pub struct Iframe { impl Iframe { pub fn from(component: &ftd::interpreter::Component) -> Iframe { - let component_definition = ftd::interpreter::default::default_bag() + let component_definition = ftd::interpreter::default::get_default_bag() .get("ftd#iframe") .unwrap() .clone() @@ -495,7 +495,7 @@ pub struct Code { impl Code { pub fn from(component: &ftd::interpreter::Component, _doc: &ftd::interpreter::TDoc) -> Code { - let component_definition = ftd::interpreter::default::default_bag() + let component_definition = ftd::interpreter::default::get_default_bag() .get("ftd#code") .unwrap() .clone() @@ -619,7 +619,7 @@ pub struct Image { impl Image { pub fn from(component: &ftd::interpreter::Component) -> Image { - let component_definition = ftd::interpreter::default::default_bag() + let component_definition = ftd::interpreter::default::get_default_bag() .get("ftd#image") .unwrap() .clone() @@ -718,7 +718,7 @@ pub struct Video { impl Video { pub fn from(component: &ftd::interpreter::Component) -> Video { - let component_definition = ftd::interpreter::default::default_bag() + let component_definition = ftd::interpreter::default::get_default_bag() .get("ftd#video") .unwrap() .clone() @@ -1124,7 +1124,7 @@ impl InheritedProperties { impl Text { pub fn from(component: &ftd::interpreter::Component) -> Text { - let component_definition = ftd::interpreter::default::default_bag() + let component_definition = ftd::interpreter::default::get_default_bag() .get("ftd#text") .unwrap() .clone() @@ -1190,7 +1190,7 @@ impl Text { impl Integer { pub fn from(component: &ftd::interpreter::Component) -> Integer { - let component_definition = ftd::interpreter::default::default_bag() + let component_definition = ftd::interpreter::default::get_default_bag() .get("ftd#integer") .unwrap() .clone() @@ -1255,7 +1255,7 @@ impl Integer { impl Decimal { pub fn from(component: &ftd::interpreter::Component) -> Decimal { - let component_definition = ftd::interpreter::default::default_bag() + let component_definition = ftd::interpreter::default::get_default_bag() .get("ftd#decimal") .unwrap() .clone() @@ -1320,7 +1320,7 @@ impl Decimal { impl Boolean { pub fn from(component: &ftd::interpreter::Component) -> Boolean { - let component_definition = ftd::interpreter::default::default_bag() + let component_definition = ftd::interpreter::default::get_default_bag() .get("ftd#boolean") .unwrap() .clone() @@ -1385,7 +1385,7 @@ impl Boolean { impl Document { pub fn from(component: &ftd::interpreter::Component) -> Document { - let component_definition = ftd::interpreter::default::default_bag() + let component_definition = ftd::interpreter::default::get_default_bag() .get("ftd#document") .unwrap() .clone() @@ -1636,7 +1636,7 @@ impl DocumentMeta { impl Column { pub fn from(component: &ftd::interpreter::Component) -> Column { - let component_definition = ftd::interpreter::default::default_bag() + let component_definition = ftd::interpreter::default::get_default_bag() .get("ftd#column") .unwrap() .clone() @@ -1702,7 +1702,7 @@ impl Column { impl Row { pub fn from(component: &ftd::interpreter::Component) -> Row { - let component_definition = ftd::interpreter::default::default_bag() + let component_definition = ftd::interpreter::default::get_default_bag() .get("ftd#row") .unwrap() .clone() @@ -1768,7 +1768,7 @@ impl Row { impl ContainerElement { pub fn from(component: &ftd::interpreter::Component) -> ContainerElement { - let component_definition = ftd::interpreter::default::default_bag() + let component_definition = ftd::interpreter::default::get_default_bag() .get("ftd#container") .unwrap() .clone() @@ -1836,7 +1836,7 @@ pub struct Device { impl Device { pub fn from(component: &ftd::interpreter::Component, device: &str) -> Device { - let component_definition = ftd::interpreter::default::default_bag() + let component_definition = ftd::interpreter::default::get_default_bag() .get(device) .unwrap() .clone() @@ -2021,7 +2021,7 @@ pub struct Rive { impl Rive { pub fn from(component: &ftd::interpreter::Component) -> Rive { - let component_definition = ftd::interpreter::default::default_bag() + let component_definition = ftd::interpreter::default::get_default_bag() .get("ftd#rive") .unwrap() .clone() @@ -2225,6 +2225,7 @@ pub struct Common { pub js: Option, pub events: Vec, pub selectable: Option, + pub mask: Option, } impl Common { @@ -2439,8 +2440,9 @@ impl Common { min_width: ftd::js::value::get_optional_js_value("min-width", properties, arguments), whitespace: ftd::js::value::get_optional_js_value("white-space", properties, arguments), shadow: ftd::js::value::get_optional_js_value("shadow", properties, arguments), - events: events.to_vec(), selectable: ftd::js::value::get_optional_js_value("selectable", properties, arguments), + mask: ftd::js::value::get_optional_js_value("mask", properties, arguments), + events: events.to_vec(), } } @@ -3037,6 +3039,11 @@ impl Common { ), )); } + if let Some(ref mask) = self.mask { + component_statements.push(fastn_js::ComponentStatement::SetProperty( + mask.to_set_property(fastn_js::PropertyKind::Mask, doc, element_name, rdata), + )); + } component_statements } } diff --git a/ftd/src/js/mod.rs b/ftd/src/js/mod.rs index 2fcde906df..0414e3570b 100644 --- a/ftd/src/js/mod.rs +++ b/ftd/src/js/mod.rs @@ -23,14 +23,14 @@ pub fn all_js_without_test(package_name: &str) -> String { /// This returns asts of things present in `ftd` module or `default_bag` pub fn default_bag_into_js_ast() -> Vec { let mut ftd_asts = vec![]; - let bag = ftd::interpreter::default::default_bag(); + let bag = ftd::interpreter::default::get_default_bag(); let doc = ftd::interpreter::TDoc { name: "", aliases: &ftd::interpreter::default::default_aliases(), - bag: ftd::interpreter::BagOrState::Bag(&bag), + bag: ftd::interpreter::BagOrState::Bag(bag), }; let mut export_asts = vec![]; - for thing in ftd::interpreter::default::default_bag().values() { + for thing in ftd::interpreter::default::get_default_bag().values() { if let ftd::interpreter::Thing::Variable(v) = thing { ftd_asts.push(v.to_ast(&doc, None, &mut false)); } else if let ftd::interpreter::Thing::Function(f) = thing { @@ -98,7 +98,7 @@ pub fn document_into_js_ast(document: ftd::interpreter::Document) -> JSAstData { &doc, &mut has_rive_components, )]; - let default_thing_name = ftd::interpreter::default::default_bag() + let default_thing_name = ftd::interpreter::default::get_default_bag() .into_iter() .map(|v| v.0) .collect_vec(); @@ -106,7 +106,7 @@ pub fn document_into_js_ast(document: ftd::interpreter::Document) -> JSAstData { let mut export_asts = vec![]; for (key, thing) in document.data.iter() { - if default_thing_name.contains(key) { + if default_thing_name.contains(&key) { continue; } if let ftd::interpreter::Thing::Component(c) = thing { diff --git a/ftd/src/js/value.rs b/ftd/src/js/value.rs index 656e07395c..92e82a794b 100644 --- a/ftd/src/js/value.rs +++ b/ftd/src/js/value.rs @@ -537,6 +537,25 @@ 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" => { + let js_variant = mask_variants(variant); + (format!("fastn_dom.Mask.{}", js_variant), true) + } + "ftd#mask-size" => { + let js_variant = mask_size_variants(variant); + (format!("fastn_dom.MaskSize.{}", js_variant.0), js_variant.1) + } + "ftd#mask-repeat" => { + let js_variant = mask_repeat_variants(variant); + (format!("fastn_dom.MaskRepeat.{}", js_variant), false) + } + "ftd#mask-position" => { + let js_variant = mask_position_variants(variant); + ( + format!("fastn_dom.MaskPosition.{}", js_variant.0), + js_variant.1, + ) + } t => todo!("{} {}", t, variant), } } @@ -900,3 +919,52 @@ fn backdrop_filter_variants(name: &str) -> &'static str { t => unimplemented!("invalid backdrop filter variant {}", t), } } + +fn mask_variants(name: &str) -> &'static str { + match name { + "image" => "Image", + "multi" => "Multi", + t => todo!("invalid mask variant {}", t), + } +} + +fn mask_size_variants(name: &str) -> (&'static str, bool) { + match name { + "auto" => ("Auto", false), + "cover" => ("Cover", false), + "contain" => ("Contain", false), + "fixed" => ("Fixed", true), + t => todo!("invalid mask variant {}", t), + } +} + +fn mask_repeat_variants(name: &str) -> &'static str { + match name { + "repeat" => "Repeat", + "repeat-x" => "RepeatX", + "repeat-y" => "RepeatY", + "no-repeat" => "NoRepeat", + "space" => "Space", + "round" => "Round", + t => todo!("invalid mask repeat variant {}", t), + } +} + +fn mask_position_variants(name: &str) -> (&'static str, bool) { + match name { + "left" => ("Left", false), + "right" => ("Right", false), + "center" => ("Center", false), + "left-top" => ("LeftTop", false), + "left-center" => ("LeftCenter", false), + "left-bottom" => ("LeftBottom", false), + "center-top" => ("CenterTop", false), + "center-center" => ("CenterCenter", false), + "center-bottom" => ("CenterBottom", false), + "right-top" => ("RightTop", false), + "right-center" => ("RightCenter", false), + "right-bottom" => ("RightBottom", false), + "length" => ("Length", true), + t => todo!("invalid mask position variant {}", t), + } +} diff --git a/ftd/src/node/test.rs b/ftd/src/node/test.rs index 9d657f9ae3..559327f8b5 100644 --- a/ftd/src/node/test.rs +++ b/ftd/src/node/test.rs @@ -76,7 +76,7 @@ fn p(s: &str, t: &str, fix: bool, file_location: &std::path::PathBuf) { let executor = ftd::executor::ExecuteDoc::from_interpreter(doc).unwrap_or_else(|e| panic!("{:?}", e)); let mut node = ftd::node::NodeData::from_rt(executor); - for thing in ftd::interpreter::default::default_bag().keys() { + for thing in ftd::interpreter::default::get_default_bag().keys() { node.bag.remove(thing); } let expected_json = serde_json::to_string_pretty(&node).unwrap(); diff --git a/ftd/t/js/68-mask.ftd b/ftd/t/js/68-mask.ftd new file mode 100644 index 0000000000..52614fe4dd --- /dev/null +++ b/ftd/t/js/68-mask.ftd @@ -0,0 +1,39 @@ +-- ftd.container: +background.solid: red +mask.image: https://mdn.github.io/css-examples/masking/star.svg +width.fixed.px: 300 +height.fixed.px: 300 + +-- ftd.color red-orange: +light: red +dark: orange + +-- ftd.color yellow-blue: +light: yellow +dark: blue + +-- ftd.linear-gradient lg: +direction: bottom-left +colors: $color-values + +-- ftd.linear-gradient-color list color-values: + +-- ftd.linear-gradient-color: $red-orange +stop-position.percent: 40 + +-- ftd.linear-gradient-color: $yellow-blue + +-- end: color-values + + +-- ftd.mask-multi mi: +image: https://mdn.github.io/css-examples/masking/star.svg +size.fixed.rem: 2 +repeat: no-repeat +position: center + +-- ftd.container: +background.solid: red +mask.multi: $mi +width.fixed.px: 300 +height.fixed.px: 300 diff --git a/ftd/t/js/68-mask.html b/ftd/t/js/68-mask.html new file mode 100644 index 0000000000..24b766bd62 --- /dev/null +++ b/ftd/t/js/68-mask.html @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + +
+ +