Skip to content

Commit e97b4fd

Browse files
CSS Grid 9/9: Yoga Playground grid property support (facebook#1887)
Summary: Pull Request resolved: facebook#1887 Add grid property support to the Yoga Playground website component. Includes: - Grid display mode support - Grid template columns/rows properties - Grid auto columns/rows properties - Grid column/row start/end properties (including span support) - Grid track value parsing (auto, fr, %, px, minmax) Differential Revision: D93959518
1 parent bbcd9d8 commit e97b4fd

1 file changed

Lines changed: 144 additions & 2 deletions

File tree

website/src/components/FlexStyle.ts

Lines changed: 144 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
Display,
1515
Edge,
1616
FlexDirection,
17+
GridTrackType,
1718
Gutter,
1819
Justify,
1920
Overflow,
@@ -63,7 +64,7 @@ export type FlexStyle = {
6364
bottom?: number | `${number}%`;
6465
boxSizing?: 'border-box' | 'content-box';
6566
direction?: 'ltr' | 'rtl';
66-
display?: 'none' | 'flex' | 'contents';
67+
display?: 'none' | 'flex' | 'contents' | 'grid';
6768
end?: number | `${number}%`;
6869
flex?: number;
6970
flexBasis?: number | 'auto' | `${number}%`;
@@ -108,6 +109,15 @@ export type FlexStyle = {
108109
insetBlock?: number | `${number}%`;
109110
inset?: number | `${number}%`;
110111
width?: number | 'auto' | `${number}%`;
112+
// Grid properties
113+
gridTemplateColumns?: string;
114+
gridTemplateRows?: string;
115+
gridAutoColumns?: string;
116+
gridAutoRows?: string;
117+
gridColumnStart?: number | `span ${number}`;
118+
gridColumnEnd?: number | `span ${number}`;
119+
gridRowStart?: number | `span ${number}`;
120+
gridRowEnd?: number | `span ${number}`;
111121
};
112122

113123
export function applyStyle(node: YogaNode, style: FlexStyle = {}): void {
@@ -297,6 +307,64 @@ export function applyStyle(node: YogaNode, style: FlexStyle = {}): void {
297307
case 'width':
298308
node.setWidth(style.width);
299309
break;
310+
case 'gridTemplateColumns':
311+
node.setGridTemplateColumns(
312+
parseGridTemplate(style.gridTemplateColumns),
313+
);
314+
break;
315+
case 'gridTemplateRows':
316+
node.setGridTemplateRows(parseGridTemplate(style.gridTemplateRows));
317+
break;
318+
case 'gridAutoColumns':
319+
node.setGridAutoColumns(parseGridTemplate(style.gridAutoColumns));
320+
break;
321+
case 'gridAutoRows':
322+
node.setGridAutoRows(parseGridTemplate(style.gridAutoRows));
323+
break;
324+
case 'gridColumnStart':
325+
if (
326+
typeof style.gridColumnStart === 'string' &&
327+
style.gridColumnStart.startsWith('span ')
328+
) {
329+
node.setGridColumnStartSpan(
330+
parseInt(style.gridColumnStart.slice(5), 10),
331+
);
332+
} else {
333+
node.setGridColumnStart(style.gridColumnStart as number);
334+
}
335+
break;
336+
case 'gridColumnEnd':
337+
if (
338+
typeof style.gridColumnEnd === 'string' &&
339+
style.gridColumnEnd.startsWith('span ')
340+
) {
341+
node.setGridColumnEndSpan(
342+
parseInt(style.gridColumnEnd.slice(5), 10),
343+
);
344+
} else {
345+
node.setGridColumnEnd(style.gridColumnEnd as number);
346+
}
347+
break;
348+
case 'gridRowStart':
349+
if (
350+
typeof style.gridRowStart === 'string' &&
351+
style.gridRowStart.startsWith('span ')
352+
) {
353+
node.setGridRowStartSpan(parseInt(style.gridRowStart.slice(5), 10));
354+
} else {
355+
node.setGridRowStart(style.gridRowStart as number);
356+
}
357+
break;
358+
case 'gridRowEnd':
359+
if (
360+
typeof style.gridRowEnd === 'string' &&
361+
style.gridRowEnd.startsWith('span ')
362+
) {
363+
node.setGridRowEndSpan(parseInt(style.gridRowEnd.slice(5), 10));
364+
} else {
365+
node.setGridRowEnd(style.gridRowEnd as number);
366+
}
367+
break;
300368
}
301369
} catch (e) {
302370
// Fail gracefully
@@ -360,14 +428,16 @@ function direction(str?: 'ltr' | 'rtl'): Direction {
360428
throw new Error(`"${str}" is not a valid value for direction`);
361429
}
362430

363-
function display(str?: 'none' | 'flex' | 'contents'): Display {
431+
function display(str?: 'none' | 'flex' | 'contents' | 'grid'): Display {
364432
switch (str) {
365433
case 'none':
366434
return Display.None;
367435
case 'flex':
368436
return Display.Flex;
369437
case 'contents':
370438
return Display.Contents;
439+
case 'grid':
440+
return Display.Grid;
371441
}
372442
throw new Error(`"${str}" is not a valid value for display`);
373443
}
@@ -441,3 +511,75 @@ function position(str?: 'absolute' | 'relative' | 'static'): PositionType {
441511
}
442512
throw new Error(`"${str}" is not a valid value for position`);
443513
}
514+
515+
type GridTrackValue = {
516+
type: GridTrackType;
517+
value?: number;
518+
min?: GridTrackValue;
519+
max?: GridTrackValue;
520+
};
521+
522+
function parseGridTrackValue(track: string): GridTrackValue {
523+
track = track.trim();
524+
525+
if (track === 'auto') {
526+
return {type: GridTrackType.Auto};
527+
}
528+
529+
if (track.endsWith('fr')) {
530+
return {type: GridTrackType.Fr, value: parseFloat(track)};
531+
}
532+
533+
if (track.endsWith('%')) {
534+
return {type: GridTrackType.Percent, value: parseFloat(track)};
535+
}
536+
537+
if (track.endsWith('px')) {
538+
return {type: GridTrackType.Points, value: parseFloat(track)};
539+
}
540+
541+
if (track.startsWith('minmax(') && track.endsWith(')')) {
542+
const inner = track.slice(7, -1);
543+
const commaIndex = findTopLevelComma(inner);
544+
if (commaIndex === -1) {
545+
throw new Error(`Invalid minmax syntax: ${track}`);
546+
}
547+
const minPart = inner.slice(0, commaIndex).trim();
548+
const maxPart = inner.slice(commaIndex + 1).trim();
549+
return {
550+
type: GridTrackType.Minmax,
551+
min: parseGridTrackValue(minPart),
552+
max: parseGridTrackValue(maxPart),
553+
};
554+
}
555+
556+
const num = parseFloat(track);
557+
if (!isNaN(num)) {
558+
return {type: GridTrackType.Points, value: num};
559+
}
560+
561+
throw new Error(`Invalid grid track value: ${track}`);
562+
}
563+
564+
function findTopLevelComma(str: string): number {
565+
let depth = 0;
566+
for (let i = 0; i < str.length; i++) {
567+
if (str[i] === '(') depth++;
568+
else if (str[i] === ')') depth--;
569+
else if (str[i] === ',' && depth === 0) return i;
570+
}
571+
return -1;
572+
}
573+
574+
function parseGridTemplate(template?: string): GridTrackValue[] {
575+
if (!template) return [];
576+
577+
const tracks: GridTrackValue[] = [];
578+
const parts = template.trim().split(/\s+/);
579+
580+
for (const part of parts) {
581+
tracks.push(parseGridTrackValue(part));
582+
}
583+
584+
return tracks;
585+
}

0 commit comments

Comments
 (0)