From 807b43f267a0fce7966c2cd61618f68604f1f3b9 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Thu, 25 Jul 2024 17:09:56 -0700 Subject: [PATCH 1/3] Expose string types for `boxShadow` and `filter` Summary: We built these to be able to parse web style string values, but the types only allow object form, and the TypeScript type is wrong. Changelog: [Internal] Differential Revision: D60263730 --- .../react-native/Libraries/StyleSheet/StyleSheetTypes.js | 4 ++-- .../__tests__/__snapshots__/public-api-test.js.snap | 4 ++-- packages/react-native/types/experimental.d.ts | 7 +++++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js index 0f00224adfff..2db5431fc564 100644 --- a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js +++ b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js @@ -778,8 +778,8 @@ export type ____ViewStyle_InternalCore = $ReadOnly<{ elevation?: number, pointerEvents?: 'auto' | 'none' | 'box-none' | 'box-only', cursor?: CursorValue, - experimental_boxShadow?: $ReadOnlyArray, - experimental_filter?: $ReadOnlyArray, + experimental_boxShadow?: $ReadOnlyArray | string, + experimental_filter?: $ReadOnlyArray | string, experimental_mixBlendMode?: ____BlendMode_Internal, }>; diff --git a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap index 910d0b3f0f4f..b5532794cb6d 100644 --- a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap +++ b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap @@ -8776,8 +8776,8 @@ export type ____ViewStyle_InternalCore = $ReadOnly<{ elevation?: number, pointerEvents?: \\"auto\\" | \\"none\\" | \\"box-none\\" | \\"box-only\\", cursor?: CursorValue, - experimental_boxShadow?: $ReadOnlyArray, - experimental_filter?: $ReadOnlyArray, + experimental_boxShadow?: $ReadOnlyArray | string, + experimental_filter?: $ReadOnlyArray | string, experimental_mixBlendMode?: ____BlendMode_Internal, }>; export type ____ViewStyle_Internal = $ReadOnly<{ diff --git a/packages/react-native/types/experimental.d.ts b/packages/react-native/types/experimental.d.ts index deea3034b457..859252932a97 100644 --- a/packages/react-native/types/experimental.d.ts +++ b/packages/react-native/types/experimental.d.ts @@ -149,8 +149,11 @@ declare module '.' { } export interface ViewStyle { - experimental_boxShadow?: BoxShadowPrimitive | undefined; - experimental_filter?: ReadonlyArray | undefined; + experimental_boxShadow?: + | ReadonlyArray + | string + | undefined; + experimental_filter?: ReadonlyArray | string | undefined; experimental_mixBlendMode?: BlendMode | undefined; } } From a1bbf1721b3137a084f7751f356e031d7d33779d Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Thu, 25 Jul 2024 23:22:48 -0700 Subject: [PATCH 2/3] Add more style value types Differential Revision: D60252277 --- .../ReactAndroid/api/ReactAndroid.api | 84 +++++++++++++++++++ .../react/uimanager/style/BorderStyle.kt | 26 ++++++ .../react/uimanager/style/BoxShadow.kt | 50 +++++++++++ .../react/uimanager/style/LogicalEdge.kt | 79 +++++++++++++++++ .../react/uimanager/style/Overflow.kt | 26 ++++++ 5 files changed, 265 insertions(+) create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BorderStyle.kt create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BoxShadow.kt create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/LogicalEdge.kt create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/Overflow.kt diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 8afeee79e05f..05f2a3eba089 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -5915,6 +5915,49 @@ public final class com/facebook/react/uimanager/style/BorderRadiusStyle { public fun toString ()Ljava/lang/String; } +public final class com/facebook/react/uimanager/style/BorderStyle : java/lang/Enum { + public static final field Companion Lcom/facebook/react/uimanager/style/BorderStyle$Companion; + public static final field DASHED Lcom/facebook/react/uimanager/style/BorderStyle; + public static final field DOTTED Lcom/facebook/react/uimanager/style/BorderStyle; + public static final field SOLID Lcom/facebook/react/uimanager/style/BorderStyle; + public static final fun fromString (Ljava/lang/String;)Lcom/facebook/react/uimanager/style/BorderStyle; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/facebook/react/uimanager/style/BorderStyle; + public static fun values ()[Lcom/facebook/react/uimanager/style/BorderStyle; +} + +public final class com/facebook/react/uimanager/style/BorderStyle$Companion { + public final fun fromString (Ljava/lang/String;)Lcom/facebook/react/uimanager/style/BorderStyle; +} + +public final class com/facebook/react/uimanager/style/BoxShadow { + public static final field Companion Lcom/facebook/react/uimanager/style/BoxShadow$Companion; + public fun (FFLjava/lang/Integer;Ljava/lang/Float;Ljava/lang/Float;Ljava/lang/Boolean;)V + public synthetic fun (FFLjava/lang/Integer;Ljava/lang/Float;Ljava/lang/Float;Ljava/lang/Boolean;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()F + public final fun component2 ()F + public final fun component3 ()Ljava/lang/Integer; + public final fun component4 ()Ljava/lang/Float; + public final fun component5 ()Ljava/lang/Float; + public final fun component6 ()Ljava/lang/Boolean; + public final fun copy (FFLjava/lang/Integer;Ljava/lang/Float;Ljava/lang/Float;Ljava/lang/Boolean;)Lcom/facebook/react/uimanager/style/BoxShadow; + public static synthetic fun copy$default (Lcom/facebook/react/uimanager/style/BoxShadow;FFLjava/lang/Integer;Ljava/lang/Float;Ljava/lang/Float;Ljava/lang/Boolean;ILjava/lang/Object;)Lcom/facebook/react/uimanager/style/BoxShadow; + public fun equals (Ljava/lang/Object;)Z + public final fun getBlurRadius ()Ljava/lang/Float; + public final fun getColor ()Ljava/lang/Integer; + public final fun getInset ()Ljava/lang/Boolean; + public final fun getOffsetX ()F + public final fun getOffsetY ()F + public final fun getSpreadDistance ()Ljava/lang/Float; + public fun hashCode ()I + public static final fun parse (Lcom/facebook/react/bridge/ReadableMap;)Lcom/facebook/react/uimanager/style/BoxShadow; + public fun toString ()Ljava/lang/String; +} + +public final class com/facebook/react/uimanager/style/BoxShadow$Companion { + public final fun parse (Lcom/facebook/react/bridge/ReadableMap;)Lcom/facebook/react/uimanager/style/BoxShadow; +} + public final class com/facebook/react/uimanager/style/ComputedBorderRadius { public fun ()V public fun (FFFF)V @@ -5945,6 +5988,47 @@ public final class com/facebook/react/uimanager/style/ComputedBorderRadiusProp : public static fun values ()[Lcom/facebook/react/uimanager/style/ComputedBorderRadiusProp; } +public abstract class com/facebook/react/uimanager/style/LogicalEdge : java/lang/Enum { + public static final field ALL Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field BLOCK Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field BLOCK_END Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field BLOCK_START Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field BOTTOM Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field Companion Lcom/facebook/react/uimanager/style/LogicalEdge$Companion; + public static final field END Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field HORIZONTAL Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field LEFT Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field RIGHT Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field START Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field TOP Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field VERTICAL Lcom/facebook/react/uimanager/style/LogicalEdge; + public synthetic fun (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public static final fun fromSpacingType (I)Lcom/facebook/react/uimanager/style/LogicalEdge; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public abstract fun toSpacingType ()I + public static fun valueOf (Ljava/lang/String;)Lcom/facebook/react/uimanager/style/LogicalEdge; + public static fun values ()[Lcom/facebook/react/uimanager/style/LogicalEdge; +} + +public final class com/facebook/react/uimanager/style/LogicalEdge$Companion { + public final fun fromSpacingType (I)Lcom/facebook/react/uimanager/style/LogicalEdge; +} + +public final class com/facebook/react/uimanager/style/Overflow : java/lang/Enum { + public static final field Companion Lcom/facebook/react/uimanager/style/Overflow$Companion; + public static final field HIDDEN Lcom/facebook/react/uimanager/style/Overflow; + public static final field SCROLL Lcom/facebook/react/uimanager/style/Overflow; + public static final field VISIBLE Lcom/facebook/react/uimanager/style/Overflow; + public static final fun fromString (Ljava/lang/String;)Lcom/facebook/react/uimanager/style/Overflow; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/facebook/react/uimanager/style/Overflow; + public static fun values ()[Lcom/facebook/react/uimanager/style/Overflow; +} + +public final class com/facebook/react/uimanager/style/Overflow$Companion { + public final fun fromString (Ljava/lang/String;)Lcom/facebook/react/uimanager/style/Overflow; +} + public class com/facebook/react/uimanager/util/ReactFindViewUtil { public fun ()V public static fun addViewListener (Lcom/facebook/react/uimanager/util/ReactFindViewUtil$OnViewFoundListener;)V diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BorderStyle.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BorderStyle.kt new file mode 100644 index 000000000000..c23030b3fc90 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BorderStyle.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.uimanager.style + +public enum class BorderStyle { + SOLID, + DASHED, + DOTTED; + + public companion object { + @JvmStatic + public fun fromString(borderStyle: String): BorderStyle? { + return when (borderStyle.lowercase()) { + "solid" -> SOLID + "dashed" -> DASHED + "dotted" -> DOTTED + else -> null + } + } + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BoxShadow.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BoxShadow.kt new file mode 100644 index 000000000000..0282f4581a86 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BoxShadow.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.uimanager.style + +import androidx.annotation.ColorInt +import com.facebook.react.bridge.ReadableMap + +/** Represents all logical properties and shorthands for border radius. */ +public data class BoxShadow( + val offsetX: Float, + val offsetY: Float, + @ColorInt val color: Int? = null, + val blurRadius: Float? = null, + val spreadDistance: Float? = null, + val inset: Boolean? = null, +) { + public companion object { + @JvmStatic + public fun parse(boxShadow: ReadableMap): BoxShadow? { + if (!(boxShadow.hasKey("offsetX") && boxShadow.hasKey("offsetY"))) { + return null + } + + val offsetX = boxShadow.getDouble("offsetX").toFloat() + val offsetY = boxShadow.getDouble("offsetY").toFloat() + + val color = if (boxShadow.hasKey("color")) boxShadow.getInt("color") else null + val blurRadius = + if (boxShadow.hasKey("blurRadius")) boxShadow.getDouble("blurRadius").toFloat() else null + val spreadDistance = + if (boxShadow.hasKey("spreadDistance")) boxShadow.getDouble("spreadDistance").toFloat() + else null + val inset = if (boxShadow.hasKey("inset")) boxShadow.getBoolean("inset") else null + + return BoxShadow( + offsetX = offsetX, + offsetY = offsetY, + color = color, + blurRadius = blurRadius, + spreadDistance = spreadDistance, + inset = inset, + ) + } + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/LogicalEdge.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/LogicalEdge.kt new file mode 100644 index 000000000000..1f35909d6f00 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/LogicalEdge.kt @@ -0,0 +1,79 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.uimanager.style + +import com.facebook.react.uimanager.Spacing +import java.lang.IllegalArgumentException + +/** Represents the collection of possible box edges and shorthands. */ +public enum class LogicalEdge { + ALL { + override fun toSpacingType(): Int = Spacing.ALL + }, + LEFT { + override fun toSpacingType(): Int = Spacing.LEFT + }, + RIGHT { + override fun toSpacingType(): Int = Spacing.RIGHT + }, + TOP { + override fun toSpacingType(): Int = Spacing.TOP + }, + BOTTOM { + override fun toSpacingType(): Int = Spacing.BOTTOM + }, + START { + override fun toSpacingType(): Int = Spacing.START + }, + END { + override fun toSpacingType(): Int = Spacing.END + }, + HORIZONTAL { + override fun toSpacingType(): Int = Spacing.HORIZONTAL + }, + VERTICAL { + override fun toSpacingType(): Int = Spacing.VERTICAL + }, + BLOCK_START { + override fun toSpacingType(): Int = Spacing.BLOCK_START + }, + BLOCK_END { + override fun toSpacingType(): Int = Spacing.BLOCK_END + }, + BLOCK { + override fun toSpacingType(): Int = Spacing.BLOCK + }; + + // TODO: not supported by Spacing users + // INLINE_START, + // INLINE_END, + // INLINE; + + abstract public fun toSpacingType(): Int + + public companion object { + @JvmStatic + public fun fromSpacingType(spacingType: Int): LogicalEdge { + return when (spacingType) { + Spacing.ALL -> ALL + Spacing.LEFT -> LEFT + Spacing.RIGHT -> RIGHT + Spacing.TOP -> TOP + Spacing.BOTTOM -> BOTTOM + Spacing.START -> START + Spacing.END -> END + Spacing.HORIZONTAL -> HORIZONTAL + Spacing.VERTICAL -> VERTICAL + Spacing.BLOCK_START -> BLOCK_START + Spacing.BLOCK_END -> BLOCK_END + Spacing.BLOCK -> BLOCK + else -> throw IllegalArgumentException("Unknown spacing type: $spacingType") + } + } + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/Overflow.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/Overflow.kt new file mode 100644 index 000000000000..a057a79b144d --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/Overflow.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.uimanager.style + +public enum class Overflow { + VISIBLE, + HIDDEN, + SCROLL; + + public companion object { + @JvmStatic + public fun fromString(overflow: String): Overflow { + return when (overflow.lowercase()) { + "visible" -> VISIBLE + "hidden" -> HIDDEN + "scroll" -> SCROLL + else -> throw IllegalArgumentException("Unknown overflow: $overflow") + } + } + } +} From 7813fd8bb9bdca5645572aacf433db2677943e19 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Thu, 25 Jul 2024 23:30:16 -0700 Subject: [PATCH 3/3] Expose more in CSSBackgroundDrawable (#45691) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/45691 1. Add some accessors, so we can keep accessors and setters symetric 2. Use the shared BorderStyle enum added in last diff 3. Fix some missing invalidation on setting style Changelog: [Internal] Differential Revision: D60252276 --- .../drawable/CSSBackgroundDrawable.java | 59 +++++++++++-------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java index 7369bc9de442..6b3bea46ab28 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java @@ -36,6 +36,7 @@ import com.facebook.react.uimanager.Spacing; import com.facebook.react.uimanager.style.BorderRadiusProp; import com.facebook.react.uimanager.style.BorderRadiusStyle; +import com.facebook.react.uimanager.style.BorderStyle; import com.facebook.react.uimanager.style.ComputedBorderRadius; import java.util.Locale; import java.util.Objects; @@ -64,29 +65,23 @@ public class CSSBackgroundDrawable extends Drawable { // 0 == 0x00000000, all bits set to 0. private static final int ALL_BITS_UNSET = 0; - private enum BorderStyle { - SOLID, - DASHED, - DOTTED; + private static @Nullable PathEffect getPathEffect(BorderStyle style, float borderWidth) { + switch (style) { + case SOLID: + return null; - public static @Nullable PathEffect getPathEffect(BorderStyle style, float borderWidth) { - switch (style) { - case SOLID: - return null; + case DASHED: + return new DashPathEffect( + new float[] {borderWidth * 3, borderWidth * 3, borderWidth * 3, borderWidth * 3}, 0); - case DASHED: - return new DashPathEffect( - new float[] {borderWidth * 3, borderWidth * 3, borderWidth * 3, borderWidth * 3}, 0); + case DOTTED: + return new DashPathEffect( + new float[] {borderWidth, borderWidth, borderWidth, borderWidth}, 0); - case DOTTED: - return new DashPathEffect( - new float[] {borderWidth, borderWidth, borderWidth, borderWidth}, 0); - - default: - return null; - } + default: + return null; } - }; + } /* Value at Spacing.ALL index used for rounded borders, whole array used by rectangular borders */ private @Nullable Spacing mBorderWidth; @@ -255,6 +250,10 @@ private void setBorderAlpha(int position, float alpha) { public void setBorderStyle(@Nullable String style) { BorderStyle borderStyle = style == null ? null : BorderStyle.valueOf(style.toUpperCase(Locale.US)); + setBorderStyle(borderStyle); + } + + public void setBorderStyle(@Nullable BorderStyle borderStyle) { if (mBorderStyle != borderStyle) { mBorderStyle = borderStyle; mNeedUpdatePathForBorderRadius = true; @@ -262,6 +261,10 @@ public void setBorderStyle(@Nullable String style) { } } + public @Nullable BorderStyle getBorderStyle() { + return mBorderStyle; + } + /** * @deprecated Use {@link #setBorderRadius(BorderRadiusProp, LengthPercentage)} instead. */ @@ -286,6 +289,7 @@ public void setRadius(float radius, int position) { if (boxedRadius == null) { mBorderRadius.set(BorderRadiusProp.values()[position], null); + invalidateSelf(); } else { setBorderRadius( BorderRadiusProp.values()[position], @@ -1012,14 +1016,23 @@ private static void getEllipseIntersectionWithLine( } public float getBorderWidthOrDefaultTo(final float defaultValue, final int spacingType) { - if (mBorderWidth == null) { + @Nullable Float width = getBorderWidth(spacingType); + if (width == null) { return defaultValue; } + return width; + } + + public @Nullable Float getBorderWidth(int spacingType) { + if (mBorderWidth == null) { + return null; + } + final float width = mBorderWidth.getRaw(spacingType); if (Float.isNaN(width)) { - return defaultValue; + return null; } return width; @@ -1029,7 +1042,7 @@ public float getBorderWidthOrDefaultTo(final float defaultValue, final int spaci private void updatePathEffect() { // Used for rounded border and rounded background PathEffect mPathEffectForBorderStyle = - mBorderStyle != null ? BorderStyle.getPathEffect(mBorderStyle, getFullBorderWidth()) : null; + mBorderStyle != null ? getPathEffect(mBorderStyle, getFullBorderWidth()) : null; mPaint.setPathEffect(mPathEffectForBorderStyle); } @@ -1037,7 +1050,7 @@ private void updatePathEffect() { private void updatePathEffect(int borderWidth) { PathEffect pathEffectForBorderStyle = null; if (mBorderStyle != null) { - pathEffectForBorderStyle = BorderStyle.getPathEffect(mBorderStyle, borderWidth); + pathEffectForBorderStyle = getPathEffect(mBorderStyle, borderWidth); } mPaint.setPathEffect(pathEffectForBorderStyle); }