Permalink
Browse files

BREAKING - Fix unconstraint sizing in main axis

Summary: Introduce `overflow:scroll` so that scrolling can be implemented without the current overflow:visible hackiness. Currently we use AT_MOST to measure in the cross axis but not in the main axis. This was done to enable scrolling containers where children are not constraint in the main axis by their parent. This caused problems for non-scrolling containers though as it meant that their children cannot be measured correctly in the main axis. Introducing `overflow:scroll` fixes this.

Reviewed By: astreet

Differential Revision: D3855801

fbshipit-source-id: 6077b0bcb68fe5ddd4aa22926acab40ff4d83949
  • Loading branch information...
1 parent 8eee79b commit 1f9c9ecb4b4007c98b019fb05d8b7b3ea193e8f9 @emilsjolander emilsjolander committed with Facebook Github Bot 5 Sep 14, 2016
@@ -547,10 +547,12 @@ const styles = StyleSheet.create({
baseVertical: {
flex: 1,
flexDirection: 'column',
+ overflow: 'scroll',
},
baseHorizontal: {
flex: 1,
flexDirection: 'row',
+ overflow: 'scroll',
},
contentContainerHorizontal: {
flexDirection: 'row',
@@ -43,7 +43,6 @@ var ViewStylePropTypes = {
borderBottomWidth: ReactPropTypes.number,
borderLeftWidth: ReactPropTypes.number,
opacity: ReactPropTypes.number,
- overflow: ReactPropTypes.oneOf(['visible', 'hidden']),
/**
* (Android-only) Sets the elevation of a view, using Android's underlying
* [elevation API](https://developer.android.com/training/material/shadows-clipping.html#Elevation).
@@ -328,6 +328,19 @@ var LayoutPropTypes = {
'stretch'
]),
+ /** `overflow` controls how a children are measured and displayed.
+ * `overflow: hidden` causes views to be clipped while `overflow: scroll`
+ * causes views to be measured independently of their parents main axis.`
+ * It works like `overflow` in CSS (default: visible).
+ * See https://developer.mozilla.org/en/docs/Web/CSS/overflow
+ * for more details.
+ */
+ overflow: ReactPropTypes.oneOf([
+ 'visible',
+ 'hidden',
+ 'scroll',
+ ]),
+
/** In React Native `flex` does not work the same way that it does in CSS.
* `flex` is a number rather than a string, and it works
* according to the `css-layout` library
@@ -612,7 +612,8 @@ + (NSPropertyList)NSPropertyList:(id)json
RCT_ENUM_CONVERTER(CSSOverflow, (@{
@"hidden": @(CSSOverflowHidden),
- @"visible": @(CSSOverflowVisible)
+ @"visible": @(CSSOverflowVisible),
+ @"scroll": @(CSSOverflowScroll),
}), CSSOverflowVisible, intValue)
RCT_ENUM_CONVERTER(CSSFlexDirection, (@{
@@ -451,6 +451,8 @@ print_css_node_rec(const CSSNodeRef node, const CSSPrintOptions options, const u
printf("overflow: 'hidden', ");
} else if (node->style.overflow == CSSOverflowVisible) {
printf("overflow: 'visible', ");
+ } else if (node->style.overflow == CSSOverflowScroll) {
+ printf("overflow: 'scroll', ");
}
if (four_equal(node->style.margin)) {
@@ -555,26 +557,23 @@ static bool isColumnDirection(const CSSFlexDirection flexDirection) {
}
static float getLeadingMargin(const CSSNodeRef node, const CSSFlexDirection axis) {
- if (isRowDirection(axis) &&
- !CSSValueIsUndefined(node->style.margin[CSSEdgeStart])) {
+ if (isRowDirection(axis) && !CSSValueIsUndefined(node->style.margin[CSSEdgeStart])) {
return node->style.margin[CSSEdgeStart];
}
return computedEdgeValue(node->style.margin, leading[axis], 0);
}
static float getTrailingMargin(const CSSNodeRef node, const CSSFlexDirection axis) {
- if (isRowDirection(axis) &&
- !CSSValueIsUndefined(node->style.margin[CSSEdgeEnd])) {
+ if (isRowDirection(axis) && !CSSValueIsUndefined(node->style.margin[CSSEdgeEnd])) {
return node->style.margin[CSSEdgeEnd];
}
return computedEdgeValue(node->style.margin, trailing[axis], 0);
}
static float getLeadingPadding(const CSSNodeRef node, const CSSFlexDirection axis) {
- if (isRowDirection(axis) &&
- !CSSValueIsUndefined(node->style.padding[CSSEdgeStart]) &&
+ if (isRowDirection(axis) && !CSSValueIsUndefined(node->style.padding[CSSEdgeStart]) &&
node->style.padding[CSSEdgeStart] >= 0) {
return node->style.padding[CSSEdgeStart];
}
@@ -587,8 +586,7 @@ static float getLeadingPadding(const CSSNodeRef node, const CSSFlexDirection axi
}
static float getTrailingPadding(const CSSNodeRef node, const CSSFlexDirection axis) {
- if (isRowDirection(axis) &&
- !CSSValueIsUndefined(node->style.padding[CSSEdgeEnd]) &&
+ if (isRowDirection(axis) && !CSSValueIsUndefined(node->style.padding[CSSEdgeEnd]) &&
node->style.padding[CSSEdgeEnd] >= 0) {
return node->style.padding[CSSEdgeEnd];
}
@@ -601,8 +599,7 @@ static float getTrailingPadding(const CSSNodeRef node, const CSSFlexDirection ax
}
static float getLeadingBorder(const CSSNodeRef node, const CSSFlexDirection axis) {
- if (isRowDirection(axis) &&
- !CSSValueIsUndefined(node->style.border[CSSEdgeStart]) &&
+ if (isRowDirection(axis) && !CSSValueIsUndefined(node->style.border[CSSEdgeStart]) &&
node->style.border[CSSEdgeStart] >= 0) {
return node->style.border[CSSEdgeStart];
}
@@ -615,8 +612,7 @@ static float getLeadingBorder(const CSSNodeRef node, const CSSFlexDirection axis
}
static float getTrailingBorder(const CSSNodeRef node, const CSSFlexDirection axis) {
- if (isRowDirection(axis) &&
- !CSSValueIsUndefined(node->style.border[CSSEdgeEnd]) &&
+ if (isRowDirection(axis) && !CSSValueIsUndefined(node->style.border[CSSEdgeEnd]) &&
node->style.border[CSSEdgeEnd] >= 0) {
return node->style.border[CSSEdgeEnd];
}
@@ -1140,21 +1136,17 @@ static void layoutNodeImpl(const CSSNodeRef node,
childHeightMeasureMode = CSSMeasureModeExactly;
}
- // According to the spec, if the main size is not definite and the
- // child's inline axis is parallel to the main axis (i.e. it's
- // horizontal), the child should be sized using "UNDEFINED" in
- // the main size. Otherwise use "AT_MOST" in the cross axis.
- if (!isMainAxisRow && CSSValueIsUndefined(childWidth) &&
- !CSSValueIsUndefined(availableInnerWidth)) {
- childWidth = availableInnerWidth;
- childWidthMeasureMode = CSSMeasureModeAtMost;
- }
-
// The W3C spec doesn't say anything about the 'overflow' property,
// but all major browsers appear to implement the following logic.
- if (node->style.overflow == CSSOverflowHidden) {
- if (isMainAxisRow && CSSValueIsUndefined(childHeight) &&
- !CSSValueIsUndefined(availableInnerHeight)) {
+ if ((!isMainAxisRow && node->style.overflow == CSSOverflowScroll) || node->style.overflow != CSSOverflowScroll) {
+ if (CSSValueIsUndefined(childWidth) && !CSSValueIsUndefined(availableInnerWidth)) {
+ childWidth = availableInnerWidth;
+ childWidthMeasureMode = CSSMeasureModeAtMost;
+ }
+ }
+
+ if ((isMainAxisRow && node->style.overflow == CSSOverflowScroll) || node->style.overflow != CSSOverflowScroll) {
+ if (CSSValueIsUndefined(childHeight) && !CSSValueIsUndefined(availableInnerHeight)) {
childHeight = availableInnerHeight;
childHeightMeasureMode = CSSMeasureModeAtMost;
}
@@ -55,6 +55,7 @@ typedef enum CSSJustify {
typedef enum CSSOverflow {
CSSOverflowVisible,
CSSOverflowHidden,
+ CSSOverflowScroll,
} CSSOverflow;
// Note: auto is only a valid value for alignSelf. It is NOT a valid value for
@@ -122,7 +122,7 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictio
RCT_CUSTOM_VIEW_PROPERTY(overflow, CSSOverflow, RCTView)
{
if (json) {
- view.clipsToBounds = [RCTConvert CSSOverflow:json] == CSSOverflowHidden;
+ view.clipsToBounds = [RCTConvert CSSOverflow:json] != CSSOverflowVisible;
} else {
view.clipsToBounds = defaultView.clipsToBounds;
}
@@ -12,4 +12,5 @@
public enum CSSOverflow {
VISIBLE,
HIDDEN,
+ SCROLL,
}
@@ -715,19 +715,17 @@ private static void layoutNodeImpl(
childHeightMeasureMode = CSSMeasureMode.EXACTLY;
}
- // According to the spec, if the main size is not definite and the
- // child's inline axis is parallel to the main axis (i.e. it's
- // horizontal), the child should be sized using "UNDEFINED" in
- // the main size. Otherwise use "AT_MOST" in the cross axis.
- if (!isMainAxisRow && Float.isNaN(childWidth) && !Float.isNaN(availableInnerWidth)) {
- childWidth = availableInnerWidth;
- childWidthMeasureMode = CSSMeasureMode.AT_MOST;
- }
-
// The W3C spec doesn't say anything about the 'overflow' property,
// but all major browsers appear to implement the following logic.
- if (node.style.overflow == CSSOverflow.HIDDEN) {
- if (isMainAxisRow && Float.isNaN(childHeight) && !Float.isNaN(availableInnerHeight)) {
+ if ((!isMainAxisRow && node.style.overflow == CSSOverflow.SCROLL) || node.style.overflow != CSSOverflow.SCROLL) {
+ if (Float.isNaN(childWidth) && !Float.isNaN(availableInnerWidth)) {
+ childWidth = availableInnerWidth;
+ childWidthMeasureMode = CSSMeasureMode.AT_MOST;
+ }
+ }
+
+ if ((isMainAxisRow && node.style.overflow == CSSOverflow.SCROLL) || node.style.overflow != CSSOverflow.SCROLL) {
+ if (Float.isNaN(childHeight) && !Float.isNaN(availableInnerHeight)) {
childHeight = availableInnerHeight;
childHeightMeasureMode = CSSMeasureMode.AT_MOST;
}
@@ -10,6 +10,7 @@
import com.facebook.csslayout.CSSConstants;
import com.facebook.csslayout.CSSFlexDirection;
import com.facebook.csslayout.CSSJustify;
+import com.facebook.csslayout.CSSOverflow;
import com.facebook.csslayout.CSSPositionType;
import com.facebook.csslayout.CSSWrap;
import com.facebook.react.uimanager.annotations.ReactProp;
@@ -102,6 +103,12 @@ public void setJustifyContent(@Nullable String justifyContent) {
justifyContent.toUpperCase(Locale.US).replace("-", "_")));
}
+ @ReactProp(name = ViewProps.OVERFLOW)
+ public void setOverflow(@Nullable String overflow) {
+ setOverflow(overflow == null ? CSSOverflow.VISIBLE : CSSOverflow.valueOf(
+ overflow.toUpperCase(Locale.US).replace("-", "_")));
+ }
+
@ReactPropGroup(names = {
ViewProps.MARGIN,
ViewProps.MARGIN_VERTICAL,
@@ -26,6 +26,7 @@
// !!! Keep in sync with LAYOUT_ONLY_PROPS below
public static final String ALIGN_ITEMS = "alignItems";
public static final String ALIGN_SELF = "alignSelf";
+ public static final String OVERFLOW = "overflow";
public static final String BOTTOM = "bottom";
public static final String COLLAPSABLE = "collapsable";
public static final String FLEX = "flex";
@@ -113,6 +114,7 @@
FLEX_DIRECTION,
FLEX_WRAP,
JUSTIFY_CONTENT,
+ OVERFLOW,
/* position */
POSITION,

0 comments on commit 1f9c9ec

Please sign in to comment.