@jbpark/use-hooks 2.3.0 → 2.5.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.ko.md +24 -21
- package/README.md +24 -21
- package/dist/hooks/index.d.mts +2 -0
- package/dist/hooks/index.mjs +2 -0
- package/dist/hooks/use-history-state.d.mts +16 -0
- package/dist/hooks/use-history-state.mjs +79 -0
- package/dist/hooks/use-history-state.mjs.map +1 -0
- package/dist/hooks/use-intersection-observer.d.mts +5 -0
- package/dist/hooks/use-intersection-observer.mjs +23 -0
- package/dist/hooks/use-intersection-observer.mjs.map +1 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +3 -1
- package/package.json +4 -1
package/README.ko.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
## 기능
|
|
13
13
|
|
|
14
|
-
- 📦 **
|
|
14
|
+
- 📦 **15개 프로덕션 레디 훅** - 스크롤, 뷰포트, 스토리지 등 다양한 유틸리티
|
|
15
15
|
- 🎯 **TypeScript 지원** - 완전한 타입 지원으로 더 나은 개발 경험
|
|
16
16
|
- ⚡ **트리 셰이킹 지원** - 필요한 것만 임포트하세요
|
|
17
17
|
- 🔒 **SSR 안전** - window/document 전역 변수에 대한 보호
|
|
@@ -67,21 +67,23 @@ function MyComponent() {
|
|
|
67
67
|
|
|
68
68
|
## 사용 가능한 훅
|
|
69
69
|
|
|
70
|
-
| 훅
|
|
71
|
-
|
|
|
72
|
-
| `useLocalStorage`
|
|
73
|
-
| `useWindowScroll`
|
|
74
|
-
| `useElementScroll`
|
|
75
|
-
| `useElementPosition`
|
|
76
|
-
| `useResponsiveSize`
|
|
77
|
-
| `useBodyScrollLock`
|
|
78
|
-
| `useClickOutside`
|
|
79
|
-
| `useScrollToElements`
|
|
80
|
-
| `useImage`
|
|
81
|
-
| `useRecursiveTimeout`
|
|
82
|
-
| `useViewport`
|
|
83
|
-
| `useDebounce`
|
|
84
|
-
| `useThrottle`
|
|
70
|
+
| 훅 | 설명 |
|
|
71
|
+
| ------------------------- | ----------------------------------------------------------------- |
|
|
72
|
+
| `useLocalStorage` | 에러 핸들링이 포함된 JSON 기반 영속 상태 (SSR 안전) |
|
|
73
|
+
| `useWindowScroll` | 윈도우 스크롤 위치 및 백분율 추적 (iOS visualViewport 대응) |
|
|
74
|
+
| `useElementScroll` | ResizeObserver를 사용한 특정 요소의 스크롤 상태 추적 |
|
|
75
|
+
| `useElementPosition` | 스크롤/리사이즈 시 요소의 바운딩 렉트 모니터링 (요소 참조 지원) |
|
|
76
|
+
| `useResponsiveSize` | Tailwind 유사 브레이크포인트를 포함한 요소 크기 추적 (debounce) |
|
|
77
|
+
| `useBodyScrollLock` | 스타일 보존을 포함한 바디 스크롤 잠금/해제 (iOS 특별 처리) |
|
|
78
|
+
| `useClickOutside` | 참조한 요소 바깥을 클릭/터치하면 콜백 실행 |
|
|
79
|
+
| `useScrollToElements` | 인덱스별로 특정 요소로 스크롤 (오프셋 조절 가능) |
|
|
80
|
+
| `useImage` | 이미지 사전로드 및 로딩/에러 상태 노출 |
|
|
81
|
+
| `useRecursiveTimeout` | 비동기/동기 콜백을 재귀적으로 스케줄링 |
|
|
82
|
+
| `useViewport` | visualViewport 지원, 인앱 모드 옵션, debounce 포함 |
|
|
83
|
+
| `useDebounce` | 함수 실행을 지연해 과도한 업데이트를 방지 (autoInvoke 지원) |
|
|
84
|
+
| `useThrottle` | 값 업데이트를 일정 간격으로 제한 |
|
|
85
|
+
| `useHistoryState` | 히스토리 개수 제한을 설정할 수 있는 실행 취소/다시 실행 상태 관리 |
|
|
86
|
+
| `useIntersectionObserver` | 콜백 ref로 요소의 뷰포트 교차 여부 추적 |
|
|
85
87
|
|
|
86
88
|
## 개발
|
|
87
89
|
|
|
@@ -112,7 +114,9 @@ src/
|
|
|
112
114
|
│ ├── use-debounce.ts
|
|
113
115
|
│ ├── use-element-position.ts
|
|
114
116
|
│ ├── use-element-scroll.ts
|
|
117
|
+
│ ├── use-history-state.ts
|
|
115
118
|
│ ├── use-image.ts
|
|
119
|
+
│ ├── use-intersection-observer.ts
|
|
116
120
|
│ ├── use-local-storage.ts
|
|
117
121
|
│ ├── use-recursive-timeout.ts
|
|
118
122
|
│ ├── use-responsive-size.ts
|
|
@@ -128,12 +132,11 @@ dist/ # 빌드된 라이브러리 (ESM + types)
|
|
|
128
132
|
|
|
129
133
|
## 빌드 및 배포
|
|
130
134
|
|
|
131
|
-
|
|
135
|
+
배포는 [changesets](https://github.com/changesets/changesets)로 완전히 자동화되어 있습니다:
|
|
132
136
|
|
|
133
|
-
-
|
|
134
|
-
- `
|
|
135
|
-
-
|
|
136
|
-
- 이 릴리스 PR을 머지하면 빌드, npm 배포, 태그 생성까지 자동으로 진행됩니다.
|
|
137
|
+
- `main`으로 향하는 PR마다 AI가 변경 내용을 요약한 changeset 파일을 초안으로 작성합니다.
|
|
138
|
+
- `main`에 changeset들이 쌓이면 "Version Packages" PR이 `package.json`의 버전을 승격시키고 `CHANGELOG.md`를 정리합니다.
|
|
139
|
+
- 이 PR을 머지하면 빌드, npm 배포, 태그 생성까지 자동으로 진행됩니다.
|
|
137
140
|
|
|
138
141
|
라이브러리는 다음과 같이 빌드됩니다:
|
|
139
142
|
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ A collection of reusable React 19 hooks for common UI and interaction patterns.
|
|
|
11
11
|
|
|
12
12
|
## Features
|
|
13
13
|
|
|
14
|
-
- 📦 **
|
|
14
|
+
- 📦 **15 Production-Ready Hooks** - Utilities for scrolling, viewport, storage, and more
|
|
15
15
|
- 🎯 **Full TypeScript Support** - Complete type definitions for better development experience
|
|
16
16
|
- ⚡ **Tree-Shakeable** - Import only what you need
|
|
17
17
|
- 🔒 **SSR-Safe** - Built-in protection for window/document globals
|
|
@@ -67,21 +67,23 @@ function MyComponent() {
|
|
|
67
67
|
|
|
68
68
|
## Available Hooks
|
|
69
69
|
|
|
70
|
-
| Hook
|
|
71
|
-
|
|
|
72
|
-
| `useLocalStorage`
|
|
73
|
-
| `useWindowScroll`
|
|
74
|
-
| `useElementScroll`
|
|
75
|
-
| `useElementPosition`
|
|
76
|
-
| `useResponsiveSize`
|
|
77
|
-
| `useBodyScrollLock`
|
|
78
|
-
| `useClickOutside`
|
|
79
|
-
| `useScrollToElements`
|
|
80
|
-
| `useImage`
|
|
81
|
-
| `useRecursiveTimeout`
|
|
82
|
-
| `useViewport`
|
|
83
|
-
| `useDebounce`
|
|
84
|
-
| `useThrottle`
|
|
70
|
+
| Hook | Description |
|
|
71
|
+
| ------------------------- | --------------------------------------------------------------------------- |
|
|
72
|
+
| `useLocalStorage` | JSON-based persistent state with error handling (SSR-safe) |
|
|
73
|
+
| `useWindowScroll` | Track window scroll position and percentage (iOS visualViewport compatible) |
|
|
74
|
+
| `useElementScroll` | Monitor scroll state of specific elements using ResizeObserver |
|
|
75
|
+
| `useElementPosition` | Monitor element bounding rect on scroll/resize (element ref support) |
|
|
76
|
+
| `useResponsiveSize` | Track element size with Tailwind-like breakpoints (debounced) |
|
|
77
|
+
| `useBodyScrollLock` | Lock/unlock body scroll with style preservation (iOS-specific handling) |
|
|
78
|
+
| `useClickOutside` | Run a callback when a click/touch happens outside a referenced element |
|
|
79
|
+
| `useScrollToElements` | Scroll to specific elements by index (adjustable offset) |
|
|
80
|
+
| `useImage` | Preload images and expose loading/error states |
|
|
81
|
+
| `useRecursiveTimeout` | Recursively schedule async/sync callbacks |
|
|
82
|
+
| `useViewport` | visualViewport support with in-app mode option and debounce |
|
|
83
|
+
| `useDebounce` | Delay function execution to prevent excessive updates (autoInvoke support) |
|
|
84
|
+
| `useThrottle` | Throttle value updates to a fixed interval |
|
|
85
|
+
| `useHistoryState` | Undo/redo state management with a configurable history limit |
|
|
86
|
+
| `useIntersectionObserver` | Track an element's viewport intersection via a callback ref |
|
|
85
87
|
|
|
86
88
|
## Development
|
|
87
89
|
|
|
@@ -112,7 +114,9 @@ src/
|
|
|
112
114
|
│ ├── use-debounce.ts
|
|
113
115
|
│ ├── use-element-position.ts
|
|
114
116
|
│ ├── use-element-scroll.ts
|
|
117
|
+
│ ├── use-history-state.ts
|
|
115
118
|
│ ├── use-image.ts
|
|
119
|
+
│ ├── use-intersection-observer.ts
|
|
116
120
|
│ ├── use-local-storage.ts
|
|
117
121
|
│ ├── use-recursive-timeout.ts
|
|
118
122
|
│ ├── use-responsive-size.ts
|
|
@@ -128,12 +132,11 @@ dist/ # Built library (ESM + types)
|
|
|
128
132
|
|
|
129
133
|
## Build & Deployment
|
|
130
134
|
|
|
131
|
-
|
|
135
|
+
Releases are fully automated via [changesets](https://github.com/changesets/changesets):
|
|
132
136
|
|
|
133
|
-
-
|
|
134
|
-
-
|
|
135
|
-
-
|
|
136
|
-
- Merging the release PR builds, publishes to npm, and tags the release.
|
|
137
|
+
- Each PR against `main` gets an AI-drafted changeset file describing its change.
|
|
138
|
+
- Once changesets accumulate on `main`, a "Version Packages" PR bumps `package.json`'s version and consolidates `CHANGELOG.md`.
|
|
139
|
+
- Merging that PR builds, publishes to npm, and tags the release.
|
|
137
140
|
|
|
138
141
|
The library is built as:
|
|
139
142
|
|
package/dist/hooks/index.d.mts
CHANGED
|
@@ -3,6 +3,8 @@ import { useBodyScrollLock } from "./use-body-scroll-lock.mjs";
|
|
|
3
3
|
import { useClickOutside } from "./use-click-outside.mjs";
|
|
4
4
|
import { useElementPosition } from "./use-element-position.mjs";
|
|
5
5
|
import { useElementScroll } from "./use-element-scroll.mjs";
|
|
6
|
+
import { useHistoryState } from "./use-history-state.mjs";
|
|
7
|
+
import { useIntersectionObserver } from "./use-intersection-observer.mjs";
|
|
6
8
|
import { useResponsiveSize } from "./use-responsive-size.mjs";
|
|
7
9
|
import { useImage } from "./use-image.mjs";
|
|
8
10
|
import { useLocalStorage } from "./use-local-storage.mjs";
|
package/dist/hooks/index.mjs
CHANGED
|
@@ -3,6 +3,8 @@ import useBodyScrollLock from "./use-body-scroll-lock.mjs";
|
|
|
3
3
|
import useClickOutside from "./use-click-outside.mjs";
|
|
4
4
|
import useElementPosition from "./use-element-position.mjs";
|
|
5
5
|
import useElementScroll from "./use-element-scroll.mjs";
|
|
6
|
+
import useHistoryState from "./use-history-state.mjs";
|
|
7
|
+
import useIntersectionObserver from "./use-intersection-observer.mjs";
|
|
6
8
|
import useResponsiveSize from "./use-responsive-size.mjs";
|
|
7
9
|
import useImage from "./use-image.mjs";
|
|
8
10
|
import useLocalStorage from "./use-local-storage.mjs";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/hooks/use-history-state.d.ts
|
|
2
|
+
interface UseHistoryStateOptions {
|
|
3
|
+
limit?: number;
|
|
4
|
+
}
|
|
5
|
+
declare const useHistoryState: <T>(initialValue: T, options?: UseHistoryStateOptions) => {
|
|
6
|
+
readonly value: T;
|
|
7
|
+
readonly setValue: (value: T | ((prev: T) => T)) => void;
|
|
8
|
+
readonly undo: () => void;
|
|
9
|
+
readonly redo: () => void;
|
|
10
|
+
readonly reset: (value: T) => void;
|
|
11
|
+
readonly canUndo: boolean;
|
|
12
|
+
readonly canRedo: boolean;
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { useHistoryState };
|
|
16
|
+
//# sourceMappingURL=use-history-state.d.mts.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { useCallback, useReducer } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/hooks/use-history-state.ts
|
|
4
|
+
const createReducer = (limit) => (state, action) => {
|
|
5
|
+
switch (action.type) {
|
|
6
|
+
case "SET": {
|
|
7
|
+
const resolved = action.value instanceof Function ? action.value(state.present) : action.value;
|
|
8
|
+
if (resolved === state.present) return state;
|
|
9
|
+
return {
|
|
10
|
+
past: [...state.past, state.present].slice(-limit),
|
|
11
|
+
present: resolved,
|
|
12
|
+
future: []
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
case "UNDO": {
|
|
16
|
+
if (state.past.length === 0) return state;
|
|
17
|
+
const previous = state.past[state.past.length - 1];
|
|
18
|
+
return {
|
|
19
|
+
past: state.past.slice(0, -1),
|
|
20
|
+
present: previous,
|
|
21
|
+
future: [state.present, ...state.future]
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
case "REDO": {
|
|
25
|
+
if (state.future.length === 0) return state;
|
|
26
|
+
const [next, ...rest] = state.future;
|
|
27
|
+
return {
|
|
28
|
+
past: [...state.past, state.present].slice(-limit),
|
|
29
|
+
present: next,
|
|
30
|
+
future: rest
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
case "RESET": return {
|
|
34
|
+
past: [],
|
|
35
|
+
present: action.value,
|
|
36
|
+
future: []
|
|
37
|
+
};
|
|
38
|
+
default: return state;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const DEFAULT_LIMIT = 50;
|
|
42
|
+
const useHistoryState = (initialValue, options) => {
|
|
43
|
+
const [state, dispatch] = useReducer(createReducer(options?.limit ?? DEFAULT_LIMIT), {
|
|
44
|
+
past: [],
|
|
45
|
+
present: initialValue,
|
|
46
|
+
future: []
|
|
47
|
+
});
|
|
48
|
+
const setValue = useCallback((value) => {
|
|
49
|
+
dispatch({
|
|
50
|
+
type: "SET",
|
|
51
|
+
value
|
|
52
|
+
});
|
|
53
|
+
}, []);
|
|
54
|
+
const undo = useCallback(() => {
|
|
55
|
+
dispatch({ type: "UNDO" });
|
|
56
|
+
}, []);
|
|
57
|
+
const redo = useCallback(() => {
|
|
58
|
+
dispatch({ type: "REDO" });
|
|
59
|
+
}, []);
|
|
60
|
+
const reset = useCallback((value) => {
|
|
61
|
+
dispatch({
|
|
62
|
+
type: "RESET",
|
|
63
|
+
value
|
|
64
|
+
});
|
|
65
|
+
}, []);
|
|
66
|
+
return {
|
|
67
|
+
value: state.present,
|
|
68
|
+
setValue,
|
|
69
|
+
undo,
|
|
70
|
+
redo,
|
|
71
|
+
reset,
|
|
72
|
+
canUndo: state.past.length > 0,
|
|
73
|
+
canRedo: state.future.length > 0
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
//#endregion
|
|
78
|
+
export { useHistoryState as default };
|
|
79
|
+
//# sourceMappingURL=use-history-state.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-history-state.mjs","names":[],"sources":["../../src/hooks/use-history-state.ts"],"sourcesContent":["import { useCallback, useReducer } from 'react';\n\ninterface HistoryState<T> {\n past: T[];\n present: T;\n future: T[];\n}\n\ntype Action<T> =\n | { type: 'SET'; value: T | ((prev: T) => T) }\n | { type: 'UNDO' }\n | { type: 'REDO' }\n | { type: 'RESET'; value: T };\n\nconst createReducer =\n <T>(limit: number) =>\n (state: HistoryState<T>, action: Action<T>): HistoryState<T> => {\n switch (action.type) {\n case 'SET': {\n const resolved =\n action.value instanceof Function\n ? action.value(state.present)\n : action.value;\n\n if (resolved === state.present) {\n return state;\n }\n\n return {\n past: [...state.past, state.present].slice(-limit),\n present: resolved,\n future: [],\n };\n }\n\n case 'UNDO': {\n if (state.past.length === 0) {\n return state;\n }\n\n const previous = state.past[state.past.length - 1] as T;\n\n return {\n past: state.past.slice(0, -1),\n present: previous,\n future: [state.present, ...state.future],\n };\n }\n\n case 'REDO': {\n if (state.future.length === 0) {\n return state;\n }\n\n const [next, ...rest] = state.future;\n\n return {\n past: [...state.past, state.present].slice(-limit),\n present: next as T,\n future: rest,\n };\n }\n\n case 'RESET': {\n return { past: [], present: action.value, future: [] };\n }\n\n default: {\n return state;\n }\n }\n };\n\ninterface UseHistoryStateOptions {\n limit?: number;\n}\n\nconst DEFAULT_LIMIT = 50;\n\nconst useHistoryState = <T>(\n initialValue: T,\n options?: UseHistoryStateOptions,\n) => {\n const limit = options?.limit ?? DEFAULT_LIMIT;\n\n const [state, dispatch] = useReducer(createReducer<T>(limit), {\n past: [],\n present: initialValue,\n future: [],\n });\n\n const setValue = useCallback((value: T | ((prev: T) => T)) => {\n dispatch({ type: 'SET', value });\n }, []);\n\n const undo = useCallback(() => {\n dispatch({ type: 'UNDO' });\n }, []);\n\n const redo = useCallback(() => {\n dispatch({ type: 'REDO' });\n }, []);\n\n const reset = useCallback((value: T) => {\n dispatch({ type: 'RESET', value });\n }, []);\n\n return {\n value: state.present,\n setValue,\n undo,\n redo,\n reset,\n canUndo: state.past.length > 0,\n canRedo: state.future.length > 0,\n } as const;\n};\n\nexport default useHistoryState;\n"],"mappings":";;;AAcA,MAAM,iBACA,WACH,OAAwB,WAAuC;AAC9D,SAAQ,OAAO,MAAf;EACE,KAAK,OAAO;GACV,MAAM,WACJ,OAAO,iBAAiB,WACpB,OAAO,MAAM,MAAM,QAAQ,GAC3B,OAAO;AAEb,OAAI,aAAa,MAAM,QACrB,QAAO;AAGT,UAAO;IACL,MAAM,CAAC,GAAG,MAAM,MAAM,MAAM,QAAQ,CAAC,MAAM,CAAC,MAAM;IAClD,SAAS;IACT,QAAQ,EAAE;IACX;;EAGH,KAAK,QAAQ;AACX,OAAI,MAAM,KAAK,WAAW,EACxB,QAAO;GAGT,MAAM,WAAW,MAAM,KAAK,MAAM,KAAK,SAAS;AAEhD,UAAO;IACL,MAAM,MAAM,KAAK,MAAM,GAAG,GAAG;IAC7B,SAAS;IACT,QAAQ,CAAC,MAAM,SAAS,GAAG,MAAM,OAAO;IACzC;;EAGH,KAAK,QAAQ;AACX,OAAI,MAAM,OAAO,WAAW,EAC1B,QAAO;GAGT,MAAM,CAAC,MAAM,GAAG,QAAQ,MAAM;AAE9B,UAAO;IACL,MAAM,CAAC,GAAG,MAAM,MAAM,MAAM,QAAQ,CAAC,MAAM,CAAC,MAAM;IAClD,SAAS;IACT,QAAQ;IACT;;EAGH,KAAK,QACH,QAAO;GAAE,MAAM,EAAE;GAAE,SAAS,OAAO;GAAO,QAAQ,EAAE;GAAE;EAGxD,QACE,QAAO;;;AASf,MAAM,gBAAgB;AAEtB,MAAM,mBACJ,cACA,YACG;CAGH,MAAM,CAAC,OAAO,YAAY,WAAW,cAFvB,SAAS,SAAS,cAE4B,EAAE;EAC5D,MAAM,EAAE;EACR,SAAS;EACT,QAAQ,EAAE;EACX,CAAC;CAEF,MAAM,WAAW,aAAa,UAAgC;AAC5D,WAAS;GAAE,MAAM;GAAO;GAAO,CAAC;IAC/B,EAAE,CAAC;CAEN,MAAM,OAAO,kBAAkB;AAC7B,WAAS,EAAE,MAAM,QAAQ,CAAC;IACzB,EAAE,CAAC;CAEN,MAAM,OAAO,kBAAkB;AAC7B,WAAS,EAAE,MAAM,QAAQ,CAAC;IACzB,EAAE,CAAC;CAEN,MAAM,QAAQ,aAAa,UAAa;AACtC,WAAS;GAAE,MAAM;GAAS;GAAO,CAAC;IACjC,EAAE,CAAC;AAEN,QAAO;EACL,OAAO,MAAM;EACb;EACA;EACA;EACA;EACA,SAAS,MAAM,KAAK,SAAS;EAC7B,SAAS,MAAM,OAAO,SAAS;EAChC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
//#region src/hooks/use-intersection-observer.d.ts
|
|
2
|
+
declare const useIntersectionObserver: <T extends Element = Element>(options?: IntersectionObserverInit) => [(node: T | null) => void, IntersectionObserverEntry | null];
|
|
3
|
+
//#endregion
|
|
4
|
+
export { useIntersectionObserver };
|
|
5
|
+
//# sourceMappingURL=use-intersection-observer.d.mts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useCallback, useRef, useState } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/hooks/use-intersection-observer.ts
|
|
4
|
+
const useIntersectionObserver = (options) => {
|
|
5
|
+
const [entry, setEntry] = useState(null);
|
|
6
|
+
const observerRef = useRef(null);
|
|
7
|
+
return [useCallback((node) => {
|
|
8
|
+
if (observerRef.current) {
|
|
9
|
+
observerRef.current.disconnect();
|
|
10
|
+
observerRef.current = null;
|
|
11
|
+
}
|
|
12
|
+
if (!node || typeof IntersectionObserver === "undefined") return;
|
|
13
|
+
const observer = new IntersectionObserver(([observedEntry]) => {
|
|
14
|
+
setEntry(observedEntry);
|
|
15
|
+
}, options);
|
|
16
|
+
observer.observe(node);
|
|
17
|
+
observerRef.current = observer;
|
|
18
|
+
}, []), entry];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
export { useIntersectionObserver as default };
|
|
23
|
+
//# sourceMappingURL=use-intersection-observer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-intersection-observer.mjs","names":[],"sources":["../../src/hooks/use-intersection-observer.ts"],"sourcesContent":["import { useCallback, useRef, useState } from 'react';\n\nconst useIntersectionObserver = <T extends Element = Element>(\n options?: IntersectionObserverInit,\n): [(node: T | null) => void, IntersectionObserverEntry | null] => {\n const [entry, setEntry] = useState<IntersectionObserverEntry | null>(null);\n const observerRef = useRef<IntersectionObserver | null>(null);\n\n // `options` is typically re-created as a fresh object on every render by\n // callers (e.g. spreading caller-provided overrides into defaults), so it\n // intentionally isn't a dependency here — that would tear down and\n // recreate the observer on every render. The ref callback only re-runs\n // when the observed node itself changes, capturing whatever `options`\n // was current at that time (matching the common useIntersectionObserver\n // convention, e.g. @uidotdev/usehooks).\n const ref = useCallback((node: T | null) => {\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n\n if (!node || typeof IntersectionObserver === 'undefined') {\n return;\n }\n\n const observer = new IntersectionObserver(([observedEntry]) => {\n setEntry(observedEntry);\n }, options);\n\n observer.observe(node);\n observerRef.current = observer;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [ref, entry];\n};\n\nexport default useIntersectionObserver;\n"],"mappings":";;;AAEA,MAAM,2BACJ,YACiE;CACjE,MAAM,CAAC,OAAO,YAAY,SAA2C,KAAK;CAC1E,MAAM,cAAc,OAAoC,KAAK;AA4B7D,QAAO,CAnBK,aAAa,SAAmB;AAC1C,MAAI,YAAY,SAAS;AACvB,eAAY,QAAQ,YAAY;AAChC,eAAY,UAAU;;AAGxB,MAAI,CAAC,QAAQ,OAAO,yBAAyB,YAC3C;EAGF,MAAM,WAAW,IAAI,sBAAsB,CAAC,mBAAmB;AAC7D,YAAS,cAAc;KACtB,QAAQ;AAEX,WAAS,QAAQ,KAAK;AACtB,cAAY,UAAU;IAErB,EAAE,CAAC,EAEO,MAAM"}
|
package/dist/index.d.mts
CHANGED
|
@@ -3,6 +3,8 @@ import { useBodyScrollLock } from "./hooks/use-body-scroll-lock.mjs";
|
|
|
3
3
|
import { useClickOutside } from "./hooks/use-click-outside.mjs";
|
|
4
4
|
import { useElementPosition } from "./hooks/use-element-position.mjs";
|
|
5
5
|
import { useElementScroll } from "./hooks/use-element-scroll.mjs";
|
|
6
|
+
import { useHistoryState } from "./hooks/use-history-state.mjs";
|
|
7
|
+
import { useIntersectionObserver } from "./hooks/use-intersection-observer.mjs";
|
|
6
8
|
import { useResponsiveSize } from "./hooks/use-responsive-size.mjs";
|
|
7
9
|
import { useImage } from "./hooks/use-image.mjs";
|
|
8
10
|
import { useLocalStorage } from "./hooks/use-local-storage.mjs";
|
|
@@ -13,4 +15,4 @@ import { useTimeline } from "./hooks/use-timeline.mjs";
|
|
|
13
15
|
import { useWindowScroll } from "./hooks/use-window-scroll.mjs";
|
|
14
16
|
import { useViewport } from "./hooks/use-viewport.mjs";
|
|
15
17
|
import "./hooks/index.mjs";
|
|
16
|
-
export { useBodyScrollLock, useClickOutside, useDebounce, useElementPosition, useElementScroll, useImage, useLocalStorage, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useTimeline, useViewport, useWindowScroll };
|
|
18
|
+
export { useBodyScrollLock, useClickOutside, useDebounce, useElementPosition, useElementScroll, useHistoryState, useImage, useIntersectionObserver, useLocalStorage, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useTimeline, useViewport, useWindowScroll };
|
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,8 @@ import useBodyScrollLock from "./hooks/use-body-scroll-lock.mjs";
|
|
|
3
3
|
import useClickOutside from "./hooks/use-click-outside.mjs";
|
|
4
4
|
import useElementPosition from "./hooks/use-element-position.mjs";
|
|
5
5
|
import useElementScroll from "./hooks/use-element-scroll.mjs";
|
|
6
|
+
import useHistoryState from "./hooks/use-history-state.mjs";
|
|
7
|
+
import useIntersectionObserver from "./hooks/use-intersection-observer.mjs";
|
|
6
8
|
import useResponsiveSize from "./hooks/use-responsive-size.mjs";
|
|
7
9
|
import useImage from "./hooks/use-image.mjs";
|
|
8
10
|
import useLocalStorage from "./hooks/use-local-storage.mjs";
|
|
@@ -14,4 +16,4 @@ import useWindowScroll from "./hooks/use-window-scroll.mjs";
|
|
|
14
16
|
import useViewport from "./hooks/use-viewport.mjs";
|
|
15
17
|
import "./hooks/index.mjs";
|
|
16
18
|
|
|
17
|
-
export { useBodyScrollLock, useClickOutside, useDebounce, useElementPosition, useElementScroll, useImage, useLocalStorage, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useTimeline, useViewport, useWindowScroll };
|
|
19
|
+
export { useBodyScrollLock, useClickOutside, useDebounce, useElementPosition, useElementScroll, useHistoryState, useImage, useIntersectionObserver, useLocalStorage, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useTimeline, useViewport, useWindowScroll };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbpark/use-hooks",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "A collection of reusable React 19 hooks for common UI and interaction patterns",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"scripts": {
|
|
42
42
|
"dev": "vite",
|
|
43
43
|
"build": "tsc -b && tsdown",
|
|
44
|
+
"build:demo": "vite build",
|
|
44
45
|
"lint": "eslint .",
|
|
45
46
|
"preview": "vite preview",
|
|
46
47
|
"prepublishOnly": "pnpm build",
|
|
@@ -51,8 +52,10 @@
|
|
|
51
52
|
"react-dom": "^19.1.1"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
55
|
+
"@changesets/cli": "^2.31.1",
|
|
54
56
|
"@eslint/js": "^9.33.0",
|
|
55
57
|
"@jbpark/ui-kit": "^2.7.0",
|
|
58
|
+
"@radix-ui/react-icons": "^1.3.2",
|
|
56
59
|
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
|
57
60
|
"@types/node": "^24.5.2",
|
|
58
61
|
"@types/react": "^19.1.10",
|