Skip to content

Commit

Permalink
main 🧊 use scroll rework
Browse files Browse the repository at this point in the history
  • Loading branch information
debabin committed Feb 8, 2025
1 parent 918452d commit a96aff0
Show file tree
Hide file tree
Showing 5 changed files with 286 additions and 330 deletions.
88 changes: 46 additions & 42 deletions src/hooks/useHover/useHover.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
import type { RefObject } from 'react';

import { useRef, useState } from 'react';
import { useEffect, useRef, useState } from 'react';

import { useEventListener } from '../useEventListener/useEventListener';
import { getElement, isTarget } from '@/utils/helpers';

//* The use hover target type */
export type UseHoverTarget = string | Element | RefObject<Element | null | undefined>;

//* The use hover options type */
export interface UseHoverOptions {
//* The on entry callback */
onEntry?: () => void;
onEntry?: (event: Event) => void;
//* The on leave callback */
onLeave?: () => void;
onLeave?: (event: Event) => void;
}

//* The use hover target type */
export type UseHoverTarget = Element | RefObject<Element | null | undefined>;

export interface UseHover {
<Target extends UseHoverTarget>(target: Target, callback?: () => void): boolean;
<Target extends UseHoverTarget>(target: Target, callback?: (event: Event) => void): boolean;

<Target extends UseHoverTarget>(target: Target, options?: UseHoverOptions): boolean;

<Target extends UseHoverTarget>(
callback?: () => void,
callback?: (event: Event) => void,
target?: never
): [RefObject<Target>, boolean];
): [(node: Target) => void, boolean];

<Target extends UseHoverTarget>(
options?: UseHoverOptions,
target?: never
): [RefObject<Target>, boolean];
): [(node: Target) => void, boolean];
}

/**
Expand All @@ -39,7 +39,7 @@ export interface UseHover {
* @overload
* @template Target The target element
* @param {Target} target The target element to be hovered
* @param {() => void} [callback] The callback function to be invoked on mouse enter
* @param {(event: Event) => void} [callback] The callback function to be invoked on mouse enter
* @returns {boolean} The state of the hover
*
* @example
Expand All @@ -48,42 +48,32 @@ export interface UseHover {
* @overload
* @template Target The target element
* @param {Target} target The target element to be hovered
* @param {() => void} [options.onEntry] The callback function to be invoked on mouse enter
* @param {() => void} [options.onLeave] The callback function to be invoked on mouse leave
* @param {(event: Event) => void} [options.onEntry] The callback function to be invoked on mouse enter
* @param {(event: Event) => void} [options.onLeave] The callback function to be invoked on mouse leave
* @returns {boolean} The state of the hover
*
* @example
* const hovering = useHover(ref, {
* onEntry: () => console.log('onEntry'),
* onLeave: () => console.log('onLeave'),
* });
* const hovering = useHover(ref, options);
*
* @overload
* @template Target The target element
* @param {() => void} [callback] The callback function to be invoked on mouse enter
* @param {(event: Event) => void} [callback] The callback function to be invoked on mouse enter
* @returns {UseHoverReturn<Target>} The state of the hover
*
* @example
* const [ref, hovering] = useHover(() => console.log('callback'));
*
* @overload
* @template Target The target element
* @param {() => void} [options.onEntry] The callback function to be invoked on mouse enter
* @param {() => void} [options.onLeave] The callback function to be invoked on mouse leave
* @param {(event: Event) => void} [options.onEntry] The callback function to be invoked on mouse enter
* @param {(event: Event) => void} [options.onLeave] The callback function to be invoked on mouse leave
* @returns {UseHoverReturn<Target>} The state of the hover
*
* @example
* const [ref, hovering] = useHover({
* onEntry: () => console.log('onEntry'),
* onLeave: () => console.log('onLeave'),
* });
* const [ref, hovering] = useHover(options);
*/
export const useHover = ((...params: any[]) => {
const target = (
params[0] instanceof Function || !('current' in params[0] || params[0] instanceof Element)
? undefined
: params[0]
) as UseHoverTarget | undefined;
const target = (isTarget(params[0]) ? params[0] : undefined) as UseHoverTarget | undefined;

const options = (
target
Expand All @@ -96,21 +86,35 @@ export const useHover = ((...params: any[]) => {
) as UseHoverOptions | undefined;

const [hovering, setHovering] = useState(false);
const internalRef = useRef<Element>();
const [internalRef, setInternalRef] = useState<Element>();
const internalOptionsRef = useRef(options);
internalOptionsRef.current = options;

useEffect(() => {
if (!target && !internalRef) return;
const element = (target ? getElement(target) : internalRef) as Element;

if (!element) return;

const onMouseEnter = (event: Event) => {
internalOptionsRef.current?.onEntry?.(event);
setHovering(true);
};

const onMouseEnter = () => {
options?.onEntry?.();
setHovering(true);
};
const onMouseLeave = (event: Event) => {
internalOptionsRef.current?.onLeave?.(event);
setHovering(false);
};

const onMouseLeave = () => {
options?.onLeave?.();
setHovering(false);
};
element.addEventListener('mouseenter', onMouseEnter);
element.addEventListener('mouseleave', onMouseLeave);

useEventListener(target ?? internalRef, 'mouseenter', onMouseEnter);
useEventListener(target ?? internalRef, 'mouseleave', onMouseLeave);
return () => {
element.removeEventListener('mouseenter', onMouseEnter);
element.removeEventListener('mouseleave', onMouseLeave);
};
}, [target, internalRef]);

if (target) return hovering;
return [internalRef, hovering] as const;
return [setInternalRef, hovering] as const;
}) as UseHover;
62 changes: 31 additions & 31 deletions src/hooks/useParallax/useParallax.demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,40 @@ import type { CSSProperties } from 'react';

import { useParallax } from './useParallax';

const layerBase: CSSProperties = {
position: 'absolute',
height: '100%',
width: '100%',
transition: '.3s ease-out all'
};

const containerStyle: CSSProperties = {
margin: '3em auto',
perspective: '200px'
};

const cardContentStyle: CSSProperties = {
overflow: 'hidden',
fontSize: '6rem',
position: 'absolute',
top: 'calc(50% - 1em)',
left: 'calc(50% - 1em)',
height: '2em',
width: '2em',
margin: 'auto'
};

const targetStyle: CSSProperties = {
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
minHeight: '500px',
transition: '.3s ease-out all'
};

const Demo = () => {
const parallax = useParallax<HTMLDivElement>();

const layerBase: CSSProperties = {
position: 'absolute',
height: '100%',
width: '100%',
transition: '.3s ease-out all'
};

const layer0 = {
...layerBase,
transform: `translateX(${parallax.value.tilt * 10}px) translateY(${parallax.value.roll * 10}px)`
Expand Down Expand Up @@ -44,30 +68,6 @@ const Demo = () => {
transform: `rotateX(${parallax.value.roll * 20}deg) rotateY(${parallax.value.tilt * 20}deg)`
};

const containerStyle: CSSProperties = {
margin: '3em auto',
perspective: '200px'
};

const cardContentStyle: CSSProperties = {
overflow: 'hidden',
fontSize: '6rem',
position: 'absolute',
top: 'calc(50% - 1em)',
left: 'calc(50% - 1em)',
height: '2em',
width: '2em',
margin: 'auto'
};

const targetStyle: CSSProperties = {
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
minHeight: '500px',
transition: '.3s ease-out all'
};

return (
<div ref={parallax.ref} style={targetStyle}>
<pre lang='json'>
Expand Down
Loading

0 comments on commit a96aff0

Please sign in to comment.