react-beautiful-dnd
Version:
Beautiful, accessible drag and drop for lists with React.js
89 lines (73 loc) • 2.45 kB
JSX
// @flow
import React, { Component, type Element } from 'react';
import type { SpringHelperConfig } from 'react-motion/lib/Types';
import { type Position } from 'css-box-model';
import { Motion, spring } from 'react-motion';
import { isEqual, origin } from '../../state/position';
import { physics } from '../animation';
import type { Props, Speed, DefaultProps } from './moveable-types';
type PositionLike = {|
x: any,
y: any,
|};
type BlockerProps = {|
change: Position,
children: Position => Element<*>,
|};
// Working around react-motion double render issue
class DoubleRenderBlocker extends React.Component<BlockerProps> {
shouldComponentUpdate(nextProps: BlockerProps): boolean {
// let a render go through if not moving anywhere
if (isEqual(origin, nextProps.change)) {
return true;
}
// blocking a duplicate change (workaround for react-motion)
if (isEqual(this.props.change, nextProps.change)) {
return false;
}
// let everything else through
return true;
}
render() {
return this.props.children(this.props.change);
}
}
export default class Moveable extends Component<Props> {
/* eslint-disable react/sort-comp */
static defaultProps: DefaultProps = {
destination: origin,
};
getFinal(): PositionLike {
const destination: Position = this.props.destination;
const speed: Speed = this.props.speed;
if (speed === 'INSTANT') {
return destination;
}
const config: SpringHelperConfig =
speed === 'FAST' ? physics.fast : physics.standard;
return {
x: spring(destination.x, config),
y: spring(destination.y, config),
};
}
render() {
const final = this.getFinal();
// bug with react-motion: https://github.com/chenglou/react-motion/issues/437
// even if both defaultStyle and style are {x: 0, y: 0 } if there was
// a previous animation it uses the last value rather than the final value
return (
<Motion defaultStyle={origin} style={final} onRest={this.props.onMoveEnd}>
{(current: { [string]: number }): Element<*> => {
const { speed, destination, children } = this.props;
const target: Position =
speed === 'INSTANT' ? destination : (current: any);
return (
<DoubleRenderBlocker change={target}>
{children}
</DoubleRenderBlocker>
);
}}
</Motion>
);
}
}