reacthub-pixitweenjs 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/.storybook/{main.js → main.ts} +9 -1
  2. package/.storybook/{preview.jsx → preview.tsx} +3 -7
  3. package/CHANGELOG.md +26 -0
  4. package/README.md +21 -2
  5. package/build-storybook.log +491 -0
  6. package/index.html +1 -1
  7. package/package.json +54 -19
  8. package/src/App.tsx +29 -0
  9. package/src/{stories/PixiImageSprite/PixiImageSprite.stories.js → components/PixiImageSprite/PixiImageSprite.stories.tsx} +25 -8
  10. package/src/components/PixiImageSprite/PixiImageSprite.tsx +346 -0
  11. package/src/components/PixiImageSprite/index.ts +1 -0
  12. package/src/hooks/usePixiTransformer.tsx +168 -0
  13. package/src/index.ts +3 -0
  14. package/src/providers/{GsapPixieContextProvider.jsx → GsapPixieContextProvider.tsx} +27 -24
  15. package/src/providers/index.ts +1 -0
  16. package/src/types/transformation.tsx +13 -0
  17. package/src/utils/AppStateProvider.tsx +70 -0
  18. package/src/utils/AppStateStage.tsx +27 -0
  19. package/src/utils/AppWrapper.tsx +43 -0
  20. package/src/utils/ContextBridge.tsx +25 -0
  21. package/src/{providers/GsapAnim.jsx → utils/GsapAnim.tsx} +35 -4
  22. package/src/utils/PixiStage.tsx +33 -0
  23. package/src/utils/VideoSeekBar.tsx +118 -0
  24. package/src/utils/useDeepEffect.tsx +23 -0
  25. package/src/vite-env.d.ts +1 -0
  26. package/tsconfig.json +37 -0
  27. package/tsconfig.node.json +10 -0
  28. package/{vite.config.js → vite.config.ts} +4 -2
  29. package/vitest.config.ts +11 -0
  30. package/src/App.jsx +0 -35
  31. package/src/index.js +0 -3
  32. package/src/providers/ContextBridge.jsx +0 -67
  33. package/src/providers/VideoSeekBar.jsx +0 -186
  34. package/src/providers/index.js +0 -3
  35. package/src/stories/PixiImageSprite/PixiImageSprite.jsx +0 -318
  36. package/src/stories/PixiImageSprite/index.js +0 -1
  37. /package/src/{main.jsx → main.tsx} +0 -0
