Skip to content

Commit fe8e3d0

Browse files
css grid ios
1 parent 336042c commit fe8e3d0

36 files changed

+4273
-177
lines changed

packages/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,18 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
107107
width: true,
108108
zIndex: true,
109109

110+
/**
111+
* Grid Layout
112+
*/
113+
gridTemplateColumns: true,
114+
gridTemplateRows: true,
115+
gridAutoColumns: true,
116+
gridAutoRows: true,
117+
gridColumnStart: true,
118+
gridColumnEnd: true,
119+
gridRowStart: true,
120+
gridRowEnd: true,
121+
110122
/**
111123
* Shadow
112124
*/

packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export interface FlexStyle {
5656
borderWidth?: number | undefined;
5757
bottom?: DimensionValue | undefined;
5858
boxSizing?: 'border-box' | 'content-box' | undefined;
59-
display?: 'none' | 'flex' | 'contents' | undefined;
59+
display?: 'none' | 'flex' | 'contents' | 'grid' | undefined;
6060
end?: DimensionValue | undefined;
6161
flex?: number | undefined;
6262
flexBasis?: DimensionValue | undefined;
@@ -113,6 +113,43 @@ export interface FlexStyle {
113113
zIndex?: number | undefined;
114114
direction?: 'inherit' | 'ltr' | 'rtl' | undefined;
115115

116+
// Grid Container Properties
117+
/**
118+
* Defines the columns of the grid with a list of track sizes.
119+
* Each track can be a number (pixels), string ('auto', '1fr', '50%'), or minmax object.
120+
*/
121+
gridTemplateColumns?: ReadonlyArray<number | string | {min: number | string; max: number | string}> | undefined;
122+
/**
123+
* Defines the rows of the grid with a list of track sizes.
124+
*/
125+
gridTemplateRows?: ReadonlyArray<number | string | {min: number | string; max: number | string}> | undefined;
126+
/**
127+
* Specifies the size of implicitly-created column tracks.
128+
*/
129+
gridAutoColumns?: ReadonlyArray<number | string | {min: number | string; max: number | string}> | undefined;
130+
/**
131+
* Specifies the size of implicitly-created row tracks.
132+
*/
133+
gridAutoRows?: ReadonlyArray<number | string | {min: number | string; max: number | string}> | undefined;
134+
135+
// Grid Item Properties
136+
/**
137+
* Specifies a grid item's start position within the grid column.
138+
*/
139+
gridColumnStart?: number | 'auto' | `span ${number}` | undefined;
140+
/**
141+
* Specifies a grid item's end position within the grid column.
142+
*/
143+
gridColumnEnd?: number | 'auto' | `span ${number}` | undefined;
144+
/**
145+
* Specifies a grid item's start position within the grid row.
146+
*/
147+
gridRowStart?: number | 'auto' | `span ${number}` | undefined;
148+
/**
149+
* Specifies a grid item's end position within the grid row.
150+
*/
151+
gridRowEnd?: number | 'auto' | `span ${number}` | undefined;
152+
116153
/**
117154
* Equivalent to `top`, `bottom`, `right` and `left`
118155
*/

packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ type ____LayoutStyle_Internal = $ReadOnly<{
6161
* It works similarly to `display` in CSS, but only support 'flex' and 'none'.
6262
* 'flex' is the default.
6363
*/
64-
display?: 'none' | 'flex' | 'contents',
64+
display?: 'none' | 'flex' | 'contents' | 'grid',
6565

6666
/** `width` sets the width of this component.
6767
*
@@ -651,6 +651,49 @@ type ____LayoutStyle_Internal = $ReadOnly<{
651651
*/
652652
direction?: 'inherit' | 'ltr' | 'rtl',
653653

654+
// Grid Container Properties
655+
/**
656+
* Defines the columns of the grid with a list of track sizes.
657+
* Each track can be a number (pixels), string ('auto', '1fr', '50%'), or minmax object.
658+
*/
659+
gridTemplateColumns?: $ReadOnlyArray<number | string | {min: number | string, max: number | string}>,
660+
661+
/**
662+
* Defines the rows of the grid with a list of track sizes.
663+
*/
664+
gridTemplateRows?: $ReadOnlyArray<number | string | {min: number | string, max: number | string}>,
665+
666+
/**
667+
* Specifies the size of implicitly-created column tracks.
668+
*/
669+
gridAutoColumns?: $ReadOnlyArray<number | string | {min: number | string, max: number | string}>,
670+
671+
/**
672+
* Specifies the size of implicitly-created row tracks.
673+
*/
674+
gridAutoRows?: $ReadOnlyArray<number | string | {min: number | string, max: number | string}>,
675+
676+
// Grid Item Properties
677+
/**
678+
* Specifies a grid item's start position within the grid column.
679+
*/
680+
gridColumnStart?: number | 'auto' | string,
681+
682+
/**
683+
* Specifies a grid item's end position within the grid column.
684+
*/
685+
gridColumnEnd?: number | 'auto' | string,
686+
687+
/**
688+
* Specifies a grid item's start position within the grid row.
689+
*/
690+
gridRowStart?: number | 'auto' | string,
691+
692+
/**
693+
* Specifies a grid item's end position within the grid row.
694+
*/
695+
gridRowEnd?: number | 'auto' | string,
696+
654697
/**
655698
* In React Native, gap works the same way it does in CSS.
656699
* If there are two or more children in a container, they will be separated from each other

packages/react-native/React/Views/RCTLayout.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ typedef NS_ENUM(NSInteger, RCTDisplayType) {
1818
RCTDisplayTypeNone,
1919
RCTDisplayTypeFlex,
2020
RCTDisplayTypeInline,
21+
RCTDisplayTypeGrid,
2122
};
2223

2324
struct RCTLayoutMetrics {

packages/react-native/React/Views/RCTLayout.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ YGDisplay RCTYogaDisplayTypeFromReactDisplayType(RCTDisplayType displayType)
121121
case RCTDisplayTypeInline:
122122
RCTAssert(NO, @"RCTDisplayTypeInline cannot be converted to YGDisplay value.");
123123
return YGDisplayNone;
124+
case RCTDisplayTypeGrid:
125+
return YGDisplayGrid;
124126
}
125127
}
126128

@@ -134,5 +136,7 @@ RCTDisplayType RCTReactDisplayTypeFromYogaDisplayType(YGDisplay displayType)
134136
case YGDisplayContents:
135137
RCTAssert(NO, @"YGDisplayContents cannot be converted to RCTDisplayType value.");
136138
return RCTDisplayTypeNone;
139+
case YGDisplayGrid:
140+
return RCTDisplayTypeGrid;
137141
}
138142
}

packages/react-native/ReactCommon/react/renderer/components/view/YogaStylableProps.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ YogaStylableProps::YogaStylableProps(
2727
: Props() {
2828
initialize(context, sourceProps, rawProps, filterObjectKeys);
2929

30-
yogaStyle = ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
30+
bool useIterator = ReactNativeFeatureFlags::enableCppPropsIteratorSetter();
31+
32+
yogaStyle = useIterator
3133
? sourceProps.yogaStyle
3234
: convertRawProp(context, rawProps, sourceProps.yogaStyle);
3335

34-
if (!ReactNativeFeatureFlags::enableCppPropsIteratorSetter()) {
36+
if (!useIterator) {
3537
convertRawPropAliases(context, sourceProps, rawProps);
3638
}
3739
};
@@ -159,6 +161,18 @@ void YogaStylableProps::setProp(
159161
REBUILD_FIELD_YG_EDGES(padding, setPadding, "padding", "");
160162
REBUILD_FIELD_YG_EDGES(border, setBorder, "border", "Width");
161163

164+
// Grid Container Properties
165+
REBUILD_FIELD_SWITCH_CASE_YSP(gridTemplateColumns, setGridTemplateColumns);
166+
REBUILD_FIELD_SWITCH_CASE_YSP(gridTemplateRows, setGridTemplateRows);
167+
REBUILD_FIELD_SWITCH_CASE_YSP(gridAutoColumns, setGridAutoColumns);
168+
REBUILD_FIELD_SWITCH_CASE_YSP(gridAutoRows, setGridAutoRows);
169+
170+
// Grid Item Properties
171+
REBUILD_FIELD_SWITCH_CASE_YSP(gridColumnStart, setGridColumnStart);
172+
REBUILD_FIELD_SWITCH_CASE_YSP(gridColumnEnd, setGridColumnEnd);
173+
REBUILD_FIELD_SWITCH_CASE_YSP(gridRowStart, setGridRowStart);
174+
REBUILD_FIELD_SWITCH_CASE_YSP(gridRowEnd, setGridRowEnd);
175+
162176
// Aliases
163177
RAW_SET_PROP_SWITCH_CASE(insetBlockEnd, "insetBlockEnd");
164178
RAW_SET_PROP_SWITCH_CASE(insetBlockStart, "insetBlockStart");

packages/react-native/ReactCommon/react/renderer/components/view/conversions.h

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include <react/renderer/graphics/ValueUnit.h>
3131
#include <yoga/YGEnums.h>
3232
#include <yoga/node/Node.h>
33+
#include <yoga/style/GridTrack.h>
34+
#include <yoga/style/GridLine.h>
3335
#include <cmath>
3436
#include <optional>
3537
#include <string>
@@ -129,6 +131,8 @@ inline DisplayType displayTypeFromYGDisplay(YGDisplay display)
129131
return DisplayType::Contents;
130132
case YGDisplayFlex:
131133
return DisplayType::Flex;
134+
case YGDisplayGrid:
135+
return DisplayType::Grid;
132136
}
133137
}
134138

@@ -422,6 +426,10 @@ inline void fromRawValue(const PropsParserContext &context, const RawValue &valu
422426
result = yoga::Display::Contents;
423427
return;
424428
}
429+
if (stringValue == "grid") {
430+
result = yoga::Display::Grid;
431+
return;
432+
}
425433
LOG(ERROR) << "Could not parse yoga::Display: " << stringValue;
426434
}
427435

@@ -1516,4 +1524,141 @@ inline std::string toString(const Transform &transform)
15161524
}
15171525
#endif
15181526

1527+
// Grid Track List conversion - parses CSS grid track syntax like "100 1fr auto 50%"
1528+
inline void fromRawValue(
1529+
const PropsParserContext& context,
1530+
const RawValue& value,
1531+
yoga::GridTrackList& result) {
1532+
result.clear();
1533+
1534+
printf("[GRID CONV] fromRawValue for GridTrackList called\n");
1535+
printf("[GRID CONV] value.hasType<std::vector<RawValue>>() = %d\n", value.hasType<std::vector<RawValue>>());
1536+
1537+
if (value.hasType<std::vector<RawValue>>()) {
1538+
auto tracks = (std::vector<RawValue>)value;
1539+
printf("[GRID CONV] tracks.size() = %zu\n", tracks.size());
1540+
for (const auto& track : tracks) {
1541+
yoga::GridTrackSize trackSize;
1542+
printf("[GRID CONV] track.hasType<Float>() = %d, hasType<int>() = %d, hasType<double>() = %d\n",
1543+
track.hasType<Float>(), track.hasType<int>(), track.hasType<double>());
1544+
1545+
if (track.hasType<Float>()) {
1546+
// Fixed pixel value
1547+
float val = (float)(Float)track;
1548+
trackSize.minSizingFunction = yoga::StyleSizeLength::points(val);
1549+
trackSize.maxSizingFunction = yoga::StyleSizeLength::points(val);
1550+
} else if (track.hasType<std::string>()) {
1551+
auto trackStr = (std::string)track;
1552+
1553+
if (trackStr == "auto") {
1554+
trackSize.minSizingFunction = yoga::StyleSizeLength::ofAuto();
1555+
trackSize.maxSizingFunction = yoga::StyleSizeLength::ofAuto();
1556+
} else if (trackStr.back() == '%') {
1557+
// Percentage
1558+
float percent = std::stof(trackStr.substr(0, trackStr.size() - 1));
1559+
trackSize.minSizingFunction = yoga::StyleSizeLength::percent(percent);
1560+
trackSize.maxSizingFunction = yoga::StyleSizeLength::percent(percent);
1561+
} else if (trackStr.size() > 2 && trackStr.substr(trackStr.size() - 2) == "fr") {
1562+
// Flex (fr) unit
1563+
float fr = std::stof(trackStr.substr(0, trackStr.size() - 2));
1564+
trackSize.minSizingFunction = yoga::StyleSizeLength::ofAuto();
1565+
trackSize.maxSizingFunction = yoga::StyleSizeLength::stretch(fr);
1566+
} else if (track.hasType<std::unordered_map<std::string, RawValue>>()) {
1567+
// minmax object: {min: x, max: y}
1568+
auto minmax = (std::unordered_map<std::string, RawValue>)track;
1569+
// Parse min and max values
1570+
if (minmax.count("min")) {
1571+
auto& minVal = minmax.at("min");
1572+
if (minVal.hasType<Float>()) {
1573+
trackSize.minSizingFunction = yoga::StyleSizeLength::points((float)(Float)minVal);
1574+
} else if (minVal.hasType<std::string>()) {
1575+
auto minStr = (std::string)minVal;
1576+
if (minStr == "auto") {
1577+
trackSize.minSizingFunction = yoga::StyleSizeLength::ofAuto();
1578+
} else if (minStr.back() == '%') {
1579+
trackSize.minSizingFunction = yoga::StyleSizeLength::percent(std::stof(minStr.substr(0, minStr.size() - 1)));
1580+
}
1581+
}
1582+
}
1583+
if (minmax.count("max")) {
1584+
auto& maxVal = minmax.at("max");
1585+
if (maxVal.hasType<Float>()) {
1586+
trackSize.maxSizingFunction = yoga::StyleSizeLength::points((float)(Float)maxVal);
1587+
} else if (maxVal.hasType<std::string>()) {
1588+
auto maxStr = (std::string)maxVal;
1589+
if (maxStr == "auto") {
1590+
trackSize.maxSizingFunction = yoga::StyleSizeLength::ofAuto();
1591+
} else if (maxStr.back() == '%') {
1592+
trackSize.maxSizingFunction = yoga::StyleSizeLength::percent(std::stof(maxStr.substr(0, maxStr.size() - 1)));
1593+
} else if (maxStr.size() > 2 && maxStr.substr(maxStr.size() - 2) == "fr") {
1594+
trackSize.maxSizingFunction = yoga::StyleSizeLength::stretch(std::stof(maxStr.substr(0, maxStr.size() - 2)));
1595+
}
1596+
}
1597+
}
1598+
} else {
1599+
// Try parsing as number (px without unit)
1600+
try {
1601+
float val = std::stof(trackStr);
1602+
trackSize.minSizingFunction = yoga::StyleSizeLength::points(val);
1603+
trackSize.maxSizingFunction = yoga::StyleSizeLength::points(val);
1604+
} catch (...) {
1605+
LOG(ERROR) << "Could not parse grid track value: " << trackStr;
1606+
continue;
1607+
}
1608+
}
1609+
}
1610+
result.push_back(trackSize);
1611+
}
1612+
}
1613+
}
1614+
1615+
// Grid Line conversion - parses grid line like 1, "auto", "span 2"
1616+
inline void fromRawValue(
1617+
const PropsParserContext& context,
1618+
const RawValue& value,
1619+
yoga::GridLine& result) {
1620+
result = yoga::GridLine{}; // Default to auto
1621+
1622+
if (value.hasType<int>()) {
1623+
result.type = yoga::GridLineType::Integer;
1624+
result.integer = (int)value;
1625+
return;
1626+
}
1627+
1628+
if (value.hasType<Float>()) {
1629+
result.type = yoga::GridLineType::Integer;
1630+
result.integer = static_cast<int32_t>((Float)value);
1631+
return;
1632+
}
1633+
1634+
if (value.hasType<std::string>()) {
1635+
auto stringValue = (std::string)value;
1636+
1637+
if (stringValue == "auto") {
1638+
result.type = yoga::GridLineType::Auto;
1639+
return;
1640+
}
1641+
1642+
// Check for "span N" format
1643+
if (stringValue.substr(0, 5) == "span ") {
1644+
result.type = yoga::GridLineType::Span;
1645+
try {
1646+
result.integer = std::stoi(stringValue.substr(5));
1647+
} catch (...) {
1648+
result.integer = 1;
1649+
}
1650+
return;
1651+
}
1652+
1653+
// Try parsing as integer
1654+
try {
1655+
result.type = yoga::GridLineType::Integer;
1656+
result.integer = std::stoi(stringValue);
1657+
return;
1658+
} catch (...) {
1659+
LOG(ERROR) << "Could not parse grid line value: " << stringValue;
1660+
}
1661+
}
1662+
}
1663+
15191664
} // namespace facebook::react

packages/react-native/ReactCommon/react/renderer/components/view/propsConversions.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,28 @@ convertRawProp(const PropsParserContext &context, const RawProps &rawProps, cons
362362
yogaStyle.setBoxSizing(
363363
convertRawProp(context, rawProps, "boxSizing", sourceValue.boxSizing(), yogaStyle.boxSizing()));
364364

365+
// Grid Container Properties
366+
const auto* gridColsRaw = rawProps.at("gridTemplateColumns", nullptr, nullptr);
367+
yogaStyle.setGridTemplateColumns(
368+
convertRawProp(context, rawProps, "gridTemplateColumns", sourceValue.gridTemplateColumns(), yogaStyle.gridTemplateColumns()));
369+
const auto* gridRowsRaw = rawProps.at("gridTemplateRows", nullptr, nullptr);
370+
yogaStyle.setGridTemplateRows(
371+
convertRawProp(context, rawProps, "gridTemplateRows", sourceValue.gridTemplateRows(), yogaStyle.gridTemplateRows()));
372+
yogaStyle.setGridAutoColumns(
373+
convertRawProp(context, rawProps, "gridAutoColumns", sourceValue.gridAutoColumns(), yogaStyle.gridAutoColumns()));
374+
yogaStyle.setGridAutoRows(
375+
convertRawProp(context, rawProps, "gridAutoRows", sourceValue.gridAutoRows(), yogaStyle.gridAutoRows()));
376+
377+
// Grid Item Properties
378+
yogaStyle.setGridColumnStart(
379+
convertRawProp(context, rawProps, "gridColumnStart", sourceValue.gridColumnStart(), yogaStyle.gridColumnStart()));
380+
yogaStyle.setGridColumnEnd(
381+
convertRawProp(context, rawProps, "gridColumnEnd", sourceValue.gridColumnEnd(), yogaStyle.gridColumnEnd()));
382+
yogaStyle.setGridRowStart(
383+
convertRawProp(context, rawProps, "gridRowStart", sourceValue.gridRowStart(), yogaStyle.gridRowStart()));
384+
yogaStyle.setGridRowEnd(
385+
convertRawProp(context, rawProps, "gridRowEnd", sourceValue.gridRowEnd(), yogaStyle.gridRowEnd()));
386+
365387
return yogaStyle;
366388
}
367389

0 commit comments

Comments
 (0)