@jbpark/use-hooks 3.0.0 → 4.0.1
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 +40 -22
- package/README.md +40 -22
- package/dist/hooks/index.d.mts +31 -30
- package/dist/hooks/index.mjs +3 -4
- package/dist/hooks/use-body-scroll-lock.d.mts +1 -1
- package/dist/hooks/use-body-scroll-lock.mjs +1 -2
- package/dist/hooks/use-body-scroll-lock.mjs.map +1 -1
- package/dist/hooks/use-click-outside.d.mts +1 -2
- package/dist/hooks/use-click-outside.mjs +4 -4
- package/dist/hooks/use-click-outside.mjs.map +1 -1
- package/dist/hooks/use-controllable-state.d.mts +1 -1
- package/dist/hooks/use-controllable-state.mjs +1 -2
- package/dist/hooks/use-controllable-state.mjs.map +1 -1
- package/dist/hooks/use-debounced-callback.d.mts +10 -0
- package/dist/hooks/{use-debounce.mjs → use-debounced-callback.mjs} +13 -13
- package/dist/hooks/use-debounced-callback.mjs.map +1 -0
- package/dist/hooks/use-debounced-value.d.mts +1 -1
- package/dist/hooks/use-debounced-value.mjs +1 -2
- package/dist/hooks/use-debounced-value.mjs.map +1 -1
- package/dist/hooks/use-element-position.d.mts +1 -2
- package/dist/hooks/use-element-position.mjs +1 -2
- package/dist/hooks/use-element-position.mjs.map +1 -1
- package/dist/hooks/use-element-scroll.d.mts +2 -4
- package/dist/hooks/use-element-scroll.mjs +3 -3
- package/dist/hooks/use-element-scroll.mjs.map +1 -1
- package/dist/hooks/use-event-listener.d.mts +1 -2
- package/dist/hooks/use-event-listener.mjs +22 -10
- package/dist/hooks/use-event-listener.mjs.map +1 -1
- package/dist/hooks/use-file-drop.d.mts +1 -1
- package/dist/hooks/use-file-drop.mjs +7 -6
- package/dist/hooks/use-file-drop.mjs.map +1 -1
- package/dist/hooks/use-file-to-data-url.d.mts +1 -1
- package/dist/hooks/use-file-to-data-url.mjs +1 -2
- package/dist/hooks/use-file-to-data-url.mjs.map +1 -1
- package/dist/hooks/use-history-state.d.mts +1 -1
- package/dist/hooks/use-history-state.mjs +3 -3
- package/dist/hooks/use-history-state.mjs.map +1 -1
- package/dist/hooks/use-image.d.mts +1 -1
- package/dist/hooks/use-image.mjs +24 -8
- package/dist/hooks/use-image.mjs.map +1 -1
- package/dist/hooks/use-intersection-observer.d.mts +1 -1
- package/dist/hooks/use-intersection-observer.mjs +1 -2
- package/dist/hooks/use-intersection-observer.mjs.map +1 -1
- package/dist/hooks/use-interval.d.mts +1 -1
- package/dist/hooks/use-interval.mjs +1 -2
- package/dist/hooks/use-interval.mjs.map +1 -1
- package/dist/hooks/use-key-press.d.mts +1 -2
- package/dist/hooks/use-key-press.mjs +36 -12
- package/dist/hooks/use-key-press.mjs.map +1 -1
- package/dist/hooks/use-local-storage.d.mts +1 -1
- package/dist/hooks/use-local-storage.mjs +5 -3
- package/dist/hooks/use-local-storage.mjs.map +1 -1
- package/dist/hooks/use-merged-ref.d.mts +2 -3
- package/dist/hooks/use-merged-ref.mjs +13 -8
- package/dist/hooks/use-merged-ref.mjs.map +1 -1
- package/dist/hooks/use-multi-select.d.mts +1 -1
- package/dist/hooks/use-multi-select.mjs +10 -9
- package/dist/hooks/use-multi-select.mjs.map +1 -1
- package/dist/hooks/use-mutation-observer.d.mts +1 -2
- package/dist/hooks/use-mutation-observer.mjs +24 -10
- package/dist/hooks/use-mutation-observer.mjs.map +1 -1
- package/dist/hooks/use-previous.d.mts +1 -1
- package/dist/hooks/use-previous.mjs +1 -2
- package/dist/hooks/use-previous.mjs.map +1 -1
- package/dist/hooks/use-recursive-timeout.d.mts +1 -1
- package/dist/hooks/use-recursive-timeout.mjs +1 -2
- package/dist/hooks/use-recursive-timeout.mjs.map +1 -1
- package/dist/hooks/use-resize-observer.d.mts +1 -1
- package/dist/hooks/use-resize-observer.mjs +34 -11
- package/dist/hooks/use-resize-observer.mjs.map +1 -1
- package/dist/hooks/use-responsive-size.d.mts +1 -1
- package/dist/hooks/use-responsive-size.mjs +3 -4
- package/dist/hooks/use-responsive-size.mjs.map +1 -1
- package/dist/hooks/use-scroll-to-elements.d.mts +1 -2
- package/dist/hooks/use-scroll-to-elements.mjs +1 -2
- package/dist/hooks/use-scroll-to-elements.mjs.map +1 -1
- package/dist/hooks/use-throttled-callback.d.mts +2 -5
- package/dist/hooks/use-throttled-callback.mjs +6 -8
- package/dist/hooks/use-throttled-callback.mjs.map +1 -1
- package/dist/hooks/use-throttled-value.d.mts +9 -0
- package/dist/hooks/{use-throttle.mjs → use-throttled-value.mjs} +5 -6
- package/dist/hooks/use-throttled-value.mjs.map +1 -0
- package/dist/hooks/use-timeout.d.mts +1 -1
- package/dist/hooks/use-timeout.mjs +1 -2
- package/dist/hooks/use-timeout.mjs.map +1 -1
- package/dist/hooks/use-toggle.d.mts +1 -1
- package/dist/hooks/use-toggle.mjs +1 -2
- package/dist/hooks/use-toggle.mjs.map +1 -1
- package/dist/hooks/use-viewport.d.mts +1 -1
- package/dist/hooks/use-viewport.mjs +1 -2
- package/dist/hooks/use-viewport.mjs.map +1 -1
- package/dist/hooks/use-window-scroll.d.mts +1 -2
- package/dist/hooks/use-window-scroll.mjs +1 -2
- package/dist/hooks/use-window-scroll.mjs.map +1 -1
- package/dist/index.d.mts +31 -31
- package/dist/index.mjs +3 -4
- package/package.json +26 -26
- package/dist/hooks/use-debounce.d.mts +0 -14
- package/dist/hooks/use-debounce.mjs.map +0 -1
- package/dist/hooks/use-throttle.d.mts +0 -9
- package/dist/hooks/use-throttle.mjs.map +0 -1
package/README.ko.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
## 기능
|
|
13
13
|
|
|
14
|
-
- 📦 **
|
|
14
|
+
- 📦 **30개 프로덕션 레디 훅** - 스크롤, 뷰포트, 스토리지, 옵저버, 이벤트, 타이밍 등 다양한 유틸리티
|
|
15
15
|
- 🎯 **TypeScript 지원** - 완전한 타입 지원으로 더 나은 개발 경험
|
|
16
16
|
- ⚡ **트리 셰이킹 지원** - 필요한 것만 임포트하세요
|
|
17
17
|
- 🔒 **SSR 안전** - window/document 전역 변수에 대한 보호
|
|
@@ -36,7 +36,7 @@ pnpm add @jbpark/use-hooks
|
|
|
36
36
|
import {
|
|
37
37
|
useLocalStorage,
|
|
38
38
|
useResponsiveSize,
|
|
39
|
-
|
|
39
|
+
useThrottledValue,
|
|
40
40
|
useWindowScroll,
|
|
41
41
|
} from '@jbpark/use-hooks';
|
|
42
42
|
|
|
@@ -51,7 +51,7 @@ function MyComponent() {
|
|
|
51
51
|
const { size, breakpoint, ref } = useResponsiveSize();
|
|
52
52
|
|
|
53
53
|
// 너비 업데이트를 스로틀링
|
|
54
|
-
const throttledWidth =
|
|
54
|
+
const throttledWidth = useThrottledValue(size.width, 200);
|
|
55
55
|
|
|
56
56
|
return (
|
|
57
57
|
<div ref={ref}>
|
|
@@ -67,25 +67,43 @@ function MyComponent() {
|
|
|
67
67
|
|
|
68
68
|
## 사용 가능한 훅
|
|
69
69
|
|
|
70
|
-
| 훅 | 설명
|
|
71
|
-
| ------------------------- |
|
|
72
|
-
| `useLocalStorage` | 에러 핸들링이 포함된 JSON 기반 영속 상태 (SSR 안전)
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
79
|
-
| `
|
|
80
|
-
| `
|
|
81
|
-
| `
|
|
82
|
-
| `useViewport` | visualViewport 지원, 인앱 모드 옵션, debounce 포함
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
70
|
+
| 훅 | 설명 |
|
|
71
|
+
| ------------------------- | ------------------------------------------------------------------------------------------- |
|
|
72
|
+
| `useLocalStorage` | 에러 핸들링이 포함된 JSON 기반 영속 상태 (SSR 안전) |
|
|
73
|
+
| `useHistoryState` | 히스토리 개수 제한을 설정할 수 있는 실행 취소/다시 실행 상태 관리 |
|
|
74
|
+
| `useControllableState` | value/defaultValue/onChange prop 조합을 훅 하나로 제어/비제어 상태 관리 |
|
|
75
|
+
| `usePrevious` | 이전 렌더 시점의 값을 반환 |
|
|
76
|
+
| `useToggle` | 토글 함수와 명시적 setter를 제공하는 불리언 상태 |
|
|
77
|
+
| `useMultiSelect` | shift-클릭 범위 선택을 지원하는 체크박스 스타일 다중 선택 |
|
|
78
|
+
| `useWindowScroll` | 윈도우 스크롤 위치 및 백분율 추적 (iOS visualViewport 대응) |
|
|
79
|
+
| `useElementScroll` | ResizeObserver를 사용한 특정 요소의 스크롤 상태 추적 |
|
|
80
|
+
| `useElementPosition` | 스크롤/리사이즈 시 요소의 바운딩 렉트 모니터링 (요소 참조 지원) |
|
|
81
|
+
| `useResponsiveSize` | Tailwind 유사 브레이크포인트를 포함한 요소 크기 추적 (debounce) |
|
|
82
|
+
| `useViewport` | visualViewport 지원, 인앱 모드 옵션, debounce 포함 |
|
|
83
|
+
| `useScrollToElements` | 키로 요소를 등록하고 키로 스크롤 (오프셋 조절, 컨테이너 지정 가능) |
|
|
84
|
+
| `useBodyScrollLock` | 스타일 보존을 포함한 바디 스크롤 잠금/해제 (iOS 특별 처리) |
|
|
85
|
+
| `useIntersectionObserver` | 뷰포트 교차 추적, `[ref, { entry, isIntersecting }]` 반환, `freezeOnceVisible` 옵션 |
|
|
86
|
+
| `useResizeObserver` | 콜백 ref로 요소 자체의 너비/높이 추적 (content-box/border-box) |
|
|
87
|
+
| `useMutationObserver` | ref 또는 raw 노드(예: `document.head`)의 DOM 변경 관측 |
|
|
88
|
+
| `useEventListener` | `window`, ref, raw 타겟에 이벤트 리스너 등록/해제 |
|
|
89
|
+
| `useClickOutside` | 참조한 요소(들) 바깥 클릭/터치(또는 opt-in `escape`로 `Escape`) 시 콜백 실행, 다중 ref 지원 |
|
|
90
|
+
| `useKeyPress` | `mod`/`ctrl`/`meta`/`shift`/`alt` 조합과 `space`/`esc` 별칭으로 키 콜백 실행 |
|
|
91
|
+
| `useFileDrop` | `accept`/`multiple` 필터링을 갖춘 드래그앤드롭 존, `{ dropRef, isDragging }` 반환 |
|
|
92
|
+
| `useFileToDataUrl` | `File`/`Blob`을 data URL로 변환 |
|
|
93
|
+
| `useDebouncedCallback` | deps 변경 시 debounce된 콜백 자동 실행 (`leading`/`autoInvoke` 옵션); 별칭: `useDebounce` |
|
|
94
|
+
| `useDebouncedValue` | 변하는 값을 일정 지연으로 debounce |
|
|
95
|
+
| `useThrottledValue` | 값 업데이트를 일정 간격으로 제한; 별칭: `useThrottle` |
|
|
96
|
+
| `useThrottledCallback` | 콜백을 일정 간격으로 제한 |
|
|
97
|
+
| `useTimeout` | 지연 후 콜백 1회 실행, `{ reset, clear }` 반환 (`null`이면 일시정지) |
|
|
98
|
+
| `useInterval` | 일정 간격으로 콜백 실행 (`null`이면 일시정지) |
|
|
99
|
+
| `useRecursiveTimeout` | 비동기/동기 콜백을 재귀적으로 스케줄링 |
|
|
100
|
+
| `useMergedRef` | 여러 객체/콜백 ref를 하나의 콜백 ref로 병합 |
|
|
101
|
+
| `useImage` | 이미지 사전로드 및 `loading`/`error`(`Error`)/`loaded`/`attemptCount`/`retry` 노출 |
|
|
102
|
+
|
|
103
|
+
## v2에서 마이그레이션
|
|
104
|
+
|
|
105
|
+
v3.0.0은 breaking change가 여럿 포함된 메이저 릴리스입니다. 전후 예제는
|
|
106
|
+
[마이그레이션 가이드](./MIGRATION.ko.md)를 참고하세요.
|
|
89
107
|
|
|
90
108
|
## 개발
|
|
91
109
|
|
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
|
+
- 📦 **30 Production-Ready Hooks** - Utilities for scrolling, viewport, storage, observers, events, timing, 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
|
|
@@ -36,7 +36,7 @@ pnpm add @jbpark/use-hooks
|
|
|
36
36
|
import {
|
|
37
37
|
useLocalStorage,
|
|
38
38
|
useResponsiveSize,
|
|
39
|
-
|
|
39
|
+
useThrottledValue,
|
|
40
40
|
useWindowScroll,
|
|
41
41
|
} from '@jbpark/use-hooks';
|
|
42
42
|
|
|
@@ -51,7 +51,7 @@ function MyComponent() {
|
|
|
51
51
|
const { size, breakpoint, ref } = useResponsiveSize();
|
|
52
52
|
|
|
53
53
|
// Throttled width update
|
|
54
|
-
const throttledWidth =
|
|
54
|
+
const throttledWidth = useThrottledValue(size.width, 200);
|
|
55
55
|
|
|
56
56
|
return (
|
|
57
57
|
<div ref={ref}>
|
|
@@ -67,25 +67,43 @@ function MyComponent() {
|
|
|
67
67
|
|
|
68
68
|
## Available Hooks
|
|
69
69
|
|
|
70
|
-
| Hook | Description
|
|
71
|
-
| ------------------------- |
|
|
72
|
-
| `useLocalStorage` | JSON-based persistent state with error handling (SSR-safe)
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
79
|
-
| `
|
|
80
|
-
| `
|
|
81
|
-
| `
|
|
82
|
-
| `useViewport` | visualViewport support with in-app mode option and debounce
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
70
|
+
| Hook | Description |
|
|
71
|
+
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
72
|
+
| `useLocalStorage` | JSON-based persistent state with error handling (SSR-safe) |
|
|
73
|
+
| `useHistoryState` | Undo/redo state management with a configurable history limit |
|
|
74
|
+
| `useControllableState` | Back a value/defaultValue/onChange prop pair with one controlled/uncontrolled state hook |
|
|
75
|
+
| `usePrevious` | Return a value as it was on the previous render |
|
|
76
|
+
| `useToggle` | Boolean state with a toggle and an explicit setter |
|
|
77
|
+
| `useMultiSelect` | Checkbox-style multi-select for a list, with shift-click range selection |
|
|
78
|
+
| `useWindowScroll` | Track window scroll position and percentage (iOS visualViewport compatible) |
|
|
79
|
+
| `useElementScroll` | Monitor scroll state of specific elements using ResizeObserver |
|
|
80
|
+
| `useElementPosition` | Monitor element bounding rect on scroll/resize (element ref support) |
|
|
81
|
+
| `useResponsiveSize` | Track element size with Tailwind-like breakpoints (debounced) |
|
|
82
|
+
| `useViewport` | visualViewport support with in-app mode option and debounce |
|
|
83
|
+
| `useScrollToElements` | Register elements by key and scroll to them by key (adjustable offset, optional container) |
|
|
84
|
+
| `useBodyScrollLock` | Lock/unlock body scroll with style preservation (iOS-specific handling) |
|
|
85
|
+
| `useIntersectionObserver` | Track viewport intersection; returns `[ref, { entry, isIntersecting }]` with optional `freezeOnceVisible` |
|
|
86
|
+
| `useResizeObserver` | Track an element's own width/height via a callback ref (content-box or border-box) |
|
|
87
|
+
| `useMutationObserver` | Observe DOM mutations on a ref or a raw node (e.g. `document.head`) |
|
|
88
|
+
| `useEventListener` | Add/remove an event listener on `window`, a ref, or a raw target |
|
|
89
|
+
| `useClickOutside` | Run a callback when a click/touch (or `Escape`, opt-in via `escape`) happens outside the referenced element(s); accepts multiple refs |
|
|
90
|
+
| `useKeyPress` | Run a callback on key combos with `mod`/`ctrl`/`meta`/`shift`/`alt` and aliases like `space`/`esc` |
|
|
91
|
+
| `useFileDrop` | Drag-and-drop file zone with `accept`/`multiple` filtering; returns `{ dropRef, isDragging }` |
|
|
92
|
+
| `useFileToDataUrl` | Read a `File`/`Blob` into a data URL |
|
|
93
|
+
| `useDebouncedCallback` | Auto-invoke a debounced callback when deps change (`leading`/`autoInvoke` options); alias: `useDebounce` |
|
|
94
|
+
| `useDebouncedValue` | Debounce a changing value to a fixed delay |
|
|
95
|
+
| `useThrottledValue` | Throttle value updates to a fixed interval; alias: `useThrottle` |
|
|
96
|
+
| `useThrottledCallback` | Throttle a callback to a fixed interval |
|
|
97
|
+
| `useTimeout` | Run a callback once after a delay; returns `{ reset, clear }` (`null` pauses) |
|
|
98
|
+
| `useInterval` | Run a callback on an interval (`null` pauses) |
|
|
99
|
+
| `useRecursiveTimeout` | Recursively schedule async/sync callbacks |
|
|
100
|
+
| `useMergedRef` | Merge multiple object/callback refs into one callback ref |
|
|
101
|
+
| `useImage` | Preload an image and expose `loading`/`error` (an `Error`)/`loaded`/`attemptCount`/`retry` |
|
|
102
|
+
|
|
103
|
+
## Migrating from v2
|
|
104
|
+
|
|
105
|
+
v3.0.0 is a major release with several breaking changes. See the
|
|
106
|
+
[Migration Guide](./MIGRATION.md) for before/after examples.
|
|
89
107
|
|
|
90
108
|
## Development
|
|
91
109
|
|
package/dist/hooks/index.d.mts
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import
|
|
20
|
-
import
|
|
21
|
-
import
|
|
22
|
-
import
|
|
23
|
-
import
|
|
24
|
-
import
|
|
25
|
-
import
|
|
26
|
-
import
|
|
27
|
-
import
|
|
28
|
-
import
|
|
29
|
-
import
|
|
30
|
-
import
|
|
1
|
+
import useDebouncedCallback from "./use-debounced-callback.mjs";
|
|
2
|
+
import useDebouncedValue from "./use-debounced-value.mjs";
|
|
3
|
+
import useBodyScrollLock from "./use-body-scroll-lock.mjs";
|
|
4
|
+
import useClickOutside from "./use-click-outside.mjs";
|
|
5
|
+
import useControllableState from "./use-controllable-state.mjs";
|
|
6
|
+
import useElementPosition from "./use-element-position.mjs";
|
|
7
|
+
import useElementScroll from "./use-element-scroll.mjs";
|
|
8
|
+
import useEventListener from "./use-event-listener.mjs";
|
|
9
|
+
import useFileDrop from "./use-file-drop.mjs";
|
|
10
|
+
import useFileToDataUrl from "./use-file-to-data-url.mjs";
|
|
11
|
+
import useHistoryState from "./use-history-state.mjs";
|
|
12
|
+
import useIntersectionObserver from "./use-intersection-observer.mjs";
|
|
13
|
+
import useInterval from "./use-interval.mjs";
|
|
14
|
+
import useResponsiveSize from "./use-responsive-size.mjs";
|
|
15
|
+
import useImage from "./use-image.mjs";
|
|
16
|
+
import useKeyPress from "./use-key-press.mjs";
|
|
17
|
+
import useLocalStorage from "./use-local-storage.mjs";
|
|
18
|
+
import useMergedRef from "./use-merged-ref.mjs";
|
|
19
|
+
import useMultiSelect from "./use-multi-select.mjs";
|
|
20
|
+
import useMutationObserver from "./use-mutation-observer.mjs";
|
|
21
|
+
import usePrevious from "./use-previous.mjs";
|
|
22
|
+
import useRecursiveTimeout from "./use-recursive-timeout.mjs";
|
|
23
|
+
import useResizeObserver from "./use-resize-observer.mjs";
|
|
24
|
+
import useScrollToElements from "./use-scroll-to-elements.mjs";
|
|
25
|
+
import useThrottledValue from "./use-throttled-value.mjs";
|
|
26
|
+
import useThrottledCallback from "./use-throttled-callback.mjs";
|
|
27
|
+
import useTimeout from "./use-timeout.mjs";
|
|
28
|
+
import useToggle from "./use-toggle.mjs";
|
|
29
|
+
import useWindowScroll from "./use-window-scroll.mjs";
|
|
30
|
+
import useViewport from "./use-viewport.mjs";
|
|
31
|
+
export { useBodyScrollLock, useClickOutside, useControllableState, useDebouncedCallback as useDebounce, useDebouncedCallback, useDebouncedValue, useElementPosition, useElementScroll, useEventListener, useFileDrop, useFileToDataUrl, useHistoryState, useImage, useIntersectionObserver, useInterval, useKeyPress, useLocalStorage, useMergedRef, useMultiSelect, useMutationObserver, usePrevious, useRecursiveTimeout, useResizeObserver, useResponsiveSize, useScrollToElements, useThrottledValue as useThrottle, useThrottledCallback, useThrottledValue, useTimeout, useToggle, useViewport, useWindowScroll };
|
package/dist/hooks/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import useDebouncedCallback from "./use-debounced-callback.mjs";
|
|
2
2
|
import useDebouncedValue from "./use-debounced-value.mjs";
|
|
3
3
|
import useBodyScrollLock from "./use-body-scroll-lock.mjs";
|
|
4
4
|
import useClickOutside from "./use-click-outside.mjs";
|
|
@@ -23,10 +23,9 @@ import useRecursiveTimeout from "./use-recursive-timeout.mjs";
|
|
|
23
23
|
import useResizeObserver from "./use-resize-observer.mjs";
|
|
24
24
|
import useScrollToElements from "./use-scroll-to-elements.mjs";
|
|
25
25
|
import useThrottledCallback from "./use-throttled-callback.mjs";
|
|
26
|
-
import
|
|
26
|
+
import useThrottledValue from "./use-throttled-value.mjs";
|
|
27
27
|
import useTimeout from "./use-timeout.mjs";
|
|
28
28
|
import useToggle from "./use-toggle.mjs";
|
|
29
29
|
import useWindowScroll from "./use-window-scroll.mjs";
|
|
30
30
|
import useViewport from "./use-viewport.mjs";
|
|
31
|
-
|
|
32
|
-
export { };
|
|
31
|
+
export { useBodyScrollLock, useClickOutside, useControllableState, useDebouncedCallback as useDebounce, useDebouncedCallback, useDebouncedValue, useElementPosition, useElementScroll, useEventListener, useFileDrop, useFileToDataUrl, useHistoryState, useImage, useIntersectionObserver, useInterval, useKeyPress, useLocalStorage, useMergedRef, useMultiSelect, useMutationObserver, usePrevious, useRecursiveTimeout, useResizeObserver, useResponsiveSize, useScrollToElements, useThrottledValue as useThrottle, useThrottledCallback, useThrottledValue, useTimeout, useToggle, useViewport, useWindowScroll };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useEffect } from "react";
|
|
2
|
-
|
|
3
2
|
//#region src/hooks/use-body-scroll-lock.ts
|
|
4
3
|
const activeLocks = /* @__PURE__ */ new Set();
|
|
5
4
|
let originalStyles = null;
|
|
@@ -86,7 +85,7 @@ const useBodyScrollLock = (enabled = true) => {
|
|
|
86
85
|
};
|
|
87
86
|
}, [enabled]);
|
|
88
87
|
};
|
|
89
|
-
|
|
90
88
|
//#endregion
|
|
91
89
|
export { useBodyScrollLock as default };
|
|
90
|
+
|
|
92
91
|
//# sourceMappingURL=use-body-scroll-lock.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-body-scroll-lock.mjs","names":[],"sources":["../../src/hooks/use-body-scroll-lock.ts"],"sourcesContent":["import { useEffect } from 'react';\n\ninterface LockedStyles {\n documentElement: {\n overflow: string;\n height: string;\n position: string;\n width: string;\n };\n body: {\n overflow: string;\n height: string;\n position: string;\n top: string;\n left: string;\n right: string;\n width: string;\n paddingRight: string;\n webkitOverflowScrolling: string;\n };\n}\n\n// A Set of per-instance tokens instead of a raw counter — deleting the same\n// token twice (or locking with a token that's already in the set) can't\n// drift the count the way increment/decrement pairs could.\nconst activeLocks = new Set<symbol>();\nlet originalStyles: LockedStyles | null = null;\nlet originalScrollY = 0;\n\nconst isIOS = () =>\n typeof navigator !== 'undefined' &&\n /iPad|iPhone|iPod/.test(navigator.userAgent);\n\nconst lockScroll = (token: symbol) => {\n if (activeLocks.size === 0) {\n originalScrollY = window.scrollY;\n const scrollbarWidth =\n window.innerWidth - document.documentElement.clientWidth;\n const currentPaddingRight =\n parseFloat(getComputedStyle(document.body).paddingRight) || 0;\n\n originalStyles = {\n documentElement: {\n overflow: document.documentElement.style.overflow,\n height: document.documentElement.style.height,\n position: document.documentElement.style.position,\n width: document.documentElement.style.width,\n },\n body: {\n overflow: document.body.style.overflow,\n height: document.body.style.height,\n position: document.body.style.position,\n top: document.body.style.top,\n left: document.body.style.left,\n right: document.body.style.right,\n width: document.body.style.width,\n paddingRight: document.body.style.paddingRight,\n webkitOverflowScrolling: document.body.style.getPropertyValue(\n '-webkit-overflow-scrolling',\n ),\n },\n };\n\n document.documentElement.style.overflow = 'hidden';\n document.documentElement.style.height = '100%';\n document.documentElement.style.position = 'fixed';\n document.documentElement.style.width = '100%';\n\n document.body.style.overflow = 'hidden';\n document.body.style.height = '100%';\n document.body.style.position = 'fixed';\n document.body.style.top = `-${originalScrollY}px`;\n document.body.style.left = '0';\n document.body.style.right = '0';\n document.body.style.width = '100%';\n\n if (scrollbarWidth > 0) {\n document.body.style.paddingRight = `${currentPaddingRight + scrollbarWidth}px`;\n }\n\n if (isIOS()) {\n document.body.style.setProperty('-webkit-overflow-scrolling', 'touch');\n }\n }\n\n activeLocks.add(token);\n};\n\nconst unlockScroll = (token: symbol) => {\n activeLocks.delete(token);\n\n if (activeLocks.size > 0 || !originalStyles) {\n return;\n }\n\n document.documentElement.style.overflow =\n originalStyles.documentElement.overflow;\n document.documentElement.style.height = originalStyles.documentElement.height;\n document.documentElement.style.position =\n originalStyles.documentElement.position;\n document.documentElement.style.width = originalStyles.documentElement.width;\n\n document.body.style.overflow = originalStyles.body.overflow;\n document.body.style.height = originalStyles.body.height;\n document.body.style.position = originalStyles.body.position;\n document.body.style.top = originalStyles.body.top;\n document.body.style.left = originalStyles.body.left;\n document.body.style.right = originalStyles.body.right;\n document.body.style.width = originalStyles.body.width;\n document.body.style.paddingRight = originalStyles.body.paddingRight;\n\n if (originalStyles.body.webkitOverflowScrolling) {\n document.body.style.setProperty(\n '-webkit-overflow-scrolling',\n originalStyles.body.webkitOverflowScrolling,\n );\n } else {\n document.body.style.removeProperty('-webkit-overflow-scrolling');\n }\n\n window.scrollTo(0, originalScrollY);\n originalStyles = null;\n};\n\nconst useBodyScrollLock = (enabled: boolean = true) => {\n useEffect(() => {\n if (typeof window === 'undefined' || !enabled) {\n return;\n }\n\n const token = Symbol('body-scroll-lock');\n lockScroll(token);\n\n // The position:fixed styles above are the real lock — this is only a\n // narrow safety net for iOS Safari, which can still rubber-band the\n // page on a touchmove that starts directly on the body/html background.\n // Only that one event is handled (no capture, no stopPropagation), so\n // a scrollable element inside the locked content (e.g. a modal body)\n // keeps scrolling normally — its touchmove target isn't body/html.\n let removeTouchListener: (() => void) | undefined;\n\n if (isIOS()) {\n const preventTouchMove = (event: TouchEvent) => {\n if (\n event.target === document.body ||\n event.target === document.documentElement\n ) {\n event.preventDefault();\n }\n };\n\n document.addEventListener('touchmove', preventTouchMove, {\n passive: false,\n });\n\n removeTouchListener = () => {\n document.removeEventListener('touchmove', preventTouchMove);\n };\n }\n\n return () => {\n removeTouchListener?.();\n unlockScroll(token);\n };\n }, [enabled]);\n};\n\nexport default useBodyScrollLock;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-body-scroll-lock.mjs","names":[],"sources":["../../src/hooks/use-body-scroll-lock.ts"],"sourcesContent":["import { useEffect } from 'react';\n\ninterface LockedStyles {\n documentElement: {\n overflow: string;\n height: string;\n position: string;\n width: string;\n };\n body: {\n overflow: string;\n height: string;\n position: string;\n top: string;\n left: string;\n right: string;\n width: string;\n paddingRight: string;\n webkitOverflowScrolling: string;\n };\n}\n\n// A Set of per-instance tokens instead of a raw counter — deleting the same\n// token twice (or locking with a token that's already in the set) can't\n// drift the count the way increment/decrement pairs could.\nconst activeLocks = new Set<symbol>();\nlet originalStyles: LockedStyles | null = null;\nlet originalScrollY = 0;\n\nconst isIOS = () =>\n typeof navigator !== 'undefined' &&\n /iPad|iPhone|iPod/.test(navigator.userAgent);\n\nconst lockScroll = (token: symbol) => {\n if (activeLocks.size === 0) {\n originalScrollY = window.scrollY;\n const scrollbarWidth =\n window.innerWidth - document.documentElement.clientWidth;\n const currentPaddingRight =\n parseFloat(getComputedStyle(document.body).paddingRight) || 0;\n\n originalStyles = {\n documentElement: {\n overflow: document.documentElement.style.overflow,\n height: document.documentElement.style.height,\n position: document.documentElement.style.position,\n width: document.documentElement.style.width,\n },\n body: {\n overflow: document.body.style.overflow,\n height: document.body.style.height,\n position: document.body.style.position,\n top: document.body.style.top,\n left: document.body.style.left,\n right: document.body.style.right,\n width: document.body.style.width,\n paddingRight: document.body.style.paddingRight,\n webkitOverflowScrolling: document.body.style.getPropertyValue(\n '-webkit-overflow-scrolling',\n ),\n },\n };\n\n document.documentElement.style.overflow = 'hidden';\n document.documentElement.style.height = '100%';\n document.documentElement.style.position = 'fixed';\n document.documentElement.style.width = '100%';\n\n document.body.style.overflow = 'hidden';\n document.body.style.height = '100%';\n document.body.style.position = 'fixed';\n document.body.style.top = `-${originalScrollY}px`;\n document.body.style.left = '0';\n document.body.style.right = '0';\n document.body.style.width = '100%';\n\n if (scrollbarWidth > 0) {\n document.body.style.paddingRight = `${currentPaddingRight + scrollbarWidth}px`;\n }\n\n if (isIOS()) {\n document.body.style.setProperty('-webkit-overflow-scrolling', 'touch');\n }\n }\n\n activeLocks.add(token);\n};\n\nconst unlockScroll = (token: symbol) => {\n activeLocks.delete(token);\n\n if (activeLocks.size > 0 || !originalStyles) {\n return;\n }\n\n document.documentElement.style.overflow =\n originalStyles.documentElement.overflow;\n document.documentElement.style.height = originalStyles.documentElement.height;\n document.documentElement.style.position =\n originalStyles.documentElement.position;\n document.documentElement.style.width = originalStyles.documentElement.width;\n\n document.body.style.overflow = originalStyles.body.overflow;\n document.body.style.height = originalStyles.body.height;\n document.body.style.position = originalStyles.body.position;\n document.body.style.top = originalStyles.body.top;\n document.body.style.left = originalStyles.body.left;\n document.body.style.right = originalStyles.body.right;\n document.body.style.width = originalStyles.body.width;\n document.body.style.paddingRight = originalStyles.body.paddingRight;\n\n if (originalStyles.body.webkitOverflowScrolling) {\n document.body.style.setProperty(\n '-webkit-overflow-scrolling',\n originalStyles.body.webkitOverflowScrolling,\n );\n } else {\n document.body.style.removeProperty('-webkit-overflow-scrolling');\n }\n\n window.scrollTo(0, originalScrollY);\n originalStyles = null;\n};\n\nconst useBodyScrollLock = (enabled: boolean = true) => {\n useEffect(() => {\n if (typeof window === 'undefined' || !enabled) {\n return;\n }\n\n const token = Symbol('body-scroll-lock');\n lockScroll(token);\n\n // The position:fixed styles above are the real lock — this is only a\n // narrow safety net for iOS Safari, which can still rubber-band the\n // page on a touchmove that starts directly on the body/html background.\n // Only that one event is handled (no capture, no stopPropagation), so\n // a scrollable element inside the locked content (e.g. a modal body)\n // keeps scrolling normally — its touchmove target isn't body/html.\n let removeTouchListener: (() => void) | undefined;\n\n if (isIOS()) {\n const preventTouchMove = (event: TouchEvent) => {\n if (\n event.target === document.body ||\n event.target === document.documentElement\n ) {\n event.preventDefault();\n }\n };\n\n document.addEventListener('touchmove', preventTouchMove, {\n passive: false,\n });\n\n removeTouchListener = () => {\n document.removeEventListener('touchmove', preventTouchMove);\n };\n }\n\n return () => {\n removeTouchListener?.();\n unlockScroll(token);\n };\n }, [enabled]);\n};\n\nexport default useBodyScrollLock;\n"],"mappings":";;AAyBA,MAAM,8BAAc,IAAI,IAAY;AACpC,IAAI,iBAAsC;AAC1C,IAAI,kBAAkB;AAEtB,MAAM,cACJ,OAAO,cAAc,eACrB,mBAAmB,KAAK,UAAU,SAAS;AAE7C,MAAM,cAAc,UAAkB;CACpC,IAAI,YAAY,SAAS,GAAG;EAC1B,kBAAkB,OAAO;EACzB,MAAM,iBACJ,OAAO,aAAa,SAAS,gBAAgB;EAC/C,MAAM,sBACJ,WAAW,iBAAiB,SAAS,IAAI,CAAC,CAAC,YAAY,KAAK;EAE9D,iBAAiB;GACf,iBAAiB;IACf,UAAU,SAAS,gBAAgB,MAAM;IACzC,QAAQ,SAAS,gBAAgB,MAAM;IACvC,UAAU,SAAS,gBAAgB,MAAM;IACzC,OAAO,SAAS,gBAAgB,MAAM;GACxC;GACA,MAAM;IACJ,UAAU,SAAS,KAAK,MAAM;IAC9B,QAAQ,SAAS,KAAK,MAAM;IAC5B,UAAU,SAAS,KAAK,MAAM;IAC9B,KAAK,SAAS,KAAK,MAAM;IACzB,MAAM,SAAS,KAAK,MAAM;IAC1B,OAAO,SAAS,KAAK,MAAM;IAC3B,OAAO,SAAS,KAAK,MAAM;IAC3B,cAAc,SAAS,KAAK,MAAM;IAClC,yBAAyB,SAAS,KAAK,MAAM,iBAC3C,4BACF;GACF;EACF;EAEA,SAAS,gBAAgB,MAAM,WAAW;EAC1C,SAAS,gBAAgB,MAAM,SAAS;EACxC,SAAS,gBAAgB,MAAM,WAAW;EAC1C,SAAS,gBAAgB,MAAM,QAAQ;EAEvC,SAAS,KAAK,MAAM,WAAW;EAC/B,SAAS,KAAK,MAAM,SAAS;EAC7B,SAAS,KAAK,MAAM,WAAW;EAC/B,SAAS,KAAK,MAAM,MAAM,IAAI,gBAAgB;EAC9C,SAAS,KAAK,MAAM,OAAO;EAC3B,SAAS,KAAK,MAAM,QAAQ;EAC5B,SAAS,KAAK,MAAM,QAAQ;EAE5B,IAAI,iBAAiB,GACnB,SAAS,KAAK,MAAM,eAAe,GAAG,sBAAsB,eAAe;EAG7E,IAAI,MAAM,GACR,SAAS,KAAK,MAAM,YAAY,8BAA8B,OAAO;CAEzE;CAEA,YAAY,IAAI,KAAK;AACvB;AAEA,MAAM,gBAAgB,UAAkB;CACtC,YAAY,OAAO,KAAK;CAExB,IAAI,YAAY,OAAO,KAAK,CAAC,gBAC3B;CAGF,SAAS,gBAAgB,MAAM,WAC7B,eAAe,gBAAgB;CACjC,SAAS,gBAAgB,MAAM,SAAS,eAAe,gBAAgB;CACvE,SAAS,gBAAgB,MAAM,WAC7B,eAAe,gBAAgB;CACjC,SAAS,gBAAgB,MAAM,QAAQ,eAAe,gBAAgB;CAEtE,SAAS,KAAK,MAAM,WAAW,eAAe,KAAK;CACnD,SAAS,KAAK,MAAM,SAAS,eAAe,KAAK;CACjD,SAAS,KAAK,MAAM,WAAW,eAAe,KAAK;CACnD,SAAS,KAAK,MAAM,MAAM,eAAe,KAAK;CAC9C,SAAS,KAAK,MAAM,OAAO,eAAe,KAAK;CAC/C,SAAS,KAAK,MAAM,QAAQ,eAAe,KAAK;CAChD,SAAS,KAAK,MAAM,QAAQ,eAAe,KAAK;CAChD,SAAS,KAAK,MAAM,eAAe,eAAe,KAAK;CAEvD,IAAI,eAAe,KAAK,yBACtB,SAAS,KAAK,MAAM,YAClB,8BACA,eAAe,KAAK,uBACtB;MAEA,SAAS,KAAK,MAAM,eAAe,4BAA4B;CAGjE,OAAO,SAAS,GAAG,eAAe;CAClC,iBAAiB;AACnB;AAEA,MAAM,qBAAqB,UAAmB,SAAS;CACrD,gBAAgB;EACd,IAAI,OAAO,WAAW,eAAe,CAAC,SACpC;EAGF,MAAM,QAAQ,OAAO,kBAAkB;EACvC,WAAW,KAAK;EAQhB,IAAI;EAEJ,IAAI,MAAM,GAAG;GACX,MAAM,oBAAoB,UAAsB;IAC9C,IACE,MAAM,WAAW,SAAS,QAC1B,MAAM,WAAW,SAAS,iBAE1B,MAAM,eAAe;GAEzB;GAEA,SAAS,iBAAiB,aAAa,kBAAkB,EACvD,SAAS,MACX,CAAC;GAED,4BAA4B;IAC1B,SAAS,oBAAoB,aAAa,gBAAgB;GAC5D;EACF;EAEA,aAAa;GACX,sBAAsB;GACtB,aAAa,KAAK;EACpB;CACF,GAAG,CAAC,OAAO,CAAC;AACd"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { RefObject } from "react";
|
|
2
|
-
|
|
3
2
|
//#region src/hooks/use-click-outside.d.ts
|
|
4
3
|
type ClickOutsideRef = RefObject<HTMLElement | null> | null | undefined;
|
|
5
4
|
type ClickOutsideEvent = PointerEvent | MouseEvent | TouchEvent | KeyboardEvent;
|
|
@@ -10,5 +9,5 @@ interface Options {
|
|
|
10
9
|
}
|
|
11
10
|
declare const useClickOutside: (refs: ClickOutsideRef | ClickOutsideRef[], handler: (event: ClickOutsideEvent) => void, options?: Options) => void;
|
|
12
11
|
//#endregion
|
|
13
|
-
export { useClickOutside };
|
|
12
|
+
export { useClickOutside as default };
|
|
14
13
|
//# sourceMappingURL=use-click-outside.d.mts.map
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { useEffect, useRef } from "react";
|
|
2
|
-
|
|
3
2
|
//#region src/hooks/use-click-outside.ts
|
|
4
3
|
const useClickOutside = (refs, handler, options = {}) => {
|
|
5
|
-
const { enabled = true, events = ["pointerdown"], escape =
|
|
4
|
+
const { enabled = true, events = ["pointerdown"], escape = false } = options;
|
|
6
5
|
const handlerRef = useRef(handler);
|
|
7
6
|
useEffect(() => {
|
|
8
7
|
handlerRef.current = handler;
|
|
@@ -12,6 +11,7 @@ const useClickOutside = (refs, handler, options = {}) => {
|
|
|
12
11
|
useEffect(() => {
|
|
13
12
|
refListRef.current = refList;
|
|
14
13
|
});
|
|
14
|
+
const eventsKey = events.join(",");
|
|
15
15
|
useEffect(() => {
|
|
16
16
|
if (!enabled || typeof document === "undefined") return;
|
|
17
17
|
const isInside = (target) => refListRef.current.some((ref) => ref?.current?.contains(target));
|
|
@@ -36,10 +36,10 @@ const useClickOutside = (refs, handler, options = {}) => {
|
|
|
36
36
|
}, [
|
|
37
37
|
enabled,
|
|
38
38
|
escape,
|
|
39
|
-
|
|
39
|
+
eventsKey
|
|
40
40
|
]);
|
|
41
41
|
};
|
|
42
|
-
|
|
43
42
|
//#endregion
|
|
44
43
|
export { useClickOutside as default };
|
|
44
|
+
|
|
45
45
|
//# sourceMappingURL=use-click-outside.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-click-outside.mjs","names":[],"sources":["../../src/hooks/use-click-outside.ts"],"sourcesContent":["import { type RefObject, useEffect, useRef } from 'react';\n\ntype ClickOutsideRef = RefObject<HTMLElement | null> | null | undefined;\n\ntype ClickOutsideEvent = PointerEvent | MouseEvent | TouchEvent | KeyboardEvent;\n\ninterface Options {\n enabled?: boolean;\n events?: ('pointerdown' | 'mousedown' | 'touchstart')[];\n escape?: boolean;\n}\n\n// Takes the \"inside\" ref(s) as an argument instead of creating and\n// returning one — lets a caller pass multiple exclusion targets (e.g. a\n// dropdown's trigger button *and* its portaled panel, which live in\n// different DOM subtrees) instead of only ever watching one element.\nconst useClickOutside = (\n refs: ClickOutsideRef | ClickOutsideRef[],\n handler: (event: ClickOutsideEvent) => void,\n options: Options = {},\n) => {\n const { enabled = true, events = ['pointerdown'], escape =
|
|
1
|
+
{"version":3,"file":"use-click-outside.mjs","names":[],"sources":["../../src/hooks/use-click-outside.ts"],"sourcesContent":["import { type RefObject, useEffect, useRef } from 'react';\n\ntype ClickOutsideRef = RefObject<HTMLElement | null> | null | undefined;\n\ntype ClickOutsideEvent = PointerEvent | MouseEvent | TouchEvent | KeyboardEvent;\n\ninterface Options {\n enabled?: boolean;\n events?: ('pointerdown' | 'mousedown' | 'touchstart')[];\n escape?: boolean;\n}\n\n// Takes the \"inside\" ref(s) as an argument instead of creating and\n// returning one — lets a caller pass multiple exclusion targets (e.g. a\n// dropdown's trigger button *and* its portaled panel, which live in\n// different DOM subtrees) instead of only ever watching one element.\nconst useClickOutside = (\n refs: ClickOutsideRef | ClickOutsideRef[],\n handler: (event: ClickOutsideEvent) => void,\n options: Options = {},\n) => {\n // `escape` is opt-in (default `false`): the hook is named for clicks, and\n // an always-on Escape handler both surprises callers who already handle\n // Escape themselves (double-fire) and closes every layer at once in\n // nested UI (a dropdown inside a modal). Turn it on where you want it.\n const { enabled = true, events = ['pointerdown'], escape = false } = options;\n\n const handlerRef = useRef(handler);\n\n useEffect(() => {\n handlerRef.current = handler;\n });\n\n const refList = Array.isArray(refs) ? refs : [refs];\n // `refs` is almost always an inline array literal at the call site, so\n // depending on it directly would tear down/rebuild the listeners on\n // every render — read the latest value from a ref instead.\n const refListRef = useRef(refList);\n\n useEffect(() => {\n refListRef.current = refList;\n });\n\n const eventsKey = events.join(',');\n\n useEffect(() => {\n if (!enabled || typeof document === 'undefined') {\n return;\n }\n\n const isInside = (target: Node) =>\n refListRef.current.some(ref => ref?.current?.contains(target));\n\n const onPointerEvent = (event: Event) => {\n const target = event.target as Node | null;\n\n if (!target || isInside(target)) {\n return;\n }\n\n handlerRef.current(event as ClickOutsideEvent);\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n handlerRef.current(event);\n }\n };\n\n events.forEach(eventName => {\n document.addEventListener(eventName, onPointerEvent);\n });\n\n if (escape) {\n document.addEventListener('keydown', onKeyDown);\n }\n\n return () => {\n events.forEach(eventName => {\n document.removeEventListener(eventName, onPointerEvent);\n });\n\n if (escape) {\n document.removeEventListener('keydown', onKeyDown);\n }\n };\n // `events` is covered by `eventsKey` below instead of the array\n // itself, which (like `refs`) is usually a fresh literal every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [enabled, escape, eventsKey]);\n};\n\nexport default useClickOutside;\n"],"mappings":";;AAgBA,MAAM,mBACJ,MACA,SACA,UAAmB,CAAC,MACjB;CAKH,MAAM,EAAE,UAAU,MAAM,SAAS,CAAC,aAAa,GAAG,SAAS,UAAU;CAErE,MAAM,aAAa,OAAO,OAAO;CAEjC,gBAAgB;EACd,WAAW,UAAU;CACvB,CAAC;CAED,MAAM,UAAU,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI;CAIlD,MAAM,aAAa,OAAO,OAAO;CAEjC,gBAAgB;EACd,WAAW,UAAU;CACvB,CAAC;CAED,MAAM,YAAY,OAAO,KAAK,GAAG;CAEjC,gBAAgB;EACd,IAAI,CAAC,WAAW,OAAO,aAAa,aAClC;EAGF,MAAM,YAAY,WAChB,WAAW,QAAQ,MAAK,QAAO,KAAK,SAAS,SAAS,MAAM,CAAC;EAE/D,MAAM,kBAAkB,UAAiB;GACvC,MAAM,SAAS,MAAM;GAErB,IAAI,CAAC,UAAU,SAAS,MAAM,GAC5B;GAGF,WAAW,QAAQ,KAA0B;EAC/C;EAEA,MAAM,aAAa,UAAyB;GAC1C,IAAI,MAAM,QAAQ,UAChB,WAAW,QAAQ,KAAK;EAE5B;EAEA,OAAO,SAAQ,cAAa;GAC1B,SAAS,iBAAiB,WAAW,cAAc;EACrD,CAAC;EAED,IAAI,QACF,SAAS,iBAAiB,WAAW,SAAS;EAGhD,aAAa;GACX,OAAO,SAAQ,cAAa;IAC1B,SAAS,oBAAoB,WAAW,cAAc;GACxD,CAAC;GAED,IAAI,QACF,SAAS,oBAAoB,WAAW,SAAS;EAErD;CAIF,GAAG;EAAC;EAAS;EAAQ;CAAS,CAAC;AACjC"}
|
|
@@ -9,5 +9,5 @@ declare function useControllableState<T>(props: UseControllableStateProps<T> & {
|
|
|
9
9
|
}): [T, (next: T) => void];
|
|
10
10
|
declare function useControllableState<T>(props: UseControllableStateProps<T>): [T | undefined, (next: T) => void];
|
|
11
11
|
//#endregion
|
|
12
|
-
export { useControllableState };
|
|
12
|
+
export { useControllableState as default };
|
|
13
13
|
//# sourceMappingURL=use-controllable-state.d.mts.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
-
|
|
3
2
|
//#region src/hooks/use-controllable-state.ts
|
|
4
3
|
function useControllableState({ value, defaultValue, onChange }) {
|
|
5
4
|
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
|
|
@@ -15,7 +14,7 @@ function useControllableState({ value, defaultValue, onChange }) {
|
|
|
15
14
|
onChange?.(next);
|
|
16
15
|
}, [isControlled, onChange])];
|
|
17
16
|
}
|
|
18
|
-
|
|
19
17
|
//#endregion
|
|
20
18
|
export { useControllableState as default };
|
|
19
|
+
|
|
21
20
|
//# sourceMappingURL=use-controllable-state.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-controllable-state.mjs","names":[],"sources":["../../src/hooks/use-controllable-state.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\ninterface UseControllableStateProps<T> {\n value?: T;\n defaultValue?: T;\n onChange?: (value: T) => void;\n}\n\n// Backs a controlled/uncontrolled prop pair (`value`/`defaultValue`/\n// `onChange`) with one hook: falls back to internal state whenever `value`\n// is `undefined`, and always calls `onChange` regardless of which mode is\n// active, so callers don't have to branch on it themselves.\n//\n// `value === undefined` is what marks a render as uncontrolled, so it can't\n// also be used as a real, controlled value of `undefined` — that's an\n// inherent limitation of this \"sentinel\" style of controlled-state\n// detection (shared by most hooks of this shape, including Radix's own),\n// not something fixable without a different API (e.g. an explicit\n// `controlled` boolean prop instead of inferring it from `value`).\nfunction useControllableState<T>(\n props: UseControllableStateProps<T> & { defaultValue: T },\n): [T, (next: T) => void];\nfunction useControllableState<T>(\n props: UseControllableStateProps<T>,\n): [T | undefined, (next: T) => void];\nfunction useControllableState<T>({\n value,\n defaultValue,\n onChange,\n}: UseControllableStateProps<T>) {\n const [uncontrolledValue, setUncontrolledValue] = useState<T | undefined>(\n defaultValue,\n );\n\n const isControlled = value !== undefined;\n const currentValue = isControlled ? value : uncontrolledValue;\n\n const wasControlledRef = useRef(isControlled);\n\n useEffect(() => {\n // Read `process.env.NODE_ENV` through `globalThis` rather than the\n // bare `process` global — this hook ships as plain ESM and can end up\n // somewhere with no bundler define for it (or no `process` at all, and\n // no @types/node in scope to even name it), where referencing it\n // directly would fail instead of just missing a warning.\n const nodeEnv = (\n globalThis as { process?: { env?: { NODE_ENV?: string } } }\n ).process?.env?.NODE_ENV;\n const isProduction = nodeEnv === 'production';\n\n if (!isProduction && wasControlledRef.current !== isControlled) {\n console.warn(\n `useControllableState is changing from ${\n wasControlledRef.current ? 'controlled' : 'uncontrolled'\n } to ${\n isControlled ? 'controlled' : 'uncontrolled'\n }. A component should not switch between controlled and uncontrolled — decide on one and stick with it (e.g. by always passing a non-undefined \\`value\\`, or never passing one).`,\n );\n }\n wasControlledRef.current = isControlled;\n }, [isControlled]);\n\n const setValue = useCallback(\n (next: T) => {\n if (!isControlled) {\n setUncontrolledValue(next);\n }\n onChange?.(next);\n },\n [isControlled, onChange],\n );\n\n return [currentValue, setValue];\n}\n\nexport default useControllableState;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-controllable-state.mjs","names":[],"sources":["../../src/hooks/use-controllable-state.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\ninterface UseControllableStateProps<T> {\n value?: T;\n defaultValue?: T;\n onChange?: (value: T) => void;\n}\n\n// Backs a controlled/uncontrolled prop pair (`value`/`defaultValue`/\n// `onChange`) with one hook: falls back to internal state whenever `value`\n// is `undefined`, and always calls `onChange` regardless of which mode is\n// active, so callers don't have to branch on it themselves.\n//\n// `value === undefined` is what marks a render as uncontrolled, so it can't\n// also be used as a real, controlled value of `undefined` — that's an\n// inherent limitation of this \"sentinel\" style of controlled-state\n// detection (shared by most hooks of this shape, including Radix's own),\n// not something fixable without a different API (e.g. an explicit\n// `controlled` boolean prop instead of inferring it from `value`).\nfunction useControllableState<T>(\n props: UseControllableStateProps<T> & { defaultValue: T },\n): [T, (next: T) => void];\nfunction useControllableState<T>(\n props: UseControllableStateProps<T>,\n): [T | undefined, (next: T) => void];\nfunction useControllableState<T>({\n value,\n defaultValue,\n onChange,\n}: UseControllableStateProps<T>) {\n const [uncontrolledValue, setUncontrolledValue] = useState<T | undefined>(\n defaultValue,\n );\n\n const isControlled = value !== undefined;\n const currentValue = isControlled ? value : uncontrolledValue;\n\n const wasControlledRef = useRef(isControlled);\n\n useEffect(() => {\n // Read `process.env.NODE_ENV` through `globalThis` rather than the\n // bare `process` global — this hook ships as plain ESM and can end up\n // somewhere with no bundler define for it (or no `process` at all, and\n // no @types/node in scope to even name it), where referencing it\n // directly would fail instead of just missing a warning.\n const nodeEnv = (\n globalThis as { process?: { env?: { NODE_ENV?: string } } }\n ).process?.env?.NODE_ENV;\n const isProduction = nodeEnv === 'production';\n\n if (!isProduction && wasControlledRef.current !== isControlled) {\n console.warn(\n `useControllableState is changing from ${\n wasControlledRef.current ? 'controlled' : 'uncontrolled'\n } to ${\n isControlled ? 'controlled' : 'uncontrolled'\n }. A component should not switch between controlled and uncontrolled — decide on one and stick with it (e.g. by always passing a non-undefined \\`value\\`, or never passing one).`,\n );\n }\n wasControlledRef.current = isControlled;\n }, [isControlled]);\n\n const setValue = useCallback(\n (next: T) => {\n if (!isControlled) {\n setUncontrolledValue(next);\n }\n onChange?.(next);\n },\n [isControlled, onChange],\n );\n\n return [currentValue, setValue];\n}\n\nexport default useControllableState;\n"],"mappings":";;AAyBA,SAAS,qBAAwB,EAC/B,OACA,cACA,YAC+B;CAC/B,MAAM,CAAC,mBAAmB,wBAAwB,SAChD,YACF;CAEA,MAAM,eAAe,UAAU,KAAA;CAC/B,MAAM,eAAe,eAAe,QAAQ;CAE5C,MAAM,mBAAmB,OAAO,YAAY;CAE5C,gBAAgB;EAWd,IAAI,EAJF,WACA,SAAS,KAAK,aACiB,iBAEZ,iBAAiB,YAAY,cAChD,QAAQ,KACN,yCACE,iBAAiB,UAAU,eAAe,eAC3C,MACC,eAAe,eAAe,eAC/B,gLACH;EAEF,iBAAiB,UAAU;CAC7B,GAAG,CAAC,YAAY,CAAC;CAYjB,OAAO,CAAC,cAVS,aACd,SAAY;EACX,IAAI,CAAC,cACH,qBAAqB,IAAI;EAE3B,WAAW,IAAI;CACjB,GACA,CAAC,cAAc,QAAQ,CAGI,CAAC;AAChC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/hooks/use-debounced-callback.d.ts
|
|
2
|
+
interface Options {
|
|
3
|
+
delay?: number;
|
|
4
|
+
autoInvoke?: boolean;
|
|
5
|
+
leading?: boolean;
|
|
6
|
+
}
|
|
7
|
+
declare const useDebouncedCallback: (callback: () => unknown, { delay, autoInvoke, leading }: Options, deps?: React.DependencyList) => () => void;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { useDebouncedCallback as default };
|
|
10
|
+
//# sourceMappingURL=use-debounced-callback.d.mts.map
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { useEffect, useRef } from "react";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const useDebounce = (callback, { delay = 100, autoInvoke = true, leading = true }, deps = []) => {
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
//#region src/hooks/use-debounced-callback.ts
|
|
3
|
+
const useDebouncedCallback = (callback, { delay = 100, autoInvoke = true, leading = true }, deps = []) => {
|
|
5
4
|
const timeoutRef = useRef(null);
|
|
6
5
|
const callbackRef = useRef(callback);
|
|
7
6
|
const delayRef = useRef(delay);
|
|
@@ -10,18 +9,19 @@ const useDebounce = (callback, { delay = 100, autoInvoke = true, leading = true
|
|
|
10
9
|
callbackRef.current = callback;
|
|
11
10
|
delayRef.current = delay;
|
|
12
11
|
});
|
|
13
|
-
const stableDebouncedCallback =
|
|
14
|
-
if (!stableDebouncedCallback.current) stableDebouncedCallback.current = () => {
|
|
12
|
+
const [stableDebouncedCallback] = useState(() => () => {
|
|
15
13
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
16
14
|
timeoutRef.current = setTimeout(() => {
|
|
17
15
|
callbackRef.current();
|
|
18
16
|
}, delayRef.current);
|
|
19
|
-
};
|
|
17
|
+
});
|
|
20
18
|
useEffect(() => {
|
|
21
19
|
const depsChanged = prevDeps.current === void 0 || prevDeps.current.length !== deps.length || prevDeps.current.some((dep, i) => dep !== deps[i]);
|
|
22
20
|
if (depsChanged && timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
23
|
-
if (autoInvoke && depsChanged)
|
|
24
|
-
|
|
21
|
+
if (autoInvoke && depsChanged) {
|
|
22
|
+
if (prevDeps.current === void 0 && leading) callbackRef.current();
|
|
23
|
+
else stableDebouncedCallback();
|
|
24
|
+
}
|
|
25
25
|
prevDeps.current = deps;
|
|
26
26
|
});
|
|
27
27
|
useEffect(() => {
|
|
@@ -29,9 +29,9 @@ const useDebounce = (callback, { delay = 100, autoInvoke = true, leading = true
|
|
|
29
29
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
30
30
|
};
|
|
31
31
|
}, []);
|
|
32
|
-
return stableDebouncedCallback
|
|
32
|
+
return stableDebouncedCallback;
|
|
33
33
|
};
|
|
34
|
-
|
|
35
34
|
//#endregion
|
|
36
|
-
export {
|
|
37
|
-
|
|
35
|
+
export { useDebouncedCallback as default };
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=use-debounced-callback.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-debounced-callback.mjs","names":[],"sources":["../../src/hooks/use-debounced-callback.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\n\ninterface Options {\n delay?: number;\n autoInvoke?: boolean;\n leading?: boolean;\n}\n\n// Auto-invokes `callback` (debounced by `delay`ms) whenever `deps` change,\n// and also returns a stable debounced version of `callback` for manual use.\n// `callback` intentionally takes no arguments — the deps-triggered\n// auto-invoke has no natural argument to supply, so an arg-taking callback\n// here was a latent type hole (it was always invoked with zero args\n// regardless of what the callback's own signature claimed).\nconst useDebouncedCallback = (\n callback: () => unknown,\n { delay = 100, autoInvoke = true, leading = true }: Options,\n deps: React.DependencyList = [],\n) => {\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const callbackRef = useRef(callback);\n // Read the latest `delay` when the timeout actually fires, instead of\n // baking in whatever `delay` was on the render that created the stable\n // debounced function (which otherwise never updates again).\n const delayRef = useRef(delay);\n const prevDeps = useRef<React.DependencyList | undefined>(undefined);\n\n useEffect(() => {\n callbackRef.current = callback;\n delayRef.current = delay;\n });\n\n // Built once via useState's lazy initializer (runs only on mount) rather\n // than the ref-guarded-by-if pattern this used to use — same \"create\n // once, keep a stable identity\" effect, but without ever reading/writing\n // a ref during render, which react-hooks/refs now disallows even for the\n // otherwise-idempotent lazy-ref-init form (see facebook/react#36896).\n const [stableDebouncedCallback] = useState(() => () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n timeoutRef.current = setTimeout(() => {\n callbackRef.current();\n }, delayRef.current);\n });\n\n useEffect(() => {\n const depsChanged =\n prevDeps.current === undefined ||\n prevDeps.current.length !== deps.length ||\n prevDeps.current.some((dep, i) => dep !== deps[i]);\n\n if (depsChanged && timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n if (autoInvoke && depsChanged) {\n const isFirstRender = prevDeps.current === undefined;\n\n // `leading` makes the \"first invocation fires immediately, without\n // waiting `delay`ms\" behavior explicit and opt-out-able, instead of\n // an unconditional special case baked into the first render.\n if (isFirstRender && leading) {\n callbackRef.current();\n } else {\n stableDebouncedCallback();\n }\n }\n\n prevDeps.current = deps;\n });\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, []);\n\n return stableDebouncedCallback;\n};\n\nexport default useDebouncedCallback;\n"],"mappings":";;AAcA,MAAM,wBACJ,UACA,EAAE,QAAQ,KAAK,aAAa,MAAM,UAAU,QAC5C,OAA6B,CAAC,MAC3B;CACH,MAAM,aAAa,OAA6C,IAAI;CACpE,MAAM,cAAc,OAAO,QAAQ;CAInC,MAAM,WAAW,OAAO,KAAK;CAC7B,MAAM,WAAW,OAAyC,KAAA,CAAS;CAEnE,gBAAgB;EACd,YAAY,UAAU;EACtB,SAAS,UAAU;CACrB,CAAC;CAOD,MAAM,CAAC,2BAA2B,qBAAqB;EACrD,IAAI,WAAW,SACb,aAAa,WAAW,OAAO;EAGjC,WAAW,UAAU,iBAAiB;GACpC,YAAY,QAAQ;EACtB,GAAG,SAAS,OAAO;CACrB,CAAC;CAED,gBAAgB;EACd,MAAM,cACJ,SAAS,YAAY,KAAA,KACrB,SAAS,QAAQ,WAAW,KAAK,UACjC,SAAS,QAAQ,MAAM,KAAK,MAAM,QAAQ,KAAK,EAAE;EAEnD,IAAI,eAAe,WAAW,SAC5B,aAAa,WAAW,OAAO;EAGjC,IAAI,cAAc,aAAa;GAM7B,IALsB,SAAS,YAAY,KAAA,KAKtB,SACnB,YAAY,QAAQ;QAEpB,wBAAwB;EAE5B;EAEA,SAAS,UAAU;CACrB,CAAC;CAED,gBAAgB;EACd,aAAa;GACX,IAAI,WAAW,SACb,aAAa,WAAW,OAAO;EAEnC;CACF,GAAG,CAAC,CAAC;CAEL,OAAO;AACT"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useEffect, useRef, useState } from "react";
|
|
2
|
-
|
|
3
2
|
//#region src/hooks/use-debounced-value.ts
|
|
4
3
|
const useDebouncedValue = (value, delay = 100) => {
|
|
5
4
|
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
@@ -14,7 +13,7 @@ const useDebouncedValue = (value, delay = 100) => {
|
|
|
14
13
|
}, [value, delay]);
|
|
15
14
|
return debouncedValue;
|
|
16
15
|
};
|
|
17
|
-
|
|
18
16
|
//#endregion
|
|
19
17
|
export { useDebouncedValue as default };
|
|
18
|
+
|
|
20
19
|
//# sourceMappingURL=use-debounced-value.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-debounced-value.mjs","names":[],"sources":["../../src/hooks/use-debounced-value.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\n\n// The value-shaped counterpart to useDebounce (which is callback-shaped),\n// symmetric with useThrottle's `(value, delay) => value` signature. The\n// common \"just give me the debounced value\" case otherwise needs two\n// pieces of state and an effect built on useDebounce by hand.\nconst useDebouncedValue = <T>(value: T, delay = 100): T => {\n const [debouncedValue, setDebouncedValue] = useState(value);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n timeoutRef.current = setTimeout(() => {\n setDebouncedValue(value);\n }, delay);\n\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, [value, delay]);\n\n return debouncedValue;\n};\n\nexport default useDebouncedValue;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-debounced-value.mjs","names":[],"sources":["../../src/hooks/use-debounced-value.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\n\n// The value-shaped counterpart to useDebounce (which is callback-shaped),\n// symmetric with useThrottle's `(value, delay) => value` signature. The\n// common \"just give me the debounced value\" case otherwise needs two\n// pieces of state and an effect built on useDebounce by hand.\nconst useDebouncedValue = <T>(value: T, delay = 100): T => {\n const [debouncedValue, setDebouncedValue] = useState(value);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n timeoutRef.current = setTimeout(() => {\n setDebouncedValue(value);\n }, delay);\n\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, [value, delay]);\n\n return debouncedValue;\n};\n\nexport default useDebouncedValue;\n"],"mappings":";;AAMA,MAAM,qBAAwB,OAAU,QAAQ,QAAW;CACzD,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,KAAK;CAC1D,MAAM,aAAa,OAA6C,IAAI;CAEpE,gBAAgB;EACd,WAAW,UAAU,iBAAiB;GACpC,kBAAkB,KAAK;EACzB,GAAG,KAAK;EAER,aAAa;GACX,IAAI,WAAW,SACb,aAAa,WAAW,OAAO;EAEnC;CACF,GAAG,CAAC,OAAO,KAAK,CAAC;CAEjB,OAAO;AACT"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { RefObject } from "react";
|
|
2
|
-
|
|
3
2
|
//#region src/hooks/use-element-position.d.ts
|
|
4
3
|
type ElementReference<T> = string | RefObject<T>;
|
|
5
4
|
declare const useElementPosition: <T>(elementRef: ElementReference<T>) => DOMRect | null;
|
|
6
5
|
//#endregion
|
|
7
|
-
export { useElementPosition };
|
|
6
|
+
export { useElementPosition as default };
|
|
8
7
|
//# sourceMappingURL=use-element-position.d.mts.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useEffect, useRef, useState } from "react";
|
|
2
|
-
|
|
3
2
|
//#region src/hooks/use-element-position.ts
|
|
4
3
|
const rectsEqual = (a, b) => {
|
|
5
4
|
if (a === b) return true;
|
|
@@ -55,7 +54,7 @@ const useElementPosition = (elementRef) => {
|
|
|
55
54
|
}, [elementRef]);
|
|
56
55
|
return rect;
|
|
57
56
|
};
|
|
58
|
-
|
|
59
57
|
//#endregion
|
|
60
58
|
export { useElementPosition as default };
|
|
59
|
+
|
|
61
60
|
//# sourceMappingURL=use-element-position.mjs.map
|