UNPKG

react-flexbox-layout

Version:
202 lines (163 loc) 5.7 kB
import extend from 'lodash/extend'; import invokeMap from 'lodash/invokeMap'; import range from 'lodash/range'; import sum from 'lodash/sum'; import React from 'react'; import ReactDOM from 'react-dom'; import VLayoutItemIE9 from './vertical_item_ie9'; import {VLayoutPropTypes, VLayoutDefaultPropTypes, cleanProps} from './prop_types'; import { getVGutterSizes, makeVLayoutItemChildProps, forEachNonEmpty, mapNonEmpty, countNonEmpty, sumSizes, addTo, getSizeCalc, didDefineHeight, didDefineWidth } from './util'; import {register, deregister, requestAsyncUpdate} from './update_engine_ie9'; export default function(defaultGutter, gutterMultiplier, defaultGutterUnit) { class VLayoutIE9 extends React.Component { componentWillMount() { register(this); } componentDidMount() { this.node = ReactDOM.findDOMNode(this); requestAsyncUpdate(); } componentWillUnmount() { deregister(this); } componentDidUpdate() { requestAsyncUpdate(); } render() { this.itemsRefs = []; this.gutterSizes = getVGutterSizes(this.props.children, this.props.gutter); let children = mapNonEmpty(this.props.children, (child, index) => { return this._buildChild(child, index, this.gutterSizes); }); return ( <div ref="wrapper" data-display-name="VLayoutWrapper" {...cleanProps(this.props)} style={extend(this._getLayoutWrapperStyles(), this.props.style)} > <div ref="container" data-display-name="VLayout" style={this._getLayoutStyles()} > {children} </div> </div> ); } _buildChild(child, index, gutterSizes) { let props = makeVLayoutItemChildProps(this.props, child.props, index, gutterSizes, gutterMultiplier); let ref = `item_${index}`; this.itemsRefs.push(ref); props.ref = ref; if (child.type && child.type._isLayoutChild) { return React.cloneElement(child, props); } else { return <VLayoutItemIE9 {...props}>{child}</VLayoutItemIE9>; } } _unsetLayoutStyles() { const style = this.node.style; if (!didDefineHeight(this.props)) { style.height = ''; } if (!didDefineWidth(this.props)) { style.width = ''; } range(countNonEmpty(this.props.children)).forEach((i) => { this.refs[`item_${i}`]._unsetLayoutStyles(); }, this); } _measureInheritedStyles() { const computedStyle = window.getComputedStyle(this.node); this._inheritedTextAlign = computedStyle.textAlign; } _measureWidths() {} _applyInheritedStyles() { const items = this.itemsRefs.map(ref => this.refs[ref]); invokeMap(items, '_applyInheritedStyles', this._inheritedTextAlign); } _applyWidths() { if (!didDefineWidth(this.props)) { // our element is display:table, so add width:100% to make it behave like a block element this.node.style.width = '100%'; } } _measureItemHeights() { const items = this.itemsRefs.map(ref => this.refs[ref]); this._measuredHeights = items.map((item) => { if (didDefineHeight(item.props) || item.props.flexGrow) { return null; } return item._measureHeight(); }); } _applyFlexHeights() { const items = this.itemsRefs.map(ref => this.refs[ref]); const flexGrowValues = items .filter(item => item.props.flexGrow) .map(item => item.props.flexGrow === true ? 1 : item.props.flexGrow); const totalFlexGrow = sum(flexGrowValues); // sum heights used up by elements const usedSpace = sumSizes('height', items); // add computed heights const measuredHeightsAsNumbers = this._measuredHeights .filter(i => i !== null) .map(measurement => parseFloat(measurement.slice(0, -2))); addTo(usedSpace, 'px', sum(measuredHeightsAsNumbers)); // add gutters addTo(usedSpace, this.props.gutterUnit, sum(this.gutterSizes)); range(countNonEmpty(this.props.children)).forEach((i) => { const item = this.refs[`item_${i}`]; if (item.props.flexGrow) { return item._applyHeight(getSizeCalc(usedSpace, item.props.flexGrow, totalFlexGrow)); } }); } _callDidLayout() { this.props.onLayout && this.props.onLayout(); } _getLayoutWrapperStyles() { let styles = { width: this.props.width, height: this.props.height }; if (this._isFlexboxLayout()) { styles.display = 'block'; } else { // NOTE: use !important override rflGrowChildStatic className, which uses display: block !important // to override children who use display: inline-block styles.display = 'table !important'; styles.tableLayout = 'fixed'; } return styles; } _getLayoutStyles() { let styles = { display: this._isFlexboxLayout() ? 'block' : 'table-cell', height: '100%', verticalAlign: this.props.alignItems }; return styles; } // Whenever one of the children has a flexGrow the whole layout // becomes 'flex' _isFlexboxLayout() { let hasFlexGrowChild = false; forEachNonEmpty(this.props.children, (child) => { if (child.props.flexGrow) { hasFlexGrowChild = true; } }); return hasFlexGrowChild; } } VLayoutIE9.propTypes = VLayoutPropTypes; VLayoutIE9.defaultProps = extend({}, VLayoutDefaultPropTypes, { gutter: defaultGutter, gutterUnit: defaultGutterUnit }); return VLayoutIE9; }