diff --git a/android/paper/src/main/java/com/facebook/react/viewmanagers/RNGestureHandlerButtonManagerDelegate.java b/android/paper/src/main/java/com/facebook/react/viewmanagers/RNGestureHandlerButtonManagerDelegate.java index 755f026e14..4f5efa64fc 100644 --- a/android/paper/src/main/java/com/facebook/react/viewmanagers/RNGestureHandlerButtonManagerDelegate.java +++ b/android/paper/src/main/java/com/facebook/react/viewmanagers/RNGestureHandlerButtonManagerDelegate.java @@ -12,6 +12,7 @@ import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.ColorPropConverter; +import com.facebook.react.bridge.DynamicFromObject; import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.BaseViewManagerDelegate; import com.facebook.react.uimanager.LayoutShadowNode; @@ -44,12 +45,63 @@ public void setProperty(T view, String propName, @Nullable Object value) { case "touchSoundDisabled": mViewManager.setTouchSoundDisabled(view, value == null ? false : (boolean) value); break; + case "borderRadius": + mViewManager.setBorderRadius(view, new DynamicFromObject(value)); + break; + case "borderTopLeftRadius": + mViewManager.setBorderTopLeftRadius(view, new DynamicFromObject(value)); + break; + case "borderTopRightRadius": + mViewManager.setBorderTopRightRadius(view, new DynamicFromObject(value)); + break; + case "borderBottomLeftRadius": + mViewManager.setBorderBottomLeftRadius(view, new DynamicFromObject(value)); + break; + case "borderBottomRightRadius": + mViewManager.setBorderBottomRightRadius(view, new DynamicFromObject(value)); + break; case "borderWidth": mViewManager.setBorderWidth(view, value == null ? 0f : ((Double) value).floatValue()); break; + case "borderLeftWidth": + mViewManager.setBorderLeftWidth(view, value == null ? 0f : ((Double) value).floatValue()); + break; + case "borderRightWidth": + mViewManager.setBorderRightWidth(view, value == null ? 0f : ((Double) value).floatValue()); + break; + case "borderTopWidth": + mViewManager.setBorderTopWidth(view, value == null ? 0f : ((Double) value).floatValue()); + break; + case "borderBottomWidth": + mViewManager.setBorderBottomWidth(view, value == null ? 0f : ((Double) value).floatValue()); + break; + case "borderStartWidth": + mViewManager.setBorderStartWidth(view, value == null ? 0f : ((Double) value).floatValue()); + break; + case "borderEndWidth": + mViewManager.setBorderEndWidth(view, value == null ? 0f : ((Double) value).floatValue()); + break; case "borderColor": mViewManager.setBorderColor(view, ColorPropConverter.getColor(value, view.getContext())); break; + case "borderLeftColor": + mViewManager.setBorderLeftColor(view, ColorPropConverter.getColor(value, view.getContext())); + break; + case "borderRightColor": + mViewManager.setBorderRightColor(view, ColorPropConverter.getColor(value, view.getContext())); + break; + case "borderTopColor": + mViewManager.setBorderTopColor(view, ColorPropConverter.getColor(value, view.getContext())); + break; + case "borderBottomColor": + mViewManager.setBorderBottomColor(view, ColorPropConverter.getColor(value, view.getContext())); + break; + case "borderStartColor": + mViewManager.setBorderStartColor(view, ColorPropConverter.getColor(value, view.getContext())); + break; + case "borderEndColor": + mViewManager.setBorderEndColor(view, ColorPropConverter.getColor(value, view.getContext())); + break; case "borderStyle": mViewManager.setBorderStyle(view, value == null ? "solid" : (String) value); break; diff --git a/android/paper/src/main/java/com/facebook/react/viewmanagers/RNGestureHandlerButtonManagerInterface.java b/android/paper/src/main/java/com/facebook/react/viewmanagers/RNGestureHandlerButtonManagerInterface.java index 975704f08b..a8b98eee4c 100644 --- a/android/paper/src/main/java/com/facebook/react/viewmanagers/RNGestureHandlerButtonManagerInterface.java +++ b/android/paper/src/main/java/com/facebook/react/viewmanagers/RNGestureHandlerButtonManagerInterface.java @@ -11,6 +11,7 @@ import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.bridge.Dynamic; import com.facebook.react.uimanager.ViewManagerWithGeneratedInterface; public interface RNGestureHandlerButtonManagerInterface extends ViewManagerWithGeneratedInterface { @@ -21,7 +22,24 @@ public interface RNGestureHandlerButtonManagerInterface extends void setRippleColor(T view, @Nullable Integer value); void setRippleRadius(T view, int value); void setTouchSoundDisabled(T view, boolean value); + void setBorderRadius(T view, Dynamic value); + void setBorderTopLeftRadius(T view, Dynamic value); + void setBorderTopRightRadius(T view, Dynamic value); + void setBorderBottomLeftRadius(T view, Dynamic value); + void setBorderBottomRightRadius(T view, Dynamic value); void setBorderWidth(T view, float value); + void setBorderLeftWidth(T view, float value); + void setBorderRightWidth(T view, float value); + void setBorderTopWidth(T view, float value); + void setBorderBottomWidth(T view, float value); + void setBorderStartWidth(T view, float value); + void setBorderEndWidth(T view, float value); void setBorderColor(T view, @Nullable Integer value); + void setBorderLeftColor(T view, @Nullable Integer value); + void setBorderRightColor(T view, @Nullable Integer value); + void setBorderTopColor(T view, @Nullable Integer value); + void setBorderBottomColor(T view, @Nullable Integer value); + void setBorderStartColor(T view, @Nullable Integer value); + void setBorderEndColor(T view, @Nullable Integer value); void setBorderStyle(T view, @Nullable String value); } diff --git a/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt b/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt index 053c0464f8..bd3056c891 100644 --- a/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt +++ b/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt @@ -3,17 +3,6 @@ package com.swmansion.gesturehandler.react import android.annotation.SuppressLint import android.annotation.TargetApi import android.content.Context -import android.content.res.ColorStateList -import android.graphics.Color -import android.graphics.DashPathEffect -import android.graphics.Paint -import android.graphics.PathEffect -import android.graphics.drawable.Drawable -import android.graphics.drawable.LayerDrawable -import android.graphics.drawable.PaintDrawable -import android.graphics.drawable.RippleDrawable -import android.graphics.drawable.ShapeDrawable -import android.graphics.drawable.shapes.RectShape import android.os.Build import android.util.TypedValue import android.view.KeyEvent @@ -25,24 +14,41 @@ import android.view.ViewParent import android.view.accessibility.AccessibilityNodeInfo import androidx.core.view.children import com.facebook.react.R +import com.facebook.react.bridge.Dynamic +import com.facebook.react.bridge.DynamicFromObject import com.facebook.react.module.annotations.ReactModule +import com.facebook.react.uimanager.LengthPercentage +import com.facebook.react.uimanager.LengthPercentageType import com.facebook.react.uimanager.PixelUtil import com.facebook.react.uimanager.ThemedReactContext import com.facebook.react.uimanager.ViewGroupManager import com.facebook.react.uimanager.ViewManagerDelegate -import com.facebook.react.uimanager.ViewProps import com.facebook.react.uimanager.annotations.ReactProp +import com.facebook.react.uimanager.common.UIManagerType +import com.facebook.react.uimanager.common.ViewUtil +import com.facebook.react.uimanager.style.BorderRadiusProp import com.facebook.react.viewmanagers.RNGestureHandlerButtonManagerDelegate import com.facebook.react.viewmanagers.RNGestureHandlerButtonManagerInterface +import com.facebook.react.views.view.ReactViewGroup import com.swmansion.gesturehandler.core.NativeViewGestureHandler import com.swmansion.gesturehandler.react.RNGestureHandlerButtonViewManager.ButtonViewGroup @ReactModule(name = RNGestureHandlerButtonViewManager.REACT_CLASS) class RNGestureHandlerButtonViewManager : ViewGroupManager(), RNGestureHandlerButtonManagerInterface { - private val mDelegate: ViewManagerDelegate + private val mDelegate: ViewManagerDelegate = + RNGestureHandlerButtonManagerDelegate(this) - init { - mDelegate = RNGestureHandlerButtonManagerDelegate(this) + private fun borderRadiusFromDynamic(view: ButtonViewGroup, value: Dynamic): LengthPercentage? { + var borderRadius = LengthPercentage.setFromDynamic(DynamicFromObject(value)) + + if (ViewUtil.getUIManagerType(view) != UIManagerType.FABRIC && + borderRadius != null && + borderRadius.type == LengthPercentageType.PERCENT + ) { + borderRadius = null + } + + return borderRadius } override fun getName() = REACT_CLASS @@ -57,7 +63,7 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager(), R @ReactProp(name = "backgroundColor") override fun setBackgroundColor(view: ButtonViewGroup, backgroundColor: Int) { - view.setBackgroundColor(backgroundColor) + view.backgroundColor = backgroundColor } @ReactProp(name = "borderless") @@ -70,44 +76,106 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager(), R view.isEnabled = enabled } - @ReactProp(name = ViewProps.BORDER_RADIUS) - override fun setBorderRadius(view: ButtonViewGroup, borderRadius: Float) { - view.borderRadius = borderRadius + @ReactProp(name = "borderRadius") + override fun setBorderRadius(view: ButtonViewGroup, borderRadius: Dynamic) { + view.setBorderRadius(BorderRadiusProp.BORDER_RADIUS, borderRadiusFromDynamic(view, borderRadius)) } @ReactProp(name = "borderTopLeftRadius") - override fun setBorderTopLeftRadius(view: ButtonViewGroup, borderTopLeftRadius: Float) { - view.borderTopLeftRadius = borderTopLeftRadius + override fun setBorderTopLeftRadius(view: ButtonViewGroup, borderTopLeftRadius: Dynamic) { + view.setBorderRadius(BorderRadiusProp.BORDER_TOP_LEFT_RADIUS, borderRadiusFromDynamic(view, borderTopLeftRadius)) } @ReactProp(name = "borderTopRightRadius") - override fun setBorderTopRightRadius(view: ButtonViewGroup, borderTopRightRadius: Float) { - view.borderTopRightRadius = borderTopRightRadius + override fun setBorderTopRightRadius(view: ButtonViewGroup, borderTopRightRadius: Dynamic) { + view.setBorderRadius(BorderRadiusProp.BORDER_TOP_RIGHT_RADIUS, borderRadiusFromDynamic(view, borderTopRightRadius)) } @ReactProp(name = "borderBottomLeftRadius") - override fun setBorderBottomLeftRadius(view: ButtonViewGroup, borderBottomLeftRadius: Float) { - view.borderBottomLeftRadius = borderBottomLeftRadius + override fun setBorderBottomLeftRadius(view: ButtonViewGroup, borderBottomLeftRadius: Dynamic) { + view.setBorderRadius(BorderRadiusProp.BORDER_BOTTOM_LEFT_RADIUS, borderRadiusFromDynamic(view, borderBottomLeftRadius)) } @ReactProp(name = "borderBottomRightRadius") - override fun setBorderBottomRightRadius(view: ButtonViewGroup, borderBottomRightRadius: Float) { - view.borderBottomRightRadius = borderBottomRightRadius + override fun setBorderBottomRightRadius(view: ButtonViewGroup, borderBottomRightRadius: Dynamic) { + view.setBorderRadius(BorderRadiusProp.BORDER_BOTTOM_RIGHT_RADIUS, borderRadiusFromDynamic(view, borderBottomRightRadius)) } @ReactProp(name = "borderWidth") override fun setBorderWidth(view: ButtonViewGroup, borderWidth: Float) { - view.borderWidth = borderWidth + view.setBorderWidth(SIDE_ALL, PixelUtil.toPixelFromDIP(borderWidth)) + } + + @ReactProp(name = "borderLeftWidth") + override fun setBorderLeftWidth(view: ButtonViewGroup, borderLeftWidth: Float) { + view.setBorderWidth(SIDE_LEFT, PixelUtil.toPixelFromDIP(borderLeftWidth)) + } + + @ReactProp(name = "borderRightWidth") + override fun setBorderRightWidth(view: ButtonViewGroup, borderRightWidth: Float) { + view.setBorderWidth(SIDE_RIGHT, PixelUtil.toPixelFromDIP(borderRightWidth)) + } + + @ReactProp(name = "borderTopWidth") + override fun setBorderTopWidth(view: ButtonViewGroup, borderTopWidth: Float) { + view.setBorderWidth(SIDE_TOP, PixelUtil.toPixelFromDIP(borderTopWidth)) + } + + @ReactProp(name = "borderBottomWidth") + override fun setBorderBottomWidth(view: ButtonViewGroup, borderBottomWidth: Float) { + view.setBorderWidth(SIDE_BOTTOM, PixelUtil.toPixelFromDIP(borderBottomWidth)) + } + + @ReactProp(name = "borderStartWidth") + override fun setBorderStartWidth(view: ButtonViewGroup, borderStartWidth: Float) { + // todo: make this dependant on the RTL setting + view.setBorderWidth(SIDE_LEFT, PixelUtil.toPixelFromDIP(borderStartWidth)) + } + + @ReactProp(name = "borderEndWidth") + override fun setBorderEndWidth(view: ButtonViewGroup, borderEndWidth: Float) { + view.setBorderWidth(SIDE_RIGHT, PixelUtil.toPixelFromDIP(borderEndWidth)) } @ReactProp(name = "borderColor") override fun setBorderColor(view: ButtonViewGroup, borderColor: Int?) { - view.borderColor = borderColor + view.setBorderColor(SIDE_ALL, borderColor) + } + + @ReactProp(name = "borderLeftColor") + override fun setBorderLeftColor(view: ButtonViewGroup, borderLeftColor: Int?) { + view.setBorderColor(SIDE_LEFT, borderLeftColor) + } + + @ReactProp(name = "borderRightColor") + override fun setBorderRightColor(view: ButtonViewGroup, borderRightColor: Int?) { + view.setBorderColor(SIDE_RIGHT, borderRightColor) + } + + @ReactProp(name = "borderTopColor") + override fun setBorderTopColor(view: ButtonViewGroup, borderTopColor: Int?) { + view.setBorderColor(SIDE_TOP, borderTopColor) + } + + @ReactProp(name = "borderBottomColor") + override fun setBorderBottomColor(view: ButtonViewGroup, borderBottomColor: Int?) { + view.setBorderColor(SIDE_BOTTOM, borderBottomColor) + } + + @ReactProp(name = "borderStartColor") + override fun setBorderStartColor(view: ButtonViewGroup, borderStartColor: Int?) { + // todo: make this dependant on the RTL setting + view.setBorderColor(SIDE_LEFT, borderStartColor) + } + + @ReactProp(name = "borderEndColor") + override fun setBorderEndColor(view: ButtonViewGroup, borderEndColor: Int?) { + view.setBorderColor(SIDE_RIGHT, borderEndColor) } @ReactProp(name = "borderStyle") override fun setBorderStyle(view: ButtonViewGroup, borderStyle: String?) { - view.borderStyle = borderStyle + view.setBorderStyle(borderStyle) } @ReactProp(name = "rippleColor") @@ -130,18 +198,12 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager(), R view.isSoundEffectsEnabled = !touchSoundDisabled } - override fun onAfterUpdateTransaction(view: ButtonViewGroup) { - super.onAfterUpdateTransaction(view) - - view.updateBackground() - } - - override fun getDelegate(): ViewManagerDelegate? { + override fun getDelegate(): ViewManagerDelegate { return mDelegate } class ButtonViewGroup(context: Context?) : - ViewGroup(context), + ReactViewGroup(context), NativeViewGestureHandler.NativeViewGestureHandlerHook { // Using object because of handling null representing no value set. var rippleColor: Int? = null @@ -153,54 +215,16 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager(), R set(radius) = withBackgroundUpdate { field = radius } + var useDrawableOnForeground = false set(useForeground) = withBackgroundUpdate { field = useForeground } - var useBorderlessDrawable = false - var borderRadius = 0f - set(radius) = withBackgroundUpdate { - field = radius * resources.displayMetrics.density - } - var borderTopLeftRadius = 0f - set(radius) = withBackgroundUpdate { - field = radius * resources.displayMetrics.density - } - var borderTopRightRadius = 0f - set(radius) = withBackgroundUpdate { - field = radius * resources.displayMetrics.density - } - var borderBottomLeftRadius = 0f - set(radius) = withBackgroundUpdate { - field = radius * resources.displayMetrics.density - } - var borderBottomRightRadius = 0f - set(radius) = withBackgroundUpdate { - field = radius * resources.displayMetrics.density - } - var borderWidth = 0f - set(width) = withBackgroundUpdate { - field = width * resources.displayMetrics.density - } - var borderColor: Int? = null - set(color) = withBackgroundUpdate { - field = color - } - var borderStyle: String? = "solid" - set(style) = withBackgroundUpdate { - field = style - } - private val hasBorderRadii: Boolean - get() = borderRadius != 0f || - borderTopLeftRadius != 0f || - borderTopRightRadius != 0f || - borderBottomLeftRadius != 0f || - borderBottomRightRadius != 0f + var useBorderlessDrawable = false var exclusive = true - private var _backgroundColor = Color.TRANSPARENT private var needBackgroundUpdate = false private var lastEventTime = -1L private var lastAction = -1 @@ -222,34 +246,6 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager(), R needBackgroundUpdate = true } - private fun buildBorderRadii(): FloatArray { - // duplicate radius for each corner, as setCornerRadii expects X radius and Y radius for each - return floatArrayOf( - borderTopLeftRadius, - borderTopLeftRadius, - borderTopRightRadius, - borderTopRightRadius, - borderBottomRightRadius, - borderBottomRightRadius, - borderBottomLeftRadius, - borderBottomLeftRadius, - ) - .map { if (it != 0f) it else borderRadius } - .toFloatArray() - } - - private fun buildBorderStyle(): PathEffect? { - return when (borderStyle) { - "dotted" -> DashPathEffect(floatArrayOf(borderWidth, borderWidth, borderWidth, borderWidth), 0f) - "dashed" -> DashPathEffect(floatArrayOf(borderWidth * 3, borderWidth * 3, borderWidth * 3, borderWidth * 3), 0f) - else -> null - } - } - - override fun setBackgroundColor(color: Int) = withBackgroundUpdate { - _backgroundColor = color - } - override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) { super.onInitializeAccessibilityNodeInfo(info) @@ -318,102 +314,20 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager(), R return false } - private fun updateBackgroundColor(backgroundColor: Int, borderDrawable: Drawable, selectable: Drawable?) { - val colorDrawable = PaintDrawable(backgroundColor) - - if (hasBorderRadii) { - colorDrawable.setCornerRadii(buildBorderRadii()) - } - - val layerDrawable = LayerDrawable(if (selectable != null) arrayOf(colorDrawable, selectable, borderDrawable) else arrayOf(colorDrawable, borderDrawable)) - background = layerDrawable - } - - fun updateBackground() { - if (!needBackgroundUpdate) { - return - } - needBackgroundUpdate = false - - if (_backgroundColor == Color.TRANSPARENT) { - // reset background - background = null - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - // reset foreground - foreground = null - } - - val selectable = createSelectableDrawable() - val borderDrawable = createBorderDrawable() - - if (hasBorderRadii && selectable is RippleDrawable) { - val mask = PaintDrawable(Color.WHITE) - mask.setCornerRadii(buildBorderRadii()) - selectable.setDrawableByLayerId(android.R.id.mask, mask) - } - - if (useDrawableOnForeground && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - foreground = selectable - if (_backgroundColor != Color.TRANSPARENT) { - updateBackgroundColor(_backgroundColor, borderDrawable, null) - } - } else if (_backgroundColor == Color.TRANSPARENT && rippleColor == null) { - background = LayerDrawable(arrayOf(selectable, borderDrawable)) - } else { - updateBackgroundColor(_backgroundColor, borderDrawable, selectable) - } - } - - private fun createBorderDrawable(): Drawable { - val borderDrawable = PaintDrawable(Color.TRANSPARENT) - - if (hasBorderRadii) { - borderDrawable.setCornerRadii(buildBorderRadii()) - } - - if (borderWidth > 0f) { - borderDrawable.paint.apply { - style = Paint.Style.STROKE - strokeWidth = borderWidth - color = borderColor ?: Color.BLACK - pathEffect = buildBorderStyle() - } - } - - return borderDrawable - } - - private fun createSelectableDrawable(): Drawable? { - // don't create ripple drawable at all when it's not supposed to be visible - if (rippleColor == Color.TRANSPARENT) { - return null - } - - val states = arrayOf(intArrayOf(android.R.attr.state_enabled)) - val rippleRadius = rippleRadius - val colorStateList = if (rippleColor != null) { - val colors = intArrayOf(rippleColor!!) - ColorStateList(states, colors) - } else { - // if rippleColor is null, reapply the default color - context.theme.resolveAttribute(android.R.attr.colorControlHighlight, resolveOutValue, true) - val colors = intArrayOf(resolveOutValue.data) - ColorStateList(states, colors) - } - - val drawable = RippleDrawable( - colorStateList, - null, - if (useBorderlessDrawable) null else ShapeDrawable(RectShape()) - ) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && rippleRadius != null) { - drawable.radius = PixelUtil.toPixelFromDIP(rippleRadius.toFloat()).toInt() - } - - return drawable - } +// fun updateBackground() { +// if (!needBackgroundUpdate) { +// return +// } +// needBackgroundUpdate = false +// +// val selectable = createSelectableDrawable() +// +// if (selectable is RippleDrawable) { +// val mask = PaintDrawable(Color.WHITE) +// mask.setCornerRadii(buildBorderRadii()) +// selectable.setDrawableByLayerId(android.R.id.mask, mask) +// } +// } override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { // No-op @@ -564,5 +478,10 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager(), R companion object { const val REACT_CLASS = "RNGestureHandlerButton" + const val SIDE_ALL = 0 + const val SIDE_LEFT = 1 + const val SIDE_RIGHT = 2 + const val SIDE_TOP = 3 + const val SIDE_BOTTOM = 4 } } diff --git a/src/specs/RNGestureHandlerButtonNativeComponent.ts b/src/specs/RNGestureHandlerButtonNativeComponent.ts index 8d4c3da4b9..5966cf47e6 100644 --- a/src/specs/RNGestureHandlerButtonNativeComponent.ts +++ b/src/specs/RNGestureHandlerButtonNativeComponent.ts @@ -3,6 +3,7 @@ import type { Int32, WithDefault, Float, + UnsafeMixed, } from 'react-native/Libraries/Types/CodegenTypes'; import type { ViewProps, ColorValue } from 'react-native'; @@ -14,8 +15,31 @@ interface NativeProps extends ViewProps { rippleColor?: ColorValue; rippleRadius?: Int32; touchSoundDisabled?: WithDefault; + + // explicitly redefine `borderFooRadius`, as `ViewProps` defines it as `Float`, + // but as of RN 0.75.0 it should be defined as `Dynamic` (`Double | string` or `UnsafeMixed`). + borderRadius?: UnsafeMixed; + borderTopLeftRadius?: UnsafeMixed; + borderTopRightRadius?: UnsafeMixed; + borderBottomLeftRadius?: UnsafeMixed; + borderBottomRightRadius?: UnsafeMixed; + borderWidth?: Float; + borderLeftWidth?: Float; + borderRightWidth?: Float; + borderTopWidth?: Float; + borderBottomWidth?: Float; + borderStartWidth?: Float; + borderEndWidth?: Float; + borderColor?: ColorValue; + borderLeftColor?: ColorValue; + borderRightColor?: ColorValue; + borderTopColor?: ColorValue; + borderBottomColor?: ColorValue; + borderStartColor?: ColorValue; + borderEndColor?: ColorValue; + borderStyle?: WithDefault; }