···11+// Copyright (c) 2026 Joseph Hale
22+//
33+// This Source Code Form is subject to the terms of the Mozilla Public
44+// License, v. 2.0. If a copy of the MPL was not distributed with this
55+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
66+77+import { View } from "react-native";
88+import Measured, { useMeasurements } from "./Measured";
99+import Flow, { type FlowDefinition } from "./Flow";
1010+import styles from "../styles";
1111+1212+export interface SquaresProps {
1313+ rows: number;
1414+ columns: number;
1515+ flow?: FlowDefinition;
1616+ children: Iterable<React.ReactNode>;
1717+}
1818+1919+const DEFAULT_FLOW: FlowDefinition = {
2020+ origin: 'top-left',
2121+ direction: 'column',
2222+}
2323+2424+/**
2525+ * Places its children into a grid of squares.
2626+ */
2727+export default function Squares(props: SquaresProps) {
2828+ return (
2929+ <Measured>
3030+ <MeasuredSquares {...props} />
3131+ </Measured>
3232+ )
3333+}
3434+3535+function MeasuredSquares(props: SquaresProps) {
3636+ const squares = useSquares(props.rows, props.columns, Array.from(props.children));
3737+ const flow = props.flow || DEFAULT_FLOW;
3838+3939+ return (
4040+ <View style={[styles.layout.centered, styles.layout.filled]}>
4141+ <Flow rows={props.rows} columns={props.columns} flow={flow}>
4242+ {squares}
4343+ </Flow>
4444+ </View>
4545+ )
4646+}
4747+4848+function useSquares(rows: number, columns: number, children: React.ReactNode[]) {
4949+ const { width, height } = useMeasurements();
5050+ const length = Math.floor(Math.min(width / columns, height / rows));
5151+ return new Array(rows * columns).fill(null).map((_, i) => (
5252+ <Square key={i} size={length}>
5353+ {children[i]}
5454+ </Square>
5555+ ));
5656+}
5757+5858+interface SquareProps {
5959+ children: React.ReactNode;
6060+ size: number;
6161+}
6262+6363+function Square(props: SquareProps) {
6464+ return (
6565+ <View style={{ width: props.size, height: props.size }}>
6666+ {props.children}
6767+ </View>
6868+ )
6969+}
+1
src/layouts/index.ts
···6677export { default as Flow, type FlowProps, type FlowDirectionProps, type FlowDefinition } from "./Flow";
88export { default as Measured, useMeasurements } from "./Measured";
99+export { default as Squares, type SquaresProps } from './Squares';