Skip to content

Commit

Permalink
feat: flex gap bindings (#34974)
Browse files Browse the repository at this point in the history
Summary:
This PR adds React native binding for facebook/yoga#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: #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
  • Loading branch information
intergalacticspacehighway authored and facebook-github-bot committed Oct 20, 2022
1 parent a68c418 commit 9f3a3e1
Show file tree
Hide file tree
Showing 17 changed files with 186 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Libraries/Components/View/ReactNativeStyleAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
borderRightWidth: true,
borderStartWidth: true,
borderTopWidth: true,
columnGap: true,
borderWidth: true,
bottom: true,
direction: true,
Expand All @@ -43,6 +44,7 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
flexGrow: true,
flexShrink: true,
flexWrap: true,
gap: true,
height: true,
justifyContent: true,
left: true,
Expand Down Expand Up @@ -71,6 +73,7 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
paddingVertical: true,
position: true,
right: true,
rowGap: true,
start: true,
top: true,
width: true,
Expand Down
3 changes: 3 additions & 0 deletions Libraries/NativeComponent/BaseViewConfig.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ const validAttributesForNonEventProps = {
maxHeight: true,
flex: true,
flexGrow: true,
rowGap: true,
columnGap: true,
gap: true,
flexShrink: true,
flexBasis: true,
aspectRatio: true,
Expand Down
3 changes: 3 additions & 0 deletions Libraries/NativeComponent/BaseViewConfig.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ const validAttributesForNonEventProps = {

flex: true,
flexGrow: true,
rowGap: true,
columnGap: true,
gap: true,
flexShrink: true,
flexBasis: true,
flexDirection: true,
Expand Down
3 changes: 3 additions & 0 deletions Libraries/StyleSheet/StyleSheetTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
13 changes: 13 additions & 0 deletions Libraries/StyleSheet/StyleSheetTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<{
Expand Down
3 changes: 3 additions & 0 deletions Libraries/StyleSheet/splitLayoutProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
3 changes: 3 additions & 0 deletions React/Views/RCTShadowView.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
14 changes: 14 additions & 0 deletions React/Views/RCTShadowView.m
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,20 @@ - (YGValue)flexBasis
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 \
{ \
Expand Down
3 changes: 3 additions & 0 deletions React/Views/RCTViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,9 @@ - (RCTShadowView *)shadowView
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,30 @@ public void setFlexGrow(float flexGrow) {
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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
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;
Expand Down Expand Up @@ -794,6 +795,21 @@ public void setFlexGrow(float flexGrow) {
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -203,6 +206,9 @@ public class ViewProps {
FLEX_BASIS,
FLEX_DIRECTION,
FLEX_GROW,
ROW_GAP,
COLUMN_GAP,
GAP,
FLEX_SHRINK,
FLEX_WRAP,
JUSTIFY_CONTENT,
Expand Down
18 changes: 18 additions & 0 deletions ReactCommon/react/renderer/components/view/YogaStylableProps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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); \
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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(
Expand Down
22 changes: 22 additions & 0 deletions ReactCommon/react/renderer/components/view/propsConversions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions ReactCommon/react/test_utils/shadowTreeGeneration.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#pragma once

#include <glog/logging.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <iostream>
#include <memory>
Expand Down Expand Up @@ -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);
Expand Down
40 changes: 40 additions & 0 deletions packages/rn-tester/js/examples/View/ViewExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -612,4 +646,10 @@ exports.examples = [
);
},
},
{
title: 'FlexGap',
render(): React.Node {
return <FlexGapExample />;
},
},
];

0 comments on commit 9f3a3e1

Please sign in to comment.