import React from "react" export interface VerticalLayoutComponentProps { /** Height of component */ height: number /** Fraction to allocate for fractional heights. Should total 1.0. Keyed by key of child */ relativeHeights: { [key: string]: number } /** Children to render */ children?: React.ReactNode } interface VerticalLayoutComponentState { availableHeight: number | null } // Lays out divs vertically, allowing fractional allocation combined with auto-sized ones // Children must all have keys // Children will be cloned with height: prop set in case of fractional ones export default class VerticalLayoutComponent extends React.Component< VerticalLayoutComponentProps, VerticalLayoutComponentState > { contents: { [key: string]: any } = {} constructor(props: VerticalLayoutComponentProps) { super(props) this.state = { availableHeight: null } } componentDidUpdate(prevProps: VerticalLayoutComponentProps) { if ( prevProps.height !== this.props.height || JSON.stringify(prevProps.relativeHeights) !== JSON.stringify(this.props.relativeHeights) ) { this.recalculateSize() } } componentDidMount() { // Use setTimeout to ensure DOM is fully rendered before calculating sizes setTimeout(() => this.recalculateSize(), 0) } recalculateSize = () => { // Calculate available height let availableHeight = this.props.height React.Children.forEach(this.props.children, (child) => { if (!React.isValidElement(child)) { return } // If child has relative height, skip if (child.key && this.props.relativeHeights[child.key]) { return } const node = child.key != null ? this.contents[child.key] : null if (node) { availableHeight -= node.offsetHeight } }) this.setState({ availableHeight }) } // Get a subcomponent getComponent(key: string) { return this.contents[key] } render() { // Calculate scaling return (