Files
goTorrent/goTorrentWebUI/node_modules/react-resizable/build/Resizable.js.flow

233 lines
7.1 KiB
Plaintext

// @flow
import React from 'react';
import PropTypes from 'prop-types';
import {DraggableCore} from 'react-draggable';
import cloneElement from './cloneElement';
import type {Element as ReactElement, Node as ReactNode} from 'react';
type Axis = 'both' | 'x' | 'y' | 'none';
type State = {
resizing: boolean,
width: number, height: number,
slackW: number, slackH: number
};
type DragCallbackData = {
node: HTMLElement,
x: number, y: number,
deltaX: number, deltaY: number,
lastX: number, lastY: number
};
export type ResizeCallbackData = {
node: HTMLElement,
size: {width: number, height: number}
};
export type Props = {
children: ReactElement<any>,
className?: ?string,
width: number,
height: number,
handleSize: [number, number],
lockAspectRatio: boolean,
axis: Axis,
minConstraints: [number, number],
maxConstraints: [number, number],
onResizeStop?: ?(e: SyntheticEvent<>, data: ResizeCallbackData) => any,
onResizeStart?: ?(e: SyntheticEvent<>, data: ResizeCallbackData) => any,
onResize?: ?(e: SyntheticEvent<>, data: ResizeCallbackData) => any,
draggableOpts?: ?Object
};
export default class Resizable extends React.Component<Props, State> {
static propTypes = {
//
// Required Props
//
// Require that one and only one child be present.
children: PropTypes.element.isRequired,
// Initial w/h
width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
//
// Optional props
//
// If you change this, be sure to update your css
handleSize: PropTypes.array,
// If true, will only allow width/height to move in lockstep
lockAspectRatio: PropTypes.bool,
// Restricts resizing to a particular axis (default: 'both')
// 'both' - allows resizing by width or height
// 'x' - only allows the width to be changed
// 'y' - only allows the height to be changed
// 'none' - disables resizing altogether
axis: PropTypes.oneOf(['both', 'x', 'y', 'none']),
// Min/max size
minConstraints: PropTypes.arrayOf(PropTypes.number),
maxConstraints: PropTypes.arrayOf(PropTypes.number),
// Callbacks
onResizeStop: PropTypes.func,
onResizeStart: PropTypes.func,
onResize: PropTypes.func,
// These will be passed wholesale to react-draggable's DraggableCore
draggableOpts: PropTypes.object
};
static defaultProps = {
handleSize: [20, 20],
lockAspectRatio: false,
axis: 'both',
minConstraints: [20, 20],
maxConstraints: [Infinity, Infinity]
};
state: State = {
resizing: false,
width: this.props.width, height: this.props.height,
slackW: 0, slackH: 0
};
componentWillReceiveProps(nextProps: Object) {
// If parent changes height/width, set that in our state.
if (!this.state.resizing &&
(nextProps.width !== this.props.width || nextProps.height !== this.props.height)) {
this.setState({
width: nextProps.width,
height: nextProps.height
});
}
}
lockAspectRatio(width: number, height: number, aspectRatio: number): [number, number] {
height = width / aspectRatio;
width = height * aspectRatio;
return [width, height];
}
// If you do this, be careful of constraints
runConstraints(width: number, height: number): [number, number] {
const [min, max] = [this.props.minConstraints, this.props.maxConstraints];
if (this.props.lockAspectRatio) {
const ratio = this.state.width / this.state.height;
height = width / ratio;
width = height * ratio;
}
if (!min && !max) return [width, height];
const [oldW, oldH] = [width, height];
// Add slack to the values used to calculate bound position. This will ensure that if
// we start removing slack, the element won't react to it right away until it's been
// completely removed.
let {slackW, slackH} = this.state;
width += slackW;
height += slackH;
if (min) {
width = Math.max(min[0], width);
height = Math.max(min[1], height);
}
if (max) {
width = Math.min(max[0], width);
height = Math.min(max[1], height);
}
// If the numbers changed, we must have introduced some slack. Record it for the next iteration.
slackW += (oldW - width);
slackH += (oldH - height);
if (slackW !== this.state.slackW || slackH !== this.state.slackH) {
this.setState({slackW, slackH});
}
return [width, height];
}
/**
* Wrapper around drag events to provide more useful data.
*
* @param {String} handlerName Handler name to wrap.
* @return {Function} Handler function.
*/
resizeHandler(handlerName: string): Function {
return (e: SyntheticEvent<> | MouseEvent, {node, deltaX, deltaY}: DragCallbackData) => {
// Axis restrictions
const canDragX = this.props.axis === 'both' || this.props.axis === 'x';
const canDragY = this.props.axis === 'both' || this.props.axis === 'y';
// Update w/h
let width = this.state.width + (canDragX ? deltaX : 0);
let height = this.state.height + (canDragY ? deltaY : 0);
// Early return if no change
const widthChanged = width !== this.state.width, heightChanged = height !== this.state.height;
if (handlerName === 'onResize' && !widthChanged && !heightChanged) return;
[width, height] = this.runConstraints(width, height);
// Set the appropriate state for this handler.
const newState = {};
if (handlerName === 'onResizeStart') {
newState.resizing = true;
} else if (handlerName === 'onResizeStop') {
newState.resizing = false;
newState.slackW = newState.slackH = 0;
} else {
// Early return if no change after constraints
if (width === this.state.width && height === this.state.height) return;
newState.width = width;
newState.height = height;
}
const hasCb = typeof this.props[handlerName] === 'function';
if (hasCb) {
if (typeof e.persist === 'function') e.persist();
this.setState(newState, () => this.props[handlerName](e, {node, size: {width, height}}));
} else {
this.setState(newState);
}
};
}
render(): ReactNode {
// eslint-disable-next-line no-unused-vars
const {children, draggableOpts, width, height, handleSize,
lockAspectRatio, axis, minConstraints, maxConstraints, onResize,
onResizeStop, onResizeStart, ...p} = this.props;
const className = p.className ?
`${p.className} react-resizable`:
'react-resizable';
// What we're doing here is getting the child of this element, and cloning it with this element's props.
// We are then defining its children as:
// Its original children (resizable's child's children), and
// A draggable handle.
return cloneElement(children, {
...p,
className,
children: [
children.props.children,
<DraggableCore
{...draggableOpts}
key="resizableHandle"
onStop={this.resizeHandler('onResizeStop')}
onStart={this.resizeHandler('onResizeStart')}
onDrag={this.resizeHandler('onResize')}
>
<span className="react-resizable-handle" />
</DraggableCore>
]
});
}
}