@jbpark/use-hooks 2.6.0 → 2.8.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 +20 -17
- package/README.md +20 -17
- package/dist/hooks/index.d.mts +2 -0
- package/dist/hooks/index.mjs +2 -0
- package/dist/hooks/use-controllable-state.d.mts +14 -0
- package/dist/hooks/use-controllable-state.mjs +15 -0
- package/dist/hooks/use-controllable-state.mjs.map +1 -0
- package/dist/hooks/use-multi-select.d.mts +11 -0
- package/dist/hooks/use-multi-select.mjs +43 -0
- package/dist/hooks/use-multi-select.mjs.map +1 -0
- package/dist/hooks/use-window-scroll.d.mts +3 -1
- package/dist/hooks/use-window-scroll.mjs +20 -18
- package/dist/hooks/use-window-scroll.mjs.map +1 -1
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +3 -1
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -67,23 +67,25 @@ function MyComponent() {
|
|
|
67
67
|
|
|
68
68
|
## 사용 가능한 훅
|
|
69
69
|
|
|
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로 요소의 뷰포트 교차 여부 추적
|
|
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로 요소의 뷰포트 교차 여부 추적 |
|
|
87
|
+
| `useMultiSelect` | shift-클릭 범위 선택을 지원하는 체크박스 스타일 다중 선택 |
|
|
88
|
+
| `useControllableState` | value/defaultValue/onChange prop 조합을 훅 하나로 제어/비제어 상태 관리 |
|
|
87
89
|
|
|
88
90
|
## 개발
|
|
89
91
|
|
|
@@ -118,6 +120,7 @@ src/
|
|
|
118
120
|
│ ├── use-image.ts
|
|
119
121
|
│ ├── use-intersection-observer.ts
|
|
120
122
|
│ ├── use-local-storage.ts
|
|
123
|
+
│ ├── use-multi-select.ts
|
|
121
124
|
│ ├── use-recursive-timeout.ts
|
|
122
125
|
│ ├── use-responsive-size.ts
|
|
123
126
|
│ ├── use-scroll-to-elements.ts
|
package/README.md
CHANGED
|
@@ -67,23 +67,25 @@ 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
|
-
| `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
|
|
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 |
|
|
87
|
+
| `useMultiSelect` | Checkbox-style multi-select for a list, with shift-click range selection |
|
|
88
|
+
| `useControllableState` | Back a value/defaultValue/onChange prop pair with one controlled/uncontrolled state hook |
|
|
87
89
|
|
|
88
90
|
## Development
|
|
89
91
|
|
|
@@ -118,6 +120,7 @@ src/
|
|
|
118
120
|
│ ├── use-image.ts
|
|
119
121
|
│ ├── use-intersection-observer.ts
|
|
120
122
|
│ ├── use-local-storage.ts
|
|
123
|
+
│ ├── use-multi-select.ts
|
|
121
124
|
│ ├── use-recursive-timeout.ts
|
|
122
125
|
│ ├── use-responsive-size.ts
|
|
123
126
|
│ ├── use-scroll-to-elements.ts
|
package/dist/hooks/index.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useDebounce } from "./use-debounce.mjs";
|
|
2
2
|
import { useBodyScrollLock } from "./use-body-scroll-lock.mjs";
|
|
3
3
|
import { useClickOutside } from "./use-click-outside.mjs";
|
|
4
|
+
import { useControllableState } from "./use-controllable-state.mjs";
|
|
4
5
|
import { useElementPosition } from "./use-element-position.mjs";
|
|
5
6
|
import { useElementScroll } from "./use-element-scroll.mjs";
|
|
6
7
|
import { useFileToDataUrl } from "./use-file-to-data-url.mjs";
|
|
@@ -9,6 +10,7 @@ import { useIntersectionObserver } from "./use-intersection-observer.mjs";
|
|
|
9
10
|
import { useResponsiveSize } from "./use-responsive-size.mjs";
|
|
10
11
|
import { useImage } from "./use-image.mjs";
|
|
11
12
|
import { useLocalStorage } from "./use-local-storage.mjs";
|
|
13
|
+
import { useMultiSelect } from "./use-multi-select.mjs";
|
|
12
14
|
import { useRecursiveTimeout } from "./use-recursive-timeout.mjs";
|
|
13
15
|
import { useScrollToElements } from "./use-scroll-to-elements.mjs";
|
|
14
16
|
import { useThrottle } from "./use-throttle.mjs";
|
package/dist/hooks/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import useDebounce from "./use-debounce.mjs";
|
|
2
2
|
import useBodyScrollLock from "./use-body-scroll-lock.mjs";
|
|
3
3
|
import useClickOutside from "./use-click-outside.mjs";
|
|
4
|
+
import useControllableState from "./use-controllable-state.mjs";
|
|
4
5
|
import useElementPosition from "./use-element-position.mjs";
|
|
5
6
|
import useElementScroll from "./use-element-scroll.mjs";
|
|
6
7
|
import useFileToDataUrl from "./use-file-to-data-url.mjs";
|
|
@@ -9,6 +10,7 @@ import useIntersectionObserver from "./use-intersection-observer.mjs";
|
|
|
9
10
|
import useResponsiveSize from "./use-responsive-size.mjs";
|
|
10
11
|
import useImage from "./use-image.mjs";
|
|
11
12
|
import useLocalStorage from "./use-local-storage.mjs";
|
|
13
|
+
import useMultiSelect from "./use-multi-select.mjs";
|
|
12
14
|
import useRecursiveTimeout from "./use-recursive-timeout.mjs";
|
|
13
15
|
import useScrollToElements from "./use-scroll-to-elements.mjs";
|
|
14
16
|
import useThrottle from "./use-throttle.mjs";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/hooks/use-controllable-state.d.ts
|
|
2
|
+
interface UseControllableStateProps<T> {
|
|
3
|
+
value?: T;
|
|
4
|
+
defaultValue?: T;
|
|
5
|
+
onChange?: (value: T) => void;
|
|
6
|
+
}
|
|
7
|
+
declare const useControllableState: <T>({
|
|
8
|
+
value,
|
|
9
|
+
defaultValue,
|
|
10
|
+
onChange
|
|
11
|
+
}: UseControllableStateProps<T>) => [T, (next: T) => void];
|
|
12
|
+
//#endregion
|
|
13
|
+
export { useControllableState };
|
|
14
|
+
//# sourceMappingURL=use-controllable-state.d.mts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useCallback, useState } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/hooks/use-controllable-state.ts
|
|
4
|
+
const useControllableState = ({ value, defaultValue, onChange }) => {
|
|
5
|
+
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
|
|
6
|
+
const isControlled = value !== void 0;
|
|
7
|
+
return [isControlled ? value : uncontrolledValue, useCallback((next) => {
|
|
8
|
+
if (!isControlled) setUncontrolledValue(next);
|
|
9
|
+
onChange?.(next);
|
|
10
|
+
}, [isControlled, onChange])];
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
//#endregion
|
|
14
|
+
export { useControllableState as default };
|
|
15
|
+
//# sourceMappingURL=use-controllable-state.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-controllable-state.mjs","names":[],"sources":["../../src/hooks/use-controllable-state.ts"],"sourcesContent":["import { useCallback, 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.\nconst useControllableState = <T>({\n value,\n defaultValue,\n onChange,\n}: UseControllableStateProps<T>): [T, (next: T) => void] => {\n const [uncontrolledValue, setUncontrolledValue] = useState<T | undefined>(\n defaultValue,\n );\n\n const isControlled = value !== undefined;\n const currentValue = (isControlled ? value : uncontrolledValue) as T;\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":";;;AAYA,MAAM,wBAA2B,EAC/B,OACA,cACA,eAC0D;CAC1D,MAAM,CAAC,mBAAmB,wBAAwB,SAChD,aACD;CAED,MAAM,eAAe,UAAU;AAa/B,QAAO,CAZe,eAAe,QAAQ,mBAE5B,aACd,SAAY;AACX,MAAI,CAAC,aACH,sBAAqB,KAAK;AAE5B,aAAW,KAAK;IAElB,CAAC,cAAc,SAAS,CACzB,CAE8B"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/hooks/use-multi-select.d.ts
|
|
2
|
+
declare const useMultiSelect: (count: number) => {
|
|
3
|
+
readonly selected: Set<number>;
|
|
4
|
+
readonly isSelected: (index: number) => boolean;
|
|
5
|
+
readonly toggle: (index: number, shiftKey?: boolean) => void;
|
|
6
|
+
readonly clear: () => void;
|
|
7
|
+
readonly replace: (indices: Set<number>) => void;
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { useMultiSelect };
|
|
11
|
+
//# sourceMappingURL=use-multi-select.d.mts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/hooks/use-multi-select.ts
|
|
4
|
+
const useMultiSelect = (count) => {
|
|
5
|
+
const [rawSelected, setRawSelected] = useState(/* @__PURE__ */ new Set());
|
|
6
|
+
const [anchor, setAnchor] = useState(null);
|
|
7
|
+
const selected = useMemo(() => {
|
|
8
|
+
const next = new Set([...rawSelected].filter((index) => index < count));
|
|
9
|
+
return next.size === rawSelected.size ? rawSelected : next;
|
|
10
|
+
}, [rawSelected, count]);
|
|
11
|
+
const toggle = useCallback((index, shiftKey = false) => {
|
|
12
|
+
setRawSelected((prev) => {
|
|
13
|
+
const next = new Set(prev);
|
|
14
|
+
if (shiftKey && anchor !== null) {
|
|
15
|
+
const [start, end] = anchor < index ? [anchor, index] : [index, anchor];
|
|
16
|
+
for (let i = start; i <= end; i++) next.add(i);
|
|
17
|
+
return next;
|
|
18
|
+
}
|
|
19
|
+
if (next.has(index)) next.delete(index);
|
|
20
|
+
else next.add(index);
|
|
21
|
+
return next;
|
|
22
|
+
});
|
|
23
|
+
setAnchor(index);
|
|
24
|
+
}, [anchor]);
|
|
25
|
+
const clear = useCallback(() => {
|
|
26
|
+
setRawSelected(/* @__PURE__ */ new Set());
|
|
27
|
+
setAnchor(null);
|
|
28
|
+
}, []);
|
|
29
|
+
const replace = useCallback((indices) => {
|
|
30
|
+
setRawSelected(indices);
|
|
31
|
+
}, []);
|
|
32
|
+
return {
|
|
33
|
+
selected,
|
|
34
|
+
isSelected: useCallback((index) => selected.has(index), [selected]),
|
|
35
|
+
toggle,
|
|
36
|
+
clear,
|
|
37
|
+
replace
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
export { useMultiSelect as default };
|
|
43
|
+
//# sourceMappingURL=use-multi-select.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-multi-select.mjs","names":[],"sources":["../../src/hooks/use-multi-select.ts"],"sourcesContent":["import { useCallback, useMemo, useState } from 'react';\n\nconst useMultiSelect = (count: number) => {\n const [rawSelected, setRawSelected] = useState<Set<number>>(new Set());\n const [anchor, setAnchor] = useState<number | null>(null);\n\n // Indices can outlive the item they pointed to (e.g. after the backing\n // list shrinks) — clamp against the current count on every render\n // instead of syncing state back via an effect.\n const selected = useMemo(() => {\n const next = new Set([...rawSelected].filter(index => index < count));\n return next.size === rawSelected.size ? rawSelected : next;\n }, [rawSelected, count]);\n\n const toggle = useCallback(\n (index: number, shiftKey = false) => {\n setRawSelected(prev => {\n const next = new Set(prev);\n\n if (shiftKey && anchor !== null) {\n const [start, end] =\n anchor < index ? [anchor, index] : [index, anchor];\n\n for (let i = start; i <= end; i++) {\n next.add(i);\n }\n return next;\n }\n\n if (next.has(index)) {\n next.delete(index);\n } else {\n next.add(index);\n }\n return next;\n });\n setAnchor(index);\n },\n [anchor],\n );\n\n const clear = useCallback(() => {\n setRawSelected(new Set());\n setAnchor(null);\n }, []);\n\n const replace = useCallback((indices: Set<number>) => {\n setRawSelected(indices);\n }, []);\n\n const isSelected = useCallback(\n (index: number) => selected.has(index),\n [selected],\n );\n\n return {\n selected,\n isSelected,\n toggle,\n clear,\n replace,\n } as const;\n};\n\nexport default useMultiSelect;\n"],"mappings":";;;AAEA,MAAM,kBAAkB,UAAkB;CACxC,MAAM,CAAC,aAAa,kBAAkB,yBAAsB,IAAI,KAAK,CAAC;CACtE,MAAM,CAAC,QAAQ,aAAa,SAAwB,KAAK;CAKzD,MAAM,WAAW,cAAc;EAC7B,MAAM,OAAO,IAAI,IAAI,CAAC,GAAG,YAAY,CAAC,QAAO,UAAS,QAAQ,MAAM,CAAC;AACrE,SAAO,KAAK,SAAS,YAAY,OAAO,cAAc;IACrD,CAAC,aAAa,MAAM,CAAC;CAExB,MAAM,SAAS,aACZ,OAAe,WAAW,UAAU;AACnC,kBAAe,SAAQ;GACrB,MAAM,OAAO,IAAI,IAAI,KAAK;AAE1B,OAAI,YAAY,WAAW,MAAM;IAC/B,MAAM,CAAC,OAAO,OACZ,SAAS,QAAQ,CAAC,QAAQ,MAAM,GAAG,CAAC,OAAO,OAAO;AAEpD,SAAK,IAAI,IAAI,OAAO,KAAK,KAAK,IAC5B,MAAK,IAAI,EAAE;AAEb,WAAO;;AAGT,OAAI,KAAK,IAAI,MAAM,CACjB,MAAK,OAAO,MAAM;OAElB,MAAK,IAAI,MAAM;AAEjB,UAAO;IACP;AACF,YAAU,MAAM;IAElB,CAAC,OAAO,CACT;CAED,MAAM,QAAQ,kBAAkB;AAC9B,iCAAe,IAAI,KAAK,CAAC;AACzB,YAAU,KAAK;IACd,EAAE,CAAC;CAEN,MAAM,UAAU,aAAa,YAAyB;AACpD,iBAAe,QAAQ;IACtB,EAAE,CAAC;AAON,QAAO;EACL;EACA,YAPiB,aAChB,UAAkB,SAAS,IAAI,MAAM,EACtC,CAAC,SAAS,CACX;EAKC;EACA;EACA;EACD"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
2
|
|
|
3
3
|
//#region src/hooks/use-window-scroll.ts
|
|
4
|
-
const useWindowScroll = () => {
|
|
4
|
+
const useWindowScroll = (targetRef) => {
|
|
5
5
|
const [state, setState] = useState({
|
|
6
6
|
x: 0,
|
|
7
7
|
y: 0,
|
|
@@ -11,15 +11,17 @@ const useWindowScroll = () => {
|
|
|
11
11
|
}
|
|
12
12
|
});
|
|
13
13
|
useEffect(() => {
|
|
14
|
+
const target = targetRef?.current?.ownerDocument.defaultView ?? window;
|
|
15
|
+
const doc = target.document;
|
|
14
16
|
const calculate = () => {
|
|
15
|
-
const x =
|
|
16
|
-
const y =
|
|
17
|
-
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
|
|
18
|
-
const visualViewport =
|
|
19
|
-
const viewportWidth = isIOS && visualViewport ? visualViewport.width :
|
|
20
|
-
const viewportHeight = isIOS && visualViewport ? visualViewport.height :
|
|
21
|
-
const maxScrollX = Math.max(0,
|
|
22
|
-
const maxScrollY = Math.max(0,
|
|
17
|
+
const x = target.scrollX || 0;
|
|
18
|
+
const y = target.scrollY || 0;
|
|
19
|
+
const isIOS = /iPad|iPhone|iPod/.test(target.navigator.userAgent);
|
|
20
|
+
const visualViewport = target.visualViewport;
|
|
21
|
+
const viewportWidth = isIOS && visualViewport ? visualViewport.width : target.innerWidth;
|
|
22
|
+
const viewportHeight = isIOS && visualViewport ? visualViewport.height : target.innerHeight;
|
|
23
|
+
const maxScrollX = Math.max(0, doc.documentElement.scrollWidth - viewportWidth);
|
|
24
|
+
const maxScrollY = Math.max(0, doc.documentElement.scrollHeight - viewportHeight);
|
|
23
25
|
const percentX = maxScrollX === 0 ? 0 : Math.min(100, x / maxScrollX * 100);
|
|
24
26
|
const percentY = maxScrollY === 0 ? 0 : Math.min(100, y / maxScrollY * 100);
|
|
25
27
|
setState({
|
|
@@ -41,17 +43,17 @@ const useWindowScroll = () => {
|
|
|
41
43
|
const onVisualViewportChange = () => {
|
|
42
44
|
setTimeout(calculate, 50);
|
|
43
45
|
};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (
|
|
46
|
+
target.addEventListener("scroll", onScroll, { passive: true });
|
|
47
|
+
target.addEventListener("resize", onResize);
|
|
48
|
+
target.addEventListener("orientationchange", onResize);
|
|
49
|
+
if (target.visualViewport) target.visualViewport.addEventListener("resize", onVisualViewportChange);
|
|
48
50
|
return () => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (
|
|
51
|
+
target.removeEventListener("scroll", onScroll);
|
|
52
|
+
target.removeEventListener("resize", onResize);
|
|
53
|
+
target.removeEventListener("orientationchange", onResize);
|
|
54
|
+
if (target.visualViewport) target.visualViewport.removeEventListener("resize", onVisualViewportChange);
|
|
53
55
|
};
|
|
54
|
-
}, []);
|
|
56
|
+
}, [targetRef]);
|
|
55
57
|
return state;
|
|
56
58
|
};
|
|
57
59
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-window-scroll.mjs","names":[],"sources":["../../src/hooks/use-window-scroll.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\nconst useWindowScroll = () => {\n const [state, setState] = useState({\n x: 0,\n y: 0,\n percent: {\n x: 0,\n y: 0,\n },\n });\n\n useEffect(() => {\n const calculate = () => {\n const x =
|
|
1
|
+
{"version":3,"file":"use-window-scroll.mjs","names":[],"sources":["../../src/hooks/use-window-scroll.ts"],"sourcesContent":["import { type RefObject, useEffect, useState } from 'react';\n\n// Pass a ref to an element rendered inside the window you actually want to\n// track (e.g. a node portaled into an iframe). Without one, this defaults\n// to the host `window`, which is wrong whenever the caller doesn't live in\n// the top-level document. Resolved once per mount from `targetRef.current`.\nconst useWindowScroll = (targetRef?: RefObject<Element | null>) => {\n const [state, setState] = useState({\n x: 0,\n y: 0,\n percent: {\n x: 0,\n y: 0,\n },\n });\n\n useEffect(() => {\n const target = targetRef?.current?.ownerDocument.defaultView ?? window;\n const doc = target.document;\n\n const calculate = () => {\n const x = target.scrollX || 0;\n const y = target.scrollY || 0;\n\n const isIOS = /iPad|iPhone|iPod/.test(target.navigator.userAgent);\n const visualViewport = target.visualViewport;\n\n const viewportWidth =\n isIOS && visualViewport ? visualViewport.width : target.innerWidth;\n\n const viewportHeight =\n isIOS && visualViewport ? visualViewport.height : target.innerHeight;\n\n const maxScrollX = Math.max(\n 0,\n doc.documentElement.scrollWidth - viewportWidth,\n );\n const maxScrollY = Math.max(\n 0,\n doc.documentElement.scrollHeight - viewportHeight,\n );\n\n const percentX =\n maxScrollX === 0 ? 0 : Math.min(100, (x / maxScrollX) * 100);\n const percentY =\n maxScrollY === 0 ? 0 : Math.min(100, (y / maxScrollY) * 100);\n\n setState({\n x,\n y,\n percent: {\n x: Math.floor(Math.max(0, percentX)),\n y: Math.floor(Math.max(0, percentY)),\n },\n });\n };\n\n calculate();\n\n const onScroll = () => {\n calculate();\n };\n\n const onResize = () => {\n setTimeout(calculate, 100);\n };\n\n const onVisualViewportChange = () => {\n setTimeout(calculate, 50);\n };\n\n target.addEventListener('scroll', onScroll, { passive: true });\n target.addEventListener('resize', onResize);\n target.addEventListener('orientationchange', onResize);\n\n if (target.visualViewport) {\n target.visualViewport.addEventListener('resize', onVisualViewportChange);\n }\n\n return () => {\n target.removeEventListener('scroll', onScroll);\n target.removeEventListener('resize', onResize);\n target.removeEventListener('orientationchange', onResize);\n\n if (target.visualViewport) {\n target.visualViewport.removeEventListener(\n 'resize',\n onVisualViewportChange,\n );\n }\n };\n }, [targetRef]);\n\n return state;\n};\n\nexport default useWindowScroll;\n"],"mappings":";;;AAMA,MAAM,mBAAmB,cAA0C;CACjE,MAAM,CAAC,OAAO,YAAY,SAAS;EACjC,GAAG;EACH,GAAG;EACH,SAAS;GACP,GAAG;GACH,GAAG;GACJ;EACF,CAAC;AAEF,iBAAgB;EACd,MAAM,SAAS,WAAW,SAAS,cAAc,eAAe;EAChE,MAAM,MAAM,OAAO;EAEnB,MAAM,kBAAkB;GACtB,MAAM,IAAI,OAAO,WAAW;GAC5B,MAAM,IAAI,OAAO,WAAW;GAE5B,MAAM,QAAQ,mBAAmB,KAAK,OAAO,UAAU,UAAU;GACjE,MAAM,iBAAiB,OAAO;GAE9B,MAAM,gBACJ,SAAS,iBAAiB,eAAe,QAAQ,OAAO;GAE1D,MAAM,iBACJ,SAAS,iBAAiB,eAAe,SAAS,OAAO;GAE3D,MAAM,aAAa,KAAK,IACtB,GACA,IAAI,gBAAgB,cAAc,cACnC;GACD,MAAM,aAAa,KAAK,IACtB,GACA,IAAI,gBAAgB,eAAe,eACpC;GAED,MAAM,WACJ,eAAe,IAAI,IAAI,KAAK,IAAI,KAAM,IAAI,aAAc,IAAI;GAC9D,MAAM,WACJ,eAAe,IAAI,IAAI,KAAK,IAAI,KAAM,IAAI,aAAc,IAAI;AAE9D,YAAS;IACP;IACA;IACA,SAAS;KACP,GAAG,KAAK,MAAM,KAAK,IAAI,GAAG,SAAS,CAAC;KACpC,GAAG,KAAK,MAAM,KAAK,IAAI,GAAG,SAAS,CAAC;KACrC;IACF,CAAC;;AAGJ,aAAW;EAEX,MAAM,iBAAiB;AACrB,cAAW;;EAGb,MAAM,iBAAiB;AACrB,cAAW,WAAW,IAAI;;EAG5B,MAAM,+BAA+B;AACnC,cAAW,WAAW,GAAG;;AAG3B,SAAO,iBAAiB,UAAU,UAAU,EAAE,SAAS,MAAM,CAAC;AAC9D,SAAO,iBAAiB,UAAU,SAAS;AAC3C,SAAO,iBAAiB,qBAAqB,SAAS;AAEtD,MAAI,OAAO,eACT,QAAO,eAAe,iBAAiB,UAAU,uBAAuB;AAG1E,eAAa;AACX,UAAO,oBAAoB,UAAU,SAAS;AAC9C,UAAO,oBAAoB,UAAU,SAAS;AAC9C,UAAO,oBAAoB,qBAAqB,SAAS;AAEzD,OAAI,OAAO,eACT,QAAO,eAAe,oBACpB,UACA,uBACD;;IAGJ,CAAC,UAAU,CAAC;AAEf,QAAO"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useDebounce } from "./hooks/use-debounce.mjs";
|
|
2
2
|
import { useBodyScrollLock } from "./hooks/use-body-scroll-lock.mjs";
|
|
3
3
|
import { useClickOutside } from "./hooks/use-click-outside.mjs";
|
|
4
|
+
import { useControllableState } from "./hooks/use-controllable-state.mjs";
|
|
4
5
|
import { useElementPosition } from "./hooks/use-element-position.mjs";
|
|
5
6
|
import { useElementScroll } from "./hooks/use-element-scroll.mjs";
|
|
6
7
|
import { useFileToDataUrl } from "./hooks/use-file-to-data-url.mjs";
|
|
@@ -9,6 +10,7 @@ import { useIntersectionObserver } from "./hooks/use-intersection-observer.mjs";
|
|
|
9
10
|
import { useResponsiveSize } from "./hooks/use-responsive-size.mjs";
|
|
10
11
|
import { useImage } from "./hooks/use-image.mjs";
|
|
11
12
|
import { useLocalStorage } from "./hooks/use-local-storage.mjs";
|
|
13
|
+
import { useMultiSelect } from "./hooks/use-multi-select.mjs";
|
|
12
14
|
import { useRecursiveTimeout } from "./hooks/use-recursive-timeout.mjs";
|
|
13
15
|
import { useScrollToElements } from "./hooks/use-scroll-to-elements.mjs";
|
|
14
16
|
import { useThrottle } from "./hooks/use-throttle.mjs";
|
|
@@ -16,4 +18,4 @@ import { useTimeline } from "./hooks/use-timeline.mjs";
|
|
|
16
18
|
import { useWindowScroll } from "./hooks/use-window-scroll.mjs";
|
|
17
19
|
import { useViewport } from "./hooks/use-viewport.mjs";
|
|
18
20
|
import "./hooks/index.mjs";
|
|
19
|
-
export { useBodyScrollLock, useClickOutside, useDebounce, useElementPosition, useElementScroll, useFileToDataUrl, useHistoryState, useImage, useIntersectionObserver, useLocalStorage, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useTimeline, useViewport, useWindowScroll };
|
|
21
|
+
export { useBodyScrollLock, useClickOutside, useControllableState, useDebounce, useElementPosition, useElementScroll, useFileToDataUrl, useHistoryState, useImage, useIntersectionObserver, useLocalStorage, useMultiSelect, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useTimeline, useViewport, useWindowScroll };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import useDebounce from "./hooks/use-debounce.mjs";
|
|
2
2
|
import useBodyScrollLock from "./hooks/use-body-scroll-lock.mjs";
|
|
3
3
|
import useClickOutside from "./hooks/use-click-outside.mjs";
|
|
4
|
+
import useControllableState from "./hooks/use-controllable-state.mjs";
|
|
4
5
|
import useElementPosition from "./hooks/use-element-position.mjs";
|
|
5
6
|
import useElementScroll from "./hooks/use-element-scroll.mjs";
|
|
6
7
|
import useFileToDataUrl from "./hooks/use-file-to-data-url.mjs";
|
|
@@ -9,6 +10,7 @@ import useIntersectionObserver from "./hooks/use-intersection-observer.mjs";
|
|
|
9
10
|
import useResponsiveSize from "./hooks/use-responsive-size.mjs";
|
|
10
11
|
import useImage from "./hooks/use-image.mjs";
|
|
11
12
|
import useLocalStorage from "./hooks/use-local-storage.mjs";
|
|
13
|
+
import useMultiSelect from "./hooks/use-multi-select.mjs";
|
|
12
14
|
import useRecursiveTimeout from "./hooks/use-recursive-timeout.mjs";
|
|
13
15
|
import useScrollToElements from "./hooks/use-scroll-to-elements.mjs";
|
|
14
16
|
import useThrottle from "./hooks/use-throttle.mjs";
|
|
@@ -17,4 +19,4 @@ import useWindowScroll from "./hooks/use-window-scroll.mjs";
|
|
|
17
19
|
import useViewport from "./hooks/use-viewport.mjs";
|
|
18
20
|
import "./hooks/index.mjs";
|
|
19
21
|
|
|
20
|
-
export { useBodyScrollLock, useClickOutside, useDebounce, useElementPosition, useElementScroll, useFileToDataUrl, useHistoryState, useImage, useIntersectionObserver, useLocalStorage, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useTimeline, useViewport, useWindowScroll };
|
|
22
|
+
export { useBodyScrollLock, useClickOutside, useControllableState, useDebounce, useElementPosition, useElementScroll, useFileToDataUrl, useHistoryState, useImage, useIntersectionObserver, useLocalStorage, useMultiSelect, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useTimeline, useViewport, useWindowScroll };
|