superdesk/superdesk-client-core

View on GitHub
scripts/core/components/resize-observer-component.tsx

Summary

Maintainability
A
1 hr
Test Coverage
import React from 'react';

interface IResizeObserverDimensions {
    width: number;
    height: number;
}

interface IPropsResizeObserverComponent {
    style?: React.CSSProperties;
    position?: 'relative' | 'absolute';
    children: (props: IResizeObserverDimensions) => JSX.Element;
}

interface IState {
    dimensions: IResizeObserverDimensions | 'not-initialized';
}

/**
 * Higher order component for dynamically retrieving element dimensions.
 * TODO: replace with the one from ui-framework
 */
export class ResizeObserverComponent extends React.PureComponent<IPropsResizeObserverComponent, IState> {
    private el: HTMLDivElement;
    private observerInstance: ResizeObserver;

    constructor(props: IPropsResizeObserverComponent) {
        super(props);

        this.state = {
            dimensions: 'not-initialized',
        };
    }

    componentDidMount() {
        this.observerInstance = new ResizeObserver((entries) => {
            this.setState({
                dimensions: {
                    width: Math.floor(entries[0].contentRect.width),
                    height: Math.floor(entries[0].contentRect.height),
                },
            });
        });

        this.observerInstance.observe(this.el);
    }

    componentWillUnmount() {
        this.observerInstance.unobserve(this.el);
    }

    render() {
        const {dimensions} = this.state;

        return (
            <div
                ref={(el) => {
                    this.el = el;
                }}
                style={{
                    position: 'relative',
                    ...this.props.style,
                }}
            >
                {/**
                 * Absolute positioning is needed for accurate calculation.
                 * Otherwise, initial calculation would work well,
                 * but if parent of `ResizeObserverComponent` is resized down,
                 * it would include its own size(which is based on the initial result from this component),
                 * including children, into calculation and would produce a wrong result.
                */}
                <div
                    style={
                        this.props.position === 'absolute'
                            ? {
                                position: 'absolute',
                                insetBlockStart: 0,
                                insetInlineStart: 0,
                                width: '100%',
                                height: '100%',
                            }
                            : undefined
                    }
                >
                    {
                        dimensions === 'not-initialized'
                            ? null
                            : this.props.children(dimensions)
                    }
                </div>
            </div>
        );
    }
}