react-reflex
Version:
Flex layout component for advanced React web applications
203 lines (165 loc) • 4.41 kB
JSX
///////////////////////////////////////////////////////////
// ReflexElement
// By Philippe Leefsma
// December 2016
//
///////////////////////////////////////////////////////////
import ReflexHandle from './ReflexHandle'
import {getDataProps} from './utilities'
import throttle from 'lodash.throttle'
import Measure from 'react-measure'
import PropTypes from 'prop-types'
import React from 'react'
const toArray = (obj) => {
return obj ? (Array.isArray(obj) ? obj : [obj]) : []
}
class SizeAwareReflexElement extends React.Component {
constructor (props) {
super (props)
this.setDimensions = throttle((dimensions) => {
this.setState(dimensions)
}, this.props.propagateDimensionsRate/1000)
this.state = {
height: "100%",
width: "100%"
}
}
onResize = (rect) => {
const { resizeHeight, resizeWidth } = this.props
const {height, width} = rect.bounds
this.setDimensions({
...(resizeHeight && {height}),
...(resizeWidth && {width})
})
}
renderChildren () {
const {propagateDimensions} = this.props
const validChildren = toArray(this.props.children).filter(child => {
return !!child
})
return React.Children.map(validChildren, (child) => {
if (this.props.withHandle || ReflexHandle.isA(child)) {
return React.cloneElement(child, {
dimensions: propagateDimensions && this.state,
...child.props,
index: this.props.index - 1,
events: this.props.events
})
}
if (propagateDimensions) {
return React.cloneElement(child, {
...child.props,
dimensions: this.state
})
}
return child
})
}
render () {
return (
<Measure bounds onResize={this.onResize}>
{
({measureRef}) => {
return (
<div ref={measureRef} className="reflex-size-aware">
<div style={this.state}>
{ this.renderChildren() }
</div>
</div>
)
}
}
</Measure>
)
}
}
class ReflexElement extends React.Component {
static propTypes = {
propagateDimensions: PropTypes.bool,
resizeHeight: PropTypes.bool,
resizeWidth: PropTypes.bool,
className: PropTypes.string,
size: PropTypes.number
}
static defaultProps = {
propagateDimensionsRate: 100,
propagateDimensions: false,
resizeHeight: true,
resizeWidth: true,
direction: [1],
className: ''
}
constructor (props) {
super (props)
this.state = {
size: props.size
}
}
static getDerivedStateFromProps (nextProps, prevState) {
if (nextProps.size !== prevState.size) {
return {
...prevState,
size: nextProps.size
}
}
return null
}
async componentDidUpdate (prevProps, prevState, snapshot) {
if (prevState.size !== this.state.size) {
const directions = toArray(this.props.direction)
for (let direction of directions) {
await this.props.events.emit('element.size', {
index: this.props.index,
size: this.props.size,
direction
})
}
}
}
renderChildren () {
const validChildren = toArray(this.props.children).filter(child => {
return !!child
})
return React.Children.map(validChildren, (child) => {
if (this.props.withHandle || ReflexHandle.isA(child)) {
return React.cloneElement(child, {
...child.props,
index: this.props.index - 1,
events: this.props.events
})
}
return child
})
}
render () {
const className = [
...this.props.className.split(' '),
this.props.orientation,
'reflex-element'
].join(' ').trim()
const style = {
...this.props.style,
flexGrow: this.props.flex,
flexShrink: 1,
flexBasis: '0%'
}
return (
<div
{...getDataProps(this.props)}
ref={this.props.innerRef}
className={className}
style={style}>
{
this.props.propagateDimensions
? <SizeAwareReflexElement {...this.props}/>
: this.renderChildren()
}
</div>
)
}
}
export default React.forwardRef((props, ref) => {
return (
<ReflexElement innerRef={ref} {...props}/>
)
})