Skip to content

Commit

Permalink
Adding global tap handler
Browse files Browse the repository at this point in the history
  • Loading branch information
mattgperry committed Jan 10, 2024
1 parent df94ca8 commit 9509740
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 7 deletions.
24 changes: 24 additions & 0 deletions dev/examples/Events-whileTap-global.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as React from "react"
import { motion } from "framer-motion"

const style = {
width: 100,
height: 100,
background: "rgba(255, 0, 0, 1)",
}

export const App = () => {
return (
<motion.div globalTapTarget whileTap="pressed">
<motion.div
variants={{
pressed: {
scale: 0.5,
backgroundColor: "rgba(0, 255, 0, .5)",
},
}}
style={style}
/>
</motion.div>
)
}
20 changes: 20 additions & 0 deletions packages/framer-motion/src/gestures/__tests__/press.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,26 @@ describe("press", () => {
expect(press).toBeCalledTimes(1)
})

test("global press event listeners fire", async () => {
const press = jest.fn()
const Component = () => (
<>
<div data-testid="target" />
<motion.div globalTapTarget onTap={() => press()} />
</>
)

const { getByTestId, rerender } = render(<Component />)
rerender(<Component />)

pointerDown(getByTestId("target") as Element)
pointerUp(getByTestId("target") as Element)

await nextFrame()

expect(press).toBeCalledTimes(1)
})

test("press event listeners fire via keyboard", async () => {
const press = jest.fn()
const pressStart = jest.fn()
Expand Down
9 changes: 5 additions & 4 deletions packages/framer-motion/src/gestures/press.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ export class PressGesture extends Feature<Element> {
startEvent: PointerEvent,
startInfo: EventInfo
) => {
this.removeEndListeners()

if (this.isPressing) return

this.removeEndListeners()

const props = this.node.getProps()

const endPointerPress = (
Expand All @@ -74,13 +74,14 @@ export class PressGesture extends Feature<Element> {
) => {
if (!this.checkPressEnd()) return

const { onTap, onTapCancel } = this.node.getProps()
const { onTap, onTapCancel, globalTapTarget } = this.node.getProps()

frame.update(() => {
/**
* We only count this as a tap gesture if the event.target is the same
* as, or a child of, this component's element
*/
!globalTapTarget &&
!isNodeOrChild(this.node.current, endEvent.target as Element)
? onTapCancel && onTapCancel(endEvent, endInfo)
: onTap && onTap(endEvent, endInfo)
Expand Down Expand Up @@ -176,7 +177,7 @@ export class PressGesture extends Feature<Element> {
const props = this.node.getProps()

const removePointerListener = addPointerEvent(
this.node.current!,
props.globalTapTarget ? window : this.node.current!,
"pointerdown",
this.startPointerPress,
{ passive: !(props.onTapStart || props["onPointerStart"]) }
Expand Down
7 changes: 7 additions & 0 deletions packages/framer-motion/src/gestures/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ export interface TapHandlers {
* ```
*/
whileTap?: VariantLabels | TargetAndTransition

/**
* If `true`, the tap gesture will attach its start listener to window.
*
* @internal
*/
globalTapTarget?: boolean
}
export type PanHandler = (event: Event, info: PanInfo) => void

Expand Down
5 changes: 2 additions & 3 deletions packages/framer-motion/src/motion/utils/valid-prop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ const validMotionProps = new Set<keyof MotionProps>([
"transformValues",
"custom",
"inherit",
"onLayoutAnimationStart",
"onLayoutAnimationComplete",
"onLayoutMeasure",
"onBeforeLayoutMeasure",
"onAnimationStart",
"onAnimationComplete",
Expand All @@ -38,6 +35,7 @@ const validMotionProps = new Set<keyof MotionProps>([
"onHoverEnd",
"onViewportEnter",
"onViewportLeave",
"globalTapTarget",
"ignoreStrict",
"viewport",
])
Expand All @@ -57,6 +55,7 @@ export function isValidMotionProp(key: string) {
key.startsWith("layout") ||
key.startsWith("onTap") ||
key.startsWith("onPan") ||
key.startsWith("onLayout") ||
validMotionProps.has(key as keyof MotionProps)
)
}

0 comments on commit 9509740

Please sign in to comment.