@leejaehyeok/use-intersection-observer 0.2.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 +5 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +62 -55
- package/dist/types.d.ts +1 -0
- package/dist/useIntersectionObserver.d.ts +1 -0
- package/dist/useIntersectionObserverGroup.d.ts +1 -0
- package/package.json +1 -1
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,...
|
|
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,
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
23
|
+
y.current?.unobserve(_.current), b.current = !1, m(() => ({
|
|
23
24
|
isVisible: !1,
|
|
24
|
-
hasEntered: !1
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
|
|
33
|
+
hasEntered: !0,
|
|
34
|
+
target: n.target
|
|
35
|
+
}))) : (C.current?.(n, t), m((e) => ({
|
|
32
36
|
...e,
|
|
33
37
|
isVisible: r
|
|
34
|
-
})),
|
|
38
|
+
})), b.current && t.unobserve(n.target));
|
|
35
39
|
}, []);
|
|
36
40
|
return {
|
|
37
41
|
setContainerRef: e((e) => {
|
|
38
42
|
if (!e) {
|
|
39
|
-
|
|
43
|
+
v.current = null, T();
|
|
40
44
|
return;
|
|
41
45
|
}
|
|
42
|
-
|
|
43
|
-
let t =
|
|
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(
|
|
50
|
-
...
|
|
53
|
+
let i = new IntersectionObserver(D, {
|
|
54
|
+
...f,
|
|
51
55
|
root: t
|
|
52
56
|
}), a = r instanceof NodeList ? r[0] : r;
|
|
53
|
-
|
|
54
|
-
}, [
|
|
55
|
-
reset:
|
|
56
|
-
...
|
|
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,
|
|
63
|
-
|
|
64
|
-
}, []),
|
|
65
|
-
if (!
|
|
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 =
|
|
71
|
-
t instanceof Element && (
|
|
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
|
-
})),
|
|
82
|
+
})), v.current?.observe(t));
|
|
78
83
|
} else {
|
|
79
|
-
|
|
80
|
-
let e =
|
|
81
|
-
...
|
|
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
|
-
|
|
89
|
+
v.current = n, e.forEach((e) => {
|
|
85
90
|
n.observe(e);
|
|
86
91
|
});
|
|
87
92
|
}
|
|
88
|
-
}, []),
|
|
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
|
-
|
|
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 && (
|
|
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
|
-
}, []),
|
|
105
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
134
|
+
_.current = null, T();
|
|
128
135
|
return;
|
|
129
136
|
}
|
|
130
|
-
|
|
131
|
-
let t =
|
|
132
|
-
...
|
|
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
|
-
|
|
136
|
-
let i = new MutationObserver(
|
|
142
|
+
v.current = r;
|
|
143
|
+
let i = new MutationObserver(D);
|
|
137
144
|
i.observe(e, {
|
|
138
145
|
childList: !0,
|
|
139
146
|
subtree: !0
|
|
140
|
-
}),
|
|
147
|
+
}), y.current = i, n.length !== 0 && n.forEach((e) => {
|
|
141
148
|
r.observe(e);
|
|
142
149
|
});
|
|
143
|
-
}, [
|
|
144
|
-
reset:
|
|
145
|
-
states:
|
|
150
|
+
}, [O]),
|
|
151
|
+
reset: E,
|
|
152
|
+
states: p
|
|
146
153
|
};
|
|
147
154
|
}
|
|
148
155
|
//#endregion
|
package/dist/types.d.ts
CHANGED
|
@@ -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.
|
|
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",
|