diff --git a/src/common/util.js b/src/common/util.js index 2126f3d45..9ab1294bc 100644 --- a/src/common/util.js +++ b/src/common/util.js @@ -245,6 +245,12 @@ module.exports = { context[method] = (...args) => refinement.call(context, args, original.apply(context, args)) + }, + + restrict(value, lower, upper) { + value = Math.max(value, (lower != null) ? lower : value) + value = Math.min(value, (upper != null) ? upper : value) + return value } } diff --git a/src/components/panel.js b/src/components/panel.js index 5259f4512..e04b406c1 100644 --- a/src/components/panel.js +++ b/src/components/panel.js @@ -57,7 +57,7 @@ const PanelGroup = ({ header, children, height }) => {
{panels.map((panel, idx) => ( - + {panel} ))} diff --git a/src/components/resizable.js b/src/components/resizable.js index 00e0e6ca4..a2569859e 100644 --- a/src/components/resizable.js +++ b/src/components/resizable.js @@ -1,9 +1,13 @@ 'use strict' const React = require('react') -const { PropTypes } = React -const { Shapes } = require('./util') -const cn = require('classnames') +const { PureComponent, PropTypes } = React +const { func, node, bool, number, oneOf } = PropTypes +const cx = require('classnames') +const { bounds, on, off } = require('../dom') +const { restrict } = require('../common/util') +const { keys } = Object + const DIR = { top: 'row', right: 'col', bottom: 'row', left: 'col' @@ -13,36 +17,132 @@ const DIM = { top: 'height', right: 'width', bottom: 'height', left: 'width' } +const OPP = { + top: 'bottom', right: 'left', bottom: 'top', left: 'right' +} -const ResizableHandle = ({ edge }) => ( -
-) - -ResizableHandle.propTypes = { - edge: Shapes.edge.isRequired +const AXS = { + top: 'pageY', right: 'pageX', bottom: 'pageY', left: 'pageX' } -const Resizable = ({ value, children, edge, relative }) => ( -
- {children} - -
-) - -Resizable.propTypes = { - children: PropTypes.node, - edge: Shapes.edge.isRequired, - relative: PropTypes.bool, - value: PropTypes.number.isRequired +class Resizable extends PureComponent { + constructor(props) { + super(props) + + this.state = { + value: props.value, + isDragging: false + } + } + + componentWillUnmount() { + if (this.state.isDragging) { + this.stop() + } + } + + componentWillReceiveProps(props) { + this.setState({ value: props.value }) + } + + get dimension() { + return DIM[this.props.edge] + } + + get value() { + return `${this.state.value}${this.props.isRelative ? '%' : 'px'}` + } + + get style() { + return { + [this.dimension]: this.value + } + } + + handleMouseDown = () => { + this.start() + } + + setContainer = (container) => { + this.container = container + } + + update = (event) => { + const { edge, min, max } = this.props + + const origin = bounds(this.container)[OPP[edge]] + const value = restrict(event[AXS[edge]] - origin, min, max) + + this.setState({ value }) + } + + start() { + this.setState({ isDragging: true }) + + on(document, 'mousemove', this.update) + on(document, 'mouseup', this.stop, { capture: true }) + on(document, 'mouseleave', this.stop) + on(window, 'blur', this.stop) + } + + stop = () => { + this.setState({ isDragging: false }) + + off(document, 'mousemove', this.update) + off(document, 'mouseup', this.stop, { capture: true }) + off(document, 'mouseleave', this.stop) + off(window, 'blur', this.stop) + + if (this.props.value !== this.state.value) { + //this.props.onResize(value) + //console.log('resize', this.state.value) + } + } + + + renderHandle() { + const { edge, isDisabled } = this.props + + return !isDisabled && ( +
+ ) + } + + render() { + return ( +
+ {this.props.children} + {this.renderHandle()} +
+ ) + } + + static propTypes = { + children: node, + edge: oneOf(keys(DIM)).isRequired, + isDisabled: bool, + isRelative: bool, + value: number.isRequired, + min: number, + max: number, + onResize: func //.isRequired + } + + static defaultProps = { + min: 0 + } } + module.exports = { - Resizable, - ResizableHandle + Resizable } diff --git a/src/components/slider.js b/src/components/slider.js index b02d11f3d..566dc1fe6 100644 --- a/src/components/slider.js +++ b/src/components/slider.js @@ -5,8 +5,9 @@ const { PureComponent, PropTypes } = React const { IconButton } = require('./button') const cx = require('classnames') const { bounds, borders, on, off } = require('../dom') -const { noop } = require('../common/util') +const { restrict } = require('../common/util') const { bool, element, func, number, oneOf } = PropTypes +const { round } = Math class Slider extends PureComponent { @@ -20,7 +21,7 @@ class Slider extends PureComponent { componentWillReceiveProps({ value }) { if (value !== this.props.value && - value !== Math.round(this.state.value)) { + value !== round(this.state.value)) { this.setState({ value }) } } @@ -44,7 +45,7 @@ class Slider extends PureComponent { const left = box.left + border.left const width = box.width - border.left - border.right - this.set(min + restrict((event.pageX - left) / width) * max) + this.set(min + restrict((event.pageX - left) / width, 0, 1) * max) } handleMouseDown = ({ button, pageX }) => { @@ -54,21 +55,10 @@ class Slider extends PureComponent { this.startDragging() } - handleMouseUp = (event) => { - try { - if (event instanceof MouseEvent) { - this.update(event) - } - - } finally { - this.stopDragging() - } - } - set = (value) => { this.setState({ value }) - const nearest = Math.round(value) + const nearest = round(value) if (nearest !== this.props.value) { this.props.onChange(nearest) @@ -106,18 +96,18 @@ class Slider extends PureComponent { this.setState({ isDragging: true }) on(document, 'mousemove', this.update) - on(document, 'mouseup', this.handleMouseUp, { capture: true }) + on(document, 'mouseup', this.stopDragging, { capture: true }) on(document, 'mouseleave', this.stopDragging) - on(window, 'blur', this.handleMouseUp) + on(window, 'blur', this.stopDragging) } stopDragging = () => { this.setState({ isDragging: false }) off(document, 'mousemove', this.update) - off(document, 'mouseup', this.handleMouseUp, { capture: true }) + off(document, 'mouseup', this.stopDragging, { capture: true }) off(document, 'mouseleave', this.stopDragging) - off(window, 'blur', this.handleMouseUp) + off(window, 'blur', this.stopDragging) if (this.delayed) { clearTimeout(this.delayed) @@ -197,15 +187,10 @@ class Slider extends PureComponent { static defaultProps = { min: 0, max: 1, - size: 'md', - onChange: noop + size: 'md' } } -function restrict(value, lower = 0, upper = 1) { - return Math.min(Math.max(value, lower), upper) -} - module.exports = { Slider } diff --git a/src/components/util.js b/src/components/util.js index 378344a3f..3c4c9af6f 100644 --- a/src/components/util.js +++ b/src/components/util.js @@ -1,7 +1,7 @@ 'use strict' const { - Children, PropTypes, PureComponent, createElement: create + Children, PureComponent, createElement: create } = require('react') const { diff } = require('../common/util') @@ -86,8 +86,6 @@ module.exports = { }, Shapes: { - edge: PropTypes.oneOf(['top', 'right', 'bottom', 'left']), - number(min, max, required = true) { return (props, name, component) => { const value = props[name]