#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace reanimated::css { namespace { // Private implementation details const auto BLACK = CSSColor(0, 0, 0, 255); const auto TRANSPARENT = CSSColor::Transparent; InterpolatorFactoriesRecord mergeInterpolators( const std::vector &maps) { InterpolatorFactoriesRecord result; for (const auto &map : maps) { result.insert(map.begin(), map.end()); } return result; } // ========================== // React Native Interpolators // ========================== const InterpolatorFactoriesRecord FLEX_INTERPOLATORS = { {"alignContent", value("flex-start")}, {"alignItems", value("stretch")}, {"alignSelf", value("auto")}, {"aspectRatio", value("auto")}, {"borderBottomWidth", value(0)}, {"borderEndWidth", value(0)}, {"borderLeftWidth", value(0)}, {"borderRightWidth", value(0)}, {"borderStartWidth", value(0)}, {"borderTopWidth", value(0)}, {"borderWidth", value(0)}, {"bottom", value(RelativeTo::Parent, "height", "auto")}, {"boxSizing", value("border-box")}, {"display", value("flex")}, {"end", value(RelativeTo::Parent, "width", "auto")}, {"flex", value(0)}, {"flexBasis", value(RelativeTo::Parent, "width", "auto")}, {"flexDirection", value("column")}, {"rowGap", value(RelativeTo::Self, "height", 0)}, {"columnGap", value(RelativeTo::Self, "width", 0)}, {"flexGrow", value(0)}, {"flexShrink", value(0)}, {"flexWrap", value("no-wrap")}, {"height", value(RelativeTo::Parent, "height", "auto")}, {"justifyContent", value("flex-start")}, {"left", value(RelativeTo::Parent, "width", "auto")}, {"margin", value(RelativeTo::Parent, "width", 0)}, {"marginBottom", value(RelativeTo::Parent, "width", 0)}, {"marginEnd", value(RelativeTo::Parent, "width", 0)}, {"marginHorizontal", value(RelativeTo::Parent, "width", 0)}, {"marginLeft", value(RelativeTo::Parent, "width", 0)}, {"marginRight", value(RelativeTo::Parent, "width", 0)}, {"marginStart", value(RelativeTo::Parent, "width", 0)}, {"marginTop", value(RelativeTo::Parent, "width", 0)}, {"marginVertical", value(RelativeTo::Parent, "width", 0)}, {"maxHeight", value(RelativeTo::Parent, "height", "auto")}, {"maxWidth", value(RelativeTo::Parent, "width", "auto")}, {"minHeight", value(RelativeTo::Parent, "height", "auto")}, {"minWidth", value(RelativeTo::Parent, "width", "auto")}, {"overflow", value("visible")}, {"padding", value(RelativeTo::Parent, "width", 0)}, {"paddingBottom", value(RelativeTo::Parent, "width", 0)}, {"paddingEnd", value(RelativeTo::Parent, "width", 0)}, {"paddingHorizontal", value(RelativeTo::Parent, "width", 0)}, {"paddingLeft", value(RelativeTo::Parent, "width", 0)}, {"paddingRight", value(RelativeTo::Parent, "width", 0)}, {"paddingStart", value(RelativeTo::Parent, "width", 0)}, {"paddingTop", value(RelativeTo::Parent, "width", 0)}, {"paddingVertical", value(RelativeTo::Parent, "width", 0)}, {"position", value("relative")}, {"right", value(RelativeTo::Parent, "width", "auto")}, {"start", value(RelativeTo::Parent, "width", "auto")}, {"top", value(RelativeTo::Parent, "height", "auto")}, {"width", value(RelativeTo::Parent, "width", "auto")}, {"zIndex", value(0)}, {"direction", value("inherit")}}; const InterpolatorFactoriesRecord SHADOW_INTERPOLATORS_IOS = { {"shadowColor", value(BLACK)}, {"shadowOffset", record({{"width", value(0)}, {"height", value(0)}})}, {"shadowRadius", value(0)}, {"shadowOpacity", value(1)}}; const InterpolatorFactoriesRecord TRANSFORMS_INTERPOLATORS = { {"transformOrigin", array( {value(RelativeTo::Self, "width", "50%"), value(RelativeTo::Self, "height", "50%"), value(0)})}, {"transform", transforms( {{"perspective", transformOp(0)}, // 0 - no perspective {"rotate", transformOp("0deg")}, {"rotateX", transformOp("0deg")}, {"rotateY", transformOp("0deg")}, {"rotateZ", transformOp("0deg")}, {"scale", transformOp(1)}, {"scaleX", transformOp(1)}, {"scaleY", transformOp(1)}, {"translateX", transformOp(RelativeTo::Self, "width", 0)}, {"translateY", transformOp(RelativeTo::Self, "height", 0)}, {"skewX", transformOp("0deg")}, {"skewY", transformOp("0deg")}, {"matrix", transformOp(TransformMatrix3D::Identity())}})}, }; const InterpolatorFactoriesRecord VIEW_INTERPOLATORS = mergeInterpolators( {FLEX_INTERPOLATORS, SHADOW_INTERPOLATORS_IOS, TRANSFORMS_INTERPOLATORS, InterpolatorFactoriesRecord{ {"backfaceVisibility", value("visible")}, {"backgroundColor", value(TRANSPARENT)}, {"borderBlockColor", value(BLACK)}, {"borderBlockEndColor", value(BLACK)}, {"borderBlockStartColor", value(BLACK)}, {"borderBottomColor", value(BLACK)}, {"borderBottomEndRadius", value(RelativeTo::Self, "width", 0)}, {"borderBottomLeftRadius", value(RelativeTo::Self, "width", 0)}, {"borderBottomRightRadius", value(RelativeTo::Self, "width", 0)}, {"borderBottomStartRadius", value(RelativeTo::Self, "width", 0)}, {"borderColor", value(BLACK)}, {"borderCurve", value("circular")}, {"borderEndColor", value(BLACK)}, {"borderEndEndRadius", value(RelativeTo::Self, "width", 0)}, {"borderEndStartRadius", value(RelativeTo::Self, "width", 0)}, {"borderLeftColor", value(BLACK)}, {"borderRadius", value(RelativeTo::Self, "width", 0)}, {"borderRightColor", value(BLACK)}, {"borderStartColor", value(BLACK)}, {"borderStartEndRadius", value(RelativeTo::Self, "width", 0)}, {"borderStartStartRadius", value(RelativeTo::Self, "width", 0)}, {"borderStyle", value("solid")}, {"borderTopColor", value(BLACK)}, {"borderTopEndRadius", value(RelativeTo::Self, "width", 0)}, {"borderTopLeftRadius", value(RelativeTo::Self, "width", 0)}, {"borderTopRightRadius", value(RelativeTo::Self, "width", 0)}, {"borderTopStartRadius", value(RelativeTo::Self, "width", 0)}, {"outlineColor", value(BLACK)}, {"outlineOffset", value(0)}, {"outlineStyle", value("solid")}, {"outlineWidth", value(0)}, {"opacity", value(1)}, {"elevation", value(0)}, {"pointerEvents", value("auto")}, {"isolation", value("auto")}, {"cursor", value("auto")}, {"boxShadow", array({record({ {"offsetX", value(0)}, {"offsetY", value(0)}, #ifdef ANDROID // For some reason Android crashes when blurRadius is smaller // than 1, so we use a custom value type that will never be // smaller than 1 {"blurRadius", value(1)}, #else {"blurRadius", value(0)}, #endif {"spreadDistance", value(0)}, {"color", value(TRANSPARENT)}, {"inset", value(false)}, })})}, {"mixBlendMode", value("normal")}}}); const InterpolatorFactoriesRecord TEXT_INTERPOLATORS_IOS = { {"fontVariant", value>(std::vector{})}, {"textDecorationColor", value(BLACK)}, {"textDecorationStyle", value("solid")}, {"writingDirection", value("auto")}, }; const InterpolatorFactoriesRecord TEXT_INTERPOLATORS_ANDROID = { {"textAlignVertical", value("auto")}, {"verticalAlign", value("auto")}, {"includeFontPadding", value(false)}, }; const InterpolatorFactoriesRecord TEXT_INTERPOLATORS = mergeInterpolators( {VIEW_INTERPOLATORS, TEXT_INTERPOLATORS_IOS, TEXT_INTERPOLATORS_ANDROID, InterpolatorFactoriesRecord{ {"color", value(BLACK)}, {"fontFamily", value("inherit")}, {"fontSize", value(14)}, {"fontStyle", value("normal")}, {"fontWeight", value("normal")}, {"letterSpacing", value(0)}, {"lineHeight", value(14)}, // TODO - should inherit from fontSize {"textAlign", value("auto")}, {"textDecorationLine", value("none")}, {"textShadowColor", value(BLACK)}, {"textShadowOffset", record( {{"width", value(0)}, {"height", value(0)}})}, {"textShadowRadius", value(0)}, {"textTransform", value("none")}, {"userSelect", value("auto")}, }}); const InterpolatorFactoriesRecord IMAGE_INTERPOLATORS = mergeInterpolators( {VIEW_INTERPOLATORS, InterpolatorFactoriesRecord{ {"resizeMode", value("cover")}, {"overlayColor", value(BLACK)}, {"tintColor", value(BLACK)}, }}); // ================= // SVG INTERPOLATORS // ================= const InterpolatorFactoriesRecord SVG_COLOR_INTERPOLATORS = { {"color", value(BLACK)}, }; const InterpolatorFactoriesRecord SVG_FILL_INTERPOLATORS = { {"fill", value(BLACK)}, {"fillOpacity", value(1)}, {"fillRule", value(0)}, }; const InterpolatorFactoriesRecord SVG_STROKE_INTERPOLATORS = { {"stroke", value(BLACK)}, {"strokeWidth", value(1)}, {"strokeOpacity", value(1)}, {"strokeDasharray", value(SVGStrokeDashArray())}, {"strokeDashoffset", value(0)}, {"strokeLinecap", value(0)}, {"strokeLinejoin", value(0)}, {"strokeMiterlimit", value(4)}, {"vectorEffect", value(0)}, }; const InterpolatorFactoriesRecord SVG_CLIP_INTERPOLATORS = { {"clipRule", value("nonzero")}, {"clipPath", value("none")}, }; const InterpolatorFactoriesRecord SVG_TRANSFORM_INTERPOLATORS = { {"translateX", value(0)}, {"translateY", value(0)}, {"originX", value(0)}, {"originY", value(0)}, {"scaleX", value(1)}, {"scaleY", value(1)}, {"skewX", value(0)}, {"skewY", value(0)}, {"rotation", value(0)}, }; const InterpolatorFactoriesRecord SVG_COMMON_INTERPOLATORS = mergeInterpolators({ SVG_COLOR_INTERPOLATORS, SVG_FILL_INTERPOLATORS, SVG_STROKE_INTERPOLATORS, }); const InterpolatorFactoriesRecord SVG_CIRCLE_INTERPOLATORS = mergeInterpolators( {SVG_COMMON_INTERPOLATORS, InterpolatorFactoriesRecord{ {"cx", value(0)}, {"cy", value(0)}, {"r", value(0)}, {"opacity", value(1)}, }}); const InterpolatorFactoriesRecord SVG_ELLIPSE_INTERPOLATORS = mergeInterpolators( {SVG_COMMON_INTERPOLATORS, InterpolatorFactoriesRecord{ {"cx", value(0)}, {"cy", value(0)}, {"rx", value(0)}, {"ry", value(0)}, {"opacity", value(1)}, }}); const InterpolatorFactoriesRecord SVG_LINE_INTERPOLATORS = mergeInterpolators( {SVG_COMMON_INTERPOLATORS, InterpolatorFactoriesRecord{ {"x1", value(0)}, {"y1", value(0)}, {"x2", value(0)}, {"y2", value(0)}, {"opacity", value(1)}, }}); const InterpolatorFactoriesRecord SVG_RECT_INTERPOLATORS = mergeInterpolators( {SVG_COMMON_INTERPOLATORS, InterpolatorFactoriesRecord{ {"x", value(0)}, {"y", value(0)}, {"width", value(0)}, {"height", value(0)}, {"rx", value(0)}, {"ry", value(0)}, {"opacity", value(1)}, }}); const InterpolatorFactoriesRecord SVG_PATH_INTERPOLATORS = mergeInterpolators( {SVG_COMMON_INTERPOLATORS, InterpolatorFactoriesRecord{ // TODO - add more properties }}); // ================== // COMPONENT REGISTRY // ================== ComponentInterpolatorsMap initializeRegistry() { ComponentInterpolatorsMap registry = { // React Native Components {"View", VIEW_INTERPOLATORS}, {"Paragraph", TEXT_INTERPOLATORS}, {"Image", IMAGE_INTERPOLATORS}, }; if (StaticFeatureFlags::getFlag( "EXPERIMENTAL_CSS_ANIMATIONS_FOR_SVG_COMPONENTS")) { // SVG Components registry["RNSVGCircle"] = SVG_CIRCLE_INTERPOLATORS; registry["RNSVGEllipse"] = SVG_ELLIPSE_INTERPOLATORS; registry["RNSVGLine"] = SVG_LINE_INTERPOLATORS; registry["RNSVGPath"] = SVG_PATH_INTERPOLATORS; registry["RNSVGRect"] = SVG_RECT_INTERPOLATORS; } return registry; } ComponentInterpolatorsMap registry = initializeRegistry(); } // namespace const InterpolatorFactoriesRecord &getComponentInterpolators( const std::string &componentName) { if (auto it = registry.find(componentName); it != registry.end()) { return it->second; } // Use View interpolators as a fallback for unknown components // (e.g. we get the ScrollView component name for the ScrollView component // but it should be styled in the same way as a View) return VIEW_INTERPOLATORS; } void registerComponentInterpolators( const std::string &componentName, const InterpolatorFactoriesRecord &interpolators) { registry[componentName] = interpolators; } } // namespace reanimated::css