Skip to content

Commit 2295875

Browse files
committed
feat: add sizzy hooks
1 parent ec1f169 commit 2295875

File tree

1 file changed

+177
-0
lines changed

1 file changed

+177
-0
lines changed

src/useSizzyHooks.ts

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import { useState, useEffect, useCallback, RefObject } from 'react';
2+
3+
export const useCanHover = () => {
4+
// assume that if device is smaller than 500 there's no hover, but actually check it on the first touch event
5+
const [canHover, setCanHover] = useState(window.innerWidth > 500);
6+
useEffect(() => {
7+
// mobile devices also emit a "mousemove" on every touch (#theplatform<3), but desktop devices don't emit "touchstart"
8+
const eventName = 'touchstart';
9+
window.addEventListener(
10+
eventName,
11+
function onFirstTouch() {
12+
setCanHover(false);
13+
window.removeEventListener(eventName, onFirstTouch, false);
14+
},
15+
false,
16+
);
17+
}, []);
18+
return canHover;
19+
};
20+
21+
export const useHovered = () => {
22+
const [hovering, setHovering] = useState(false);
23+
const canHover = useCanHover();
24+
25+
return {
26+
value: hovering,
27+
setValue: setHovering,
28+
bind: canHover
29+
? {
30+
onMouseOver: () => setHovering(true),
31+
onMouseLeave: () => setHovering(false),
32+
}
33+
: {
34+
onClick: () => setHovering(h => !h),
35+
},
36+
};
37+
};
38+
39+
export const useToggleBodyClass = (bool: boolean, [on, off]: [string, string]) => {
40+
useEffect(() => {
41+
document.body.classList.remove(bool ? off : on);
42+
document.body.classList.add(bool ? on : off);
43+
});
44+
};
45+
46+
export const usePose = (initial: string, poses: object = {}) => {
47+
const [pose, setPose] = useState(initial);
48+
return { pose, setPose, poses };
49+
};
50+
51+
export const useVisiblePose = (initial: any) => {
52+
const VISIBLE = 'visible';
53+
const HIDDEN = 'hidden';
54+
const { setPose, pose, ...rest } = usePose(initial ? VISIBLE : HIDDEN, [HIDDEN, VISIBLE]);
55+
return [pose, (v: any) => setPose(v ? VISIBLE : HIDDEN), rest];
56+
};
57+
58+
function gtag(...args: any[]) {
59+
// @ts-ignore
60+
window.dataLayer.push(args);
61+
}
62+
63+
export const useGoogleAnalytics = (id: string, startLoading: boolean) => {
64+
useEffect(() => {
65+
if (!id) {
66+
return;
67+
}
68+
69+
if (startLoading) {
70+
const script = document.createElement('script');
71+
script.type = 'text/javascript';
72+
script.src = `https://www.googletagmanager.com/gtag/js?id=${id}`;
73+
document.body.appendChild(script);
74+
// @ts-ignore
75+
window.dataLayer = window.dataLayer || [];
76+
77+
gtag('js', new Date());
78+
gtag('config', id, {
79+
anonymize_ip: true,
80+
cookie_expires: 0,
81+
});
82+
}
83+
}, [id, startLoading]);
84+
};
85+
86+
export const useFindElementCenter = (elementRef: RefObject<HTMLElement>) => {
87+
const [windowSize, setWindowSize] = useState();
88+
useEffect(() => {
89+
if (elementRef.current) {
90+
const { offsetTop, offsetLeft, offsetWidth, offsetHeight } = elementRef.current;
91+
setWindowSize({
92+
x: window.innerWidth / 2 - offsetWidth / 2 - offsetLeft,
93+
y: window.innerHeight / 2 - offsetHeight / 2 - offsetTop,
94+
});
95+
}
96+
}, [elementRef]);
97+
return windowSize;
98+
};
99+
100+
export const useMousePosition = (shouldTrack: boolean) => {
101+
const [mousePosition, setMousePosition] = useState<{ x: number; y: number }>({ x: 0, y: 0 });
102+
const canHover = useCanHover();
103+
104+
useEffect(() => {
105+
if (canHover && shouldTrack) {
106+
const handler = ({ clientX, clientY }: { clientX: number; clientY: number }) => {
107+
setMousePosition({
108+
x: clientX,
109+
y: clientY,
110+
});
111+
};
112+
window.document.addEventListener('mousemove', handler);
113+
return () => window.document.removeEventListener('mousemove', handler);
114+
}
115+
return () => {};
116+
}, [canHover, shouldTrack]);
117+
118+
return canHover ? mousePosition : {};
119+
};
120+
121+
export type TimeFormattingFunction = (date: Date) => string;
122+
123+
export const useClock = (
124+
timeFormattingFunction: TimeFormattingFunction = date => date.toLocaleDateString(),
125+
) => {
126+
const getCurrentTime = useCallback(() => timeFormattingFunction(new Date()), [timeFormattingFunction]);
127+
const [time, setTime] = useState(getCurrentTime());
128+
129+
useEffect(() => {
130+
const t = setInterval(() => setTime(getCurrentTime()), 1000);
131+
132+
return () => clearInterval(t);
133+
}, [getCurrentTime]);
134+
135+
return time;
136+
};
137+
138+
export const useOnPageLoad = () => {
139+
const [loaded, setLoaded] = useState(false);
140+
useEffect(() => {
141+
window.onload = () => setLoaded(true);
142+
}, []);
143+
return loaded;
144+
};
145+
146+
type UseScript = { startLoading: boolean; src: string };
147+
148+
export const useLoadScript = ({ startLoading, src }: UseScript) => {
149+
const [state, setState] = useState<{ ready: boolean; error: string | null | Event }>({
150+
ready: false,
151+
error: null,
152+
});
153+
154+
useEffect(() => {
155+
if (startLoading) {
156+
const script = document.createElement('script');
157+
script.src = src;
158+
script.onload = () => setState({ ready: true, error: null });
159+
script.onerror = error => setState({ ready: false, error });
160+
document.body.appendChild(script);
161+
}
162+
}, [src, startLoading]);
163+
164+
return state;
165+
};
166+
167+
export const useScript = (props: UseScript) => useLoadScript(props);
168+
169+
export const useDelay = (ms: number) => {
170+
const [done, setDone] = useState(false);
171+
useEffect(() => {
172+
setTimeout(() => {
173+
setDone(true);
174+
}, ms);
175+
}, [ms]);
176+
return done;
177+
};

0 commit comments

Comments
 (0)