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 53c6251 commit 918452d
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 184 deletions.
2 changes: 1 addition & 1 deletion src/hooks/useEventListener/useEventListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export const useEventListener = ((...params: any[]) => {
const event = (target ? params[1] : params[0]) as string | string[];
const events = Array.isArray(event) ? event : [event];
const listener = (target ? params[2] : params[1]) as (...arg: any[]) => undefined | void;
const options: UseEventListenerOptions | undefined = target ? params[3] : params[2];
const options = (target ? params[3] : params[2]) as UseEventListenerOptions | undefined;

const [internalRef, setInternalRef] = useState<Element>();
const internalListener = useEvent(listener);
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useField/useField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ export interface UseFieldReturn<Value> {
* const { register, getValue, setValue, reset, dirty, error, setError, clearError, touched, focus, watch } = useField();
*/
export const useField = <
Value extends boolean | string = string,
Type = Value extends string ? string : boolean
Value extends boolean | number | string = string,
Type = Value extends string ? string : Value extends boolean ? boolean : number
>(
params?: UseFieldParams<Value>
): UseFieldReturn<Type> => {
Expand Down
11 changes: 7 additions & 4 deletions src/hooks/useFocus/useFocus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ import type { RefObject } from 'react';

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

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

/** The use focus target type */
export type UseFocusTarget = (() => Element) | Element | RefObject<Element | null | undefined>;
export type UseFocusTarget =
| (() => Element)
| string
| Element
| RefObject<Element | null | undefined>;

/** The use focus options type */
export interface UseFocusOptions {
Expand Down Expand Up @@ -53,8 +57,7 @@ export interface UseFocus {
* const { ref, focus, blur, focused } = useFocus();
*/
export const useFocus = ((...params: any[]) => {
const target =
(params[0] && 'current' in params[0]) || params[0] instanceof Element ? params[0] : undefined;
const target = isTarget(params[0]) ? params[0] : undefined;
const options = ((target ? params[1] : params[0]) as UseFocusOptions) ?? {};
const initialValue = options.initialValue ?? false;

Expand Down
8 changes: 5 additions & 3 deletions src/hooks/useMutationObserver/useMutationObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ export const useMutationObserver = ((...params: any[]) => {
const [internalRef, setInternalRef] = useState<Element>();
const internalCallbackRef = useRef<MutationCallback>(callback);
internalCallbackRef.current = callback;
const internalOptionsRef = useRef(options);
internalOptionsRef.current = options;

useEffect(() => {
if (!enabled && !target && !internalRef) return;
Expand All @@ -102,7 +104,7 @@ export const useMutationObserver = ((...params: any[]) => {
target.forEach((target) => {
const element = getElement(target);
if (!element) return;
observer.observe(element as Element, options);
observer.observe(element as Element, internalOptionsRef.current);
});

return () => {
Expand All @@ -115,12 +117,12 @@ export const useMutationObserver = ((...params: any[]) => {

const observer = new MutationObserver(internalCallbackRef.current);
setObserver(observer);
observer.observe(element as Element, options);
observer.observe(element as Element, internalOptionsRef.current);

return () => {
observer.disconnect();
};
}, [internalRef, target, ...Object.values(options ?? {})]);
}, [internalRef, target]);

const stop = () => observer?.disconnect();

Expand Down
118 changes: 68 additions & 50 deletions src/hooks/useScroll/useScroll.demo.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import type { CSSProperties } from 'react';

import { useMemo, useRef, useState } from 'react';
import { register } from 'node:module';

import { useField } from '../useField/useField';
import { useToggle } from '../useToggle/useToggle';
import { useScroll } from './useScroll';

const styles: Record<string, CSSProperties> = {
container: {
width: '300px',
height: '300px',
margin: 'auto',
overflow: 'scroll',
backgroundColor: 'rgba(128, 128, 128, 0.05)',
borderRadius: '8px'
Expand Down Expand Up @@ -64,65 +65,82 @@ const styles: Record<string, CSSProperties> = {
};

const Demo = () => {
const elementRef = useRef<HTMLDivElement>(null);
const xInput = useField({ initialValue: 0 });
const yInput = useField({ initialValue: 0 });
const [behavior, setBehavior] = useToggle<ScrollBehavior>(['auto', 'smooth']);

const [scrollX, setScrollX] = useState(0);
const [scrollY, setScrollY] = useState(0);
const [behavior, setBehavior] = useState<ScrollBehavior>('auto');
const scrollX = xInput.watch();
const scrollY = yInput.watch();

const { x, y, isScrolling, arrivedState, directions } = useScroll(elementRef, {
const scroll = useScroll<HTMLDivElement>({
x: scrollX,
y: scrollY,
behavior
behavior,
onScroll: (event) => {
console.log('onScroll', event);
}
});

const { left, right, top, bottom } = useMemo(() => arrivedState, [arrivedState]);

const {
left: toLeft,
right: toRight,
top: toTop,
bottom: toBottom
} = useMemo(() => directions, [directions]);

return (
<div style={{ display: 'flex' }}>
<div ref={elementRef} style={styles.container}>
<div style={styles.inner}>
<div style={styles.topLeft}>TopLeft</div>
<div style={styles.bottomLeft}>BottomLeft</div>
<div style={styles.topRight}>TopRight</div>
<div style={styles.bottomRight}>BottomRight</div>
<div style={styles.center}>Scroll Me</div>
<div>
<div style={{ display: 'flex', gap: 10, flexDirection: 'column', marginBottom: 10 }}>
<div style={{ display: 'flex', gap: 10 }}>
<div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
<span>x:</span>
<input type='number'{...xInput.register()} />
</div>
<div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
<span>y:</span>
<input type='number' {...yInput.register()} />
</div>
</div>
</div>
<div style={styles.containerInfo}>
<div>
X Position:
<input value={x} onChange={(event) => setScrollX(+event.target.value)} />
<div style={{ display: 'flex', gap: 10, flexDirection: 'column' }}>
<div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
Smooth scrolling:{' '}
<input
type='checkbox'
value={behavior}
onChange={(event) => setBehavior(event.target.checked ? 'smooth' : 'auto')}
/>
</div>
<div>scrolling: <code>{String(scroll.scrolling)}</code></div>
</div>
<div>
Y Position:
<input value={y} onChange={(event) => setScrollY(+event.target.value)} />
</div>


<div style={{ display: 'flex', gap: 10, justifyContent: 'space-between' }}>
<div ref={scroll.ref} style={styles.container}>
<div style={styles.inner}>
<div style={styles.topLeft}>TopLeft</div>
<div style={styles.bottomLeft}>BottomLeft</div>
<div style={styles.topRight}>TopRight</div>
<div style={styles.bottomRight}>BottomRight</div>
<div style={styles.center}>Scroll Me</div>
</div>
</div>
<div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
Smooth scrolling:{' '}
<input
type='checkbox'
value={behavior}
onChange={(event) => setBehavior(event.target.checked ? 'smooth' : 'auto')}
/>

<div style={{ display: 'flex', gap: 10, alignItems: 'center', padding: 15 }}>
<div style={{ display: 'flex', gap: 10, flexDirection: 'column', minWidth: 150 }}>
<h4>Arrived</h4>
<div style={{ display: 'flex', gap: 15, flexDirection: 'column' }}>
<div>top: <code>{String(scroll.arrived.top)}</code></div>
<div>right: <code>{String(scroll.arrived.right)}</code></div>
<div>bottom: <code>{String(scroll.arrived.bottom)}</code></div>
<div>left: <code>{String(scroll.arrived.left)}</code></div>
</div>
</div>

<div style={{ display: 'flex', gap: 10, flexDirection: 'column' }}>
<h4>Directions</h4>
<div style={{ display: 'flex', gap: 15, flexDirection: 'column', minWidth: 150 }}>
<div>top: <code>{String(scroll.directions.top)}</code></div>
<div>right: <code>{String(scroll.directions.right)}</code></div>
<div>bottom: <code>{String(scroll.directions.bottom)}</code></div>
<div>left: <code>{String(scroll.directions.left)}</code></div>
</div>
</div>
</div>
<div>isScrolling: {JSON.stringify(isScrolling)}</div>
<div>Top Arrived: {JSON.stringify(top)}</div>
<div>Right Arrived: {JSON.stringify(right)}</div>
<div>Bottom Arrived: {JSON.stringify(bottom)}</div>
<div>Left Arrived: {JSON.stringify(left)}</div>
<div>Scrolling Up: {JSON.stringify(toTop)}</div>
<div>Scrolling Right: {JSON.stringify(toRight)}</div>
<div>Scrolling Down: {JSON.stringify(toBottom)}</div>
<div>Scrolling Left: {JSON.stringify(toLeft)}</div>
</div>
</div >
</div>
);
};
Expand Down
Loading

0 comments on commit 918452d

Please sign in to comment.