mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
feat: flex gap bindings (#34974)
Summary: This PR adds React native binding for https://github.com/facebook/yoga/pull/1116 ## Changelog [General] [Added] - Flex gap yoga bindings <!-- Help reviewers and the release process by writing your own changelog entry. For an example, see: https://reactnative.dev/contributing/changelogs-in-pull-requests --> Pull Request resolved: https://github.com/facebook/react-native/pull/34974 Test Plan: Run rn tester app and go to view example. You'll find a flex gap example. Example location - `packages/rn-tester/js/examples/View/ViewExample.js` ### Tested on - [x] iOS Fabric - [x] iOS non-fabric - [x] Android Fabric - [x] Android non-fabric To test on non-fabric Android, I just switched these booleans. Let me know if there's anything else I might have missed. <img width="674" alt="Screenshot 2022-10-14 at 3 30 48 AM" src="https://user-images.githubusercontent.com/23293248/195718971-7aee4e7e-dbf0-4452-9d47-7925919c61dc.png"> Reviewed By: mdvacca Differential Revision: D40527474 Pulled By: NickGerleman fbshipit-source-id: 81c2c97c76f58fad3bb40fb378aaf8b6ebd30d63
This commit is contained in:
committed by
Facebook GitHub Bot
parent
a68c418082
commit
9f3a3e13cc
@@ -32,6 +32,7 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
|
||||
borderRightWidth: true,
|
||||
borderStartWidth: true,
|
||||
borderTopWidth: true,
|
||||
columnGap: true,
|
||||
borderWidth: true,
|
||||
bottom: true,
|
||||
direction: true,
|
||||
@@ -43,6 +44,7 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
|
||||
flexGrow: true,
|
||||
flexShrink: true,
|
||||
flexWrap: true,
|
||||
gap: true,
|
||||
height: true,
|
||||
justifyContent: true,
|
||||
left: true,
|
||||
@@ -71,6 +73,7 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
|
||||
paddingVertical: true,
|
||||
position: true,
|
||||
right: true,
|
||||
rowGap: true,
|
||||
start: true,
|
||||
top: true,
|
||||
width: true,
|
||||
|
||||
@@ -196,6 +196,9 @@ const validAttributesForNonEventProps = {
|
||||
maxHeight: true,
|
||||
flex: true,
|
||||
flexGrow: true,
|
||||
rowGap: true,
|
||||
columnGap: true,
|
||||
gap: true,
|
||||
flexShrink: true,
|
||||
flexBasis: true,
|
||||
aspectRatio: true,
|
||||
|
||||
@@ -271,6 +271,9 @@ const validAttributesForNonEventProps = {
|
||||
|
||||
flex: true,
|
||||
flexGrow: true,
|
||||
rowGap: true,
|
||||
columnGap: true,
|
||||
gap: true,
|
||||
flexShrink: true,
|
||||
flexBasis: true,
|
||||
flexDirection: true,
|
||||
|
||||
+3
@@ -52,6 +52,9 @@ export interface FlexStyle {
|
||||
| 'row-reverse'
|
||||
| 'column-reverse'
|
||||
| undefined;
|
||||
rowGap?: number | undefined;
|
||||
gap?: number | undefined;
|
||||
columnGap?: number | undefined;
|
||||
flexGrow?: number | undefined;
|
||||
flexShrink?: number | undefined;
|
||||
flexWrap?: 'wrap' | 'nowrap' | 'wrap-reverse' | undefined;
|
||||
|
||||
@@ -518,6 +518,19 @@ export type ____ShadowStyle_InternalCore = $ReadOnly<{
|
||||
* @platform ios
|
||||
*/
|
||||
shadowRadius?: number,
|
||||
|
||||
/**
|
||||
* In React Native, gap works the same way it does in CSS.
|
||||
* If there are two or more children in a container, they will be separated from each other
|
||||
* by the value of the gap - but the children will not be separated from the edges of their parent container.
|
||||
* For horizontal gaps, use columnGap, for vertical gaps, use rowGap, and to apply both at the same time, it's gap.
|
||||
* When align-content or justify-content are set to space-between or space-around, the separation
|
||||
* between children may be larger than the gap value.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/gap for more details.
|
||||
*/
|
||||
rowGap?: number,
|
||||
columnGap?: number,
|
||||
gap?: number,
|
||||
}>;
|
||||
|
||||
export type ____ShadowStyle_Internal = $ReadOnly<{
|
||||
|
||||
@@ -49,6 +49,9 @@ export default function splitLayoutProps(props: ?____ViewStyle_Internal): {
|
||||
case 'bottom':
|
||||
case 'top':
|
||||
case 'transform':
|
||||
case 'rowGap':
|
||||
case 'columnGap':
|
||||
case 'gap':
|
||||
// $FlowFixMe[cannot-write]
|
||||
// $FlowFixMe[incompatible-use]
|
||||
// $FlowFixMe[prop-missing]
|
||||
|
||||
@@ -152,6 +152,9 @@ typedef void (^RCTApplierBlock)(NSDictionary<NSNumber *, UIView *> *viewRegistry
|
||||
|
||||
@property (nonatomic, assign) float flex;
|
||||
@property (nonatomic, assign) float flexGrow;
|
||||
@property (nonatomic, assign) float rowGap;
|
||||
@property (nonatomic, assign) float columnGap;
|
||||
@property (nonatomic, assign) float gap;
|
||||
@property (nonatomic, assign) float flexShrink;
|
||||
@property (nonatomic, assign) YGValue flexBasis;
|
||||
|
||||
|
||||
@@ -642,6 +642,20 @@ RCTShadowViewMeasure(YGNodeRef node, float width, YGMeasureMode widthMode, float
|
||||
return YGNodeStyleGetFlexBasis(_yogaNode);
|
||||
}
|
||||
|
||||
#define RCT_GAP_PROPERTY(setProp, getProp, cssProp, type, gap) \
|
||||
-(void)set##setProp : (type)value \
|
||||
{ \
|
||||
YGNodeStyleSet##cssProp(_yogaNode, gap, value); \
|
||||
} \
|
||||
-(type)getProp \
|
||||
{ \
|
||||
return YGNodeStyleGet##cssProp(_yogaNode, gap); \
|
||||
}
|
||||
|
||||
RCT_GAP_PROPERTY(RowGap, rowGap, Gap, float, YGGutterRow);
|
||||
RCT_GAP_PROPERTY(ColumnGap, columnGap, Gap, float, YGGutterColumn);
|
||||
RCT_GAP_PROPERTY(Gap, gap, Gap, float, YGGutterAll);
|
||||
|
||||
#define RCT_STYLE_PROPERTY(setProp, getProp, cssProp, type) \
|
||||
-(void)set##setProp : (type)value \
|
||||
{ \
|
||||
|
||||
@@ -426,6 +426,9 @@ RCT_EXPORT_SHADOW_PROPERTY(alignSelf, YGAlign)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(alignContent, YGAlign)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(position, YGPositionType)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(aspectRatio, float)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(rowGap, float)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(columnGap, float)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(gap, float)
|
||||
|
||||
RCT_EXPORT_SHADOW_PROPERTY(overflow, YGOverflow)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(display, YGDisplay)
|
||||
|
||||
@@ -224,6 +224,30 @@ public class LayoutShadowNode extends ReactShadowNodeImpl {
|
||||
super.setFlexGrow(flexGrow);
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.ROW_GAP, defaultFloat = YogaConstants.UNDEFINED)
|
||||
public void setRowGap(float rowGap) {
|
||||
if (isVirtual()) {
|
||||
return;
|
||||
}
|
||||
super.setRowGap(PixelUtil.toPixelFromDIP(rowGap));
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.COLUMN_GAP, defaultFloat = YogaConstants.UNDEFINED)
|
||||
public void setColumnGap(float columnGap) {
|
||||
if (isVirtual()) {
|
||||
return;
|
||||
}
|
||||
super.setColumnGap(PixelUtil.toPixelFromDIP(columnGap));
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.GAP, defaultFloat = YogaConstants.UNDEFINED)
|
||||
public void setGap(float gap) {
|
||||
if (isVirtual()) {
|
||||
return;
|
||||
}
|
||||
super.setGap(PixelUtil.toPixelFromDIP(gap));
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.FLEX_SHRINK, defaultFloat = 0f)
|
||||
public void setFlexShrink(float flexShrink) {
|
||||
if (isVirtual()) {
|
||||
|
||||
@@ -308,6 +308,12 @@ public interface ReactShadowNode<T extends ReactShadowNode> {
|
||||
|
||||
void setFlexGrow(float flexGrow);
|
||||
|
||||
void setRowGap(float rowGap);
|
||||
|
||||
void setColumnGap(float columnGap);
|
||||
|
||||
void setGap(float gap);
|
||||
|
||||
void setFlexShrink(float flexShrink);
|
||||
|
||||
void setFlexBasis(float flexBasis);
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.facebook.yoga.YogaDirection;
|
||||
import com.facebook.yoga.YogaDisplay;
|
||||
import com.facebook.yoga.YogaEdge;
|
||||
import com.facebook.yoga.YogaFlexDirection;
|
||||
import com.facebook.yoga.YogaGutter;
|
||||
import com.facebook.yoga.YogaJustify;
|
||||
import com.facebook.yoga.YogaMeasureFunction;
|
||||
import com.facebook.yoga.YogaNode;
|
||||
@@ -794,6 +795,21 @@ public class ReactShadowNodeImpl implements ReactShadowNode<ReactShadowNodeImpl>
|
||||
mYogaNode.setFlexGrow(flexGrow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRowGap(float rowGap) {
|
||||
mYogaNode.setGap(YogaGutter.ROW, rowGap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColumnGap(float columnGap) {
|
||||
mYogaNode.setGap(YogaGutter.COLUMN, columnGap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGap(float gap) {
|
||||
mYogaNode.setGap(YogaGutter.ALL, gap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlexShrink(float flexShrink) {
|
||||
mYogaNode.setFlexShrink(flexShrink);
|
||||
|
||||
@@ -32,6 +32,9 @@ public class ViewProps {
|
||||
public static final String FLEX_BASIS = "flexBasis";
|
||||
public static final String FLEX_DIRECTION = "flexDirection";
|
||||
public static final String FLEX_WRAP = "flexWrap";
|
||||
public static final String ROW_GAP = "rowGap";
|
||||
public static final String COLUMN_GAP = "columnGap";
|
||||
public static final String GAP = "gap";
|
||||
public static final String HEIGHT = "height";
|
||||
public static final String JUSTIFY_CONTENT = "justifyContent";
|
||||
public static final String LEFT = "left";
|
||||
@@ -203,6 +206,9 @@ public class ViewProps {
|
||||
FLEX_BASIS,
|
||||
FLEX_DIRECTION,
|
||||
FLEX_GROW,
|
||||
ROW_GAP,
|
||||
COLUMN_GAP,
|
||||
GAP,
|
||||
FLEX_SHRINK,
|
||||
FLEX_WRAP,
|
||||
JUSTIFY_CONTENT,
|
||||
|
||||
@@ -65,6 +65,11 @@ static inline T const getFieldValue(
|
||||
REBUILD_YG_FIELD_SWITCH_CASE_INDEXED(field, YGDimensionWidth, widthStr); \
|
||||
REBUILD_YG_FIELD_SWITCH_CASE_INDEXED(field, YGDimensionHeight, heightStr);
|
||||
|
||||
#define REBUILD_FIELD_YG_GUTTER(field, rowGapStr, columnGapStr, gapStr) \
|
||||
REBUILD_YG_FIELD_SWITCH_CASE_INDEXED(field, YGGutterRow, rowGapStr); \
|
||||
REBUILD_YG_FIELD_SWITCH_CASE_INDEXED(field, YGGutterColumn, columnGapStr); \
|
||||
REBUILD_YG_FIELD_SWITCH_CASE_INDEXED(field, YGGutterAll, gapStr);
|
||||
|
||||
#define REBUILD_FIELD_YG_EDGES(field, prefix, suffix) \
|
||||
REBUILD_YG_FIELD_SWITCH_CASE_INDEXED( \
|
||||
field, YGEdgeLeft, prefix "Left" suffix); \
|
||||
@@ -114,6 +119,7 @@ void YogaStylableProps::setProp(
|
||||
REBUILD_FIELD_SWITCH_CASE_YSP(flexShrink);
|
||||
REBUILD_FIELD_SWITCH_CASE_YSP(flexBasis);
|
||||
REBUILD_FIELD_SWITCH_CASE2(positionType, "position");
|
||||
REBUILD_FIELD_YG_GUTTER(gap, "rowGap", "columnGap", "gap");
|
||||
REBUILD_FIELD_SWITCH_CASE_YSP(aspectRatio);
|
||||
REBUILD_FIELD_YG_DIMENSION(dimensions, "width", "height");
|
||||
REBUILD_FIELD_YG_DIMENSION(minDimensions, "minWidth", "minHeight");
|
||||
@@ -163,6 +169,18 @@ SharedDebugStringConvertibleList YogaStylableProps::getDebugProps() const {
|
||||
"flex", yogaStyle.flex(), defaultYogaStyle.flex()),
|
||||
debugStringConvertibleItem(
|
||||
"flexGrow", yogaStyle.flexGrow(), defaultYogaStyle.flexGrow()),
|
||||
debugStringConvertibleItem(
|
||||
"rowGap",
|
||||
yogaStyle.gap()[YGGutterRow],
|
||||
defaultYogaStyle.gap()[YGGutterRow]),
|
||||
debugStringConvertibleItem(
|
||||
"columnGap",
|
||||
yogaStyle.gap()[YGGutterColumn],
|
||||
defaultYogaStyle.gap()[YGGutterColumn]),
|
||||
debugStringConvertibleItem(
|
||||
"gap",
|
||||
yogaStyle.gap()[YGGutterAll],
|
||||
defaultYogaStyle.gap()[YGGutterAll]),
|
||||
debugStringConvertibleItem(
|
||||
"flexShrink", yogaStyle.flexShrink(), defaultYogaStyle.flexShrink()),
|
||||
debugStringConvertibleItem(
|
||||
|
||||
@@ -266,6 +266,28 @@ static inline YGStyle convertRawProp(
|
||||
"",
|
||||
sourceValue.padding(),
|
||||
yogaStyle.padding());
|
||||
|
||||
yogaStyle.gap()[YGGutterRow] = convertRawProp(
|
||||
context,
|
||||
rawProps,
|
||||
"rowGap",
|
||||
sourceValue.gap()[YGGutterRow],
|
||||
yogaStyle.gap()[YGGutterRow]);
|
||||
|
||||
yogaStyle.gap()[YGGutterColumn] = convertRawProp(
|
||||
context,
|
||||
rawProps,
|
||||
"columnGap",
|
||||
sourceValue.gap()[YGGutterColumn],
|
||||
yogaStyle.gap()[YGGutterColumn]);
|
||||
|
||||
yogaStyle.gap()[YGGutterAll] = convertRawProp(
|
||||
context,
|
||||
rawProps,
|
||||
"gap",
|
||||
sourceValue.gap()[YGGutterAll],
|
||||
yogaStyle.gap()[YGGutterAll]);
|
||||
|
||||
yogaStyle.border() = convertRawProp(
|
||||
context,
|
||||
rawProps,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <glog/logging.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
@@ -233,6 +234,11 @@ static inline ShadowNode::Unshared messWithYogaStyles(
|
||||
"maxWidth", "maxHeight", "minWidth", "minHeight",
|
||||
};
|
||||
|
||||
// It is not safe to add new Yoga properties to this list. Unit tests
|
||||
// validate specific seeds, and what they test may change and cause unrelated
|
||||
// failures if the size of properties also changes.
|
||||
EXPECT_EQ(properties.size(), 20);
|
||||
|
||||
for (auto const &property : properties) {
|
||||
if (entropy.random<bool>(0.1)) {
|
||||
dynamic[property] = entropy.random<int>(0, 1024);
|
||||
|
||||
@@ -299,6 +299,40 @@ class DisplayNoneStyle extends React.Component<
|
||||
this.setState({index: this.state.index + 1});
|
||||
};
|
||||
}
|
||||
|
||||
class FlexGapExample extends React.Component<$ReadOnly<{||}>> {
|
||||
render(): React.Node {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
borderWidth: 1,
|
||||
rowGap: 20,
|
||||
columnGap: 30,
|
||||
}}>
|
||||
<View style={{backgroundColor: 'black', height: 30, width: 30}} />
|
||||
<View style={{backgroundColor: 'black', height: 30, width: 30}} />
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: 'pink',
|
||||
height: 30,
|
||||
flexBasis: 30,
|
||||
}}
|
||||
/>
|
||||
<View style={{backgroundColor: 'black', height: 30, width: 30}} />
|
||||
<View style={{backgroundColor: 'black', height: 30, width: 30}} />
|
||||
<View style={{backgroundColor: 'black', height: 30, width: 30}} />
|
||||
<View style={{backgroundColor: 'black', height: 30, width: 30}} />
|
||||
<View style={{backgroundColor: 'pink', height: 30, width: 30}} />
|
||||
<View style={{backgroundColor: 'pink', height: 30, width: 30}} />
|
||||
<View style={{backgroundColor: 'pink', height: 30, width: 30}} />
|
||||
<View style={{backgroundColor: 'pink', height: 30, width: 30}} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
exports.title = 'View';
|
||||
exports.documentationURL = 'https://reactnative.dev/docs/view';
|
||||
exports.category = 'Basic';
|
||||
@@ -612,4 +646,10 @@ exports.examples = [
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'FlexGap',
|
||||
render(): React.Node {
|
||||
return <FlexGapExample />;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user