From 4a4180a01aab5946225222bc5419a3f95449a2ac Mon Sep 17 00:00:00 2001 From: Marek Rozmus Date: Thu, 16 Jan 2020 10:29:47 +0100 Subject: [PATCH 1/8] Closes issue 77 --- examples/src/App.js | 3 +++ src/SwipeableListItem.js | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/examples/src/App.js b/examples/src/App.js index baddf30..86d3288 100644 --- a/examples/src/App.js +++ b/examples/src/App.js @@ -85,6 +85,9 @@ function App() { console.log('Swipe started')} + onSwipeEnd={() => console.log('Swipe ended')} + onSwipeStateChange={state => console.log(state)} > {itemContentSimple('Item with both swipes')} diff --git a/src/SwipeableListItem.js b/src/SwipeableListItem.js index 67c1a26..8185553 100644 --- a/src/SwipeableListItem.js +++ b/src/SwipeableListItem.js @@ -29,6 +29,8 @@ class SwipeableListItem extends PureComponent { this.startTime = null; + this.prevSwipeDistancePercent = 0; + this.resetState(); } @@ -36,6 +38,7 @@ class SwipeableListItem extends PureComponent { this.dragStartPoint = { x: -1, y: -1 }; this.dragDirection = DragDirection.UNKNOWN; this.left = 0; + this.prevSwipeDistancePercent = 0; }; get dragHorizontalDirectionThreshold() { @@ -169,6 +172,10 @@ class SwipeableListItem extends PureComponent { } else if (this.left > this.listElement.offsetWidth * threshold) { this.handleSwipedRight(); } + + if (this.props.onSwipeEnd) { + this.props.onSwipeEnd(); + } } this.resetState(); @@ -250,6 +257,14 @@ class SwipeableListItem extends PureComponent { } break; } + + if ( + this.props.onSwipeStart && + (this.dragDirection === DragDirection.LEFT || + this.dragDirection === DragDirection.RIGHT) + ) { + this.props.onSwipeStart(); + } } }; @@ -279,6 +294,24 @@ class SwipeableListItem extends PureComponent { const opacity = (Math.abs(this.left) / 100).toFixed(2); + if (this.props.onSwipeStateChange) { + const swipeDistance = Math.max( + 0, + this.listElement.offsetWidth - Math.abs(this.left) + ); + + const swipeDistancePercent = + 100 - + Math.round((100 * swipeDistance) / this.listElement.offsetWidth); + + if (this.prevSwipeDistancePercent !== swipeDistancePercent) { + this.props.onSwipeStateChange({ + swipeDistancePercent + }); + this.prevSwipeDistancePercent = swipeDistancePercent; + } + } + if (opacity < 1 && opacity.toString() !== contentToShow.style.opacity) { contentToShow.style.opacity = opacity.toString(); @@ -361,7 +394,11 @@ SwipeableListItem.propTypes = { swipeRight: SwipeActionPropType, scrollStartThreshold: PropTypes.number, swipeStartThreshold: PropTypes.number, - threshold: PropTypes.number + threshold: PropTypes.number, + + onSwipeStart: PropTypes.func, + onSwipeEnd: PropTypes.func, + onSwipeStateChange: PropTypes.func }; export default SwipeableListItem; From d8fd22dc5c59a3d5c4c7a2368533ba8133eba206 Mon Sep 17 00:00:00 2001 From: Marek Rozmus Date: Mon, 27 Jan 2020 08:59:20 +0100 Subject: [PATCH 2/8] Add typescript typings and change progress callback name --- examples/src/App.js | 2 +- src/SwipeableListItem.js | 10 ++++------ src/module.d.ts | 3 +++ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/examples/src/App.js b/examples/src/App.js index 86d3288..1d966ec 100644 --- a/examples/src/App.js +++ b/examples/src/App.js @@ -87,7 +87,7 @@ function App() { swipeLeft={swipeLeftDataSimple('Item with both swipes')} onSwipeStart={() => console.log('Swipe started')} onSwipeEnd={() => console.log('Swipe ended')} - onSwipeStateChange={state => console.log(state)} + onSwipeProgress={progress => console.log(progress)} > {itemContentSimple('Item with both swipes')} diff --git a/src/SwipeableListItem.js b/src/SwipeableListItem.js index 8185553..6b05023 100644 --- a/src/SwipeableListItem.js +++ b/src/SwipeableListItem.js @@ -294,7 +294,7 @@ class SwipeableListItem extends PureComponent { const opacity = (Math.abs(this.left) / 100).toFixed(2); - if (this.props.onSwipeStateChange) { + if (this.props.onSwipeProgress) { const swipeDistance = Math.max( 0, this.listElement.offsetWidth - Math.abs(this.left) @@ -305,9 +305,7 @@ class SwipeableListItem extends PureComponent { Math.round((100 * swipeDistance) / this.listElement.offsetWidth); if (this.prevSwipeDistancePercent !== swipeDistancePercent) { - this.props.onSwipeStateChange({ - swipeDistancePercent - }); + this.props.onSwipeProgress(swipeDistancePercent); this.prevSwipeDistancePercent = swipeDistancePercent; } } @@ -396,9 +394,9 @@ SwipeableListItem.propTypes = { swipeStartThreshold: PropTypes.number, threshold: PropTypes.number, - onSwipeStart: PropTypes.func, onSwipeEnd: PropTypes.func, - onSwipeStateChange: PropTypes.func + onSwipeProgress: PropTypes.func, + onSwipeStart: PropTypes.func }; export default SwipeableListItem; diff --git a/src/module.d.ts b/src/module.d.ts index e0284c5..7ac7c79 100644 --- a/src/module.d.ts +++ b/src/module.d.ts @@ -20,6 +20,9 @@ interface ISwipeableListItemProps { scrollStartThreshold?: number; swipeStartThreshold?: number; threshold?: number; + onSwipeStart: () => void; + onSwipeEnd: () => void; + onSwipeProgress: (progress: number) => void; } export class SwipeableListItem extends React.Component< From 3ddeb666892d2b32833f7d3f1423184c62857670 Mon Sep 17 00:00:00 2001 From: Marek Rozmus Date: Mon, 27 Jan 2020 13:26:24 +0100 Subject: [PATCH 3/8] Add optional flags for callbacks --- src/module.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/module.d.ts b/src/module.d.ts index 7ac7c79..18b57c9 100644 --- a/src/module.d.ts +++ b/src/module.d.ts @@ -20,9 +20,9 @@ interface ISwipeableListItemProps { scrollStartThreshold?: number; swipeStartThreshold?: number; threshold?: number; - onSwipeStart: () => void; - onSwipeEnd: () => void; - onSwipeProgress: (progress: number) => void; + onSwipeStart?: () => void; + onSwipeEnd?: () => void; + onSwipeProgress?: (progress: number) => void; } export class SwipeableListItem extends React.Component< From 5496b2ffa0de39e921fdcc8ec5355e0916f112d8 Mon Sep 17 00:00:00 2001 From: Marek Rozmus Date: Thu, 30 Jan 2020 13:42:07 +0100 Subject: [PATCH 4/8] Add callback tests and guess fixes for null pointers --- examples/src/App.js | 35 ++++++++-- examples/src/app.module.css | 6 +- src/SwipeableListItem.js | 84 +++++++++++------------ src/__tests__/SwipeableListItem.test.js | 89 ++++++++++++++++++++++++- 4 files changed, 159 insertions(+), 55 deletions(-) diff --git a/examples/src/App.js b/examples/src/App.js index 1d966ec..eb04dcd 100644 --- a/examples/src/App.js +++ b/examples/src/App.js @@ -11,8 +11,10 @@ import { MailIcon, ReplyIcon, DeleteIcon } from '../images/icons'; import styles from './app.module.css'; function App() { - const [triggeredSimpleItemAction, triggerSimpleItemAction] = useState(''); + const [triggeredSimpleItemAction, triggerSimpleItemAction] = useState('None'); const [triggeredComplexItemAction, triggerComplexItemAction] = useState(''); + const [swipeProgress, handleSwipeProgress] = useState('0'); + const [swipeAction, handleSwipeAction] = useState('None'); const swipeRightDataSimple = name => ({ content: ( @@ -64,30 +66,53 @@ function App() { triggerComplexItemAction(`Reply action triggered on "${name}" item`) }); + const handleSwipeStart = () => { + triggerSimpleItemAction('None'); + handleSwipeAction('Swipe started'); + }; + + const handleSwipeEnd = () => { + handleSwipeAction('Swipe ended'); + }; + return (

react-swipeable-list example

(try also mobile view in dev tools for touch events)

Simple example (with default 0.5 action trigger threshold)

- {triggeredSimpleItemAction} + + Triggered action: {triggeredSimpleItemAction} + + + Callback swipe action: {swipeAction} + + + Callback swipe progress: {swipeProgress} +
{itemContentSimple('Item with swipe right')} {itemContentSimple('Item with swipe left')} console.log('Swipe started')} - onSwipeEnd={() => console.log('Swipe ended')} - onSwipeProgress={progress => console.log(progress)} + onSwipeStart={handleSwipeStart} + onSwipeEnd={handleSwipeEnd} + onSwipeProgress={handleSwipeProgress} > {itemContentSimple('Item with both swipes')} diff --git a/examples/src/app.module.css b/examples/src/app.module.css index 4d5a5cf..17bdcbc 100644 --- a/examples/src/app.module.css +++ b/examples/src/app.module.css @@ -3,7 +3,7 @@ html { } h3 { - margin-bottom: 0; + margin-bottom: 8px; text-align: center; } @@ -95,8 +95,8 @@ footer a:hover { } .actionInfo { - padding: 16px; - font-weight: 600; + padding: 0 8px 4px 8px; + font-weight: 400; font-size: 14px; } diff --git a/src/SwipeableListItem.js b/src/SwipeableListItem.js index 6b05023..1b29af5 100644 --- a/src/SwipeableListItem.js +++ b/src/SwipeableListItem.js @@ -25,6 +25,7 @@ class SwipeableListItem extends PureComponent { this.contentLeft = null; this.contentRight = null; this.listElement = null; + this.requestedAnimationFrame = null; this.wrapper = null; this.startTime = null; @@ -61,6 +62,8 @@ class SwipeableListItem extends PureComponent { } componentWillUnmount() { + cancelAnimationFrame(this.requestedAnimationFrame); + this.wrapper.removeEventListener('mousedown', this.handleDragStartMouse); this.wrapper.removeEventListener('touchstart', this.handleDragStartTouch); @@ -102,7 +105,7 @@ class SwipeableListItem extends PureComponent { } this.startTime = Date.now(); - requestAnimationFrame(this.updatePosition); + this.requestedAnimationFrame = requestAnimationFrame(this.updatePosition); }; handleMouseMove = event => { @@ -115,11 +118,11 @@ class SwipeableListItem extends PureComponent { event.stopPropagation(); event.preventDefault(); - const delta = clientX - this.dragStartPoint.x; + this.left = clientX - this.dragStartPoint.x; - if (this.shouldMoveItem(delta)) { - this.left = delta; - } + this.requestedAnimationFrame = requestAnimationFrame( + this.updatePosition + ); } } }; @@ -138,11 +141,11 @@ class SwipeableListItem extends PureComponent { event.stopPropagation(); event.preventDefault(); - const delta = clientX - this.dragStartPoint.x; + this.left = clientX - this.dragStartPoint.x; - if (this.shouldMoveItem(delta)) { - this.left = delta; - } + this.requestedAnimationFrame = requestAnimationFrame( + this.updatePosition + ); } } }; @@ -151,8 +154,10 @@ class SwipeableListItem extends PureComponent { window.removeEventListener('mouseup', this.handleDragEndMouse); window.removeEventListener('mousemove', this.handleMouseMove); - this.wrapper.removeEventListener('mouseup', this.handleDragEndMouse); - this.wrapper.removeEventListener('mousemove', this.handleMouseMove); + if (this.wrapper) { + this.wrapper.removeEventListener('mouseup', this.handleDragEndMouse); + this.wrapper.removeEventListener('mousemove', this.handleMouseMove); + } this.handleDragEnd(); }; @@ -179,8 +184,11 @@ class SwipeableListItem extends PureComponent { } this.resetState(); - this.listElement.className = styles.contentReturn; - this.listElement.style.transform = `translateX(${this.left}px)`; + + if (this.listElement) { + this.listElement.className = styles.contentReturn; + this.listElement.style.transform = `translateX(${this.left}px)`; + } // hide backgrounds if (this.contentLeft) { @@ -194,21 +202,6 @@ class SwipeableListItem extends PureComponent { } }; - shouldMoveItem = delta => { - const { - swipeLeft: { content: contentLeft } = {}, - swipeRight: { content: contentRight } = {}, - blockSwipe - } = this.props; - const swipingLeft = delta < 0; - const swipingRight = delta > 0; - - return ( - !blockSwipe && - ((swipingLeft && contentLeft) || (swipingRight && contentRight)) - ); - }; - dragStartedWithinItem = () => { const { x, y } = this.dragStartPoint; @@ -233,7 +226,10 @@ class SwipeableListItem extends PureComponent { switch (octant) { case 0: - if (horizontalDistance > this.dragHorizontalDirectionThreshold) { + if ( + this.contentRight && + horizontalDistance > this.dragHorizontalDirectionThreshold + ) { this.dragDirection = DragDirection.RIGHT; } break; @@ -245,7 +241,10 @@ class SwipeableListItem extends PureComponent { } break; case 4: - if (horizontalDistance > this.dragHorizontalDirectionThreshold) { + if ( + this.contentLeft && + horizontalDistance > this.dragHorizontalDirectionThreshold + ) { this.dragDirection = DragDirection.LEFT; } break; @@ -258,28 +257,23 @@ class SwipeableListItem extends PureComponent { break; } - if ( - this.props.onSwipeStart && - (this.dragDirection === DragDirection.LEFT || - this.dragDirection === DragDirection.RIGHT) - ) { + if (this.props.onSwipeStart && this.isSwiping()) { this.props.onSwipeStart(); } } }; - isSwiping = () => - this.dragStartedWithinItem() && - (this.dragDirection === DragDirection.LEFT || - this.dragDirection === DragDirection.RIGHT); - - updatePosition = () => { + isSwiping = () => { const { blockSwipe } = this.props; + return ( + !blockSwipe && + this.dragStartedWithinItem() && + (this.dragDirection === DragDirection.LEFT || + this.dragDirection === DragDirection.RIGHT) + ); + }; - if (this.dragStartedWithinItem() && !blockSwipe) { - requestAnimationFrame(this.updatePosition); - } - + updatePosition = () => { const now = Date.now(); const elapsed = now - this.startTime; diff --git a/src/__tests__/SwipeableListItem.test.js b/src/__tests__/SwipeableListItem.test.js index 9d4d9d9..0cac81d 100644 --- a/src/__tests__/SwipeableListItem.test.js +++ b/src/__tests__/SwipeableListItem.test.js @@ -204,8 +204,8 @@ test('right swipe action triggering if no left swipe defined', () => { const contentContainer = getByTestId('content'); - swipeLeftMouse(contentContainer); - swipeLeftTouch(contentContainer); + // swipeLeftMouse(contentContainer); + // swipeLeftTouch(contentContainer); swipeRightMouse(contentContainer); swipeRightTouch(contentContainer); @@ -242,3 +242,88 @@ test('swipe actions triggering if block swipe prop is set', () => { expect(callbackLeft).toHaveBeenCalledTimes(0); expect(callbackRight).toHaveBeenCalledTimes(0); }); + +test('start and end callbacks not triggered if swipe content not defined', () => { + const callbackSwipeStart = jest.fn(); + const callbackSwipeEnd = jest.fn(); + + const { getByTestId } = render( + + Item content + + ); + + const contentContainer = getByTestId('content'); + swipeLeftMouse(contentContainer); + swipeLeftTouch(contentContainer); + swipeRightMouse(contentContainer); + swipeRightTouch(contentContainer); + + expect(callbackSwipeStart).toHaveBeenCalledTimes(0); + expect(callbackSwipeEnd).toHaveBeenCalledTimes(0); +}); + +test('start and end callbacks not triggered if blockSwipe is set', () => { + const callbackSwipeStart = jest.fn(); + const callbackSwipeEnd = jest.fn(); + const callbackLeft = jest.fn(); + + const { getByTestId } = render( + Left swipe content, + action: callbackLeft + }} + onSwipeStart={callbackSwipeStart} + onSwipeEnd={callbackSwipeEnd} + > + Item content + + ); + + const contentContainer = getByTestId('content'); + swipeLeftMouse(contentContainer); + swipeLeftTouch(contentContainer); + swipeRightMouse(contentContainer); + swipeRightTouch(contentContainer); + + expect(callbackSwipeStart).toHaveBeenCalledTimes(0); + expect(callbackSwipeEnd).toHaveBeenCalledTimes(0); +}); + +test('start and end callbacks triggered if swipe content is defined', () => { + const callbackSwipeStart = jest.fn(); + const callbackSwipeEnd = jest.fn(); + const callbackLeft = jest.fn(); + const callbackRight = jest.fn(); + + const { getByTestId } = render( + Left swipe content, + action: callbackLeft + }} + swipeRight={{ + content: Right swipe content, + action: callbackRight + }} + onSwipeStart={callbackSwipeStart} + onSwipeEnd={callbackSwipeEnd} + > + Item content + + ); + + const contentContainer = getByTestId('content'); + swipeLeftMouse(contentContainer); + swipeLeftTouch(contentContainer); + swipeRightMouse(contentContainer); + swipeRightTouch(contentContainer); + + expect(callbackSwipeStart).toHaveBeenCalledTimes(4); + expect(callbackSwipeEnd).toHaveBeenCalledTimes(4); +}); From 4569d2f018d21750c5fe09f5ba4627e23b294c48 Mon Sep 17 00:00:00 2001 From: Marek Rozmus Date: Thu, 30 Jan 2020 14:02:19 +0100 Subject: [PATCH 5/8] Add documentation for new callbacks --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 11a7a0d..6231aa8 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ import '@sandstreamdev/react-swipeable-list/dist/styles.css'; content:
Revealed content during swipe
, action: () => console.info('swipe action triggered') }} + onSwipeProgress={progress => console.info(progress)} >
Item name
@@ -132,6 +133,24 @@ Type: `number` (default: `0.5`) It can be set for the whole list or for every item. See `threshold` for `SwipeableList`. Value from the `SwipeableListItem` takes precedence. +### onSwipeStart + +Type: () => void + +Fired after swipe has started. + +### onSwipeEnd + +Type: () => void + +Fired after swipe has ended. + +### onSwipeProgress + +Type: (progress: number) => void + +Fired every time swipe progress changes. + ## Contributors ✨ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): From 029adacc38c02bac153d5a6189bd84867ee18dbb Mon Sep 17 00:00:00 2001 From: Marek Rozmus Date: Thu, 30 Jan 2020 14:10:34 +0100 Subject: [PATCH 6/8] Update docs and uncomment tests --- README.md | 2 +- src/__tests__/SwipeableListItem.test.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6231aa8..59779e1 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ Fired after swipe has ended. Type: (progress: number) => void -Fired every time swipe progress changes. +Fired every time swipe progress changes. Progress value is in range 0..100. ## Contributors ✨ diff --git a/src/__tests__/SwipeableListItem.test.js b/src/__tests__/SwipeableListItem.test.js index 0cac81d..c4b23c0 100644 --- a/src/__tests__/SwipeableListItem.test.js +++ b/src/__tests__/SwipeableListItem.test.js @@ -204,8 +204,8 @@ test('right swipe action triggering if no left swipe defined', () => { const contentContainer = getByTestId('content'); - // swipeLeftMouse(contentContainer); - // swipeLeftTouch(contentContainer); + swipeLeftMouse(contentContainer); + swipeLeftTouch(contentContainer); swipeRightMouse(contentContainer); swipeRightTouch(contentContainer); From 21809ba1e5b49044d5900eb6b1c9ddb28166300b Mon Sep 17 00:00:00 2001 From: Marek Rozmus Date: Thu, 30 Jan 2020 15:01:42 +0100 Subject: [PATCH 7/8] Fix code review issues --- README.md | 12 +++--- examples/src/App.js | 5 ++- src/SwipeableListItem.js | 81 +++++++++++++++++++++++----------------- 3 files changed, 56 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 59779e1..04011d8 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ import '@sandstreamdev/react-swipeable-list/dist/styles.css'; content:
Revealed content during swipe
, action: () => console.info('swipe action triggered') }} - onSwipeProgress={progress => console.info(progress)} + onSwipeProgress={progress => console.info(`Swipe progress: ${progress}%`)} >
Item name
@@ -135,21 +135,21 @@ It can be set for the whole list or for every item. See `threshold` for `Swipeab ### onSwipeStart -Type: () => void +Type: `() => void` -Fired after swipe has started. +Fired after swipe has started (after drag gesture passes the `swipeStartThreshold` distance in pixels). ### onSwipeEnd -Type: () => void +Type: `() => void` Fired after swipe has ended. ### onSwipeProgress -Type: (progress: number) => void +Type: `(progress: number) => void` -Fired every time swipe progress changes. Progress value is in range 0..100. +Fired every time swipe progress changes. The reported `progress` value is always an integer in range 0 to 100 inclusive. ## Contributors ✨ diff --git a/examples/src/App.js b/examples/src/App.js index eb04dcd..4f3b49b 100644 --- a/examples/src/App.js +++ b/examples/src/App.js @@ -13,7 +13,7 @@ import styles from './app.module.css'; function App() { const [triggeredSimpleItemAction, triggerSimpleItemAction] = useState('None'); const [triggeredComplexItemAction, triggerComplexItemAction] = useState(''); - const [swipeProgress, handleSwipeProgress] = useState('0'); + const [swipeProgress, handleSwipeProgress] = useState(); const [swipeAction, handleSwipeAction] = useState('None'); const swipeRightDataSimple = name => ({ @@ -73,6 +73,7 @@ function App() { const handleSwipeEnd = () => { handleSwipeAction('Swipe ended'); + handleSwipeProgress(); }; return ( @@ -87,7 +88,7 @@ function App() { Callback swipe action: {swipeAction} - Callback swipe progress: {swipeProgress} + Callback swipe progress: {swipeProgress || '-'}%
diff --git a/src/SwipeableListItem.js b/src/SwipeableListItem.js index 1b29af5..9efa472 100644 --- a/src/SwipeableListItem.js +++ b/src/SwipeableListItem.js @@ -30,7 +30,7 @@ class SwipeableListItem extends PureComponent { this.startTime = null; - this.prevSwipeDistancePercent = 0; + this.previousSwipeDistancePercent = 0; this.resetState(); } @@ -39,7 +39,7 @@ class SwipeableListItem extends PureComponent { this.dragStartPoint = { x: -1, y: -1 }; this.dragDirection = DragDirection.UNKNOWN; this.left = 0; - this.prevSwipeDistancePercent = 0; + this.previousSwipeDistancePercent = 0; }; get dragHorizontalDirectionThreshold() { @@ -62,7 +62,11 @@ class SwipeableListItem extends PureComponent { } componentWillUnmount() { - cancelAnimationFrame(this.requestedAnimationFrame); + if (this.requestedAnimationFrame) { + cancelAnimationFrame(this.requestedAnimationFrame); + + this.requestedAnimationFrame = null; + } this.wrapper.removeEventListener('mousedown', this.handleDragStartMouse); @@ -96,16 +100,16 @@ class SwipeableListItem extends PureComponent { this.dragStartPoint = { x: clientX, y: clientY }; this.listElement.className = styles.content; - if (this.contentLeft) { + if (this.contentLeft !== null) { this.contentLeft.className = styles.contentLeft; } - if (this.contentRight) { + if (this.contentRight !== null) { this.contentRight.className = styles.contentRight; } this.startTime = Date.now(); - this.requestedAnimationFrame = requestAnimationFrame(this.updatePosition); + this.scheduleUpdatePosition(); }; handleMouseMove = event => { @@ -119,10 +123,7 @@ class SwipeableListItem extends PureComponent { event.preventDefault(); this.left = clientX - this.dragStartPoint.x; - - this.requestedAnimationFrame = requestAnimationFrame( - this.updatePosition - ); + this.scheduleUpdatePosition(); } } }; @@ -142,10 +143,7 @@ class SwipeableListItem extends PureComponent { event.preventDefault(); this.left = clientX - this.dragStartPoint.x; - - this.requestedAnimationFrame = requestAnimationFrame( - this.updatePosition - ); + this.scheduleUpdatePosition(); } } }; @@ -191,12 +189,12 @@ class SwipeableListItem extends PureComponent { } // hide backgrounds - if (this.contentLeft) { + if (this.contentLeft !== null) { this.contentLeft.style.opacity = 0; this.contentLeft.className = styles.contentLeftReturn; } - if (this.contentRight) { + if (this.contentRight !== null) { this.contentRight.style.opacity = 0; this.contentRight.className = styles.contentRightReturn; } @@ -227,7 +225,7 @@ class SwipeableListItem extends PureComponent { switch (octant) { case 0: if ( - this.contentRight && + this.contentRight !== null && horizontalDistance > this.dragHorizontalDirectionThreshold ) { this.dragDirection = DragDirection.RIGHT; @@ -242,7 +240,7 @@ class SwipeableListItem extends PureComponent { break; case 4: if ( - this.contentLeft && + this.contentLeft !== null && horizontalDistance > this.dragHorizontalDirectionThreshold ) { this.dragDirection = DragDirection.LEFT; @@ -265,12 +263,23 @@ class SwipeableListItem extends PureComponent { isSwiping = () => { const { blockSwipe } = this.props; - return ( - !blockSwipe && - this.dragStartedWithinItem() && - (this.dragDirection === DragDirection.LEFT || - this.dragDirection === DragDirection.RIGHT) - ); + const horizontalDrag = + this.dragDirection === DragDirection.LEFT || + this.dragDirection === DragDirection.RIGHT; + + return !blockSwipe && this.dragStartedWithinItem() && horizontalDrag; + }; + + scheduleUpdatePosition = () => { + if (this.requestedAnimationFrame) { + return; + } + + this.requestedAnimationFrame = requestAnimationFrame(() => { + this.requestedAnimationFrame = null; + + this.updatePosition(); + }); }; updatePosition = () => { @@ -288,19 +297,23 @@ class SwipeableListItem extends PureComponent { const opacity = (Math.abs(this.left) / 100).toFixed(2); - if (this.props.onSwipeProgress) { - const swipeDistance = Math.max( - 0, - this.listElement.offsetWidth - Math.abs(this.left) - ); + if (this.props.onSwipeProgress && this.listElement !== null) { + const listElementWidth = this.listElement.offsetWidth; + let swipeDistancePercent = this.previousSwipeDistancePercent; + + if (listElementWidth !== 0) { + const swipeDistance = Math.max( + 0, + listElementWidth - Math.abs(this.left) + ); - const swipeDistancePercent = - 100 - - Math.round((100 * swipeDistance) / this.listElement.offsetWidth); + swipeDistancePercent = + 100 - Math.round((100 * swipeDistance) / listElementWidth); + } - if (this.prevSwipeDistancePercent !== swipeDistancePercent) { + if (this.previousSwipeDistancePercent !== swipeDistancePercent) { this.props.onSwipeProgress(swipeDistancePercent); - this.prevSwipeDistancePercent = swipeDistancePercent; + this.previousSwipeDistancePercent = swipeDistancePercent; } } From 57d7edae2dabf34e63bda8d267c1adfcb6e8634d Mon Sep 17 00:00:00 2001 From: Marek Rozmus Date: Thu, 30 Jan 2020 15:10:59 +0100 Subject: [PATCH 8/8] Fix code review issues --- examples/src/App.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/src/App.js b/examples/src/App.js index 4f3b49b..b240c0c 100644 --- a/examples/src/App.js +++ b/examples/src/App.js @@ -88,7 +88,8 @@ function App() { Callback swipe action: {swipeAction} - Callback swipe progress: {swipeProgress || '-'}% + Callback swipe progress:{' '} + {swipeProgress !== undefined ? swipeProgress : '-'}%