From 084454e0306bd20346695c4a3574272d52394a45 Mon Sep 17 00:00:00 2001 From: Iago Espinoza Date: Fri, 28 Feb 2025 14:12:12 -0300 Subject: [PATCH] fix: lint errors and some warnings --- react/Drawer.tsx | 243 +++++++++++------------ react/DrawerTrigger.tsx | 31 ++- react/Overlay.tsx | 26 +-- react/Swipable.tsx | 423 ++++++++++++++++++++-------------------- 4 files changed, 362 insertions(+), 361 deletions(-) diff --git a/react/Drawer.tsx b/react/Drawer.tsx index d9b416a..8f53cbc 100644 --- a/react/Drawer.tsx +++ b/react/Drawer.tsx @@ -1,119 +1,120 @@ -import type { MouseEventHandler } from "react"; import React, { Suspense, useReducer, + MouseEventHandler, useMemo, useState, useEffect, -} from "react"; -import { defineMessages } from "react-intl"; -import { IconMenu } from "vtex.store-icons"; -import { useCssHandles } from "vtex.css-handles"; -import { useChildBlock, ExtensionPoint } from "vtex.render-runtime"; -import type { PixelEventTypes } from "vtex.pixel-manager"; -import { usePixelEventCallback } from "vtex.pixel-manager"; -import type { MaybeResponsiveValue } from "vtex.responsive-values"; -import { useResponsiveValue } from "vtex.responsive-values"; - -import Portal from "./Portal"; -import Overlay from "./Overlay"; -import useLockScroll from "./modules/useLockScroll"; -import DrawerCloseButton from "./DrawerCloseButton"; -import { DrawerContextProvider } from "./DrawerContext"; -import { isElementInsideLink } from "./modules/isElementInsideLink"; - -const Swipable = React.lazy(() => import("./Swipable")); +} from 'react' +import { defineMessages } from 'react-intl' +import { IconMenu } from 'vtex.store-icons' +import { useCssHandles } from 'vtex.css-handles' +import { useChildBlock, ExtensionPoint } from 'vtex.render-runtime' +import { usePixelEventCallback, PixelEventTypes } from 'vtex.pixel-manager' +import { + MaybeResponsiveValue, + useResponsiveValue, +} from 'vtex.responsive-values' + +import Portal from './Portal' +import Overlay from './Overlay' +import useLockScroll from './modules/useLockScroll' +import DrawerCloseButton from './DrawerCloseButton' +import { DrawerContextProvider } from './DrawerContext' +import { isElementInsideLink } from './modules/isElementInsideLink' + +const Swipable = React.lazy(() => import('./Swipable')) interface MenuState { - isOpen: boolean; - hasBeenOpened: boolean; + isOpen: boolean + hasBeenOpened: boolean } interface MenuAction { - type: "open" | "close"; + type: 'open' | 'close' } const initialMenuState: MenuState = { isOpen: false, hasBeenOpened: false, -}; +} -type Position = "left" | "right" | "up" | "down"; -type SlideDirection = "vertical" | "horizontal" | "rightToLeft" | "leftToRight"; -type Height = "100%" | "auto" | "fullscreen"; -type Width = "100%" | "auto"; -type BackdropMode = "visible" | "none"; -type RenderingStrategy = "lazy" | "eager"; +type Position = 'left' | 'right' | 'up' | 'down' +type SlideDirection = 'vertical' | 'horizontal' | 'rightToLeft' | 'leftToRight' +type Height = '100%' | 'auto' | 'fullscreen' +type Width = '100%' | 'auto' +type BackdropMode = 'visible' | 'none' +type RenderingStrategy = 'lazy' | 'eager' interface Props { - actionIconId?: string; - dismissIconId?: string; - position: Position; - width?: Width; - height?: Height; - slideDirection?: SlideDirection; - isFullWidth?: boolean; - maxWidth?: number | string; - children: React.ReactNode; - customIcon?: React.ReactElement; - header?: React.ReactElement; - backdropMode?: MaybeResponsiveValue; - renderingStrategy?: RenderingStrategy; - customPixelEventId?: PixelEventTypes.PixelData["id"]; - customPixelEventName?: PixelEventTypes.PixelData["event"]; - onVisibilityChanged?: (visible: boolean) => void; - zIndex?: number; + actionIconId?: string + dismissIconId?: string + position: Position + width?: Width + height?: Height + slideDirection?: SlideDirection + isFullWidth?: boolean + maxWidth?: number | string + children: React.ReactNode + customIcon?: React.ReactElement + header?: React.ReactElement + backdropMode?: MaybeResponsiveValue + renderingStrategy?: RenderingStrategy + customPixelEventId?: PixelEventTypes.PixelData['id'] + customPixelEventName?: PixelEventTypes.PixelData['event'] + onVisibilityChanged?: (visible: boolean) => void + zIndex?: number } function menuReducer(state: MenuState, action: MenuAction) { switch (action.type) { - case "open": + case 'open': return { ...state, isOpen: true, hasBeenOpened: true, - }; + } - case "close": + case 'close': return { ...state, isOpen: false, - }; + } default: - return state; + return state } } const useMenuState = () => { - const [state, dispatch] = useReducer(menuReducer, initialMenuState); - const setLockScroll = useLockScroll(); + const [state, dispatch] = useReducer(menuReducer, initialMenuState) + const setLockScroll = useLockScroll() const setMenuOpen = (value: boolean) => { - dispatch({ type: value ? "open" : "close" }); - setLockScroll(value); - }; + dispatch({ type: value ? 'open' : 'close' }) + setLockScroll(value) + } - const openMenu = () => setMenuOpen(true); - const closeMenu = () => setMenuOpen(false); + const openMenu = () => setMenuOpen(true) + const closeMenu = () => setMenuOpen(false) return { state, openMenu, closeMenu, - }; -}; + } +} const CSS_HANDLES = [ - "openIconContainer", - "drawer", - "opened", - "closed", - "moving", - "drawerContent", - "childrenContainer", - "closeIconContainer", -] as const; + 'openIconContainer', + 'drawer', + 'opened', + 'closed', + 'moving', + 'drawerContent', + 'childrenContainer', + 'closeIconContainer', +] as const function Drawer(props: Props) { const { @@ -123,63 +124,63 @@ function Drawer(props: Props) { customIcon, isFullWidth, maxWidth = 450, - slideDirection = "horizontal", - backdropMode: backdropModeProp = "visible", - renderingStrategy = "lazy", + slideDirection = 'horizontal', + backdropMode: backdropModeProp = 'visible', + renderingStrategy = 'lazy', customPixelEventId, customPixelEventName, onVisibilityChanged, zIndex = 999, - } = props; - - const handles = useCssHandles(CSS_HANDLES); - const backdropMode = useResponsiveValue(backdropModeProp); - const hasTriggerBlock = Boolean(useChildBlock({ id: "drawer-trigger" })); - const hasHeaderBlock = Boolean(useChildBlock({ id: "drawer-header" })); - const { state: menuState, openMenu, closeMenu } = useMenuState(); - const { isOpen: isMenuOpen, hasBeenOpened: hasMenuBeenOpened } = menuState; + } = props + + const handles = useCssHandles(CSS_HANDLES) + const backdropMode = useResponsiveValue(backdropModeProp) + const hasTriggerBlock = Boolean(useChildBlock({ id: 'drawer-trigger' })) + const hasHeaderBlock = Boolean(useChildBlock({ id: 'drawer-header' })) + const { state: menuState, openMenu, closeMenu } = useMenuState() + const { isOpen: isMenuOpen, hasBeenOpened: hasMenuBeenOpened } = menuState const [shouldRenderChildren, setShouldRenderChildren] = useState( - renderingStrategy === "eager" - ); + renderingStrategy === 'eager' + ) - const [isMoving, setIsMoving] = useState(false); + const [isMoving, setIsMoving] = useState(false) // Always add the listener for 'openDrawer' events, since they're sent by // the drawer-trigger block. usePixelEventCallback({ eventId: customPixelEventId, handler: openMenu, - eventName: "openDrawer", - }); + eventName: 'openDrawer', + }) usePixelEventCallback({ eventId: customPixelEventId, handler: openMenu, eventName: customPixelEventName, - }); + }) useEffect(() => { if (onVisibilityChanged !== undefined) { - onVisibilityChanged(isMenuOpen); + onVisibilityChanged(isMenuOpen) } - }, [onVisibilityChanged, isMenuOpen]); + }, [onVisibilityChanged, isMenuOpen]) - const handleContainerClick: MouseEventHandler = (event) => { + const handleContainerClick: MouseEventHandler = event => { // target is the clicked element // currentTarget is the element which was attached the event (e.g. the container) - const { target, currentTarget } = event; + const { target, currentTarget } = event if (isElementInsideLink(target as HTMLElement, currentTarget)) { - closeMenu(); + closeMenu() } - }; + } const direction = - slideDirection === "horizontal" || slideDirection === "leftToRight" - ? "left" - : "right"; + slideDirection === 'horizontal' || slideDirection === 'leftToRight' + ? 'left' + : 'right' - const swipeHandler = direction === "left" ? "onSwipeLeft" : "onSwipeRight"; + const swipeHandler = direction === 'left' ? 'onSwipeLeft' : 'onSwipeRight' const contextValue = useMemo( () => ({ @@ -188,37 +189,37 @@ function Drawer(props: Props) { close: closeMenu, }), [isMenuOpen, openMenu, closeMenu] - ); + ) - const overlayVisible = backdropMode === "visible" && isMenuOpen; + const overlayVisible = backdropMode === 'visible' && isMenuOpen useEffect(() => { - if (isMenuOpen || hasMenuBeenOpened || renderingStrategy === "eager") { - setShouldRenderChildren(true); + if (isMenuOpen || hasMenuBeenOpened || renderingStrategy === 'eager') { + setShouldRenderChildren(true) } }, [ hasMenuBeenOpened, renderingStrategy, setShouldRenderChildren, isMenuOpen, - ]); + ]) return (
{ + onKeyDown={e => { if ( isMenuOpen && - (e.key === "Enter" || e.key === " " || e.key === "Escape") + (e.key === 'Enter' || e.key === ' ' || e.key === 'Escape') ) { - e.preventDefault(); - openMenu(); + e.preventDefault() + openMenu() } }} > @@ -236,28 +237,28 @@ function Drawer(props: Props) { [swipeHandler]: closeMenu, }} enabled={isMenuOpen} - position={isMenuOpen ? "center" : direction} + position={isMenuOpen ? 'center' : direction} allowOutsideDrag - onUpdateOffset={(value) => { - setIsMoving(!(value === "0%" || value === "-100%")); + onUpdateOffset={value => { + setIsMoving(!(value === '0%' || value === '-100%')) }} className={`${handles.drawer} ${ isMenuOpen ? handles.opened : handles.closed - } ${isMoving ? handles.moving : ""} ${ - direction === "right" ? "right-0" : "left-0" + } ${isMoving ? handles.moving : ''} ${ + direction === 'right' ? 'right-0' : 'left-0' } fixed top-0 bottom-0 bg-base z-999 flex flex-column`} style={{ - width: width ?? (isFullWidth ? "100%" : "85%"), + width: width ?? (isFullWidth ? '100%' : '85%'), maxWidth, minWidth: 280, - pointerEvents: isMenuOpen ? "auto" : "none", + pointerEvents: isMenuOpen ? 'auto' : 'none', zIndex, }} >
{hasHeaderBlock ? ( @@ -286,18 +287,18 @@ function Drawer(props: Props) { - ); + ) } const messages = defineMessages({ title: { - defaultMessage: "", - id: "admin/editor.drawer.title", + defaultMessage: '', + id: 'admin/editor.drawer.title', }, -}); +}) Drawer.schema = { title: messages.title.id, -}; +} -export default Drawer; +export default Drawer diff --git a/react/DrawerTrigger.tsx b/react/DrawerTrigger.tsx index 21fe2d4..e3f6e8e 100644 --- a/react/DrawerTrigger.tsx +++ b/react/DrawerTrigger.tsx @@ -1,30 +1,29 @@ -import React from "react"; -import type { PixelEventTypes } from "vtex.pixel-manager"; -import { usePixel } from "vtex.pixel-manager"; -import { useCssHandles } from "vtex.css-handles"; +import React from 'react' +import { usePixel, PixelEventTypes } from 'vtex.pixel-manager' +import { useCssHandles } from 'vtex.css-handles' interface Props { - customPixelEventId?: string; + customPixelEventId?: string } -const CSS_HANDLES = ["drawerTriggerContainer"] as const; +const CSS_HANDLES = ['drawerTriggerContainer'] as const const DrawerTrigger: React.FC = ({ children, customPixelEventId }) => { - const { push } = usePixel(); - const handles = useCssHandles(CSS_HANDLES); + const { push } = usePixel() + const handles = useCssHandles(CSS_HANDLES) const handleInteraction = () => { if (!customPixelEventId) { - return; + return } const pixelEvent: PixelEventTypes.PixelData = { id: customPixelEventId, - event: "openDrawer", - }; + event: 'openDrawer', + } - push(pixelEvent); - }; + push(pixelEvent) + } return (
= ({ children, customPixelEventId }) => { > {children}
- ); -}; + ) +} -export default DrawerTrigger; +export default DrawerTrigger diff --git a/react/Overlay.tsx b/react/Overlay.tsx index 337015a..081f003 100644 --- a/react/Overlay.tsx +++ b/react/Overlay.tsx @@ -1,20 +1,20 @@ -import React from "react"; -import { useCssHandles, applyModifiers } from "vtex.css-handles"; +import React from 'react' +import { useCssHandles, applyModifiers } from 'vtex.css-handles' interface Props { - visible: boolean; - onClick(event: React.MouseEvent | React.TouchEvent): void; - zIndex?: number; + visible: boolean + onClick(event: React.MouseEvent | React.TouchEvent): void + zIndex?: number } -const CSS_HANDLES = ["overlay"] as const; +const CSS_HANDLES = ['overlay'] as const const Overlay: React.ForwardRefRenderFunction = ( { visible, onClick, zIndex = 999 }: Props, ref ) => { - const handles = useCssHandles(CSS_HANDLES); - const ariaHidden = visible ? "false" : "true"; + const handles = useCssHandles(CSS_HANDLES) + const ariaHidden = visible ? 'false' : 'true' return (
= ( onClick={onClick} style={{ opacity: visible ? 0.5 : 0, - pointerEvents: visible ? "auto" : "none", + pointerEvents: visible ? 'auto' : 'none', zIndex, }} className={`${applyModifiers( handles.overlay, - visible ? "visible" : "" + visible ? 'visible' : '' )} bg-base--inverted z-999 fixed top-0 bottom-0 left-0 right-0`} /> - ); -}; + ) +} -export default React.forwardRef(Overlay); +export default React.forwardRef(Overlay) diff --git a/react/Swipable.tsx b/react/Swipable.tsx index a98b65a..e4cfeab 100644 --- a/react/Swipable.tsx +++ b/react/Swipable.tsx @@ -1,70 +1,70 @@ -import React from "react"; +import React from 'react' -import { animate } from "./modules/animation"; -import parseMeasure from "./modules/parseMeasure"; -import getPointerPosition from "./modules/getPointerPosition"; +import { animate } from './modules/animation' +import parseMeasure from './modules/parseMeasure' +import getPointerPosition from './modules/getPointerPosition' -const CENTER = "center"; -const LEFT = "left"; -const RIGHT = "right"; +const CENTER = 'center' +const LEFT = 'left' +const RIGHT = 'right' -type Position = "center" | "left" | "right"; +type Position = 'center' | 'left' | 'right' interface Props { - children: React.ReactElement; - onSwipeLeft: () => void; - onSwipeRight: () => void; + children: React.ReactElement + onSwipeLeft: () => void + onSwipeRight: () => void onTriggerChange: ( args: { - type: string; - speed: number; + type: string + speed: number } | null - ) => void; - onLockScroll: () => void; - onUnlockScroll: () => void; - onDragStart: () => void; - onDragEnd: () => void; + ) => void + onLockScroll: () => void + onUnlockScroll: () => void + onDragStart: () => void + onDragEnd: () => void onSetPosition: (args: { - element: React.ReactElement | HTMLDivElement; - offset: number | string; - }) => void; - onUpdateOffset: (offset: number | string) => void; - threshold: number; - enabled: boolean; - rubberBanding: boolean; - element: React.ReactElement; - position: Position; - className: string; - style: Record; - positionRight: number | string; - positionLeft: number | string; - preserveMomentum: boolean; - allowOutsideDrag: boolean; + element: React.ReactElement | HTMLDivElement + offset: number | string + }) => void + onUpdateOffset: (offset: number | string) => void + threshold: number + enabled: boolean + rubberBanding: boolean + element: React.ReactElement + position: Position + className: string + style: Record + positionRight: number | string + positionLeft: number | string + preserveMomentum: boolean + allowOutsideDrag: boolean } export default class Swipable extends React.Component { - private dragContainer: React.RefObject; - private dragStartPos: { x: number; y: number }; - private isDragging: boolean; - private isPointerDown: boolean; - private isScrolling: boolean; - private momentum: number | null; - private momentumTimeout: NodeJS.Timeout | number | null; - private offset: string | number; - private offsetAnimation: { value: number | string }; - private stopAnimation: () => void; - private wasDragging: boolean; + private dragContainer: React.RefObject + private dragStartPos: { x: number; y: number } + private isDragging: boolean + private isPointerDown: boolean + private isScrolling: boolean + private momentum: number | null + private momentumTimeout: NodeJS.Timeout | number | null + private offset: string | number + private offsetAnimation: { value: number | string } + private stopAnimation: () => void + private wasDragging: boolean private willTrigger: { - type: string; - speed: number; - } | null; + type: string + speed: number + } | null private previousDragPositions: Array<{ - x: number; - y: number; - source: string; - timeStamp: number; - }>; + x: number + y: number + source: string + timeStamp: number + }> public static defaultProps = { onSwipeLeft: null, @@ -81,12 +81,12 @@ export default class Swipable extends React.Component { element, offset, }: { - element: HTMLDivElement; - offset: number; + element: HTMLDivElement + offset: number }) => { - const unit = typeof offset === "number" ? "px" : ""; + const unit = typeof offset === 'number' ? 'px' : '' - element.style.transform = `translate3d(${offset}${unit},0,0)`; + element.style.transform = `translate3d(${offset}${unit},0,0)` }, onUpdateOffset: () => {}, element: null, @@ -94,172 +94,172 @@ export default class Swipable extends React.Component { enabled: true, rubberBanding: false, style: {}, - positionRight: "100%", - positionLeft: "-100%", + positionRight: '100%', + positionLeft: '-100%', preserveMomentum: true, allowOutsideDrag: false, - }; + } constructor(props: Props) { - super(props); + super(props) - this.dragContainer = React.createRef(); + this.dragContainer = React.createRef() - this.stopAnimation = () => {}; - this.isPointerDown = false; - this.isDragging = false; - this.isScrolling = false; + this.stopAnimation = () => {} + this.isPointerDown = false + this.isDragging = false + this.isScrolling = false /* Used for the click event handler, which is * fired after mouseup is fired. * See comment on handleClick function */ - this.wasDragging = false; + this.wasDragging = false - this.offset = this.getOffsetFromPosition(); - this.offsetAnimation = { value: this.offset }; - this.momentum = null; - this.momentumTimeout = null; + this.offset = this.getOffsetFromPosition() + this.offsetAnimation = { value: this.offset } + this.momentum = null + this.momentumTimeout = null this.dragStartPos = { x: 0, y: 0, - }; - this.previousDragPositions = []; - this.willTrigger = null; + } + this.previousDragPositions = [] + this.willTrigger = null } public componentDidMount() { if (!window || !window.document) { - return; + return } - window.document.addEventListener("mousedown", this.handleDragStart); - window.document.addEventListener("touchstart", this.handleDragStart); - window.document.addEventListener("mousemove", this.handleDragMove); - window.document.addEventListener("touchmove", this.handleDragMove); - window.document.addEventListener("mouseup", this.handleDragEnd); - window.document.addEventListener("touchend", this.handleDragEnd); - window.document.addEventListener("click", this.handleClick, { + window.document.addEventListener('mousedown', this.handleDragStart) + window.document.addEventListener('touchstart', this.handleDragStart) + window.document.addEventListener('mousemove', this.handleDragMove) + window.document.addEventListener('touchmove', this.handleDragMove) + window.document.addEventListener('mouseup', this.handleDragEnd) + window.document.addEventListener('touchend', this.handleDragEnd) + window.document.addEventListener('click', this.handleClick, { capture: true, - }); + }) } public componentWillUnmount() { if (!window || !window.document) { - return; + return } - window.document.removeEventListener("mousedown", this.handleDragStart); - window.document.removeEventListener("touchstart", this.handleDragStart); - window.document.removeEventListener("mousemove", this.handleDragMove); - window.document.removeEventListener("touchmove", this.handleDragMove); - window.document.removeEventListener("mouseup", this.handleDragEnd); - window.document.removeEventListener("touchend", this.handleDragEnd); - window.document.removeEventListener("click", this.handleClick, { + window.document.removeEventListener('mousedown', this.handleDragStart) + window.document.removeEventListener('touchstart', this.handleDragStart) + window.document.removeEventListener('mousemove', this.handleDragMove) + window.document.removeEventListener('touchmove', this.handleDragMove) + window.document.removeEventListener('mouseup', this.handleDragEnd) + window.document.removeEventListener('touchend', this.handleDragEnd) + window.document.removeEventListener('click', this.handleClick, { capture: true, - }); + }) } /* Prevents click events from firing in the event of * firing swipe events. */ private handleClick = (event: Event) => { if (this.wasDragging || this.isPointerDown) { - event.preventDefault(); - event.stopPropagation(); + event.preventDefault() + event.stopPropagation() } - this.wasDragging = false; - }; + this.wasDragging = false + } public componentDidUpdate(prevProps: Props) { if (prevProps.enabled && !this.props.enabled && this.isPointerDown) { - this.isPointerDown = false; + this.isPointerDown = false } if (prevProps.position !== this.props.position) { - this.updatePosition(); + this.updatePosition() } } private updatePosition = () => { if (this.momentumTimeout) { - clearTimeout(this.momentumTimeout as number); - this.momentumTimeout = null; + clearTimeout(this.momentumTimeout as number) + this.momentumTimeout = null } - this.offsetAnimation.value = this.offset; + this.offsetAnimation.value = this.offset animate({ object: this.offsetAnimation, - prop: "value", + prop: 'value', target: this.getOffsetFromPosition(), ...(this.momentum ? { speed: this.momentum, acceleration: 1.25 } : { duration: 0.25 }), onUpdate: (value: string) => { - this.setOffset(value); + this.setOffset(value) }, - }); + }) - this.momentum = null; - }; + this.momentum = null + } private getOffsetFromPosition = () => { switch (this.props.position) { - case "center": - return 0; + case 'center': + return 0 - case "right": - return this.props.positionRight; + case 'right': + return this.props.positionRight - case "left": - return this.props.positionLeft; + case 'left': + return this.props.positionLeft default: - return 0; + return 0 } - }; + } private handleDragStart = (event: MouseEvent | TouchEvent) => { if (this.isPointerDown || !this.props.enabled) { - return; + return } if ( !this.props.allowOutsideDrag && !this.dragContainer.current?.contains(event.target as Node) ) { - return; + return } - const pos = getPointerPosition(event); + const pos = getPointerPosition(event) - if (pos === null) return; + if (pos === null) return - this.isPointerDown = true; - this.isScrolling = false; - this.isDragging = false; - this.stopAnimation(); + this.isPointerDown = true + this.isScrolling = false + this.isDragging = false + this.stopAnimation() this.dragStartPos = { ...pos, - }; - this.previousDragPositions = [pos]; - this.willTrigger = null; - this.dispatchTriggerChange(this.willTrigger); - }; + } + this.previousDragPositions = [pos] + this.willTrigger = null + this.dispatchTriggerChange(this.willTrigger) + } /** Lets the parent know that a swipe event will be triggered if the cursor * is released. Useful for displaying UI feedback */ private dispatchTriggerChange = ( willTrigger: { - type: string; - speed: number; + type: string + speed: number } | null ) => { - const { onTriggerChange } = this.props; + const { onTriggerChange } = this.props if (onTriggerChange) { - onTriggerChange(willTrigger); + onTriggerChange(willTrigger) } - }; + } private handleDragMove = (event: TouchEvent | MouseEvent) => { const { @@ -269,222 +269,223 @@ export default class Swipable extends React.Component { onSwipeRight, enabled, rubberBanding, - } = this.props; + } = this.props if (!this.isPointerDown || this.isScrolling) { - return; + return } - const pos = getPointerPosition(event); + const pos = getPointerPosition(event) - if (pos === null) return; + if (pos === null) return - const lastPos = - this.previousDragPositions[this.previousDragPositions.length - 1]; + const lastPos = this.previousDragPositions[ + this.previousDragPositions.length - 1 + ] if (lastPos && pos.source !== lastPos.source) { - return; + return } /* Values to determine if the dragging means either a swipe or a scroll */ const threshold = { x: 15, y: 10, - }; + } const distance = { x: pos.x - this.dragStartPos.x, y: pos.y - this.dragStartPos.y, - }; + } if (!this.isDragging) { if (Math.abs(distance.x) >= threshold.x) { - this.isDragging = true; - onDragStart(); - onLockScroll(); + this.isDragging = true + onDragStart() + onLockScroll() } else if (Math.abs(distance.y) >= threshold.y) { - this.isScrolling = true; + this.isScrolling = true } } else { - this.offset = distance.x; + this.offset = distance.x /** Either applies rubberbanding or stops dragging at the limits */ const limitDragging = (offset: number) => { - const RUBBER_BANDING_MULTIPLIER = 0.3; + const RUBBER_BANDING_MULTIPLIER = 0.3 - return rubberBanding ? offset * RUBBER_BANDING_MULTIPLIER : 0; - }; + return rubberBanding ? offset * RUBBER_BANDING_MULTIPLIER : 0 + } if (!enabled || (!onSwipeLeft && this.offset < 0)) { - this.offset = limitDragging(this.offset); + this.offset = limitDragging(this.offset) } else if (!enabled || (!onSwipeRight && this.offset > 0)) { - this.offset = limitDragging(this.offset); + this.offset = limitDragging(this.offset) } - this.setOffset(this.offset); - this.previousDragPositions.push(pos); + this.setOffset(this.offset) + this.previousDragPositions.push(pos) - const willTrigger = this.checkTrigger(); + const willTrigger = this.checkTrigger() // eslint-disable-next-line eqeqeq if (this.willTrigger != willTrigger) { - this.dispatchTriggerChange(willTrigger); + this.dispatchTriggerChange(willTrigger) } - this.willTrigger = willTrigger; + this.willTrigger = willTrigger } - }; + } private setOffset = (offset: number | string) => { if (this.dragContainer?.current) { this.props.onSetPosition({ element: this.props.element || this.dragContainer.current, offset, - }); - this.props.onUpdateOffset(offset); + }) + this.props.onUpdateOffset(offset) } - this.offset = offset; - }; + this.offset = offset + } private setMomentum = (momentum: number, target: number | string) => { - const [, targetUnit] = parseMeasure(target) ?? []; + const [, targetUnit] = parseMeasure(target) ?? [] - if (this.dragContainer.current == null) return; + if (this.dragContainer.current == null) return - if (targetUnit === "%") { - const bounds = this.dragContainer.current.getBoundingClientRect(); - const { width } = bounds; + if (targetUnit === '%') { + const bounds = this.dragContainer.current.getBoundingClientRect() + const { width } = bounds - this.offset = `${(Number(this.offset) / width) * 100}%`; - momentum = (momentum / width) * 100; + this.offset = `${(Number(this.offset) / width) * 100}%` + momentum = (momentum / width) * 100 } - this.momentum = momentum; + this.momentum = momentum /* Lets momentum live only for a brief time. If position changes in this meantime, * then momentum is applied. Otherwise it's ignored and a regular transition is done */ if (this.momentumTimeout) { - clearTimeout(this.momentumTimeout as number); + clearTimeout(this.momentumTimeout as number) } this.momentumTimeout = setTimeout(() => { - this.momentum = null; - this.momentumTimeout = null; - }, 100); - }; + this.momentum = null + this.momentumTimeout = null + }, 100) + } /** Checks if the mouse/touch movement at the time of release triggers a * swipe, and if so, to which direction */ private checkTrigger = () => { if (!this.props.enabled) { - return null; + return null } /* Checks the last $samples cursor positions to determine the * average speed and direction of movement */ - const samples = 6; - const fps = 60; + const samples = 6 + const fps = 60 const releaseSpeed = this.previousDragPositions .slice(-samples) .map((cur, i, arr) => { - const last = arr[i - 1]; + const last = arr[i - 1] if (last == null) { - return null; + return null } - return cur.x - last.x; + return cur.x - last.x }) - .filter((cur) => cur != null) - .reduce((sum: number, cur) => sum + (cur ?? 0) / samples, 0) * fps; + .filter(cur => cur != null) + .reduce((sum: number, cur) => sum + (cur ?? 0) / samples, 0) * fps - const { onSwipeLeft, onSwipeRight } = this.props; + const { onSwipeLeft, onSwipeRight } = this.props const triggers = { left: onSwipeLeft != null && releaseSpeed < 0 && - typeof this.offset === "number" && + typeof this.offset === 'number' && this.offset < -this.props.threshold, right: onSwipeRight != null && releaseSpeed > 0 && - typeof this.offset === "number" && + typeof this.offset === 'number' && this.offset > this.props.threshold, - }; + } if (triggers.left) { return { type: LEFT, speed: releaseSpeed, - }; + } } if (triggers.right) { return { type: RIGHT, speed: releaseSpeed, - }; + } } - return null; - }; + return null + } private handleDragEnd = (event: Event) => { if (this.isPointerDown && this.isDragging) { - event.preventDefault(); - event.stopPropagation(); - const trigger = this.checkTrigger(); + event.preventDefault() + event.stopPropagation() + const trigger = this.checkTrigger() if (trigger) { switch (trigger.type) { case LEFT: if (this.props.preserveMomentum) { - this.setMomentum(trigger.speed, this.props.positionLeft); + this.setMomentum(trigger.speed, this.props.positionLeft) } - this.props.onSwipeLeft(); - break; + this.props.onSwipeLeft() + break case RIGHT: if (this.props.preserveMomentum) { - this.setMomentum(trigger.speed, this.props.positionRight); + this.setMomentum(trigger.speed, this.props.positionRight) } - this.props.onSwipeRight(); - break; + this.props.onSwipeRight() + break default: - break; + break } } else { - this.offsetAnimation.value = this.offset; + this.offsetAnimation.value = this.offset animate({ object: this.offsetAnimation, - prop: "value", + prop: 'value', target: 0, duration: 0.2, onUpdate: (value: string) => { - this.setOffset(value); + this.setOffset(value) }, - }); + }) } - this.props.onDragEnd(); - this.props.onUnlockScroll(); + this.props.onDragEnd() + this.props.onUnlockScroll() } - this.isScrolling = false; - this.wasDragging = this.isPointerDown && this.isDragging; - this.isPointerDown = false; - }; + this.isScrolling = false + this.wasDragging = this.isPointerDown && this.isDragging + this.isPointerDown = false + } public render() { return (
{ > {this.props.children}
- ); + ) } }