···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 React, { createContext, useCallback, useContext, useRef, useState, type ComponentRef } from "react";
88+import { View } from "react-native";
99+1010+type Measurements = Readonly<{
1111+ x: number;
1212+ y: number;
1313+ width: number;
1414+ height: number;
1515+}>
1616+1717+const UNMEASURED = Object.freeze({ x: -1, y: -1, width: -1, height: -1 } satisfies Measurements);
1818+1919+const MeasurementsContext = createContext<Measurements>(UNMEASURED);
2020+2121+export function useMeasurements() {
2222+ return useContext(MeasurementsContext);
2323+}
2424+2525+type OnLayout = NonNullable<React.ComponentProps<typeof View>['onLayout']>;
2626+2727+export default function Measured({ children }: { children: React.ReactNode }) {
2828+ const ref = useRef<ComponentRef<View>>(null);
2929+ const [measurements, setMeasurements] = useState(UNMEASURED);
3030+ const saveMeasurements = useCallback<OnLayout>(
3131+ ({ nativeEvent }) => { setMeasurements(nativeEvent.layout) },
3232+ [setMeasurements]
3333+ );
3434+ return (
3535+ <View ref={ref} onLayout={saveMeasurements}>
3636+ <MeasurementsContext.Provider value={measurements}>
3737+ {children}
3838+ </MeasurementsContext.Provider>
3939+ </View>
4040+ );
4141+}
+1
src/layouts/index.ts
···55// file, You can obtain one at https://mozilla.org/MPL/2.0/.
6677export { default as Centered } from './Centered';
88+export { default as Measured, useMeasurements } from "./Measured";