import React from 'react' import ReactDOM from 'react-dom' import { identity } from '../../func' export interface BoundingClientRect { width: number height: number left: number right: number top: number bottom: number } interface Config { debounceFunc: (func: () => void) => () => void delay: number } const defaultConfig = { debounceFunc: identity, delay: 1000, } type WithClientRect

= P & { clientRect: BoundingClientRect } function hocWithConfig

(Component: React.ComponentClass

| React.SFC

, config: Config) { class Composed extends React.Component> { static displayName = `withBoundingRect<${Component.displayName || Component.name || 'Unknown'}>` state: BoundingClientRect = { width: 0, height: 0, left: 0, right: 0, top: 0, bottom: 0, } calcRect = config.debounceFunc(() => { let node: Element = ReactDOM.findDOMNode(this) as Element // ! to get correct client rect, have to remove all children // then append them back after the calculation ! let children: any[] = [] while (node.firstChild) { children.push(node.removeChild(node.firstChild!)) } setTimeout(() => { const rect = node.getBoundingClientRect() children.forEach(e => node.appendChild(e)) this.setState({ width: rect.width, height: rect.height, left: rect.left, right: rect.right, top: rect.top, bottom: rect.bottom, }) }, config.delay) }) componentDidMount() { setTimeout(() => this.calcRect()) window.addEventListener('resize', this.calcRect) window.addEventListener('resize-custom', this.calcRect) } componentWillUnmount() { window.removeEventListener('resize', this.calcRect) window.removeEventListener('resize-custom', this.calcRect) } render() { return } } return Composed as React.ComponentClass> } export function withBoundingRect(config: Partial = {}) { return Component => hocWithConfig(Component, Object.assign({}, defaultConfig, config)) }