@leejaehyeok/use-intersection-observer 0.1.0 → 0.3.0

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.
package/README.md CHANGED
@@ -21,7 +21,7 @@ import React, { useRef } from "react";
21
21
  import { useIntersectionObserver } from "@leejaehyeok/use-intersection-observer";
22
22
 
23
23
  export function MyComponent() {
24
- const { setContainerRef, isVisible, hasEntered } = useIntersectionObserver({
24
+ const { setContainerRef, isVisible, hasEntered, target } = useIntersectionObserver({
25
25
  targetSelector: "[data-target]",
26
26
  onEntered: (entry) => console.log("Entered!"),
27
27
  onExited: (entry) => console.log("Exited!"),
@@ -30,6 +30,9 @@ export function MyComponent() {
30
30
  return (
31
31
  <div ref={setContainerRef}>
32
32
  <div data-target>{isVisible ? "Visible" : "Hidden"}</div>
33
+ <button onClick={() => target?.scrollIntoView({ behavior: "smooth" })} disabled={!target}>
34
+ Scroll to target
35
+ </button>
33
36
  </div>
34
37
  );
35
38
  }
@@ -66,6 +69,7 @@ export function MyComponent() {
66
69
  - **Single & Multiple Elements:** Track one or many elements with dedicated hooks.
67
70
  - **Lifecycle Callbacks:** `onEntered`, `onExited`, and `onChange` callbacks for full control.
68
71
  - **State Tracking:** Know if elements are visible or have been entered.
72
+ - **Target Element Access:** Get a direct reference to the observed DOM element via `target`.
69
73
  - **Once Option:** Auto-unobserve after first intersection with `once: true`.
70
74
  - **Flexible Root:** Use the viewport or a scrollable container as the intersection root.
71
75
  - **Reset Control:** Reset observation state programmatically with `reset()` method.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`react`);function t(t){let n=(0,e.useRef)(t);return(0,e.useLayoutEffect)(()=>{n.current=t},[t]),n}function n(n){let{root:r=null,once:i=!1,targetSelector:a=`[data-intersection-target]`,onEntered:o,onExited:s,onChange:c,...l}=n||{},[u,d]=(0,e.useState)(()=>({isVisible:!1,hasEntered:!1})),f=(0,e.useRef)(i),p=(0,e.useRef)(r),m=(0,e.useRef)(null),h=(0,e.useRef)(null),g=(0,e.useRef)(null),_=(0,e.useRef)(!1),v=t(o),y=t(s),b=t(c),x=(0,e.useCallback)(()=>{g.current?.disconnect()},[]),S=(0,e.useCallback)(()=>{if(!h.current||!m.current){console.warn(`[use-intersection-observer] 관찰할 요소가 없습니다. reset이 실패했습니다.`);return}g.current?.unobserve(m.current),_.current=!1,d(()=>({isVisible:!1,hasEntered:!1})),g.current?.observe(m.current)},[]),C=(0,e.useCallback)((e,t)=>{let n=e[0],r=n.isIntersecting;m.current=n.target,b.current?.(r?`enter`:`exit`,n,t),r?(_.current||=f.current,v.current?.(n,t),d(()=>({isVisible:r,hasEntered:!0}))):(y.current?.(n,t),d(e=>({...e,isVisible:r})),_.current&&t.unobserve(n.target))},[]);return{setContainerRef:(0,e.useCallback)(e=>{if(!e){h.current=null,x();return}h.current=e;let t=p.current===`container`?e:null,n=e.querySelectorAll(a),r=n.length>0?n:e.firstElementChild;if(!r){console.warn(`[use-intersection-observer] 대상을 찾을 수 없습니다: "${a}"`);return}r instanceof NodeList&&r.length>1&&console.warn(`여러 개의 요소가 발견되었습니다. 첫 번째 요소만 추적합니다.`);let i=new IntersectionObserver(C,{...l,root:t}),o=r instanceof NodeList?r[0]:r;g.current=i,i.observe(o)},[C]),reset:S,...u}}function r(n){let{root:r=null,once:i=!1,keyAttribute:a=`data-intersection-key`,onEntered:o,onExited:s,onChange:c,...l}=n||{},[u,d]=(0,e.useState)(()=>({})),f=(0,e.useRef)(i),p=(0,e.useRef)(r),m=(0,e.useRef)(null),h=(0,e.useRef)(null),g=(0,e.useRef)(null),_=(0,e.useRef)({}),v=t(o),y=t(s),b=t(c),x=(0,e.useCallback)(()=>{h.current?.disconnect(),g.current?.disconnect()},[]),S=(0,e.useCallback)(e=>{if(!m.current){console.warn(`[use-intersection-observer-group] 컨테이너가 없습니다. reset이 실패했습니다.`);return}if(e){let t=m.current.querySelector(`[${a}="${e}"]`);t instanceof Element&&(h.current?.unobserve(t),_.current[e]=!1,d(t=>({...t,[e]:{isVisible:!1,hasEntered:!1}})),h.current?.observe(t))}else{h.current?.disconnect(),_.current={},d(()=>({}));let e=m.current.querySelectorAll(`[${a}]`),t=p.current===`container`?m.current:null,n=new IntersectionObserver(w,{...l,root:t});h.current=n,e.forEach(e=>{n.observe(e)})}},[]),C=(0,e.useCallback)(e=>{e.forEach(e=>{e.addedNodes.forEach(e=>{e instanceof Element&&[...e.hasAttribute(a)?[e]:[],...Array.from(e.querySelectorAll(`[${a}]`))].forEach(e=>{h.current?.observe(e)})}),e.removedNodes.forEach(e=>{e instanceof Element&&[...e.hasAttribute(a)?[e]:[],...Array.from(e.querySelectorAll(`[${a}]`))].forEach(e=>{let t=e.getAttribute(a);t&&(h.current?.unobserve(e),delete _.current[t],d(e=>{let n={...e};return delete n[t],n}))})})})},[]),w=(0,e.useCallback)((e,t)=>{d(n=>{let r={...n};return e.forEach(e=>{let i=e.target.getAttribute(a);if(!i){console.warn(`[use-intersection-observer-group] ${a} 속성이 없습니다.`);return}let o=e.isIntersecting,s=n[i];b.current?.(i,o?`enter`:`exit`,e,t),o?(_.current[i]||=f.current,v.current?.(i,e,t),r[i]={isVisible:o,hasEntered:!0}):(y.current?.(i,e,t),r[i]={isVisible:o,hasEntered:s?.hasEntered??!1},_.current[i]&&t.unobserve(e.target))}),r})},[]);return{setContainerRef:(0,e.useCallback)(e=>{if(!e){m.current=null,x();return}m.current=e;let t=p.current===`container`?e:null,n=e.querySelectorAll(`[${a}]`),r=new IntersectionObserver(w,{...l,root:t});h.current=r;let i=new MutationObserver(C);i.observe(e,{childList:!0,subtree:!0}),g.current=i,n.length!==0&&n.forEach(e=>{r.observe(e)})},[w]),reset:S,states:u}}exports.useIntersectionObserver=n,exports.useIntersectionObserverGroup=r;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`react`);function t(t){let n=(0,e.useRef)(t);return(0,e.useLayoutEffect)(()=>{n.current=t},[t]),n}function n(n){let{root:r=null,once:i=!1,targetSelector:a=`[data-intersection-target]`,onEntered:o,onExited:s,onChange:c,enable:l=!0,...u}=n||{},[d,f]=(0,e.useState)(()=>({isVisible:!1,hasEntered:!1,target:null})),p=(0,e.useRef)(i),m=(0,e.useRef)(r),h=(0,e.useRef)(null),g=(0,e.useRef)(null),_=(0,e.useRef)(null),v=(0,e.useRef)(!1),y=t(l),b=t(o),x=t(s),S=t(c),C=(0,e.useCallback)(()=>{_.current?.disconnect()},[]),w=(0,e.useCallback)(()=>{if(!g.current||!h.current){console.warn(`[use-intersection-observer] 관찰할 요소가 없습니다. reset이 실패했습니다.`);return}_.current?.unobserve(h.current),v.current=!1,f(()=>({isVisible:!1,hasEntered:!1,target:null})),_.current?.observe(h.current)},[]),T=(0,e.useCallback)((e,t)=>{if(!y.current)return;let n=e[0],r=n.isIntersecting;h.current=n.target,S.current?.(r?`enter`:`exit`,n,t),r?(v.current||=p.current,b.current?.(n,t),f(()=>({isVisible:r,hasEntered:!0,target:n.target}))):(x.current?.(n,t),f(e=>({...e,isVisible:r})),v.current&&t.unobserve(n.target))},[]);return{setContainerRef:(0,e.useCallback)(e=>{if(!e){g.current=null,C();return}g.current=e;let t=m.current===`container`?e:null,n=e.querySelectorAll(a),r=n.length>0?n:e.firstElementChild;if(!r){console.warn(`[use-intersection-observer] 대상을 찾을 수 없습니다: "${a}"`);return}r instanceof NodeList&&r.length>1&&console.warn(`여러 개의 요소가 발견되었습니다. 첫 번째 요소만 추적합니다.`);let i=new IntersectionObserver(T,{...u,root:t}),o=r instanceof NodeList?r[0]:r;_.current=i,i.observe(o)},[T]),reset:w,...d}}function r(n){let{root:r=null,once:i=!1,keyAttribute:a=`data-intersection-key`,onEntered:o,onExited:s,onChange:c,enable:l=!0,...u}=n||{},[d,f]=(0,e.useState)(()=>({})),p=(0,e.useRef)(i),m=(0,e.useRef)(r),h=(0,e.useRef)(null),g=(0,e.useRef)(null),_=(0,e.useRef)(null),v=(0,e.useRef)({}),y=t(l),b=t(o),x=t(s),S=t(c),C=(0,e.useCallback)(()=>{g.current?.disconnect(),_.current?.disconnect()},[]),w=(0,e.useCallback)(e=>{if(!h.current){console.warn(`[use-intersection-observer-group] 컨테이너가 없습니다. reset이 실패했습니다.`);return}if(e){let t=h.current.querySelector(`[${a}="${e}"]`);t instanceof Element&&(g.current?.unobserve(t),v.current[e]=!1,f(t=>({...t,[e]:{isVisible:!1,hasEntered:!1,target:null}})),g.current?.observe(t))}else{g.current?.disconnect(),v.current={},f(()=>({}));let e=h.current.querySelectorAll(`[${a}]`),t=m.current===`container`?h.current:null,n=new IntersectionObserver(E,{...u,root:t});g.current=n,e.forEach(e=>{n.observe(e)})}},[]),T=(0,e.useCallback)(e=>{e.forEach(e=>{e.addedNodes.forEach(e=>{e instanceof Element&&[...e.hasAttribute(a)?[e]:[],...Array.from(e.querySelectorAll(`[${a}]`))].forEach(e=>{g.current?.observe(e)})}),e.removedNodes.forEach(e=>{e instanceof Element&&[...e.hasAttribute(a)?[e]:[],...Array.from(e.querySelectorAll(`[${a}]`))].forEach(e=>{let t=e.getAttribute(a);t&&(g.current?.unobserve(e),delete v.current[t],f(e=>{let n={...e};return delete n[t],n}))})})})},[]),E=(0,e.useCallback)((e,t)=>{y.current&&f(n=>{let r={...n};return e.forEach(e=>{let i=e.target.getAttribute(a);if(!i){console.warn(`[use-intersection-observer-group] ${a} 속성이 없습니다.`);return}let o=e.isIntersecting,s=n[i];S.current?.(i,o?`enter`:`exit`,e,t),o?(v.current[i]||=p.current,b.current?.(i,e,t),r[i]={isVisible:o,hasEntered:!0,target:e.target}):(x.current?.(i,e,t),r[i]={isVisible:o,hasEntered:s?.hasEntered??!1,target:e.target},v.current[i]&&t.unobserve(e.target))}),r})},[]);return{setContainerRef:(0,e.useCallback)(e=>{if(!e){h.current=null,C();return}h.current=e;let t=m.current===`container`?e:null,n=e.querySelectorAll(`[${a}]`),r=new IntersectionObserver(E,{...u,root:t});g.current=r;let i=new MutationObserver(T);i.observe(e,{childList:!0,subtree:!0}),_.current=i,n.length!==0&&n.forEach(e=>{r.observe(e)})},[E]),reset:w,states:d}}exports.useIntersectionObserver=n,exports.useIntersectionObserverGroup=r;
package/dist/index.js CHANGED
@@ -9,100 +9,105 @@ function i(e) {
9
9
  //#endregion
10
10
  //#region src/useIntersectionObserver.ts
11
11
  function a(t) {
12
- let { root: a = null, once: o = !1, targetSelector: s = "[data-intersection-target]", onEntered: c, onExited: l, onChange: u, ...d } = t || {}, [f, p] = r(() => ({
12
+ let { root: a = null, once: o = !1, targetSelector: s = "[data-intersection-target]", onEntered: c, onExited: l, onChange: u, enable: d = !0, ...f } = t || {}, [p, m] = r(() => ({
13
13
  isVisible: !1,
14
- hasEntered: !1
15
- })), m = n(o), h = n(a), g = n(null), _ = n(null), v = n(null), y = n(!1), b = i(c), x = i(l), S = i(u), C = e(() => {
16
- v.current?.disconnect();
17
- }, []), w = e(() => {
18
- if (!_.current || !g.current) {
14
+ hasEntered: !1,
15
+ target: null
16
+ })), h = n(o), g = n(a), _ = n(null), v = n(null), y = n(null), b = n(!1), x = i(d), S = i(c), C = i(l), w = i(u), T = e(() => {
17
+ y.current?.disconnect();
18
+ }, []), E = e(() => {
19
+ if (!v.current || !_.current) {
19
20
  console.warn("[use-intersection-observer] 관찰할 요소가 없습니다. reset이 실패했습니다.");
20
21
  return;
21
22
  }
22
- v.current?.unobserve(g.current), y.current = !1, p(() => ({
23
+ y.current?.unobserve(_.current), b.current = !1, m(() => ({
23
24
  isVisible: !1,
24
- hasEntered: !1
25
- })), v.current?.observe(g.current);
26
- }, []), T = e((e, t) => {
25
+ hasEntered: !1,
26
+ target: null
27
+ })), y.current?.observe(_.current);
28
+ }, []), D = e((e, t) => {
29
+ if (!x.current) return;
27
30
  let n = e[0], r = n.isIntersecting;
28
- g.current = n.target, S.current?.(r ? "enter" : "exit", n, t), r ? (y.current ||= m.current, b.current?.(n, t), p(() => ({
31
+ _.current = n.target, w.current?.(r ? "enter" : "exit", n, t), r ? (b.current ||= h.current, S.current?.(n, t), m(() => ({
29
32
  isVisible: r,
30
- hasEntered: !0
31
- }))) : (x.current?.(n, t), p((e) => ({
33
+ hasEntered: !0,
34
+ target: n.target
35
+ }))) : (C.current?.(n, t), m((e) => ({
32
36
  ...e,
33
37
  isVisible: r
34
- })), y.current && t.unobserve(n.target));
38
+ })), b.current && t.unobserve(n.target));
35
39
  }, []);
36
40
  return {
37
41
  setContainerRef: e((e) => {
38
42
  if (!e) {
39
- _.current = null, C();
43
+ v.current = null, T();
40
44
  return;
41
45
  }
42
- _.current = e;
43
- let t = h.current === "container" ? e : null, n = e.querySelectorAll(s), r = n.length > 0 ? n : e.firstElementChild;
46
+ v.current = e;
47
+ let t = g.current === "container" ? e : null, n = e.querySelectorAll(s), r = n.length > 0 ? n : e.firstElementChild;
44
48
  if (!r) {
45
49
  console.warn(`[use-intersection-observer] 대상을 찾을 수 없습니다: "${s}"`);
46
50
  return;
47
51
  }
48
52
  r instanceof NodeList && r.length > 1 && console.warn("여러 개의 요소가 발견되었습니다. 첫 번째 요소만 추적합니다.");
49
- let i = new IntersectionObserver(T, {
50
- ...d,
53
+ let i = new IntersectionObserver(D, {
54
+ ...f,
51
55
  root: t
52
56
  }), a = r instanceof NodeList ? r[0] : r;
53
- v.current = i, i.observe(a);
54
- }, [T]),
55
- reset: w,
56
- ...f
57
+ y.current = i, i.observe(a);
58
+ }, [D]),
59
+ reset: E,
60
+ ...p
57
61
  };
58
62
  }
59
63
  //#endregion
60
64
  //#region src/useIntersectionObserverGroup.ts
61
65
  function o(t) {
62
- let { root: a = null, once: o = !1, keyAttribute: s = "data-intersection-key", onEntered: c, onExited: l, onChange: u, ...d } = t || {}, [f, p] = r(() => ({})), m = n(o), h = n(a), g = n(null), _ = n(null), v = n(null), y = n({}), b = i(c), x = i(l), S = i(u), C = e(() => {
63
- _.current?.disconnect(), v.current?.disconnect();
64
- }, []), w = e((e) => {
65
- if (!g.current) {
66
+ let { root: a = null, once: o = !1, keyAttribute: s = "data-intersection-key", onEntered: c, onExited: l, onChange: u, enable: d = !0, ...f } = t || {}, [p, m] = r(() => ({})), h = n(o), g = n(a), _ = n(null), v = n(null), y = n(null), b = n({}), x = i(d), S = i(c), C = i(l), w = i(u), T = e(() => {
67
+ v.current?.disconnect(), y.current?.disconnect();
68
+ }, []), E = e((e) => {
69
+ if (!_.current) {
66
70
  console.warn("[use-intersection-observer-group] 컨테이너가 없습니다. reset이 실패했습니다.");
67
71
  return;
68
72
  }
69
73
  if (e) {
70
- let t = g.current.querySelector(`[${s}="${e}"]`);
71
- t instanceof Element && (_.current?.unobserve(t), y.current[e] = !1, p((t) => ({
74
+ let t = _.current.querySelector(`[${s}="${e}"]`);
75
+ t instanceof Element && (v.current?.unobserve(t), b.current[e] = !1, m((t) => ({
72
76
  ...t,
73
77
  [e]: {
74
78
  isVisible: !1,
75
- hasEntered: !1
79
+ hasEntered: !1,
80
+ target: null
76
81
  }
77
- })), _.current?.observe(t));
82
+ })), v.current?.observe(t));
78
83
  } else {
79
- _.current?.disconnect(), y.current = {}, p(() => ({}));
80
- let e = g.current.querySelectorAll(`[${s}]`), t = h.current === "container" ? g.current : null, n = new IntersectionObserver(E, {
81
- ...d,
84
+ v.current?.disconnect(), b.current = {}, m(() => ({}));
85
+ let e = _.current.querySelectorAll(`[${s}]`), t = g.current === "container" ? _.current : null, n = new IntersectionObserver(O, {
86
+ ...f,
82
87
  root: t
83
88
  });
84
- _.current = n, e.forEach((e) => {
89
+ v.current = n, e.forEach((e) => {
85
90
  n.observe(e);
86
91
  });
87
92
  }
88
- }, []), T = e((e) => {
93
+ }, []), D = e((e) => {
89
94
  e.forEach((e) => {
90
95
  e.addedNodes.forEach((e) => {
91
96
  e instanceof Element && [...e.hasAttribute(s) ? [e] : [], ...Array.from(e.querySelectorAll(`[${s}]`))].forEach((e) => {
92
- _.current?.observe(e);
97
+ v.current?.observe(e);
93
98
  });
94
99
  }), e.removedNodes.forEach((e) => {
95
100
  e instanceof Element && [...e.hasAttribute(s) ? [e] : [], ...Array.from(e.querySelectorAll(`[${s}]`))].forEach((e) => {
96
101
  let t = e.getAttribute(s);
97
- t && (_.current?.unobserve(e), delete y.current[t], p((e) => {
102
+ t && (v.current?.unobserve(e), delete b.current[t], m((e) => {
98
103
  let n = { ...e };
99
104
  return delete n[t], n;
100
105
  }));
101
106
  });
102
107
  });
103
108
  });
104
- }, []), E = e((e, t) => {
105
- p((n) => {
109
+ }, []), O = e((e, t) => {
110
+ x.current && m((n) => {
106
111
  let r = { ...n };
107
112
  return e.forEach((e) => {
108
113
  let i = e.target.getAttribute(s);
@@ -111,38 +116,40 @@ function o(t) {
111
116
  return;
112
117
  }
113
118
  let a = e.isIntersecting, o = n[i];
114
- S.current?.(i, a ? "enter" : "exit", e, t), a ? (y.current[i] ||= m.current, b.current?.(i, e, t), r[i] = {
119
+ w.current?.(i, a ? "enter" : "exit", e, t), a ? (b.current[i] ||= h.current, S.current?.(i, e, t), r[i] = {
115
120
  isVisible: a,
116
- hasEntered: !0
117
- }) : (x.current?.(i, e, t), r[i] = {
121
+ hasEntered: !0,
122
+ target: e.target
123
+ }) : (C.current?.(i, e, t), r[i] = {
118
124
  isVisible: a,
119
- hasEntered: o?.hasEntered ?? !1
120
- }, y.current[i] && t.unobserve(e.target));
125
+ hasEntered: o?.hasEntered ?? !1,
126
+ target: e.target
127
+ }, b.current[i] && t.unobserve(e.target));
121
128
  }), r;
122
129
  });
123
130
  }, []);
124
131
  return {
125
132
  setContainerRef: e((e) => {
126
133
  if (!e) {
127
- g.current = null, C();
134
+ _.current = null, T();
128
135
  return;
129
136
  }
130
- g.current = e;
131
- let t = h.current === "container" ? e : null, n = e.querySelectorAll(`[${s}]`), r = new IntersectionObserver(E, {
132
- ...d,
137
+ _.current = e;
138
+ let t = g.current === "container" ? e : null, n = e.querySelectorAll(`[${s}]`), r = new IntersectionObserver(O, {
139
+ ...f,
133
140
  root: t
134
141
  });
135
- _.current = r;
136
- let i = new MutationObserver(T);
142
+ v.current = r;
143
+ let i = new MutationObserver(D);
137
144
  i.observe(e, {
138
145
  childList: !0,
139
146
  subtree: !0
140
- }), v.current = i, n.length !== 0 && n.forEach((e) => {
147
+ }), y.current = i, n.length !== 0 && n.forEach((e) => {
141
148
  r.observe(e);
142
149
  });
143
- }, [E]),
144
- reset: w,
145
- states: f
150
+ }, [O]),
151
+ reset: E,
152
+ states: p
146
153
  };
147
154
  }
148
155
  //#endregion
package/dist/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export type IntersectionBaseOptions = {
2
2
  root?: "container" | null;
3
3
  once?: boolean;
4
+ enable?: boolean;
4
5
  } & Omit<IntersectionObserverInit, "root">;
@@ -9,6 +9,7 @@ type IntersectionObserverOption = IntersectionBaseOptions & {
9
9
  declare function useIntersectionObserver(options?: IntersectionObserverOption): {
10
10
  isVisible: boolean;
11
11
  hasEntered: boolean;
12
+ target: Element | null;
12
13
  setContainerRef: RefCallback<HTMLElement>;
13
14
  reset: () => void;
14
15
  };
@@ -9,6 +9,7 @@ type IntersectionGroupOption = IntersectionBaseOptions & {
9
9
  type TargetState = {
10
10
  isVisible: boolean;
11
11
  hasEntered: boolean;
12
+ target: Element | null;
12
13
  };
13
14
  type GroupStates = Record<string, TargetState>;
14
15
  declare function useIntersectionObserverGroup(options?: IntersectionGroupOption): {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leejaehyeok/use-intersection-observer",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "React hooks for observing element visibility using the Intersection Observer API. Supports single element and multiple elements tracking with lifecycle callbacks.",
5
5
  "author": "leejh1316",
6
6
  "license": "MIT",