@@ -0,0 +1,168 @@
1
+ import { Transformer } from "@pixi-essentials/transformer";
2
+ import * as PIXI from "pixi.js";
3
+ import { useApp } from "@pixi/react";
4
+ import * as React from "react";
5
+ import { useEffect, useRef, useCallback } from "react";
6
+ import { Transformation } from "../types/transformation";
7
+ // @ts-ignore
8
+ import has from "lodash/has";
9
+
10
+ const colorYellow = PIXI.utils.string2hex("#F3C409");
11
+ const anchorFill = PIXI.utils.string2hex("#F3C409");
12
+
13
+ interface Events {
14
+ dblclick?: (e: any) => void;
15
+ }
16
+
17
+ interface Props {
18
+ uniqueId?: string;
19
+ onTransformEnd?: (data: {
20
+ uniqueId: string;
21
+ transformation: Transformation;
22
+ }) => void;
23
+ events?: Events;
24
+ mouseoverEvent?: (flag: boolean) => void;
25
+ setDragging?: (flag: boolean) => void;
26
+ }
27
+
28
+ export const usePixiTransformer = ({
29
+ uniqueId = "",
30
+ onTransformEnd,
31
+ events = {},
32
+ mouseoverEvent = (f) => f,
33
+ setDragging = (f) => f,
34
+ }: Props) => {
35
+ const [isDragging, setIsDragging] = React.useState(false);
36
+ const PixiTransformer = React.useRef<PIXI.Container>(null);
37
+ const mounted = React.useRef(false);
38
+ const app = useApp();
39
+ const transformerRef = useRef<Transformer>(null);
40
+ const parentRef = useRef<PIXI.Container>(null);
41
+
42
+ useEffect(() => {
43
+ mounted.current = true;
44
+ return () => {
45
+ mounted.current = false;
46
+ if (transformerRef.current) {
47
+ parentRef.current?.removeChild(transformerRef.current);
48
+ }
49
+ };
50
+ }, []);
51
+
52
+ const transformChange = useCallback(() => {
53
+ setIsDragging(true);
54
+ setDragging(true);
55
+ }, []);
56
+
57
+ const transformCommit = useCallback(() => {
58
+ if (PixiTransformer.current === null) return;
59
+ const x = PixiTransformer.current.x;
60
+ const y = PixiTransformer.current.y;
61
+ const width = PixiTransformer.current.width;
62
+ const height = PixiTransformer.current.height;
63
+ const rotate = PixiTransformer.current.rotation;
64
+ const scale: [number, number] = [
65
+ PixiTransformer.current.scale.x,
66
+ PixiTransformer.current.scale.y,
67
+ ];
68
+ if (onTransformEnd) {
69
+ onTransformEnd({
70
+ uniqueId,
71
+ transformation: {
72
+ x: Math.round(x),
73
+ y: Math.round(y),
74
+ width: Math.round(Math.max(5, width)),
75
+ height: Math.round(Math.max(5, height)),
76
+ rotate: Math.round(rotate),
77
+ scale: scale,
78
+ },
79
+ });
80
+ }
81
+ setIsDragging(false);
82
+ setDragging(false);
83
+ }, []);
84
+
85
+ const handleOnDblClick = useCallback((e: any) => {
86
+ if (events && has(events, "dblclick") && events.dblclick) {
87
+ events.dblclick(e);
88
+ }
89
+ }, []);
90
+
91
+ const handleOnMouseOver = useCallback(
92
+ (msevent: any) => {
93
+ if (mouseoverEvent) {
94
+ mouseoverEvent(true);
95
+ }
96
+ },
97
+ [mouseoverEvent],
98
+ );
99
+
100
+ const handleOnMouseOut = useCallback(
101
+ (msevent: any) => {
102
+ if (mouseoverEvent) {
103
+ mouseoverEvent(false);
104
+ }
105
+ },
106
+ [mouseoverEvent],
107
+ );
108
+
109
+ const handleTransformer = useCallback(
110
+ (node: PIXI.Container, parentNode: PIXI.Container) => {
111
+ const isEnabled = true;
112
+ // @ts-ignore
113
+ PixiTransformer.current = node;
114
+ const transformer = new Transformer({
115
+ rotateEnabled: isEnabled,
116
+ boxRotationEnabled: isEnabled,
117
+ boxScalingEnabled: isEnabled,
118
+ scaleEnabled: isEnabled,
119
+ group: [node],
120
+ wireframeStyle: {
121
+ thickness: 2,
122
+ color: anchorFill,
123
+ },
124
+ lockAspectRatio: true,
125
+ handleStyle: {
126
+ // color: colorYellow,
127
+ outlineColor: colorYellow,
128
+ shape: "circle",
129
+ radius: 10,
130
+ },
131
+ });
132
+
133
+ // transformer.addListener("transformchange", transformChange);
134
+ // @ts-ignore
135
+ transformer.addListener("transformcommit", transformCommit);
136
+ // transformer.addListener("dblclick", (evt) => {
137
+ // handleOnDblClick(evt);
138
+ // });
139
+ // transformer.on("mouseover", () => {
140
+ // handleOnMouseOver(true);
141
+ // });
142
+ // transformer.on("mouseout", () => {
143
+ // handleOnMouseOut(false);
144
+ // });
145
+ // @ts-ignore
146
+ transformerRef.current = transformer;
147
+ // @ts-ignore
148
+ parentRef.current = parentNode;
149
+ parentRef.current.addChild(transformerRef.current);
150
+ },
151
+ [
152
+ transformCommit,
153
+ transformChange,
154
+ handleOnMouseOver,
155
+ handleOnMouseOut,
156
+ handleOnDblClick,
157
+ ],
158
+ );
159
+
160
+ const removeTransformer = useCallback(() => {
161
+ if (transformerRef.current) {
162
+ // @ts-ignore
163
+ parentRef.current.removeChild(transformerRef.current);
164
+ }
165
+ }, []);
166
+
167
+ return { handleTransformer, removeTransformer, isDragging };
168
+ };
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./components/PixiImageSprite";
2
+ // Path: src\index.js
3
+ export * from "./providers";
@@ -1,4 +1,5 @@
1
- import React, {
1
+ import * as React from "react";
2
+ import {
2
3
  useState,
3
4
  useEffect,
4
5
  useCallback,
@@ -7,10 +8,22 @@ import React, {
7
8
  } from "react";
8
9
  import gsap from "gsap";
9
10
  import { emitCustomEvent } from "react-custom-events";
10
- // import { Power4 } from "gsap";
11
+
12
+ interface GsapPixieContextProps {
13
+ gsapCtx: React.MutableRefObject<any>;
14
+ tl: React.MutableRefObject<any>;
15
+ handlePlay: () => void;
16
+ handlePause: () => void;
17
+ handleReset: () => void;
18
+ handleSeek: () => void;
19
+ handleRestart: () => void;
20
+ handleRepeat: () => void;
21
+ playerTimeRef: React.MutableRefObject<number>;
22
+ }
11
23
 
12
24
  // Context has been created
13
- const GsapPixieContext = React.createContext({});
25
+ // @ts-ignore
26
+ const GsapPixieContext = React.createContext<GsapPixieContextProps>({});
14
27
 
15
28
  const Events = {
16
29
  STOP: "GSAP_STOP",
@@ -18,21 +31,19 @@ const Events = {
18
31
  PLAY: "GSAP_PLAY",
19
32
  RESUME: "GSAP_RESUME",
20
33
  };
21
- // Provider
22
- const GsapPixieContextProvider = ({ children }) => {
23
- const [play, setPlay] = useState(false);
24
- const [frameNumber, setFrameNumber] = useState(0);
25
34
 
26
- const tl = useRef();
27
- const gsapCtx = useRef();
28
- const playerTimeRef = useRef(0.001);
35
+ // Provider
36
+ const GsapPixieContextProvider: React.FC<{ children: React.ReactNode }> = ({
37
+ children,
38
+ }) => {
39
+ const [play, setPlay] = useState<boolean>(false);
40
+ const [frameNumber, setFrameNumber] = useState<number>(0);
29
41
 
30
- // gsap.ticker.add((time, deltaTime, frame) => {
31
- // console.log("timeframe", time, deltaTime, frame);
32
- // setFrameNumber(frame);
33
- // });
42
+ const tl = useRef<any>();
43
+ const gsapCtx = useRef<any>();
44
+ const playerTimeRef = useRef<number>(0.001);
34
45
 
35
- const parentElementRef = useRef();
46
+ const parentElementRef = useRef<any>();
36
47
  // передаем предка анимируемых элементов
37
48
  const q = gsap.utils.selector(parentElementRef);
38
49
 
@@ -62,14 +73,6 @@ const GsapPixieContextProvider = ({ children }) => {
62
73
  playerTimeRef.current = now;
63
74
  }, []);
64
75
 
65
- // const timeline = React.useCallback(
66
- // gsap.timeline({
67
- // paused: true,
68
- // defaults: { duration: 0, onUpdate: onUpdate },
69
- // }),
70
- // [],
71
- // );
72
-
73
76
  useEffect(() => {
74
77
  const timeline = tl.current;
75
78
  timeline
@@ -93,7 +96,7 @@ const GsapPixieContextProvider = ({ children }) => {
93
96
  }
94
97
  }, [play]);
95
98
 
96
- const addTotalDuration = (duration) => {
99
+ const addTotalDuration = (duration: number) => {
97
100
  const timeline = tl.current;
98
101
  timeline.totalDuration(duration);
99
102
  };
@@ -0,0 +1 @@
1
+ export * from "./GsapPixieContextProvider";
@@ -0,0 +1,13 @@
1
+ export interface Transformation {
2
+ x: number;
3
+ y: number;
4
+ width: number;
5
+ height: number;
6
+ rotate: number;
7
+ scale: [number, number];
8
+ }
9
+
10
+ export interface TransformationEnd {
11
+ uniqueId: string;
12
+ transformation: Transformation;
13
+ }
@@ -0,0 +1,70 @@
1
+ import * as React from "react";
2
+ import useDeepEffect from "./useDeepEffect";
3
+ import { TransformationEnd } from "../types/transformation";
4
+
5
+ interface AppState {
6
+ transformation: {
7
+ [key: string]: unknown;
8
+ };
9
+ }
10
+
11
+ interface AppStateContextProps extends AppState {
12
+ onAnchorTransformationEnd: (data: TransformationEnd) => void;
13
+ onMouseOverSprite: (data: unknown) => void;
14
+ onClickSprite: (data: unknown) => void;
15
+ }
16
+
17
+ export const AppStateContext = React.createContext<AppStateContextProps>({
18
+ transformation: {},
19
+ onAnchorTransformationEnd: () => {},
20
+ onMouseOverSprite: () => {},
21
+ onClickSprite: () => {},
22
+ });
23
+
24
+ interface AppStateContextProviderProps extends AppState {
25
+ children: React.ReactNode;
26
+ }
27
+
28
+ export const AppStateContextProvider: React.FC<
29
+ AppStateContextProviderProps
30
+ > = ({ children, ...props }) => {
31
+ const [appState, setAppState] = React.useState<AppState>(props);
32
+ useDeepEffect(() => {
33
+ setAppState(props);
34
+ }, [props]);
35
+
36
+ const onAnchorTransformationEnd = React.useCallback(
37
+ (data: {
38
+ uniqueId: string;
39
+ transformation: { [key: string]: unknown };
40
+ }) => {
41
+ const { uniqueId, transformation } = data;
42
+ const newAppState = {
43
+ ...props,
44
+ transformation: {
45
+ ...props.transformation,
46
+ ...transformation,
47
+ },
48
+ };
49
+ setAppState(newAppState);
50
+ },
51
+ [props],
52
+ );
53
+
54
+ const onMouseOverSprite = React.useCallback((data: unknown) => {}, []);
55
+
56
+ const onClickSprite = React.useCallback((data: unknown) => {}, []);
57
+
58
+ return (
59
+ <AppStateContext.Provider
60
+ value={{
61
+ ...appState,
62
+ onAnchorTransformationEnd,
63
+ onMouseOverSprite,
64
+ onClickSprite,
65
+ }}
66
+ >
67
+ {children}
68
+ </AppStateContext.Provider>
69
+ );
70
+ };
@@ -0,0 +1,27 @@
1
+ import * as React from "react";
2
+ import ContextBridge from "./ContextBridge";
3
+ import { AppStateContext } from "../providers/AppStateProvider";
4
+ import { Container as PixiContainer } from "@pixi/react";
5
+
6
+ interface AppStateStageProps {
7
+ children: React.ReactNode;
8
+ }
9
+
10
+ const AppStateStage: React.FC<AppStateStageProps> = ({
11
+ children,
12
+ ...props
13
+ }) => {
14
+ return (
15
+ <ContextBridge
16
+ Context={AppStateContext}
17
+ render={(children: React.ReactNode) => (
18
+ // @ts-ignore
19
+ <PixiContainer {...props}>{children}</PixiContainer>
20
+ )}
21
+ >
22
+ {children}
23
+ </ContextBridge>
24
+ );
25
+ };
26
+
27
+ export default AppStateStage;
@@ -0,0 +1,43 @@
1
+ import * as React from "react";
2
+ import { Flex, Box } from "@chakra-ui/react";
3
+ import * as PIXI from "pixi.js";
4
+ import { ChakraProvider } from "@chakra-ui/react";
5
+ import { GsapPixieContextProvider } from "../providers/GsapPixieContextProvider";
6
+ import { VideoSeekBar } from "./VideoSeekBar";
7
+ import Stage from "./PixiStage";
8
+
9
+ interface AppProps {
10
+ children: React.ReactNode;
11
+ backgroundColor?: string;
12
+ }
13
+
14
+ export const AppWrapper: React.FC<AppProps> = ({
15
+ children,
16
+ backgroundColor,
17
+ }) => {
18
+ const backgroundColorx = PIXI.utils.string2hex(backgroundColor || "#2D2E3C");
19
+ const width = 600;
20
+ const height = 500;
21
+ /// color: 0x1099bb,
22
+ return (
23
+ <div className="App">
24
+ <GsapPixieContextProvider>
25
+ <ChakraProvider>
26
+ <Flex flexDir={"column"}>
27
+ <Stage
28
+ width={width}
29
+ height={height}
30
+ // options={{ background: backgroundColor }}
31
+ options={{ backgroundColor: backgroundColorx, resolution: 2 }}
32
+ >
33
+ {children}
34
+ </Stage>
35
+ <Box mt={5} w={600}>
36
+ <VideoSeekBar />
37
+ </Box>
38
+ </Flex>
39
+ </ChakraProvider>
40
+ </GsapPixieContextProvider>
41
+ </div>
42
+ );
43
+ };
@@ -0,0 +1,25 @@
1
+ // your Stage:
2
+ // the context bridge:
3
+ import * as React from "react";
4
+
5
+ const ContextBridge = <T,>({
6
+ children,
7
+ Context,
8
+ render,
9
+ }: {
10
+ children: React.ReactNode;
11
+ Context: React.Context<T>;
12
+ render: (children: React.ReactNode) => JSX.Element;
13
+ }) => {
14
+ return (
15
+ <Context.Consumer>
16
+ {(value) => {
17
+ return render(
18
+ <Context.Provider value={value}>{children}</Context.Provider>,
19
+ );
20
+ }}
21
+ </Context.Consumer>
22
+ );
23
+ };
24
+
25
+ export default ContextBridge;
@@ -1,8 +1,39 @@
1
- /// import { Elastic } from "gsap";
2
- // import BezierEasing from 'bezier-easing';
1
+ interface Ease {
2
+ from?: {
3
+ duration: number;
4
+ ease: string;
5
+ y?: number;
6
+ x?: number;
7
+ };
8
+ fromTo?: {
9
+ from: {
10
+ y?: number;
11
+ x?: number;
12
+ alpha?: number;
13
+ };
14
+ to: {
15
+ ease: string;
16
+ y?: number;
17
+ x?: number;
18
+ alpha?: number;
19
+ duration: number;
20
+ };
21
+ };
22
+ to?: {
23
+ ease: string;
24
+ alpha: number;
25
+ duration: number;
26
+ };
27
+ }
3
28
 
4
- export const getAnimByName = (animate, stageWidth, stageHeight, x, y) => {
5
- let ease = {};
29
+ export const getAnimByName = (
30
+ animate: string,
31
+ stageWidth?: number,
32
+ stageHeight?: number,
33
+ x?: number,
34
+ y?: number,
35
+ ): Ease => {
36
+ let ease: Ease = {};
6
37
  switch (animate) {
7
38
  case "NONE":
8
39
  case "none":
@@ -0,0 +1,33 @@
1
+ import * as React from "react";
2
+ import ContextBridge from "./ContextBridge";
3
+ import { Stage as PixiStage } from "@pixi/react";
4
+ import { AppStateContextProvider } from "./AppStateProvider";
5
+ import { GsapPixieContext } from "../providers/GsapPixieContextProvider";
6
+
7
+ interface StageProps {
8
+ children: React.ReactNode;
9
+ width: number;
10
+ height: number;
11
+ options: any;
12
+ }
13
+
14
+ const Stage: React.FC<StageProps> = ({ children, ...props }) => {
15
+ return (
16
+ <ContextBridge
17
+ Context={GsapPixieContext}
18
+ render={(children: React.ReactNode | React.ReactElement) => (
19
+ <PixiStage {...props}>
20
+ {/* @ts-ignore */}
21
+ <AppStateContextProvider {...props}>
22
+ {/* @ts-ignore */}
23
+ {children}
24
+ </AppStateContextProvider>
25
+ </PixiStage>
26
+ )}
27
+ >
28
+ {children}
29
+ </ContextBridge>
30
+ );
31
+ };
32
+
33
+ export default Stage;
@@ -0,0 +1,118 @@
1
+ import * as React from "react";
2
+ import {
3
+ Slider,
4
+ SliderTrack,
5
+ SliderFilledTrack,
6
+ SliderThumb,
7
+ SliderMark,
8
+ Flex,
9
+ Icon,
10
+ Text,
11
+ Box,
12
+ Button,
13
+ } from "@chakra-ui/react";
14
+ import { GsapPixieContext } from "../providers/GsapPixieContextProvider";
15
+
16
+ import { gsap } from "gsap";
17
+ import { Draggable } from "gsap/Draggable";
18
+
19
+ gsap.registerPlugin(Draggable);
20
+
21
+ export const VideoSeekBar: React.FC = () => {
22
+ const rafRef = React.useRef<number>();
23
+ const sliderRef = React.useRef<HTMLDivElement>(null);
24
+ const sliderThumbRef = React.useRef<HTMLDivElement>(null);
25
+ const sliderProgressRef = React.useRef<HTMLDivElement>(null);
26
+ const {
27
+ tl: timeline,
28
+ playerTimeRef,
29
+ handlePlay,
30
+ handlePause,
31
+ handleReset,
32
+ handleSeek,
33
+ handleRestart,
34
+ handleRepeat,
35
+ } = React.useContext(GsapPixieContext);
36
+
37
+ /// state
38
+ const [time, setTime] = React.useState<number>(0);
39
+ const [duration, setDuration] = React.useState<number>(0);
40
+
41
+ const totalVideoSeconds = 20;
42
+
43
+ const animate = (time: number) => {
44
+ /// console.log("totalDuration", timeline.current.totalDuration());
45
+ setDuration(timeline.current.totalDuration());
46
+ if (playerTimeRef && playerTimeRef.current) {
47
+ setTime(playerTimeRef.current);
48
+ }
49
+ rafRef.current = requestAnimationFrame(animate);
50
+ };
51
+
52
+ React.useEffect(() => {
53
+ rafRef.current = requestAnimationFrame(animate);
54
+ return () => cancelAnimationFrame(rafRef.current!);
55
+ }, []); // Make sure the effect runs only once
56
+
57
+ const handleOnSliderChange = (value: number) => {
58
+ const nTotSeconds =
59
+ totalVideoSeconds && totalVideoSeconds > 0 ? totalVideoSeconds : 1;
60
+ const progress = value / nTotSeconds;
61
+ console.log("progressbar", value, progress);
62
+ playerTimeRef.current = value;
63
+ // @ts-ignore
64
+ timeline && timeline.progress(progress);
65
+ gsap.set(sliderProgressRef.current!, {
66
+ scaleX: progress,
67
+ });
68
+ };
69
+ const handleOnSliderChangeStart = (value: number) => {};
70
+ const handleOnSliderChangeEnd = (value: number) => {};
71
+
72
+ /** Create Draggable for the Gsap Player */
73
+
74
+ const updateDrag = React.useCallback(
75
+ (x: number, maxX: number) => {
76
+ console.log("updatgeDrag running", x, maxX);
77
+ const currentTime = (x / maxX) * totalVideoSeconds;
78
+ const prog = x / sliderRef.current!.offsetWidth;
79
+ console.log("drage update", currentTime, prog);
80
+ },
81
+ [totalVideoSeconds],
82
+ );
83
+
84
+ return (
85
+ <Flex flexDir={"column"}>
86
+ <Box>
87
+ <Button onClick={handleRestart}>Play</Button>
88
+ <Button onClick={handlePause}>Pause</Button>
89
+ <Button onClick={handlePlay}>Resume</Button>
90
+ <Button onClick={handleSeek}>Seek (4:00)</Button>
91
+ </Box>
92
+ <Flex px={1} mt={2}>
93
+ <Slider
94
+ aria-label="slider-ex-1"
95
+ data-testid="sliderthumb"
96
+ value={playerTimeRef.current || 0}
97
+ min={0}
98
+ max={duration}
99
+ step={0.1}
100
+ onChange={handleOnSliderChange}
101
+ onChangeStart={handleOnSliderChangeStart}
102
+ onChangeEnd={handleOnSliderChangeEnd}
103
+ >
104
+ <SliderTrack>
105
+ <SliderFilledTrack />
106
+ </SliderTrack>
107
+ <SliderThumb data-testid="sliderthumb" ref={sliderThumbRef} />
108
+ </Slider>
109
+ </Flex>
110
+ <Flex flexDir={"row"}>
111
+ <Text>
112
+ Gsap-CurrentTime: {Math.round((time + Number.EPSILON) * 100) / 100} /{" "}
113
+ {duration}
114
+ </Text>
115
+ </Flex>
116
+ </Flex>
117
+ );
118
+ };
@@ -0,0 +1,23 @@
1
+ import { useEffect, useRef } from "react";
2
+ // @ts-ignore
3
+ import isEqual from "react-fast-compare";
4
+
5
+ type EffectFunc = () => void;
6
+ type Deps = ReadonlyArray<unknown>;
7
+
8
+ const useDeepEffect = (effectFunc: EffectFunc, deps: Deps) => {
9
+ const isFirst = useRef(true);
10
+ const prevDeps = useRef(deps);
11
+ useEffect(() => {
12
+ const isSame = prevDeps.current.every((obj, index) =>
13
+ isEqual(obj, deps[index]),
14
+ );
15
+ if (isFirst.current || !isSame) {
16
+ effectFunc();
17
+ }
18
+ isFirst.current = false;
19
+ prevDeps.current = deps;
20
+ }, deps);
21
+ };
22
+
23
+ export default useDeepEffect;
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />