Utilities and UI components for cross-platform React Native apps
0

Configure Feed

Select the types of activity you want to include in your feed.

feat: add grid `Flow` layout

Lays out its children in a grid-like manner, with full customizability over the direction and order.

Joseph Hale (Jan 25, 2026, 2:53 AM -0700) 3e58797c 79c2804d

+139
+21
src/layouts/Column.tsx
··· 1 + // Copyright (c) 2026 Joseph Hale 2 + // 3 + // This Source Code Form is subject to the terms of the Mozilla Public 4 + // License, v. 2.0. If a copy of the MPL was not distributed with this 5 + // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 + 7 + import React from "react" 8 + import { View } from "react-native"; 9 + 10 + export interface ColumnProps extends React.ComponentProps<typeof View> { 11 + reverse?: boolean; 12 + } 13 + 14 + export default function Column(props: ColumnProps) { 15 + const direction = props.reverse ? 'column-reverse' : 'column'; 16 + return ( 17 + <View {...props} style={[props.style, { flexDirection: direction }]}> 18 + {props.children} 19 + </View> 20 + ) 21 + }
+84
src/layouts/Flow.tsx
··· 1 + // Copyright (c) 2026 Joseph Hale 2 + // 3 + // This Source Code Form is subject to the terms of the Mozilla Public 4 + // License, v. 2.0. If a copy of the MPL was not distributed with this 5 + // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 + 7 + import Column from "./Column"; 8 + import Row from "./Row"; 9 + import { range } from "../utils/iteration"; 10 + 11 + export interface FlowDefinition { 12 + origin: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'; 13 + direction: 'row' | 'column'; 14 + } 15 + 16 + export interface FlowProps { 17 + rows: number; 18 + columns: number; 19 + children: React.ReactNode[]; 20 + flow: FlowDefinition; 21 + } 22 + 23 + export interface FlowDirectionProps { 24 + rows: number; 25 + columns: number; 26 + children: React.ReactNode[]; 27 + origin: FlowDefinition['origin']; 28 + } 29 + 30 + function Flow(props: FlowProps) { 31 + if (props.flow.direction === 'column') { 32 + return ( 33 + <ColumnFlow {...props} origin={props.flow.origin}> 34 + {props.children} 35 + </ColumnFlow> 36 + ) 37 + } else if (props.flow.direction === 'row') { 38 + return ( 39 + <RowFlow {...props} origin={props.flow.origin}> 40 + {props.children} 41 + </RowFlow> 42 + ) 43 + } else { 44 + throw new Error(`Invalid flow direction: ${props.flow.direction}`); 45 + } 46 + } 47 + 48 + function ColumnFlow(props: FlowDirectionProps) { 49 + const { rows, columns, children, origin } = props; 50 + 51 + return ( 52 + <Row reverse={origin.endsWith("right")}> 53 + {range(0, columns).map((column) => ( 54 + <Column key={column} reverse={origin.startsWith("bottom")}> 55 + {range(0, rows).map((row) => children[column * rows + row])} 56 + </Column> 57 + ))} 58 + </Row> 59 + ) 60 + } 61 + 62 + function RowFlow(props: FlowDirectionProps) { 63 + const { rows, columns, children, origin } = props; 64 + 65 + return ( 66 + <Column reverse={origin.startsWith("bottom")}> 67 + {range(0, rows).map((row) => ( 68 + <Row key={row} reverse={origin.endsWith("right")}> 69 + {range(0, columns).map((column) => children[row * columns + column])} 70 + </Row> 71 + ))} 72 + </Column> 73 + ) 74 + } 75 + 76 + interface FlowComponent extends React.FC<FlowProps> { 77 + Column: typeof ColumnFlow; 78 + Row: typeof RowFlow; 79 + } 80 + 81 + const _Flow = Flow as FlowComponent; 82 + _Flow.Column = ColumnFlow; 83 + _Flow.Row = RowFlow; 84 + export default _Flow;
+21
src/layouts/Row.tsx
··· 1 + // Copyright (c) 2026 Joseph Hale 2 + // 3 + // This Source Code Form is subject to the terms of the Mozilla Public 4 + // License, v. 2.0. If a copy of the MPL was not distributed with this 5 + // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 + 7 + import React from "react" 8 + import { View } from "react-native"; 9 + 10 + export interface RowProps extends React.ComponentProps<typeof View> { 11 + reverse?: boolean; 12 + } 13 + 14 + export default function Row(props: RowProps) { 15 + const direction = props.reverse ? 'row-reverse' : 'row'; 16 + return ( 17 + <View {...props} style={[props.style, { flexDirection: direction }]}> 18 + {props.children} 19 + </View> 20 + ) 21 + }
+1
src/layouts/index.ts
··· 4 4 // License, v. 2.0. If a copy of the MPL was not distributed with this 5 5 // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 6 7 + export { default as Flow, type FlowProps, type FlowDirectionProps, type FlowDefinition } from "./Flow"; 7 8 export { default as Measured, useMeasurements } from "./Measured";
+12
src/utils/iteration.ts
··· 1 + // Copyright (c) 2026 Joseph Hale 2 + // 3 + // This Source Code Form is subject to the terms of the Mozilla Public 4 + // License, v. 2.0. If a copy of the MPL was not distributed with this 5 + // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 + 7 + /** 8 + * Produces an array of numbers from [start, end). 9 + */ 10 + export function range(start: number, end: number) { 11 + return Array.from({ length: end - start }).map((_, index) => index + start); 12 + }