diff --git a/index.js b/index.js index 4b91802..2e6b934 100644 --- a/index.js +++ b/index.js @@ -11,33 +11,33 @@ import { import _ from 'lodash' // Default values -const ITEMS_PER_ROW = 4 -const DRAG_ACTIVATION_TRESHOLD = 200 // Milliseconds -const BLOCK_TRANSITION_DURATION = 300 // Milliseconds +const ITEMS_PER_ROW = 4 +const DRAG_ACTIVATION_TRESHOLD = 200 // Milliseconds +const BLOCK_TRANSITION_DURATION = 300 // Milliseconds const ACTIVE_BLOCK_CENTERING_DURATION = 200 // Milliseconds -const DOUBLETAP_TRESHOLD = 150 // Milliseconds -const NULL_FN = () => {} +const DOUBLETAP_TRESHOLD = 150 // Milliseconds +const NULL_FN = () => { } class Block extends Component { render = () => this.props.inactive || this.props.onLongPress() } - onPress = { () => this.props.inactive || this.props.onPress() }> - - - - {this.props.children} - - { this.props.deletionView } + style={{ flex: 1 }} + delayLongPress={this.props.delayLongPress} + onLongPress={() => this.props.inactive || this.props.onLongPress()} + onPress={() => this.props.inactive || this.props.onPress()}> + + + + {this.props.children} + {this.props.deletionView} + @@ -46,61 +46,61 @@ class Block extends Component { class SortableGrid extends Component { - render = () => - - { this.state.gridLayout && - this.items.map( (item, key) => - - {item} - + render = () => + + {this.state.gridLayout && + this.items.map((item, key) => + + {item} + )} - + constructor() { super() - this.blockTransitionDuration = BLOCK_TRANSITION_DURATION + this.blockTransitionDuration = BLOCK_TRANSITION_DURATION this.activeBlockCenteringDuration = ACTIVE_BLOCK_CENTERING_DURATION - this.itemsPerRow = ITEMS_PER_ROW - this.dragActivationTreshold = DRAG_ACTIVATION_TRESHOLD - this.doubleTapTreshold = DOUBLETAP_TRESHOLD - this.onDragRelease = NULL_FN - this.onDragStart = NULL_FN - this.onDeleteItem = NULL_FN - this.dragStartAnimation = null - - this.rows = null - this.dragPosition = null + this.itemsPerRow = ITEMS_PER_ROW + this.dragActivationTreshold = DRAG_ACTIVATION_TRESHOLD + this.doubleTapTreshold = DOUBLETAP_TRESHOLD + this.onDragRelease = NULL_FN + this.onDragStart = NULL_FN + this.onDeleteItem = NULL_FN + this.dragStartAnimation = null + + this.rows = null + this.dragPosition = null this.activeBlockOffset = null - this.blockWidth = null - this.blockHeight = null - this.itemWidth = null - this.itemHeight = null - this.gridHeightTarget = null - this.ghostBlocks = [] - this.itemOrder = [] - this.panCapture = false - this.items = [] + this.blockWidth = null + this.blockHeight = null + this.itemWidth = null + this.itemHeight = null + this.gridHeightTarget = null + this.ghostBlocks = [] + this.itemOrder = [] + this.panCapture = false + this.items = [] this.initialLayoutDone = false - this.initialDragDone = false + this.initialDragDone = false - this.tapTimer = null - this.tapIgnore = false - this.doubleTapWait = false + this.tapTimer = null + this.tapIgnore = false + this.doubleTapWait = false this.state = { gridLayout: null, @@ -125,13 +125,13 @@ class SortableGrid extends Component { return { deleteModeOn } } - componentWillMount = () => this.createTouchHandlers() + UNSAFE_componentWillMount = () => this.createTouchHandlers() componentDidMount = () => this.handleNewProps(this.props) componentWillUnmount = () => { if (this.tapTimer) clearTimeout(this.tapTimer) } - componentWillReceiveProps = (properties) => this.handleNewProps(properties) + UNSAFE_componentWillReceiveProps = (properties) => this.handleNewProps(properties) handleNewProps = (properties) => { this._assignReceivedPropertiesIntoThis(properties) @@ -150,11 +150,11 @@ class SortableGrid extends Component { } } - onMoveBlock = (evt, {moveX, moveY, dx, dy}) => { + onMoveBlock = (evt, { moveX, moveY, dx, dy }) => { if (this.state.activeBlock != null && this._blockPositionsSet()) { if (this.state.deleteModeOn) return this.deleteModeMove({ x: moveX, y: moveY }) - if (dx != 0 || dy != 0) this.initialDragDone = true + if (dx != 0 || dy != 0) this.initialDragDone = true let yChokeAmount = Math.max(0, (this.activeBlockOffset.y + moveY) - (this.state.gridLayout.height - this.blockHeight)) let xChokeAmount = Math.max(0, (this.activeBlockOffset.x + moveX) - (this.state.gridLayout.width - this.blockWidth)) @@ -169,7 +169,7 @@ class SortableGrid extends Component { let closest = this.state.activeBlock let closestDistance = distanceToOrigin - this.state.blockPositions.forEach( (block, index) => { + this.state.blockPositions.forEach((block, index) => { if (index !== this.state.activeBlock && block.origin) { let blockPosition = block.origin let distance = this._getDistanceTo(blockPosition) @@ -181,7 +181,7 @@ class SortableGrid extends Component { } }) - this.ghostBlocks.forEach( ghostBlockPosition => { + this.ghostBlocks.forEach(ghostBlockPosition => { let distance = this._getDistanceTo(ghostBlockPosition) if (distance < closestDistance) { closest = this.state.activeBlock @@ -193,7 +193,8 @@ class SortableGrid extends Component { this._getBlock(closest).currentPosition, { toValue: this._getActiveBlock().origin, - duration: this.blockTransitionDuration + duration: this.blockTransitionDuration, + useNativeDriver: false } ).start() let blockPositions = this.state.blockPositions @@ -219,22 +220,25 @@ class SortableGrid extends Component { deleteBlock = () => { this.setState({ deleteBlock: this.state.activeBlock }) this.blockAnimateFadeOut() - .then( () => { - let activeBlock = this.state.activeBlock - this.setState({ activeBlock: null, deleteBlock: null }, () => { - this.onDeleteItem({ item: this.itemOrder[ activeBlock ] }) - this.deleteBlocks([ activeBlock ]) - this.afterDragRelease() + .then(() => { + let activeBlock = this.state.activeBlock + this.setState({ activeBlock: null, deleteBlock: null }, () => { + this.onDeleteItem({ item: this.itemOrder[activeBlock] }) + this.deleteBlocks([activeBlock]) + this.afterDragRelease() + }) }) - }) } blockAnimateFadeOut = () => { this.state.deleteBlockOpacity.setValue(1) - return new Promise( (resolve, reject) => { + return new Promise((resolve, reject) => { Animated.timing( this.state.deleteBlockOpacity, - { toValue: 0, duration: 2 * this.activeBlockCenteringDuration } + { toValue: 0, + duration: 2 * this.activeBlockCenteringDuration, + useNativeDriver: false + } ).start(resolve) }) } @@ -244,7 +248,8 @@ class SortableGrid extends Component { this._getBlock(blockIndex).currentPosition, { toValue: position, - duration: this.blockTransitionDuration + duration: this.blockTransitionDuration, + useNativeDriver: false } ).start() } @@ -256,19 +261,20 @@ class SortableGrid extends Component { activeBlockCurrentPosition, { toValue: this._getActiveBlock().origin, - duration: this.activeBlockCenteringDuration + duration: this.activeBlockCenteringDuration, + useNativeDriver: false } ).start() } afterDragRelease = () => { - let itemOrder = _.sortBy( this.itemOrder, item => item.order ) + let itemOrder = _.sortBy(this.itemOrder, item => item.order) this.onDragRelease({ itemOrder }) this.setState({ activeBlock: null }) this.panCapture = false } - deleteModeMove = ({x, y}) => { + deleteModeMove = ({ x, y }) => { let slideDistance = 50 let moveY = y + this.activeBlockOffset.y - this._getActiveBlock().origin.y let adjustY = 0 @@ -276,11 +282,10 @@ class SortableGrid extends Component { else if (moveY > slideDistance) adjustY = moveY - slideDistance let deletionSwipePercent = (moveY - adjustY) / slideDistance * 100 this._getActiveBlock().currentPosition.y.setValue(y - adjustY) - this.setState({deletionSwipePercent}) + this.setState({ deletionSwipePercent }) } - assessGridSize = ({nativeEvent}) => { - console.log("Calculating grid size"); + assessGridSize = ({ nativeEvent }) => { if (this.props.itemWidth && this.props.itemWidth < nativeEvent.layout.width) { this.itemsPerRow = Math.floor(nativeEvent.layout.width / this.props.itemWidth) this.blockWidth = nativeEvent.layout.width / this.itemsPerRow @@ -305,7 +310,7 @@ class SortableGrid extends Component { if (this.state.blockWidth && oldRows != this.rows) this._animateGridHeight() } - saveBlockPositions = (key) => ({nativeEvent}) => { + saveBlockPositions = (key) => ({ nativeEvent }) => { let blockPositions = this.state.blockPositions if (!blockPositions[key]) { let blockPositionsSetCount = blockPositions[key] ? this.state.blockPositionsSetCount : ++this.state.blockPositionsSetCount @@ -315,10 +320,10 @@ class SortableGrid extends Component { } blockPositions[key] = { - currentPosition : new Animated.ValueXY( thisPosition ), - origin : thisPosition + currentPosition: new Animated.ValueXY(thisPosition), + origin: thisPosition } - this.setState({ blockPositions, blockPositionsSetCount }) + this.setState({ blockPositions, blockPositionsSetCount }) if (this._blockPositionsSet()) { this.setGhostPositions() @@ -344,17 +349,17 @@ class SortableGrid extends Component { let fullGridItemCount = this.rows * this.itemsPerRow let ghostBlockCount = fullGridItemCount - this.items.length let y = blockHeight * (this.rows - 1) - let initialX = blockWidth * (this.itemsPerRow - ghostBlockCount) + let initialX = blockWidth * (this.itemsPerRow - ghostBlockCount) for (let i = 0; i < ghostBlockCount; ++i) { let x = initialX + blockWidth * i - this.ghostBlocks.push({x, y}) + this.ghostBlocks.push({ x, y }) } } activateDrag = (key) => () => { this.panCapture = true - this.onDragStart( this.itemOrder[key] ) + this.onDragStart(this.itemOrder[key]) this.setState({ activeBlock: key }) this._defaultDragActivationWiggle() } @@ -368,14 +373,14 @@ class SortableGrid extends Component { // Helpers & other boring stuff - _getActiveBlock = () => this.state.blockPositions[ this.state.activeBlock ] + _getActiveBlock = () => this.state.blockPositions[this.state.activeBlock] - _getBlock = (blockIndex) => this.state.blockPositions[ blockIndex ] + _getBlock = (blockIndex) => this.state.blockPositions[blockIndex] _blockPositionsSet = () => this.state.blockPositionsSetCount === this.items.length _saveItemOrder = (items) => { - items.forEach( (item, index) => { + items.forEach((item, index) => { const foundKey = _.findKey(this.itemOrder, oldItem => oldItem.key === item.key); if (foundKey) { @@ -391,8 +396,8 @@ class SortableGrid extends Component { let blockPositionsSetCount = ++this.state.blockPositionsSetCount let thisPosition = this.getNextBlockCoordinates() blockPositions.push({ - currentPosition : new Animated.ValueXY( thisPosition ), - origin : thisPosition + currentPosition: new Animated.ValueXY(thisPosition), + origin: thisPosition }) this.items.push(item) this.setState({ blockPositions, blockPositionsSetCount }) @@ -404,7 +409,7 @@ class SortableGrid extends Component { _removeDisappearedChildren = (items) => { let deleteBlockIndices = [] - _.cloneDeep(this.itemOrder).forEach( (item, index) => { + _.cloneDeep(this.itemOrder).forEach((item, index) => { if (!_.findKey(items, (oldItem) => oldItem.key === item.key)) { deleteBlockIndices.push(index) } @@ -415,26 +420,26 @@ class SortableGrid extends Component { } deleteBlocks = (deleteBlockIndices) => { - let blockPositions = this.state.blockPositions - let blockPositionsSetCount = this.state.blockPositionsSetCount - _.sortBy(deleteBlockIndices, index => -index).forEach(index => { - --blockPositionsSetCount - let order = this.itemOrder[index].order - blockPositions.splice(index, 1) - this._fixItemOrderOnDeletion(this.itemOrder[index]) - this.itemOrder.splice(index, 1) - this.items.splice(index, 1) - }) - this.setState({ blockPositions, blockPositionsSetCount }, () => { - this.items.forEach( (item, order) => { - let blockIndex = _.findIndex(this.itemOrder, item => item.order === order) - let x = (order * this.state.blockWidth) % (this.itemsPerRow * this.state.blockWidth) - let y = Math.floor(order / this.itemsPerRow) * this.state.blockHeight - this.state.blockPositions[blockIndex].origin = {x, y} - this.animateBlockMove(blockIndex, {x, y}) - }) - this.setGhostPositions() + let blockPositions = this.state.blockPositions + let blockPositionsSetCount = this.state.blockPositionsSetCount + _.sortBy(deleteBlockIndices, index => -index).forEach(index => { + --blockPositionsSetCount + let order = this.itemOrder[index].order + blockPositions.splice(index, 1) + this._fixItemOrderOnDeletion(this.itemOrder[index]) + this.itemOrder.splice(index, 1) + this.items.splice(index, 1) + }) + this.setState({ blockPositions, blockPositionsSetCount }, () => { + this.items.forEach((item, order) => { + let blockIndex = _.findIndex(this.itemOrder, item => item.order === order) + let x = (order * this.state.blockWidth) % (this.itemsPerRow * this.state.blockWidth) + let y = Math.floor(order / this.itemsPerRow) * this.state.blockHeight + this.state.blockPositions[blockIndex].origin = { x, y } + this.animateBlockMove(blockIndex, { x, y }) }) + this.setGhostPositions() + }) } _fixItemOrderOnDeletion = (orderItem) => { @@ -452,7 +457,8 @@ class SortableGrid extends Component { this.state.gridHeight, { toValue: this.gridHeightTarget, - duration: this.blockTransitionDuration + duration: this.blockTransitionDuration, + useNativeDriver: false } ).start() } @@ -461,7 +467,7 @@ class SortableGrid extends Component { _getDistanceTo = (point) => { let xDistance = this.dragPosition.x + this.activeBlockOffset.x - point.x let yDistance = this.dragPosition.y + this.activeBlockOffset.y - point.y - return Math.sqrt( Math.pow(xDistance, 2) + Math.pow(yDistance, 2) ) + return Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2)) } _defaultDragActivationWiggle = () => { @@ -471,16 +477,22 @@ class SortableGrid extends Component { toValue: 0, velocity: 2000, tension: 2000, - friction: 5 + friction: 5, + useNativeDriver: false }).start() } } _blockActivationWiggle = () => { return this.dragStartAnimation || - { transform: [{ rotate: this.state.startDragWiggle.interpolate({ - inputRange: [0, 360], - outputRange: ['0 deg', '360 deg']})}]} + { + transform: [{ + rotate: this.state.startDragWiggle.interpolate({ + inputRange: [0, 360], + outputRange: ['0 deg', '360 deg'] + }) + }] + } } _assignReceivedPropertiesIntoThis(properties) { @@ -493,7 +505,7 @@ class SortableGrid extends Component { _onSingleTap = (onTap) => { this.doubleTapWait = true - this.tapTimer = setTimeout( () => { + this.tapTimer = setTimeout(() => { this.doubleTapWait = false onTap() }, this.doubleTapTreshold) @@ -513,15 +525,15 @@ class SortableGrid extends Component { createTouchHandlers = () => this._panResponder = PanResponder.create({ - onPanResponderTerminate: (evt, gestureState) => {}, - onStartShouldSetPanResponder: (evt, gestureState) => true, + onPanResponderTerminate: (evt, gestureState) => { }, + onStartShouldSetPanResponder: (evt, gestureState) => true, onStartShouldSetPanResponderCapture: (evt, gestureState) => false, - onMoveShouldSetPanResponder: (evt, gestureState) => this.panCapture, - onMoveShouldSetPanResponderCapture: (evt, gestureState) => this.panCapture, - onShouldBlockNativeResponder: (evt, gestureState) => false, - onPanResponderTerminationRequest: (evt, gestureState) => false, - onPanResponderGrant: this.onActiveBlockIsSet(this.onStartDrag), - onPanResponderMove: this.onActiveBlockIsSet(this.onMoveBlock), + onMoveShouldSetPanResponder: (evt, gestureState) => this.panCapture, + onMoveShouldSetPanResponderCapture: (evt, gestureState) => this.panCapture, + onShouldBlockNativeResponder: (evt, gestureState) => false, + onPanResponderTerminationRequest: (evt, gestureState) => false, + onPanResponderGrant: this.onActiveBlockIsSet(this.onStartDrag), + onPanResponderMove: this.onActiveBlockIsSet(this.onMoveBlock), onPanResponderRelease: this.onActiveBlockIsSet(this.onReleaseBlock) }) @@ -539,44 +551,48 @@ class SortableGrid extends Component { _getDeletionView = (key) => { if (this.state.deleteModeOn) - return + return } _getItemWrapperStyle = (key) => [ { flex: 1 }, - this.state.activeBlock == key + this.state.activeBlock == key && this.state.deleteModeOn - && this._getBlock( key ).origin + && this._getBlock(key).origin && { opacity: 1.5 - this._getDynamicOpacity(key) } ] _getImageDeleteIconStyle = (key) => [ - { position: 'absolute', - top: this.state.blockHeight/2 - 15, - left: this.state.blockWidth/2 - 15, + { + position: 'absolute', + top: this.state.blockHeight / 2 - 15, + left: this.state.blockWidth / 2 - 15, width: 30, height: 30, opacity: .5 }, this.state.activeBlock == key - && this._getBlock( key ).origin + && this._getBlock(key).origin && { opacity: .5 + this._getDynamicOpacity(key) } ] _getDynamicOpacity = (key) => - ( this._getBlock( key ).currentPosition.y._value - + this._getBlock( key ).currentPosition.y._offset - - this._getBlock( key ).origin.y + (this._getBlock(key).currentPosition.y._value + + this._getBlock(key).currentPosition.y._offset + - this._getBlock(key).origin.y ) / 50 _getBlockStyle = (key) => [ - { width: this.state.blockWidth, + { + width: this.state.blockWidth, height: this.state.blockHeight, - justifyContent: 'center' }, - this._blockPositionsSet() && (this.initialDragDone || this.state.deleteModeOn) && - { position: 'absolute', + justifyContent: 'center' + }, + this._blockPositionsSet() && (this.initialDragDone || this.state.deleteModeOn) && + { + position: 'absolute', top: this._getBlock(key).currentPosition.getLayout().top, left: this._getBlock(key).currentPosition.getLayout().left }, @@ -589,23 +605,23 @@ class SortableGrid extends Component { } const styles = StyleSheet.create( -{ - sortableGrid: { - flexDirection: 'row', - flexWrap: 'wrap' - }, - deletedBlock: { - opacity: 0, - position: 'absolute', - left: 0, - top: 0, - height: 0, - width: 0 - }, - itemImageContainer: { - flex: 1, - justifyContent: 'center' - } -}) + { + sortableGrid: { + flexDirection: 'row', + flexWrap: 'wrap' + }, + deletedBlock: { + opacity: 0, + position: 'absolute', + left: 0, + top: 0, + height: 0, + width: 0 + }, + itemImageContainer: { + flex: 1, + justifyContent: 'center' + } + }) module.exports = SortableGrid