From 4cb1211f2ca85231a3001c0030cae2db818763da Mon Sep 17 00:00:00 2001 From: Gibson Murray Date: Thu, 3 Oct 2024 14:46:56 -0400 Subject: [PATCH] add: useFlip hook --- src/index.js | 40 +++++++++++++++++++++++++++++++++++++++- types/index.d.ts | 10 +++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index ad9119f..7434281 100644 --- a/src/index.js +++ b/src/index.js @@ -8,8 +8,9 @@ * @author: Jack Doyle, jack@greensock.com */ /* eslint-disable */ -import { useEffect, useLayoutEffect, useRef } from "react"; +import { useEffect, useLayoutEffect, useRef, useState, useCallback } from "react"; import gsap from "gsap"; +import Flip from "gsap/Flip"; let useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect, isConfig = value => value && !Array.isArray(value) && typeof(value) === "object", @@ -47,3 +48,40 @@ export const useGSAP = (callback, dependencies = emptyArray) => { }; useGSAP.register = core => { _gsap = core; }; useGSAP.headless = true; // doesn't require the window to be registered. + +export const useFlip = ( + target, + options +) => { + const [isFlipped, setIsFlipped] = useState(false) + const flipStateRef = useRef(null) + const scopeRef = useRef(null) + + const { props, simple, revertOnUpdate, ...vars } = options + + const captureState = useCallback(() => { + const mergedVars = { props, simple, targets: target } + flipStateRef.current = Flip.getState(target, mergedVars) + }, [target, props, simple]) + + const flip = useCallback(() => { + captureState() + setIsFlipped((prev) => !prev) + }, [captureState]) + + useGSAP( + () => { + if (flipStateRef.current) { + Flip.from(flipStateRef.current, vars) + flipStateRef.current = null + } + }, + { + scope: scopeRef, + dependencies: [isFlipped], + revertOnUpdate: revertOnUpdate, + } + ) + + return { isFlipped, flip, scopeRef } +} diff --git a/types/index.d.ts b/types/index.d.ts index 91b62b0..2d5e0fc 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -30,4 +30,12 @@ interface useGSAPConfig { * @param {Array | useGSAPConfig} [dependencies] * @returns {useGSAPReturn} Object with "context" and "contextSafe" properties */ -export function useGSAP(func?: ContextFunc | useGSAPConfig, dependencies?: unknown[] | useGSAPConfig): useGSAPReturn; \ No newline at end of file +export function useGSAP(func?: ContextFunc | useGSAPConfig, dependencies?: unknown[] | useGSAPConfig): useGSAPReturn; + +/** + * Flip animation hook for animating React components that encapsulates the GSAP Flip API. + * @param target - The target element to animate. + * @param options - The options for the flip animation. + * @returns {isFlipped: boolean, flip: () => void, scope: ReactRef} - An object containing the flip state, the flip function, and the scope of the animation. + */ +export function useFlip(target: gsap.DOMTarget, options: Flip.FlipStateVars & Flip.FromToVars & { revertOnUpdate?: boolean }): { isFlipped: boolean, flip: () => void, scope: ReactRef }; \ No newline at end of file