@sohanemon/utils 5.1.2 → 5.1.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.
@@ -160,3 +160,31 @@ export declare const useIsAtTop: ({ offset }?: {
160
160
  scrollableContainerRef: React.RefObject<HTMLElement>;
161
161
  isAtTop: boolean;
162
162
  };
163
+ interface UseIntersectionOptions extends IntersectionObserverInit {
164
+ onInteractionStart?: () => void;
165
+ onInteractionEnd?: () => void;
166
+ }
167
+ /**
168
+ * React hook that tracks when an element enters or leaves the viewport
169
+ * using the Intersection Observer API.
170
+ *
171
+ * @example
172
+ * ```tsx
173
+ * const { ref, isIntersecting } = useIntersection({
174
+ * threshold: 0.1,
175
+ * onInteractionStart: () => console.log('👀 Element entered viewport'),
176
+ * onInteractionEnd: () => console.log('🙈 Element left viewport'),
177
+ * });
178
+ *
179
+ * return <div ref={ref}>Watch me</div>;
180
+ * ```
181
+ *
182
+ * @param options - Configuration for the intersection observer.
183
+ * @returns Object containing:
184
+ * - `ref`: React ref to attach to the observed element.
185
+ * - `isIntersecting`: Whether the element is currently visible.
186
+ */
187
+ export declare const useIntersection: ({ threshold, root, rootMargin, onInteractionStart, onInteractionEnd, }?: UseIntersectionOptions) => {
188
+ ref: React.RefObject<any>;
189
+ isIntersecting: boolean;
190
+ };
@@ -154,17 +154,15 @@ export const useSessionStorage = (key, defaultValue) => {
154
154
  setStoredValue(JSON.parse(value));
155
155
  }
156
156
  }, [key]);
157
- const updateStoredValue = (valueOrFn) => {
158
- let newValue;
159
- if (typeof valueOrFn === 'function') {
160
- newValue = valueOrFn(storedValue);
161
- }
162
- else {
163
- newValue = valueOrFn;
164
- }
165
- sessionStorage.setItem(key, JSON.stringify(newValue));
166
- setStoredValue(newValue);
167
- };
157
+ const updateStoredValue = React.useCallback((valueOrFn) => {
158
+ setStoredValue((prev) => {
159
+ const newValue = typeof valueOrFn === 'function'
160
+ ? valueOrFn(prev)
161
+ : valueOrFn;
162
+ sessionStorage.setItem(key, JSON.stringify(newValue));
163
+ return newValue;
164
+ });
165
+ }, [key]);
168
166
  return [storedValue, updateStoredValue];
169
167
  };
170
168
  /**
@@ -181,7 +179,7 @@ export const useLocalStorage = (key, defaultValue) => {
181
179
  setStoredValue(JSON.parse(value));
182
180
  }
183
181
  }, [key]);
184
- const updateStoredValue = (valueOrFn) => {
182
+ const updateStoredValue = React.useCallback((valueOrFn) => {
185
183
  let newValue;
186
184
  if (typeof valueOrFn === 'function') {
187
185
  newValue = valueOrFn(storedValue);
@@ -191,7 +189,7 @@ export const useLocalStorage = (key, defaultValue) => {
191
189
  }
192
190
  localStorage.setItem(key, JSON.stringify(newValue));
193
191
  setStoredValue(newValue);
194
- };
192
+ }, [key]);
195
193
  return [storedValue, updateStoredValue];
196
194
  };
197
195
  /**
@@ -475,3 +473,59 @@ export const useIsAtTop = ({ offset } = {}) => {
475
473
  });
476
474
  return { scrollableContainerRef, isAtTop };
477
475
  };
476
+ /**
477
+ * React hook that tracks when an element enters or leaves the viewport
478
+ * using the Intersection Observer API.
479
+ *
480
+ * @example
481
+ * ```tsx
482
+ * const { ref, isIntersecting } = useIntersection({
483
+ * threshold: 0.1,
484
+ * onInteractionStart: () => console.log('👀 Element entered viewport'),
485
+ * onInteractionEnd: () => console.log('🙈 Element left viewport'),
486
+ * });
487
+ *
488
+ * return <div ref={ref}>Watch me</div>;
489
+ * ```
490
+ *
491
+ * @param options - Configuration for the intersection observer.
492
+ * @returns Object containing:
493
+ * - `ref`: React ref to attach to the observed element.
494
+ * - `isIntersecting`: Whether the element is currently visible.
495
+ */
496
+ export const useIntersection = ({ threshold = 0.1, root = null, rootMargin, onInteractionStart, onInteractionEnd, } = {}) => {
497
+ const [isIntersecting, setIsIntersecting] = React.useState(false);
498
+ const ref = React.useRef(null);
499
+ React.useEffect(() => {
500
+ if (!ref.current)
501
+ return;
502
+ const observer = new IntersectionObserver((entries) => {
503
+ for (const entry of entries) {
504
+ if (entry.isIntersecting) {
505
+ if (!isIntersecting) {
506
+ onInteractionStart?.();
507
+ setIsIntersecting(true);
508
+ }
509
+ }
510
+ else {
511
+ if (isIntersecting) {
512
+ onInteractionEnd?.();
513
+ setIsIntersecting(false);
514
+ }
515
+ }
516
+ }
517
+ }, { threshold, root, rootMargin });
518
+ observer.observe(ref.current);
519
+ return () => {
520
+ observer.disconnect();
521
+ };
522
+ }, [
523
+ threshold,
524
+ root,
525
+ rootMargin,
526
+ onInteractionStart,
527
+ onInteractionEnd,
528
+ isIntersecting,
529
+ ]);
530
+ return { ref, isIntersecting };
531
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sohanemon/utils",
3
- "version": "5.1.2",
3
+ "version": "5.1.4",
4
4
  "author": "Sohan Emon <sohanemon@outlook.com>",
5
5
  "description": "",
6
6
  "type": "module",