@jbpark/use-hooks 2.2.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +18 -14
- package/README.md +18 -14
- package/dist/hooks/index.d.mts +14 -13
- package/dist/hooks/index.mjs +14 -13
- package/dist/hooks/{use-body-scroll-lock/index.d.mts → use-body-scroll-lock.d.mts} +2 -2
- package/dist/hooks/{use-body-scroll-lock/index.mjs → use-body-scroll-lock.mjs} +2 -2
- package/dist/hooks/use-body-scroll-lock.mjs.map +1 -0
- package/dist/hooks/use-click-outside.d.mts +7 -0
- package/dist/hooks/use-click-outside.mjs +29 -0
- package/dist/hooks/use-click-outside.mjs.map +1 -0
- package/dist/hooks/{use-debounce/index.d.mts → use-debounce.d.mts} +2 -2
- package/dist/hooks/{use-debounce/index.mjs → use-debounce.mjs} +2 -2
- package/dist/hooks/use-debounce.mjs.map +1 -0
- package/dist/hooks/{use-element-position/index.d.mts → use-element-position.d.mts} +2 -2
- package/dist/hooks/{use-element-position/index.mjs → use-element-position.mjs} +2 -2
- package/dist/hooks/use-element-position.mjs.map +1 -0
- package/dist/hooks/{use-element-scroll/index.d.mts → use-element-scroll.d.mts} +2 -2
- package/dist/hooks/{use-element-scroll/index.mjs → use-element-scroll.mjs} +2 -2
- package/dist/hooks/use-element-scroll.mjs.map +1 -0
- package/dist/hooks/{use-image/index.d.mts → use-image.d.mts} +2 -2
- package/dist/hooks/{use-image/index.mjs → use-image.mjs} +2 -2
- package/dist/hooks/use-image.mjs.map +1 -0
- package/dist/hooks/{use-local-storage/index.d.mts → use-local-storage.d.mts} +2 -2
- package/dist/hooks/{use-local-storage/index.mjs → use-local-storage.mjs} +2 -2
- package/dist/hooks/use-local-storage.mjs.map +1 -0
- package/dist/hooks/{use-recursive-timeout/index.d.mts → use-recursive-timeout.d.mts} +2 -2
- package/dist/hooks/{use-recursive-timeout/index.mjs → use-recursive-timeout.mjs} +2 -2
- package/dist/hooks/use-recursive-timeout.mjs.map +1 -0
- package/dist/hooks/{use-responsive-size/index.d.mts → use-responsive-size.d.mts} +2 -2
- package/dist/hooks/{use-responsive-size/index.mjs → use-responsive-size.mjs} +3 -3
- package/dist/hooks/use-responsive-size.mjs.map +1 -0
- package/dist/hooks/{use-scroll-to-elements/index.d.mts → use-scroll-to-elements.d.mts} +2 -2
- package/dist/hooks/{use-scroll-to-elements/index.mjs → use-scroll-to-elements.mjs} +2 -2
- package/dist/hooks/use-scroll-to-elements.mjs.map +1 -0
- package/dist/hooks/{use-throttle/index.d.mts → use-throttle.d.mts} +2 -2
- package/dist/hooks/{use-throttle/index.mjs → use-throttle.mjs} +2 -2
- package/dist/hooks/use-throttle.mjs.map +1 -0
- package/dist/hooks/{use-timeline/index.d.mts → use-timeline.d.mts} +2 -2
- package/dist/hooks/{use-timeline/index.mjs → use-timeline.mjs} +2 -2
- package/dist/hooks/use-timeline.mjs.map +1 -0
- package/dist/hooks/{use-viewport/index.d.mts → use-viewport.d.mts} +2 -2
- package/dist/hooks/{use-viewport/index.mjs → use-viewport.mjs} +2 -2
- package/dist/hooks/use-viewport.mjs.map +1 -0
- package/dist/hooks/{use-window-scroll/index.d.mts → use-window-scroll.d.mts} +2 -2
- package/dist/hooks/{use-window-scroll/index.mjs → use-window-scroll.mjs} +2 -2
- package/dist/hooks/use-window-scroll.mjs.map +1 -0
- package/dist/index.d.mts +15 -14
- package/dist/index.mjs +15 -14
- package/package.json +3 -1
- package/dist/hooks/use-body-scroll-lock/index.mjs.map +0 -1
- package/dist/hooks/use-debounce/index.mjs.map +0 -1
- package/dist/hooks/use-element-position/index.mjs.map +0 -1
- package/dist/hooks/use-element-scroll/index.mjs.map +0 -1
- package/dist/hooks/use-image/index.mjs.map +0 -1
- package/dist/hooks/use-local-storage/index.mjs.map +0 -1
- package/dist/hooks/use-recursive-timeout/index.mjs.map +0 -1
- package/dist/hooks/use-responsive-size/index.mjs.map +0 -1
- package/dist/hooks/use-scroll-to-elements/index.mjs.map +0 -1
- package/dist/hooks/use-throttle/index.mjs.map +0 -1
- package/dist/hooks/use-timeline/index.mjs.map +0 -1
- package/dist/hooks/use-viewport/index.mjs.map +0 -1
- package/dist/hooks/use-window-scroll/index.mjs.map +0 -1
package/README.ko.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
## 기능
|
|
13
13
|
|
|
14
|
-
- 📦 **
|
|
14
|
+
- 📦 **13개 프로덕션 레디 훅** - 스크롤, 뷰포트, 스토리지 등 다양한 유틸리티
|
|
15
15
|
- 🎯 **TypeScript 지원** - 완전한 타입 지원으로 더 나은 개발 경험
|
|
16
16
|
- ⚡ **트리 셰이킹 지원** - 필요한 것만 임포트하세요
|
|
17
17
|
- 🔒 **SSR 안전** - window/document 전역 변수에 대한 보호
|
|
@@ -75,6 +75,7 @@ function MyComponent() {
|
|
|
75
75
|
| `useElementPosition` | 스크롤/리사이즈 시 요소의 바운딩 렉트 모니터링 (요소 참조 지원) |
|
|
76
76
|
| `useResponsiveSize` | Tailwind 유사 브레이크포인트를 포함한 요소 크기 추적 (debounce) |
|
|
77
77
|
| `useBodyScrollLock` | 스타일 보존을 포함한 바디 스크롤 잠금/해제 (iOS 특별 처리) |
|
|
78
|
+
| `useClickOutside` | 참조한 요소 바깥을 클릭/터치하면 콜백 실행 |
|
|
78
79
|
| `useScrollToElements` | 인덱스별로 특정 요소로 스크롤 (오프셋 조절 가능) |
|
|
79
80
|
| `useImage` | 이미지 사전로드 및 로딩/에러 상태 노출 |
|
|
80
81
|
| `useRecursiveTimeout` | 비동기/동기 콜백을 재귀적으로 스케줄링 |
|
|
@@ -105,19 +106,20 @@ pnpm exec prettier --write .
|
|
|
105
106
|
|
|
106
107
|
```
|
|
107
108
|
src/
|
|
108
|
-
├── hooks/ # 개별 훅 구현
|
|
109
|
-
│ ├── use-body-scroll-lock
|
|
110
|
-
│ ├── use-
|
|
111
|
-
│ ├── use-
|
|
112
|
-
│ ├── use-element-
|
|
113
|
-
│ ├── use-
|
|
114
|
-
│ ├── use-
|
|
115
|
-
│ ├── use-
|
|
116
|
-
│ ├── use-
|
|
117
|
-
│ ├── use-
|
|
118
|
-
│ ├── use-
|
|
119
|
-
│ ├── use-
|
|
120
|
-
│ ├── use-
|
|
109
|
+
├── hooks/ # 개별 훅 구현 (훅 1개 = 파일 1개)
|
|
110
|
+
│ ├── use-body-scroll-lock.ts
|
|
111
|
+
│ ├── use-click-outside.ts
|
|
112
|
+
│ ├── use-debounce.ts
|
|
113
|
+
│ ├── use-element-position.ts
|
|
114
|
+
│ ├── use-element-scroll.ts
|
|
115
|
+
│ ├── use-image.ts
|
|
116
|
+
│ ├── use-local-storage.ts
|
|
117
|
+
│ ├── use-recursive-timeout.ts
|
|
118
|
+
│ ├── use-responsive-size.ts
|
|
119
|
+
│ ├── use-scroll-to-elements.ts
|
|
120
|
+
│ ├── use-throttle.ts
|
|
121
|
+
│ ├── use-viewport.ts
|
|
122
|
+
│ ├── use-window-scroll.ts
|
|
121
123
|
│ └── index.ts # 배럴 익스포트
|
|
122
124
|
└── index.ts # 패키지 진입점
|
|
123
125
|
|
|
@@ -140,6 +142,8 @@ dist/ # 빌드된 라이브러리 (ESM + types)
|
|
|
140
142
|
|
|
141
143
|
## 주요 패턴
|
|
142
144
|
|
|
145
|
+
- **훅 1개 = 파일 1개**: 각 훅은 `src/hooks/use-x.ts` 형태의 단일 파일로 존재하며 (폴더/`index.ts` 없음), `src/hooks/index.ts`에서 재수출됩니다
|
|
146
|
+
- **데모 파일 네이밍**: 각 훅의 데모 페이지 파일명은 훅 파일명에 `-demo`를 붙인 `src/demo/use-x-demo.tsx` 형태를 따릅니다 (컴포넌트명 자체는 기존처럼 PascalCase 유지, 예: `ClickOutsideDemo`)
|
|
143
147
|
- **Window 보호**: `window`/`document`에 접근하는 훅은 SSR 안전성을 위해 `typeof window` 체크 (예: `useLocalStorage`)
|
|
144
148
|
- **이벤트 리스너**: 모든 스크롤/리사이즈 리스너는 가능한 한 passive 플래그 사용
|
|
145
149
|
- **ResizeObserver**: `useResponsiveSize`와 `useElementPosition`에서 사용하여 성능 최적화
|
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
|
+
- 📦 **13 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
|
|
@@ -75,6 +75,7 @@ function MyComponent() {
|
|
|
75
75
|
| `useElementPosition` | Monitor element bounding rect on scroll/resize (element ref support) |
|
|
76
76
|
| `useResponsiveSize` | Track element size with Tailwind-like breakpoints (debounced) |
|
|
77
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 |
|
|
78
79
|
| `useScrollToElements` | Scroll to specific elements by index (adjustable offset) |
|
|
79
80
|
| `useImage` | Preload images and expose loading/error states |
|
|
80
81
|
| `useRecursiveTimeout` | Recursively schedule async/sync callbacks |
|
|
@@ -105,19 +106,20 @@ pnpm exec prettier --write .
|
|
|
105
106
|
|
|
106
107
|
```
|
|
107
108
|
src/
|
|
108
|
-
├── hooks/ # Individual hook implementations
|
|
109
|
-
│ ├── use-body-scroll-lock
|
|
110
|
-
│ ├── use-
|
|
111
|
-
│ ├── use-
|
|
112
|
-
│ ├── use-element-
|
|
113
|
-
│ ├── use-
|
|
114
|
-
│ ├── use-
|
|
115
|
-
│ ├── use-
|
|
116
|
-
│ ├── use-
|
|
117
|
-
│ ├── use-
|
|
118
|
-
│ ├── use-
|
|
119
|
-
│ ├── use-
|
|
120
|
-
│ ├── use-
|
|
109
|
+
├── hooks/ # Individual hook implementations (one file per hook)
|
|
110
|
+
│ ├── use-body-scroll-lock.ts
|
|
111
|
+
│ ├── use-click-outside.ts
|
|
112
|
+
│ ├── use-debounce.ts
|
|
113
|
+
│ ├── use-element-position.ts
|
|
114
|
+
│ ├── use-element-scroll.ts
|
|
115
|
+
│ ├── use-image.ts
|
|
116
|
+
│ ├── use-local-storage.ts
|
|
117
|
+
│ ├── use-recursive-timeout.ts
|
|
118
|
+
│ ├── use-responsive-size.ts
|
|
119
|
+
│ ├── use-scroll-to-elements.ts
|
|
120
|
+
│ ├── use-throttle.ts
|
|
121
|
+
│ ├── use-viewport.ts
|
|
122
|
+
│ ├── use-window-scroll.ts
|
|
121
123
|
│ └── index.ts # Barrel export
|
|
122
124
|
└── index.ts # Package entry point
|
|
123
125
|
|
|
@@ -140,6 +142,8 @@ The library is built as:
|
|
|
140
142
|
|
|
141
143
|
## Key Patterns
|
|
142
144
|
|
|
145
|
+
- **One File Per Hook**: Each hook lives in a single flat file at `src/hooks/use-x.ts` (no per-hook folder/`index.ts`) and is re-exported from `src/hooks/index.ts`
|
|
146
|
+
- **Demo File Naming**: Each hook's demo page file is named `src/demo/use-x-demo.tsx`, matching the hook's own filename with a `-demo` suffix (the component name itself stays PascalCase, e.g. `ClickOutsideDemo`)
|
|
143
147
|
- **Window Protection**: Hooks accessing `window`/`document` check `typeof window` for SSR safety (e.g., `useLocalStorage`)
|
|
144
148
|
- **Event Listeners**: All scroll/resize listeners use passive flag when possible
|
|
145
149
|
- **ResizeObserver**: Used in `useResponsiveSize` and `useElementPosition` for performance
|
package/dist/hooks/index.d.mts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { useDebounce } from "./use-debounce
|
|
2
|
-
import { useBodyScrollLock } from "./use-body-scroll-lock
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
1
|
+
import { useDebounce } from "./use-debounce.mjs";
|
|
2
|
+
import { useBodyScrollLock } from "./use-body-scroll-lock.mjs";
|
|
3
|
+
import { useClickOutside } from "./use-click-outside.mjs";
|
|
4
|
+
import { useElementPosition } from "./use-element-position.mjs";
|
|
5
|
+
import { useElementScroll } from "./use-element-scroll.mjs";
|
|
6
|
+
import { useResponsiveSize } from "./use-responsive-size.mjs";
|
|
7
|
+
import { useImage } from "./use-image.mjs";
|
|
8
|
+
import { useLocalStorage } from "./use-local-storage.mjs";
|
|
9
|
+
import { useRecursiveTimeout } from "./use-recursive-timeout.mjs";
|
|
10
|
+
import { useScrollToElements } from "./use-scroll-to-elements.mjs";
|
|
11
|
+
import { useThrottle } from "./use-throttle.mjs";
|
|
12
|
+
import { useTimeline } from "./use-timeline.mjs";
|
|
13
|
+
import { useWindowScroll } from "./use-window-scroll.mjs";
|
|
14
|
+
import { useViewport } from "./use-viewport.mjs";
|
package/dist/hooks/index.mjs
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import useDebounce from "./use-debounce
|
|
2
|
-
import useBodyScrollLock from "./use-body-scroll-lock
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
1
|
+
import useDebounce from "./use-debounce.mjs";
|
|
2
|
+
import useBodyScrollLock from "./use-body-scroll-lock.mjs";
|
|
3
|
+
import useClickOutside from "./use-click-outside.mjs";
|
|
4
|
+
import useElementPosition from "./use-element-position.mjs";
|
|
5
|
+
import useElementScroll from "./use-element-scroll.mjs";
|
|
6
|
+
import useResponsiveSize from "./use-responsive-size.mjs";
|
|
7
|
+
import useImage from "./use-image.mjs";
|
|
8
|
+
import useLocalStorage from "./use-local-storage.mjs";
|
|
9
|
+
import useRecursiveTimeout from "./use-recursive-timeout.mjs";
|
|
10
|
+
import useScrollToElements from "./use-scroll-to-elements.mjs";
|
|
11
|
+
import useThrottle from "./use-throttle.mjs";
|
|
12
|
+
import useTimeline from "./use-timeline.mjs";
|
|
13
|
+
import useWindowScroll from "./use-window-scroll.mjs";
|
|
14
|
+
import useViewport from "./use-viewport.mjs";
|
|
14
15
|
|
|
15
16
|
export { };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
//#region src/hooks/use-body-scroll-lock
|
|
1
|
+
//#region src/hooks/use-body-scroll-lock.d.ts
|
|
2
2
|
declare const useBodyScrollLock: (enabled?: boolean) => void;
|
|
3
3
|
//#endregion
|
|
4
4
|
export { useBodyScrollLock };
|
|
5
|
-
//# sourceMappingURL=
|
|
5
|
+
//# sourceMappingURL=use-body-scroll-lock.d.mts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/use-body-scroll-lock
|
|
3
|
+
//#region src/hooks/use-body-scroll-lock.ts
|
|
4
4
|
let lockCount = 0;
|
|
5
5
|
let originalStyles = null;
|
|
6
6
|
let originalScrollY = 0;
|
|
@@ -121,4 +121,4 @@ const useBodyScrollLock = (enabled = true) => {
|
|
|
121
121
|
|
|
122
122
|
//#endregion
|
|
123
123
|
export { useBodyScrollLock as default };
|
|
124
|
-
//# sourceMappingURL=
|
|
124
|
+
//# sourceMappingURL=use-body-scroll-lock.mjs.map
|
|
@@ -0,0 +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\nlet lockCount = 0;\nlet originalStyles: LockedStyles | null = null;\nlet originalScrollY = 0;\n\nconst isIOS = () => /iPad|iPhone|iPod/.test(navigator.userAgent);\n\nconst lockScroll = () => {\n if (lockCount === 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 lockCount += 1;\n};\n\nconst unlockScroll = () => {\n lockCount = Math.max(0, lockCount - 1);\n\n if (lockCount > 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 (!enabled) {\n return;\n }\n\n lockScroll();\n\n if (isIOS()) {\n const preventAll = (e: Event) => {\n if (\n e.target === document.body ||\n e.target === document.documentElement ||\n e.target === window\n ) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n }\n };\n\n const preventScroll = () => {\n window.scrollTo(0, 0);\n document.body.scrollTop = 0;\n document.documentElement.scrollTop = 0;\n };\n\n const events = ['scroll', 'touchmove', 'touchstart', 'touchend'];\n events.forEach(event => {\n window.addEventListener(event, preventAll, {\n passive: false,\n capture: true,\n });\n document.addEventListener(event, preventAll, {\n passive: false,\n capture: true,\n });\n document.body.addEventListener(event, preventAll, {\n passive: false,\n capture: true,\n });\n });\n\n const scrollInterval = setInterval(preventScroll, 16);\n\n return () => {\n clearInterval(scrollInterval);\n events.forEach(event => {\n window.removeEventListener(event, preventAll, { capture: true });\n document.removeEventListener(event, preventAll, { capture: true });\n document.body.removeEventListener(event, preventAll, {\n capture: true,\n });\n });\n unlockScroll();\n };\n }\n\n return () => {\n unlockScroll();\n };\n }, [enabled]);\n};\n\nexport default useBodyScrollLock;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as react from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/hooks/use-click-outside.d.ts
|
|
4
|
+
declare const useClickOutside: <T extends HTMLElement = HTMLElement>(handler: (event: MouseEvent | TouchEvent) => void, enabled?: boolean) => react.RefObject<T | null>;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { useClickOutside };
|
|
7
|
+
//# sourceMappingURL=use-click-outside.d.mts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/hooks/use-click-outside.ts
|
|
4
|
+
const useClickOutside = (handler, enabled = true) => {
|
|
5
|
+
const ref = useRef(null);
|
|
6
|
+
const handlerRef = useRef(handler);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
handlerRef.current = handler;
|
|
9
|
+
});
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (!enabled || typeof document === "undefined") return;
|
|
12
|
+
const listener = (event) => {
|
|
13
|
+
const el = ref.current;
|
|
14
|
+
if (!el || el.contains(event.target)) return;
|
|
15
|
+
handlerRef.current(event);
|
|
16
|
+
};
|
|
17
|
+
document.addEventListener("mousedown", listener);
|
|
18
|
+
document.addEventListener("touchstart", listener);
|
|
19
|
+
return () => {
|
|
20
|
+
document.removeEventListener("mousedown", listener);
|
|
21
|
+
document.removeEventListener("touchstart", listener);
|
|
22
|
+
};
|
|
23
|
+
}, [enabled]);
|
|
24
|
+
return ref;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
export { useClickOutside as default };
|
|
29
|
+
//# sourceMappingURL=use-click-outside.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-click-outside.mjs","names":[],"sources":["../../src/hooks/use-click-outside.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\nconst useClickOutside = <T extends HTMLElement = HTMLElement>(\n handler: (event: MouseEvent | TouchEvent) => void,\n enabled = true,\n) => {\n const ref = useRef<T>(null);\n const handlerRef = useRef(handler);\n\n useEffect(() => {\n handlerRef.current = handler;\n });\n\n useEffect(() => {\n if (!enabled || typeof document === 'undefined') {\n return;\n }\n\n const listener = (event: MouseEvent | TouchEvent) => {\n const el = ref.current;\n if (!el || el.contains(event.target as Node)) {\n return;\n }\n handlerRef.current(event);\n };\n\n document.addEventListener('mousedown', listener);\n document.addEventListener('touchstart', listener);\n\n return () => {\n document.removeEventListener('mousedown', listener);\n document.removeEventListener('touchstart', listener);\n };\n }, [enabled]);\n\n return ref;\n};\n\nexport default useClickOutside;\n"],"mappings":";;;AAEA,MAAM,mBACJ,SACA,UAAU,SACP;CACH,MAAM,MAAM,OAAU,KAAK;CAC3B,MAAM,aAAa,OAAO,QAAQ;AAElC,iBAAgB;AACd,aAAW,UAAU;GACrB;AAEF,iBAAgB;AACd,MAAI,CAAC,WAAW,OAAO,aAAa,YAClC;EAGF,MAAM,YAAY,UAAmC;GACnD,MAAM,KAAK,IAAI;AACf,OAAI,CAAC,MAAM,GAAG,SAAS,MAAM,OAAe,CAC1C;AAEF,cAAW,QAAQ,MAAM;;AAG3B,WAAS,iBAAiB,aAAa,SAAS;AAChD,WAAS,iBAAiB,cAAc,SAAS;AAEjD,eAAa;AACX,YAAS,oBAAoB,aAAa,SAAS;AACnD,YAAS,oBAAoB,cAAc,SAAS;;IAErD,CAAC,QAAQ,CAAC;AAEb,QAAO"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/hooks/use-debounce
|
|
1
|
+
//#region src/hooks/use-debounce.d.ts
|
|
2
2
|
interface Options {
|
|
3
3
|
delay?: number;
|
|
4
4
|
autoInvoke?: boolean;
|
|
@@ -9,4 +9,4 @@ declare const useDebounce: <T extends (...args: unknown[]) => unknown>(callback:
|
|
|
9
9
|
}: Options, deps?: React.DependencyList) => T;
|
|
10
10
|
//#endregion
|
|
11
11
|
export { useDebounce };
|
|
12
|
-
//# sourceMappingURL=
|
|
12
|
+
//# sourceMappingURL=use-debounce.d.mts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useRef } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/use-debounce
|
|
3
|
+
//#region src/hooks/use-debounce.ts
|
|
4
4
|
const useDebounce = (callback, { delay = 100, autoInvoke = true }, deps = []) => {
|
|
5
5
|
const timeoutRef = useRef(null);
|
|
6
6
|
const callbackRef = useRef(callback);
|
|
@@ -34,4 +34,4 @@ const useDebounce = (callback, { delay = 100, autoInvoke = true }, deps = []) =>
|
|
|
34
34
|
|
|
35
35
|
//#endregion
|
|
36
36
|
export { useDebounce as default };
|
|
37
|
-
//# sourceMappingURL=
|
|
37
|
+
//# sourceMappingURL=use-debounce.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-debounce.mjs","names":[],"sources":["../../src/hooks/use-debounce.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\ninterface Options {\n delay?: number;\n autoInvoke?: boolean;\n}\n\nconst useDebounce = <T extends (...args: unknown[]) => unknown>(\n callback: T,\n { delay = 100, autoInvoke = true }: Options,\n deps: React.DependencyList = [],\n): T => {\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const callbackRef = useRef(callback);\n const prevDeps = useRef<React.DependencyList | undefined>(undefined);\n\n useEffect(() => {\n callbackRef.current = callback;\n });\n\n const stableDebouncedCallback = useRef<T | null>(null);\n\n if (!stableDebouncedCallback.current) {\n stableDebouncedCallback.current = ((...args: Parameters<T>) => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n timeoutRef.current = setTimeout(() => {\n callbackRef.current(...args);\n }, delay);\n }) as T;\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 if (isFirstRender) {\n callbackRef.current();\n } else if (stableDebouncedCallback.current) {\n stableDebouncedCallback.current();\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.current;\n};\n\nexport default useDebounce;\n"],"mappings":";;;AAOA,MAAM,eACJ,UACA,EAAE,QAAQ,KAAK,aAAa,QAC5B,OAA6B,EAAE,KACzB;CACN,MAAM,aAAa,OAA6C,KAAK;CACrE,MAAM,cAAc,OAAO,SAAS;CACpC,MAAM,WAAW,OAAyC,OAAU;AAEpE,iBAAgB;AACd,cAAY,UAAU;GACtB;CAEF,MAAM,0BAA0B,OAAiB,KAAK;AAEtD,KAAI,CAAC,wBAAwB,QAC3B,yBAAwB,YAAY,GAAG,SAAwB;AAC7D,MAAI,WAAW,QACb,cAAa,WAAW,QAAQ;AAGlC,aAAW,UAAU,iBAAiB;AACpC,eAAY,QAAQ,GAAG,KAAK;KAC3B,MAAM;;AAIb,iBAAgB;EACd,MAAM,cACJ,SAAS,YAAY,UACrB,SAAS,QAAQ,WAAW,KAAK,UACjC,SAAS,QAAQ,MAAM,KAAK,MAAM,QAAQ,KAAK,GAAG;AAEpD,MAAI,eAAe,WAAW,QAC5B,cAAa,WAAW,QAAQ;AAGlC,MAAI,cAAc,aAEhB;OADsB,SAAS,YAAY,OAEzC,aAAY,SAAS;YACZ,wBAAwB,QACjC,yBAAwB,SAAS;;AAIrC,WAAS,UAAU;GACnB;AAEF,iBAAgB;AACd,eAAa;AACX,OAAI,WAAW,QACb,cAAa,WAAW,QAAQ;;IAGnC,EAAE,CAAC;AAEN,QAAO,wBAAwB"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { RefObject } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/use-element-position
|
|
3
|
+
//#region src/hooks/use-element-position.d.ts
|
|
4
4
|
type ElementReference<T> = string | RefObject<T>;
|
|
5
5
|
declare const useElementPosition: <T>(elementRef: ElementReference<T>) => DOMRect | null;
|
|
6
6
|
//#endregion
|
|
7
7
|
export { useElementPosition };
|
|
8
|
-
//# sourceMappingURL=
|
|
8
|
+
//# sourceMappingURL=use-element-position.d.mts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/use-element-position
|
|
3
|
+
//#region src/hooks/use-element-position.ts
|
|
4
4
|
const useElementPosition = (elementRef) => {
|
|
5
5
|
const [rect, setRect] = useState(null);
|
|
6
6
|
useEffect(() => {
|
|
@@ -29,4 +29,4 @@ const useElementPosition = (elementRef) => {
|
|
|
29
29
|
|
|
30
30
|
//#endregion
|
|
31
31
|
export { useElementPosition as default };
|
|
32
|
-
//# sourceMappingURL=
|
|
32
|
+
//# sourceMappingURL=use-element-position.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-element-position.mjs","names":[],"sources":["../../src/hooks/use-element-position.ts"],"sourcesContent":["import { type RefObject, useEffect, useState } from 'react';\n\ntype ElementReference<T> = string | RefObject<T>;\n\nconst useElementPosition = <T>(elementRef: ElementReference<T>) => {\n const [rect, setRect] = useState<DOMRect | null>(null);\n\n useEffect(() => {\n const getElement = (ref: ElementReference<T>): T | null => {\n if (typeof ref === 'string') {\n return document.querySelector(ref) as T;\n }\n return ref.current;\n };\n\n const updateRect = () => {\n const element = getElement(elementRef) as HTMLElement | null;\n if (element) {\n setRect(element.getBoundingClientRect());\n } else {\n setRect(null);\n }\n };\n\n const onUpdate = () => {\n requestAnimationFrame(updateRect);\n };\n\n updateRect();\n\n window.addEventListener('scroll', onUpdate, { passive: true });\n window.addEventListener('resize', onUpdate, { passive: true });\n\n return () => {\n window.removeEventListener('scroll', onUpdate);\n window.removeEventListener('resize', onUpdate);\n };\n }, [elementRef]);\n\n return rect;\n};\n\nexport default useElementPosition;\n"],"mappings":";;;AAIA,MAAM,sBAAyB,eAAoC;CACjE,MAAM,CAAC,MAAM,WAAW,SAAyB,KAAK;AAEtD,iBAAgB;EACd,MAAM,cAAc,QAAuC;AACzD,OAAI,OAAO,QAAQ,SACjB,QAAO,SAAS,cAAc,IAAI;AAEpC,UAAO,IAAI;;EAGb,MAAM,mBAAmB;GACvB,MAAM,UAAU,WAAW,WAAW;AACtC,OAAI,QACF,SAAQ,QAAQ,uBAAuB,CAAC;OAExC,SAAQ,KAAK;;EAIjB,MAAM,iBAAiB;AACrB,yBAAsB,WAAW;;AAGnC,cAAY;AAEZ,SAAO,iBAAiB,UAAU,UAAU,EAAE,SAAS,MAAM,CAAC;AAC9D,SAAO,iBAAiB,UAAU,UAAU,EAAE,SAAS,MAAM,CAAC;AAE9D,eAAa;AACX,UAAO,oBAAoB,UAAU,SAAS;AAC9C,UAAO,oBAAoB,UAAU,SAAS;;IAE/C,CAAC,WAAW,CAAC;AAEhB,QAAO"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/hooks/use-element-scroll
|
|
1
|
+
//#region src/hooks/use-element-scroll.d.ts
|
|
2
2
|
declare const useElementScroll: () => {
|
|
3
3
|
element: HTMLElement | null;
|
|
4
4
|
setRef: (el: HTMLElement | null) => void;
|
|
@@ -12,4 +12,4 @@ declare const useElementScroll: () => {
|
|
|
12
12
|
};
|
|
13
13
|
//#endregion
|
|
14
14
|
export { useElementScroll };
|
|
15
|
-
//# sourceMappingURL=
|
|
15
|
+
//# sourceMappingURL=use-element-scroll.d.mts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/use-element-scroll
|
|
3
|
+
//#region src/hooks/use-element-scroll.ts
|
|
4
4
|
const useElementScroll = () => {
|
|
5
5
|
const [element, setElement] = useState(null);
|
|
6
6
|
const [scrollPosition, setScrollPosition] = useState({
|
|
@@ -65,4 +65,4 @@ const useElementScroll = () => {
|
|
|
65
65
|
|
|
66
66
|
//#endregion
|
|
67
67
|
export { useElementScroll as default };
|
|
68
|
-
//# sourceMappingURL=
|
|
68
|
+
//# sourceMappingURL=use-element-scroll.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-element-scroll.mjs","names":[],"sources":["../../src/hooks/use-element-scroll.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\n\ninterface ScrollPosition {\n scrollY: number;\n scrollPercentage: number;\n isAtTop: boolean;\n isAtBottom: boolean;\n scrollableHeight: number;\n clientHeight: number;\n scrollHeight: number;\n}\n\nconst useElementScroll = () => {\n const [element, setElement] = useState<HTMLElement | null>(null);\n const [scrollPosition, setScrollPosition] = useState<ScrollPosition>({\n scrollY: 0,\n scrollPercentage: 0,\n isAtTop: true,\n isAtBottom: false,\n scrollableHeight: 0,\n clientHeight: 0,\n scrollHeight: 0,\n });\n\n const setRef = useCallback((el: HTMLElement | null) => {\n setElement(el);\n }, []);\n\n useEffect(() => {\n if (!element) {\n return;\n }\n\n const updateScrollPosition = () => {\n const { scrollTop, scrollHeight, clientHeight } = element;\n const scrollableHeight = scrollHeight - clientHeight;\n\n if (scrollableHeight <= 0) {\n setScrollPosition({\n scrollY: 0,\n scrollPercentage: 0,\n isAtTop: true,\n isAtBottom: true,\n scrollableHeight: 0,\n clientHeight,\n scrollHeight,\n });\n return;\n }\n\n const percentage = Math.min(\n 100,\n Math.max(0, (scrollTop / scrollableHeight) * 100),\n );\n\n setScrollPosition({\n scrollY: scrollTop,\n scrollPercentage: percentage,\n isAtTop: scrollTop <= 0,\n isAtBottom: scrollTop >= scrollableHeight - 1,\n scrollableHeight,\n clientHeight,\n scrollHeight,\n });\n };\n\n updateScrollPosition();\n\n const onScroll = () => {\n updateScrollPosition();\n };\n\n element.addEventListener('scroll', onScroll, { passive: true });\n\n const resizeObserver = new ResizeObserver(() => {\n updateScrollPosition();\n });\n\n resizeObserver.observe(element);\n\n return () => {\n element.removeEventListener('scroll', onScroll);\n resizeObserver.unobserve(element);\n };\n }, [element]);\n\n return { ...scrollPosition, element, setRef };\n};\n\nexport default useElementScroll;\n"],"mappings":";;;AAYA,MAAM,yBAAyB;CAC7B,MAAM,CAAC,SAAS,cAAc,SAA6B,KAAK;CAChE,MAAM,CAAC,gBAAgB,qBAAqB,SAAyB;EACnE,SAAS;EACT,kBAAkB;EAClB,SAAS;EACT,YAAY;EACZ,kBAAkB;EAClB,cAAc;EACd,cAAc;EACf,CAAC;CAEF,MAAM,SAAS,aAAa,OAA2B;AACrD,aAAW,GAAG;IACb,EAAE,CAAC;AAEN,iBAAgB;AACd,MAAI,CAAC,QACH;EAGF,MAAM,6BAA6B;GACjC,MAAM,EAAE,WAAW,cAAc,iBAAiB;GAClD,MAAM,mBAAmB,eAAe;AAExC,OAAI,oBAAoB,GAAG;AACzB,sBAAkB;KAChB,SAAS;KACT,kBAAkB;KAClB,SAAS;KACT,YAAY;KACZ,kBAAkB;KAClB;KACA;KACD,CAAC;AACF;;AAQF,qBAAkB;IAChB,SAAS;IACT,kBAPiB,KAAK,IACtB,KACA,KAAK,IAAI,GAAI,YAAY,mBAAoB,IAAI,CAClD;IAKC,SAAS,aAAa;IACtB,YAAY,aAAa,mBAAmB;IAC5C;IACA;IACA;IACD,CAAC;;AAGJ,wBAAsB;EAEtB,MAAM,iBAAiB;AACrB,yBAAsB;;AAGxB,UAAQ,iBAAiB,UAAU,UAAU,EAAE,SAAS,MAAM,CAAC;EAE/D,MAAM,iBAAiB,IAAI,qBAAqB;AAC9C,yBAAsB;IACtB;AAEF,iBAAe,QAAQ,QAAQ;AAE/B,eAAa;AACX,WAAQ,oBAAoB,UAAU,SAAS;AAC/C,kBAAe,UAAU,QAAQ;;IAElC,CAAC,QAAQ,CAAC;AAEb,QAAO;EAAE,GAAG;EAAgB;EAAS;EAAQ"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/hooks/use-image
|
|
1
|
+
//#region src/hooks/use-image.d.ts
|
|
2
2
|
interface Options {
|
|
3
3
|
retryCount?: number;
|
|
4
4
|
retryDelay?: number;
|
|
@@ -11,4 +11,4 @@ declare const useImage: (src: string, options?: Options) => {
|
|
|
11
11
|
};
|
|
12
12
|
//#endregion
|
|
13
13
|
export { useImage };
|
|
14
|
-
//# sourceMappingURL=
|
|
14
|
+
//# sourceMappingURL=use-image.d.mts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/use-image
|
|
3
|
+
//#region src/hooks/use-image.ts
|
|
4
4
|
const useImage = (src, options = {}) => {
|
|
5
5
|
const { retryCount = 0, retryDelay = 1e3 } = options;
|
|
6
6
|
const [loading, setLoading] = useState(true);
|
|
@@ -61,4 +61,4 @@ const useImage = (src, options = {}) => {
|
|
|
61
61
|
|
|
62
62
|
//#endregion
|
|
63
63
|
export { useImage as default };
|
|
64
|
-
//# sourceMappingURL=
|
|
64
|
+
//# sourceMappingURL=use-image.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-image.mjs","names":[],"sources":["../../src/hooks/use-image.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\n\ninterface Options {\n retryCount?: number;\n retryDelay?: number;\n}\n\nconst useImage = (src: string, options: Options = {}) => {\n const { retryCount = 0, retryDelay = 1000 } = options;\n\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | Event | null>(null);\n const [loaded, setLoaded] = useState(false);\n const [attemptCount, setAttemptCount] = useState(0);\n const [reloadToken, setReloadToken] = useState(0);\n\n useEffect(() => {\n if (!src) {\n setLoading(false);\n setLoaded(false);\n return;\n }\n\n let cancelled = false;\n let retryTimeoutId: ReturnType<typeof setTimeout> | undefined;\n\n setLoading(true);\n setError(null);\n\n const img = new Image();\n img.src = src;\n\n img.onload = () => {\n if (cancelled) {\n return;\n }\n setLoading(false);\n setLoaded(true);\n setError(null);\n };\n\n img.onerror = err => {\n if (cancelled) {\n return;\n }\n setLoading(false);\n setLoaded(false);\n setError(err);\n\n if (attemptCount < retryCount) {\n retryTimeoutId = setTimeout(() => {\n if (!cancelled) {\n setAttemptCount(prev => prev + 1);\n }\n }, retryDelay);\n }\n };\n\n return () => {\n cancelled = true;\n img.onload = null;\n img.onerror = null;\n if (retryTimeoutId) {\n clearTimeout(retryTimeoutId);\n }\n };\n }, [src, attemptCount, retryCount, retryDelay, reloadToken]);\n\n const retry = useCallback(() => {\n setAttemptCount(0);\n setReloadToken(token => token + 1);\n }, []);\n\n return {\n loading,\n error,\n loaded,\n retry,\n };\n};\n\nexport default useImage;\n"],"mappings":";;;AAOA,MAAM,YAAY,KAAa,UAAmB,EAAE,KAAK;CACvD,MAAM,EAAE,aAAa,GAAG,aAAa,QAAS;CAE9C,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,CAAC,OAAO,YAAY,SAAgC,KAAK;CAC/D,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,CAAC,cAAc,mBAAmB,SAAS,EAAE;CACnD,MAAM,CAAC,aAAa,kBAAkB,SAAS,EAAE;AAEjD,iBAAgB;AACd,MAAI,CAAC,KAAK;AACR,cAAW,MAAM;AACjB,aAAU,MAAM;AAChB;;EAGF,IAAI,YAAY;EAChB,IAAI;AAEJ,aAAW,KAAK;AAChB,WAAS,KAAK;EAEd,MAAM,MAAM,IAAI,OAAO;AACvB,MAAI,MAAM;AAEV,MAAI,eAAe;AACjB,OAAI,UACF;AAEF,cAAW,MAAM;AACjB,aAAU,KAAK;AACf,YAAS,KAAK;;AAGhB,MAAI,WAAU,QAAO;AACnB,OAAI,UACF;AAEF,cAAW,MAAM;AACjB,aAAU,MAAM;AAChB,YAAS,IAAI;AAEb,OAAI,eAAe,WACjB,kBAAiB,iBAAiB;AAChC,QAAI,CAAC,UACH,kBAAgB,SAAQ,OAAO,EAAE;MAElC,WAAW;;AAIlB,eAAa;AACX,eAAY;AACZ,OAAI,SAAS;AACb,OAAI,UAAU;AACd,OAAI,eACF,cAAa,eAAe;;IAG/B;EAAC;EAAK;EAAc;EAAY;EAAY;EAAY,CAAC;AAO5D,QAAO;EACL;EACA;EACA;EACA,OATY,kBAAkB;AAC9B,mBAAgB,EAAE;AAClB,mBAAe,UAAS,QAAQ,EAAE;KACjC,EAAE,CAAC;EAOL"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
//#region src/hooks/use-local-storage
|
|
1
|
+
//#region src/hooks/use-local-storage.d.ts
|
|
2
2
|
declare const useLocalStorage: <T>(key: string, initialValue: T) => readonly [T, (value: T | ((val: T) => T)) => void];
|
|
3
3
|
//#endregion
|
|
4
4
|
export { useLocalStorage };
|
|
5
|
-
//# sourceMappingURL=
|
|
5
|
+
//# sourceMappingURL=use-local-storage.d.mts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/use-local-storage
|
|
3
|
+
//#region src/hooks/use-local-storage.ts
|
|
4
4
|
const useLocalStorage = (key, initialValue) => {
|
|
5
5
|
const initialValueRef = useRef(initialValue);
|
|
6
6
|
useEffect(() => {
|
|
@@ -53,4 +53,4 @@ const useLocalStorage = (key, initialValue) => {
|
|
|
53
53
|
|
|
54
54
|
//#endregion
|
|
55
55
|
export { useLocalStorage as default };
|
|
56
|
-
//# sourceMappingURL=
|
|
56
|
+
//# sourceMappingURL=use-local-storage.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-local-storage.mjs","names":[],"sources":["../../src/hooks/use-local-storage.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nconst useLocalStorage = <T>(key: string, initialValue: T) => {\n const initialValueRef = useRef(initialValue);\n\n useEffect(() => {\n initialValueRef.current = initialValue;\n });\n\n const [storedValue, setStoredValue] = useState<T>(() => {\n if (typeof window === 'undefined') {\n return initialValue;\n }\n try {\n const item = window.localStorage.getItem(key);\n return item ? (JSON.parse(item) as T) : initialValue;\n } catch {\n return initialValue;\n }\n });\n\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n try {\n const item = window.localStorage.getItem(key);\n if (item) {\n setStoredValue(JSON.parse(item) as T);\n } else {\n window.localStorage.setItem(\n key,\n JSON.stringify(initialValueRef.current),\n );\n }\n } catch (e) {\n console.error(`Error reading localStorage key \"${key}\":`, e);\n }\n }, [key]);\n\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n const onStorage = (event: StorageEvent) => {\n if (event.key !== key) {\n return;\n }\n\n try {\n setStoredValue(\n event.newValue\n ? (JSON.parse(event.newValue) as T)\n : initialValueRef.current,\n );\n } catch (e) {\n console.error(\n `Error parsing localStorage key \"${key}\" from storage event:`,\n e,\n );\n }\n };\n\n window.addEventListener('storage', onStorage);\n return () => window.removeEventListener('storage', onStorage);\n }, [key]);\n\n const setValue = useCallback(\n (value: T | ((val: T) => T)) => {\n try {\n setStoredValue(prev => {\n const valueToStore = value instanceof Function ? value(prev) : value;\n localStorage.setItem(key, JSON.stringify(valueToStore));\n return valueToStore;\n });\n } catch (e) {\n console.error(`Error setting localStorage key \"${key}\":`, e);\n }\n },\n [key],\n );\n\n return [storedValue, setValue] as const;\n};\n\nexport default useLocalStorage;\n"],"mappings":";;;AAEA,MAAM,mBAAsB,KAAa,iBAAoB;CAC3D,MAAM,kBAAkB,OAAO,aAAa;AAE5C,iBAAgB;AACd,kBAAgB,UAAU;GAC1B;CAEF,MAAM,CAAC,aAAa,kBAAkB,eAAkB;AACtD,MAAI,OAAO,WAAW,YACpB,QAAO;AAET,MAAI;GACF,MAAM,OAAO,OAAO,aAAa,QAAQ,IAAI;AAC7C,UAAO,OAAQ,KAAK,MAAM,KAAK,GAAS;UAClC;AACN,UAAO;;GAET;AAEF,iBAAgB;AACd,MAAI,OAAO,WAAW,YACpB;AAGF,MAAI;GACF,MAAM,OAAO,OAAO,aAAa,QAAQ,IAAI;AAC7C,OAAI,KACF,gBAAe,KAAK,MAAM,KAAK,CAAM;OAErC,QAAO,aAAa,QAClB,KACA,KAAK,UAAU,gBAAgB,QAAQ,CACxC;WAEI,GAAG;AACV,WAAQ,MAAM,mCAAmC,IAAI,KAAK,EAAE;;IAE7D,CAAC,IAAI,CAAC;AAET,iBAAgB;AACd,MAAI,OAAO,WAAW,YACpB;EAGF,MAAM,aAAa,UAAwB;AACzC,OAAI,MAAM,QAAQ,IAChB;AAGF,OAAI;AACF,mBACE,MAAM,WACD,KAAK,MAAM,MAAM,SAAS,GAC3B,gBAAgB,QACrB;YACM,GAAG;AACV,YAAQ,MACN,mCAAmC,IAAI,wBACvC,EACD;;;AAIL,SAAO,iBAAiB,WAAW,UAAU;AAC7C,eAAa,OAAO,oBAAoB,WAAW,UAAU;IAC5D,CAAC,IAAI,CAAC;AAiBT,QAAO,CAAC,aAfS,aACd,UAA+B;AAC9B,MAAI;AACF,mBAAe,SAAQ;IACrB,MAAM,eAAe,iBAAiB,WAAW,MAAM,KAAK,GAAG;AAC/D,iBAAa,QAAQ,KAAK,KAAK,UAAU,aAAa,CAAC;AACvD,WAAO;KACP;WACK,GAAG;AACV,WAAQ,MAAM,mCAAmC,IAAI,KAAK,EAAE;;IAGhE,CAAC,IAAI,CACN,CAE6B"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
//#region src/hooks/use-recursive-timeout
|
|
1
|
+
//#region src/hooks/use-recursive-timeout.d.ts
|
|
2
2
|
declare const useRecursiveTimeout: <T>(callback: () => Promise<T> | (() => void), delay: number | null) => void;
|
|
3
3
|
//#endregion
|
|
4
4
|
export { useRecursiveTimeout };
|
|
5
|
-
//# sourceMappingURL=
|
|
5
|
+
//# sourceMappingURL=use-recursive-timeout.d.mts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useRef } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/use-recursive-timeout
|
|
3
|
+
//#region src/hooks/use-recursive-timeout.ts
|
|
4
4
|
const useRecursiveTimeout = (callback, delay) => {
|
|
5
5
|
const savedCallback = useRef(callback);
|
|
6
6
|
useEffect(() => {
|
|
@@ -26,4 +26,4 @@ const useRecursiveTimeout = (callback, delay) => {
|
|
|
26
26
|
|
|
27
27
|
//#endregion
|
|
28
28
|
export { useRecursiveTimeout as default };
|
|
29
|
-
//# sourceMappingURL=
|
|
29
|
+
//# sourceMappingURL=use-recursive-timeout.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-recursive-timeout.mjs","names":[],"sources":["../../src/hooks/use-recursive-timeout.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\nconst useRecursiveTimeout = <T>(\n callback: () => Promise<T> | (() => void),\n delay: number | null,\n) => {\n const savedCallback = useRef(callback);\n\n useEffect(() => {\n savedCallback.current = callback;\n }, [callback]);\n\n useEffect(() => {\n let id: ReturnType<typeof setTimeout>;\n\n function tick() {\n const ret = savedCallback.current();\n\n if (ret instanceof Promise) {\n ret.then(() => {\n if (delay) {\n id = setTimeout(tick, delay);\n }\n });\n } else {\n if (delay) {\n id = setTimeout(tick, delay);\n }\n }\n }\n\n if (delay) {\n id = setTimeout(tick, delay);\n return () => {\n clearTimeout(id);\n };\n }\n }, [delay]);\n};\n\nexport default useRecursiveTimeout;\n"],"mappings":";;;AAEA,MAAM,uBACJ,UACA,UACG;CACH,MAAM,gBAAgB,OAAO,SAAS;AAEtC,iBAAgB;AACd,gBAAc,UAAU;IACvB,CAAC,SAAS,CAAC;AAEd,iBAAgB;EACd,IAAI;EAEJ,SAAS,OAAO;GACd,MAAM,MAAM,cAAc,SAAS;AAEnC,OAAI,eAAe,QACjB,KAAI,WAAW;AACb,QAAI,MACF,MAAK,WAAW,MAAM,MAAM;KAE9B;YAEE,MACF,MAAK,WAAW,MAAM,MAAM;;AAKlC,MAAI,OAAO;AACT,QAAK,WAAW,MAAM,MAAM;AAC5B,gBAAa;AACX,iBAAa,GAAG;;;IAGnB,CAAC,MAAM,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/hooks/use-responsive-size
|
|
1
|
+
//#region src/hooks/use-responsive-size.d.ts
|
|
2
2
|
type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
|
|
3
3
|
interface BreakpointInfo {
|
|
4
4
|
current: Breakpoint;
|
|
@@ -23,4 +23,4 @@ declare const useResponsiveSize: <T extends HTMLElement>(options?: Options) => {
|
|
|
23
23
|
};
|
|
24
24
|
//#endregion
|
|
25
25
|
export { useResponsiveSize };
|
|
26
|
-
//# sourceMappingURL=
|
|
26
|
+
//# sourceMappingURL=use-responsive-size.d.mts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import useDebounce from "
|
|
1
|
+
import useDebounce from "./use-debounce.mjs";
|
|
2
2
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
3
|
|
|
4
|
-
//#region src/hooks/use-responsive-size
|
|
4
|
+
//#region src/hooks/use-responsive-size.ts
|
|
5
5
|
const BREAKPOINTS = {
|
|
6
6
|
xs: 0,
|
|
7
7
|
sm: 640,
|
|
@@ -105,4 +105,4 @@ const useResponsiveSize = (options) => {
|
|
|
105
105
|
|
|
106
106
|
//#endregion
|
|
107
107
|
export { useResponsiveSize as default };
|
|
108
|
-
//# sourceMappingURL=
|
|
108
|
+
//# sourceMappingURL=use-responsive-size.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-responsive-size.mjs","names":[],"sources":["../../src/hooks/use-responsive-size.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nimport useDebounce from './use-debounce';\n\ntype Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\ninterface BreakpointInfo {\n current: Breakpoint;\n xs: boolean;\n sm: boolean;\n md: boolean;\n lg: boolean;\n xl: boolean;\n '2xl': boolean;\n}\n\ninterface Options {\n delay?: number;\n container?: HTMLElement | null;\n}\n\nconst BREAKPOINTS = {\n xs: 0, // < 640px\n sm: 640, // >= 640px\n md: 768, // >= 768px\n lg: 1024, // >= 1024px\n xl: 1280, // >= 1280px\n '2xl': 1536, // >= 1536px\n} as const;\n\nconst getBreakpointInfo = (width: number): BreakpointInfo => {\n let current: Breakpoint = 'xs';\n\n if (width >= BREAKPOINTS['2xl']) {\n current = '2xl';\n } else if (width >= BREAKPOINTS.xl) {\n current = 'xl';\n } else if (width >= BREAKPOINTS.lg) {\n current = 'lg';\n } else if (width >= BREAKPOINTS.md) {\n current = 'md';\n } else if (width >= BREAKPOINTS.sm) {\n current = 'sm';\n } else {\n current = 'xs';\n }\n\n return {\n current,\n xs: width < BREAKPOINTS.sm,\n sm: width >= BREAKPOINTS.sm && width < BREAKPOINTS.md,\n md: width >= BREAKPOINTS.md && width < BREAKPOINTS.lg,\n lg: width >= BREAKPOINTS.lg && width < BREAKPOINTS.xl,\n xl: width >= BREAKPOINTS.xl && width < BREAKPOINTS['2xl'],\n '2xl': width >= BREAKPOINTS['2xl'],\n };\n};\n\nconst useResponsiveSize = <T extends HTMLElement>(options?: Options) => {\n const { delay = 100, container } = options || {};\n const [size, setSize] = useState({ width: 0, height: 0 });\n const [breakpoint, setBreakpoint] = useState<BreakpointInfo>({\n current: 'xs',\n xs: true,\n sm: false,\n md: false,\n lg: false,\n xl: false,\n '2xl': false,\n });\n\n const [debouncedSize, setDebouncedSize] = useState({ width: 0, height: 0 });\n\n const [element, setElement] = useState<T | null>(null);\n const observerRef = useRef<ResizeObserver | null>(null);\n\n const ref = useCallback((node: T | null) => {\n setElement(node);\n }, []);\n\n useDebounce(\n () => {\n setDebouncedSize(size);\n },\n { delay },\n [size],\n );\n\n useEffect(() => {\n const updateSize = () => {\n const target = container ?? element ?? document.body;\n\n if (!target) {\n return;\n }\n\n const { offsetWidth, offsetHeight } = target;\n\n setSize(prev => {\n if (prev.width !== offsetWidth || prev.height !== offsetHeight) {\n return { width: offsetWidth, height: offsetHeight };\n }\n return prev;\n });\n\n setBreakpoint(prev => {\n const next = getBreakpointInfo(offsetWidth);\n if (prev.current !== next.current) {\n return next;\n }\n return prev;\n });\n };\n\n const connect = () => {\n const target = container ?? element ?? document.body;\n\n if (!target) {\n return;\n }\n\n updateSize();\n\n if (observerRef.current) {\n observerRef.current.disconnect();\n }\n\n observerRef.current = new ResizeObserver(() => {\n requestAnimationFrame(() => {\n updateSize();\n });\n });\n\n observerRef.current.observe(target);\n };\n\n const disconnect = () => {\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n };\n\n connect();\n\n return () => {\n disconnect();\n };\n }, [container, element]);\n\n return {\n size: debouncedSize,\n breakpoint,\n ref,\n };\n};\n\nexport default useResponsiveSize;\n"],"mappings":";;;;AAqBA,MAAM,cAAc;CAClB,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,OAAO;CACR;AAED,MAAM,qBAAqB,UAAkC;CAC3D,IAAI,UAAsB;AAE1B,KAAI,SAAS,YAAY,OACvB,WAAU;UACD,SAAS,YAAY,GAC9B,WAAU;UACD,SAAS,YAAY,GAC9B,WAAU;UACD,SAAS,YAAY,GAC9B,WAAU;UACD,SAAS,YAAY,GAC9B,WAAU;KAEV,WAAU;AAGZ,QAAO;EACL;EACA,IAAI,QAAQ,YAAY;EACxB,IAAI,SAAS,YAAY,MAAM,QAAQ,YAAY;EACnD,IAAI,SAAS,YAAY,MAAM,QAAQ,YAAY;EACnD,IAAI,SAAS,YAAY,MAAM,QAAQ,YAAY;EACnD,IAAI,SAAS,YAAY,MAAM,QAAQ,YAAY;EACnD,OAAO,SAAS,YAAY;EAC7B;;AAGH,MAAM,qBAA4C,YAAsB;CACtE,MAAM,EAAE,QAAQ,KAAK,cAAc,WAAW,EAAE;CAChD,MAAM,CAAC,MAAM,WAAW,SAAS;EAAE,OAAO;EAAG,QAAQ;EAAG,CAAC;CACzD,MAAM,CAAC,YAAY,iBAAiB,SAAyB;EAC3D,SAAS;EACT,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,OAAO;EACR,CAAC;CAEF,MAAM,CAAC,eAAe,oBAAoB,SAAS;EAAE,OAAO;EAAG,QAAQ;EAAG,CAAC;CAE3E,MAAM,CAAC,SAAS,cAAc,SAAmB,KAAK;CACtD,MAAM,cAAc,OAA8B,KAAK;CAEvD,MAAM,MAAM,aAAa,SAAmB;AAC1C,aAAW,KAAK;IACf,EAAE,CAAC;AAEN,mBACQ;AACJ,mBAAiB,KAAK;IAExB,EAAE,OAAO,EACT,CAAC,KAAK,CACP;AAED,iBAAgB;EACd,MAAM,mBAAmB;GACvB,MAAM,SAAS,aAAa,WAAW,SAAS;AAEhD,OAAI,CAAC,OACH;GAGF,MAAM,EAAE,aAAa,iBAAiB;AAEtC,YAAQ,SAAQ;AACd,QAAI,KAAK,UAAU,eAAe,KAAK,WAAW,aAChD,QAAO;KAAE,OAAO;KAAa,QAAQ;KAAc;AAErD,WAAO;KACP;AAEF,kBAAc,SAAQ;IACpB,MAAM,OAAO,kBAAkB,YAAY;AAC3C,QAAI,KAAK,YAAY,KAAK,QACxB,QAAO;AAET,WAAO;KACP;;EAGJ,MAAM,gBAAgB;GACpB,MAAM,SAAS,aAAa,WAAW,SAAS;AAEhD,OAAI,CAAC,OACH;AAGF,eAAY;AAEZ,OAAI,YAAY,QACd,aAAY,QAAQ,YAAY;AAGlC,eAAY,UAAU,IAAI,qBAAqB;AAC7C,gCAA4B;AAC1B,iBAAY;MACZ;KACF;AAEF,eAAY,QAAQ,QAAQ,OAAO;;EAGrC,MAAM,mBAAmB;AACvB,OAAI,YAAY,SAAS;AACvB,gBAAY,QAAQ,YAAY;AAChC,gBAAY,UAAU;;;AAI1B,WAAS;AAET,eAAa;AACX,eAAY;;IAEb,CAAC,WAAW,QAAQ,CAAC;AAExB,QAAO;EACL,MAAM;EACN;EACA;EACD"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/use-scroll-to-elements
|
|
3
|
+
//#region src/hooks/use-scroll-to-elements.d.ts
|
|
4
4
|
interface Options extends ScrollIntoViewOptions {
|
|
5
5
|
offset?: number;
|
|
6
6
|
}
|
|
@@ -11,4 +11,4 @@ declare const useScrollToElements: (options?: Options) => {
|
|
|
11
11
|
};
|
|
12
12
|
//#endregion
|
|
13
13
|
export { useScrollToElements };
|
|
14
|
-
//# sourceMappingURL=
|
|
14
|
+
//# sourceMappingURL=use-scroll-to-elements.d.mts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useRef } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/use-scroll-to-elements
|
|
3
|
+
//#region src/hooks/use-scroll-to-elements.ts
|
|
4
4
|
const useScrollToElements = (options) => {
|
|
5
5
|
const elementRefs = useRef([]);
|
|
6
6
|
const scrollToElement = useCallback((index) => {
|
|
@@ -31,4 +31,4 @@ const useScrollToElements = (options) => {
|
|
|
31
31
|
|
|
32
32
|
//#endregion
|
|
33
33
|
export { useScrollToElements as default };
|
|
34
|
-
//# sourceMappingURL=
|
|
34
|
+
//# sourceMappingURL=use-scroll-to-elements.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-scroll-to-elements.mjs","names":[],"sources":["../../src/hooks/use-scroll-to-elements.ts"],"sourcesContent":["import { useCallback, useRef } from 'react';\n\ninterface Options extends ScrollIntoViewOptions {\n offset?: number;\n}\nconst useScrollToElements = (options?: Options) => {\n const elementRefs = useRef<(HTMLElement | null)[]>([]);\n\n const scrollToElement = useCallback(\n (index: number) => {\n const element = elementRefs.current[index];\n\n if (!element) {\n return;\n }\n\n element.scrollIntoView({\n behavior: 'smooth',\n block: 'start',\n inline: 'start',\n ...options,\n });\n\n if (options?.offset) {\n const top =\n element.getBoundingClientRect().top + window.scrollY - options.offset;\n\n window.scrollTo({\n top,\n behavior: options.behavior || 'smooth',\n });\n }\n },\n [options],\n );\n\n const setElementRef = useCallback(\n (element: HTMLElement | null, index: number) => {\n elementRefs.current[index] = element;\n },\n [],\n );\n\n return { elementRefs, setElementRef, scrollToElement };\n};\n\nexport default useScrollToElements;\n"],"mappings":";;;AAKA,MAAM,uBAAuB,YAAsB;CACjD,MAAM,cAAc,OAA+B,EAAE,CAAC;CAEtD,MAAM,kBAAkB,aACrB,UAAkB;EACjB,MAAM,UAAU,YAAY,QAAQ;AAEpC,MAAI,CAAC,QACH;AAGF,UAAQ,eAAe;GACrB,UAAU;GACV,OAAO;GACP,QAAQ;GACR,GAAG;GACJ,CAAC;AAEF,MAAI,SAAS,QAAQ;GACnB,MAAM,MACJ,QAAQ,uBAAuB,CAAC,MAAM,OAAO,UAAU,QAAQ;AAEjE,UAAO,SAAS;IACd;IACA,UAAU,QAAQ,YAAY;IAC/B,CAAC;;IAGN,CAAC,QAAQ,CACV;AASD,QAAO;EAAE;EAAa,eAPA,aACnB,SAA6B,UAAkB;AAC9C,eAAY,QAAQ,SAAS;KAE/B,EAAE,CACH;EAEoC;EAAiB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
//#region src/hooks/use-throttle
|
|
1
|
+
//#region src/hooks/use-throttle.d.ts
|
|
2
2
|
declare const useThrottle: <T>(value: T, delay?: number) => T;
|
|
3
3
|
//#endregion
|
|
4
4
|
export { useThrottle };
|
|
5
|
-
//# sourceMappingURL=
|
|
5
|
+
//# sourceMappingURL=use-throttle.d.mts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useRef, useState } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/use-throttle
|
|
3
|
+
//#region src/hooks/use-throttle.ts
|
|
4
4
|
const useThrottle = (value, delay = 100) => {
|
|
5
5
|
const [throttledValue, setThrottledValue] = useState(value);
|
|
6
6
|
const lastExecutedRef = useRef(0);
|
|
@@ -39,4 +39,4 @@ const useThrottle = (value, delay = 100) => {
|
|
|
39
39
|
|
|
40
40
|
//#endregion
|
|
41
41
|
export { useThrottle as default };
|
|
42
|
-
//# sourceMappingURL=
|
|
42
|
+
//# sourceMappingURL=use-throttle.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-throttle.mjs","names":[],"sources":["../../src/hooks/use-throttle.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\n\nconst useThrottle = <T>(value: T, delay = 100): T => {\n const [throttledValue, setThrottledValue] = useState(value);\n const lastExecutedRef = useRef(0);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const latestValueRef = useRef(value);\n\n useEffect(() => {\n latestValueRef.current = value;\n }, [value]);\n\n useEffect(() => {\n const now = Date.now();\n const elapsed = now - lastExecutedRef.current;\n\n if (elapsed >= delay) {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n\n lastExecutedRef.current = now;\n setThrottledValue(latestValueRef.current);\n return;\n }\n\n const remaining = delay - elapsed;\n\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n timeoutRef.current = setTimeout(() => {\n lastExecutedRef.current = Date.now();\n setThrottledValue(latestValueRef.current);\n }, remaining);\n\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n };\n }, [value, delay]);\n\n return throttledValue;\n};\n\nexport default useThrottle;\n"],"mappings":";;;AAEA,MAAM,eAAkB,OAAU,QAAQ,QAAW;CACnD,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,MAAM;CAC3D,MAAM,kBAAkB,OAAO,EAAE;CACjC,MAAM,aAAa,OAA6C,KAAK;CACrE,MAAM,iBAAiB,OAAO,MAAM;AAEpC,iBAAgB;AACd,iBAAe,UAAU;IACxB,CAAC,MAAM,CAAC;AAEX,iBAAgB;EACd,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,UAAU,MAAM,gBAAgB;AAEtC,MAAI,WAAW,OAAO;AACpB,OAAI,WAAW,SAAS;AACtB,iBAAa,WAAW,QAAQ;AAChC,eAAW,UAAU;;AAGvB,mBAAgB,UAAU;AAC1B,qBAAkB,eAAe,QAAQ;AACzC;;EAGF,MAAM,YAAY,QAAQ;AAE1B,MAAI,WAAW,QACb,cAAa,WAAW,QAAQ;AAGlC,aAAW,UAAU,iBAAiB;AACpC,mBAAgB,UAAU,KAAK,KAAK;AACpC,qBAAkB,eAAe,QAAQ;KACxC,UAAU;AAEb,eAAa;AACX,OAAI,WAAW,SAAS;AACtB,iBAAa,WAAW,QAAQ;AAChC,eAAW,UAAU;;;IAGxB,CAAC,OAAO,MAAM,CAAC;AAElB,QAAO"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/use-timeline
|
|
3
|
+
//#region src/hooks/use-timeline.d.ts
|
|
4
4
|
interface TimelineStep {
|
|
5
5
|
scale?: number;
|
|
6
6
|
rotate?: {
|
|
@@ -47,4 +47,4 @@ declare const useTimeline: ({
|
|
|
47
47
|
};
|
|
48
48
|
//#endregion
|
|
49
49
|
export { useTimeline };
|
|
50
|
-
//# sourceMappingURL=
|
|
50
|
+
//# sourceMappingURL=use-timeline.d.mts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/use-timeline
|
|
3
|
+
//#region src/hooks/use-timeline.ts
|
|
4
4
|
const registeredProperties = /* @__PURE__ */ new Set();
|
|
5
5
|
const registerCSSProperty = (name, syntax = "<length>", initialValue = "0px") => {
|
|
6
6
|
if (registeredProperties.has(name) || typeof CSS === "undefined" || !CSS.registerProperty) return;
|
|
@@ -173,4 +173,4 @@ const useTimeline = ({ steps = [], loading, immediate, loop }) => {
|
|
|
173
173
|
|
|
174
174
|
//#endregion
|
|
175
175
|
export { useTimeline as default };
|
|
176
|
-
//# sourceMappingURL=
|
|
176
|
+
//# sourceMappingURL=use-timeline.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-timeline.mjs","names":[],"sources":["../../src/hooks/use-timeline.ts"],"sourcesContent":["import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';\n\nconst registeredProperties = new Set<string>();\n\nconst registerCSSProperty = (\n name: string,\n syntax = '<length>',\n initialValue = '0px',\n) => {\n if (\n registeredProperties.has(name) ||\n typeof CSS === 'undefined' ||\n !CSS.registerProperty\n ) {\n return;\n }\n\n try {\n CSS.registerProperty({\n name,\n syntax,\n inherits: false,\n initialValue,\n });\n registeredProperties.add(name);\n } catch {\n // 이미 등록된 경우 무시\n }\n};\n\nexport interface TimelineStep {\n scale?: number;\n rotate?: { x?: number; y?: number; z?: number };\n position?: { x?: number; y?: number };\n top?: number;\n left?: number;\n width?: number;\n height?: number;\n zIndex?: number;\n filter?: string;\n backgroundColor?: string;\n opacity?: number;\n selector: string;\n transition: Transition;\n}\n\ninterface Transition {\n duration: number;\n ease?: string;\n delay?: number;\n easeX?: string;\n easeY?: string;\n}\n\ninterface Options {\n steps?: TimelineStep[];\n loading?: boolean;\n immediate?: boolean;\n loop?: boolean;\n}\n\nconst buildTimeline = (steps: TimelineStep[]): TimelineStep[][] => {\n const groups: TimelineStep[][] = [];\n\n for (const step of steps) {\n if (step.transition.delay === 0 && groups.length > 0) {\n groups[groups.length - 1]?.push(step);\n } else {\n groups.push([step]);\n }\n }\n\n return groups;\n};\n\nconst buildTransform = (step: TimelineStep) => {\n const parts: string[] = [];\n\n if (step.position?.x != null || step.position?.y != null) {\n parts.push(\n `translate(${step.position.x ?? 0}px, ${step.position.y ?? 0}px)`,\n );\n }\n\n if (step.scale != null) {\n parts.push(`scale(${step.scale})`);\n }\n\n if (step.rotate?.x != null) {\n parts.push(`rotateX(${step.rotate.x}deg)`);\n }\n\n if (step.rotate?.y != null) {\n parts.push(`rotateY(${step.rotate.y}deg)`);\n }\n\n if (step.rotate?.z != null) {\n parts.push(`rotateZ(${step.rotate.z}deg)`);\n }\n\n return parts.join(' ') || undefined;\n};\n\nconst applyStep = (\n container: HTMLElement,\n step: TimelineStep,\n skipTransition?: boolean,\n) => {\n const elements = container.matches(step.selector)\n ? [container]\n : Array.from(container.querySelectorAll<HTMLElement>(step.selector));\n\n if (!elements.length) {\n return;\n }\n\n const dur = `${step.transition.duration}ms`;\n const eas = step.transition.ease ?? 'ease-out';\n const easX = step.transition.easeX;\n const easY = step.transition.easeY;\n const hasCurve = !!(easX || easY);\n const timers: ReturnType<typeof setTimeout>[] = [];\n\n elements.forEach(el => {\n if (skipTransition) {\n el.style.transition = 'none';\n } else if (hasCurve) {\n const xEase = easX ?? eas;\n const yEase = easY ?? eas;\n el.style.transition = [\n `--tx ${dur} ${xEase}`,\n `--ty ${dur} ${yEase}`,\n `--s ${dur} ${eas}`,\n `rotate ${dur} ${eas}`,\n `width ${dur} ${eas}`,\n `height ${dur} ${eas}`,\n `filter ${dur} ${eas}`,\n `opacity ${dur} ${eas}`,\n ].join(', ');\n } else {\n el.style.transition = `all ${dur} ${eas}`;\n }\n\n if (step.backgroundColor) {\n el.style.backgroundColor = step.backgroundColor;\n }\n\n if (step.opacity !== undefined) {\n el.style.opacity = String(step.opacity);\n }\n\n if (step.top !== undefined) {\n el.style.top = `${step.top}px`;\n }\n\n if (step.left !== undefined) {\n el.style.left = `${step.left}px`;\n }\n\n if (step.width !== undefined) {\n el.style.width = `${step.width}px`;\n }\n\n if (step.height !== undefined) {\n el.style.height = `${step.height}px`;\n }\n\n if (step.zIndex !== undefined) {\n if (skipTransition) {\n el.style.zIndex = String(step.zIndex);\n } else {\n const timerId = setTimeout(() => {\n el.style.zIndex = String(step.zIndex);\n }, step.transition.duration / 2);\n timers.push(timerId);\n }\n }\n\n if (step.filter !== undefined) {\n el.style.filter = step.filter;\n }\n\n if (step.rotate?.z !== undefined) {\n el.style.rotate = `${step.rotate.z}deg`;\n }\n\n if (hasCurve) {\n if (step.position?.x != null || step.position?.y != null) {\n el.style.setProperty('--tx', `${step.position.x ?? 0}px`);\n el.style.setProperty('--ty', `${step.position.y ?? 0}px`);\n }\n if (step.scale != null) {\n el.style.setProperty('--s', String(step.scale));\n }\n el.style.transform = `translate(var(--tx), var(--ty)) scale(var(--s, 1))`;\n } else {\n const transform = buildTransform(step);\n\n if (transform) {\n el.style.transform = transform;\n }\n }\n });\n\n return timers;\n};\n\nconst useTimeline = ({ steps = [], loading, immediate, loop }: Options) => {\n const [slotIndex, setSlotIndex] = useState(-1);\n const [completed, setCompleted] = useState(() => !!immediate);\n\n const ref = useRef<HTMLDivElement>(null);\n const timeline = useMemo(() => buildTimeline(steps), [steps]);\n const hasCurveSteps = useMemo(\n () => steps.some(s => s.transition.easeX || s.transition.easeY),\n [steps],\n );\n\n useLayoutEffect(() => {\n if (hasCurveSteps) {\n registerCSSProperty('--tx');\n registerCSSProperty('--ty');\n registerCSSProperty('--s', '<number>', '1');\n }\n }, [hasCurveSteps]);\n\n useLayoutEffect(() => {\n if (!immediate || !ref.current || !steps.length) {\n return;\n }\n\n const container = ref.current;\n steps.forEach(step => applyStep(container, step, true));\n }, [immediate, steps]);\n\n useEffect(() => {\n if (immediate || loading || !timeline.length) {\n return;\n }\n\n const firstDelay = timeline[0]?.[0]?.transition.delay ?? 0;\n const timerId = window.setTimeout(() => {\n requestAnimationFrame(() => setSlotIndex(0));\n }, firstDelay);\n\n return () => clearTimeout(timerId);\n }, [loading, timeline, immediate]);\n\n useEffect(() => {\n if (immediate || loading || slotIndex < 0) {\n return;\n }\n\n const isLast = slotIndex >= timeline.length - 1;\n\n if (isLast && !loop) {\n return;\n }\n\n const currentSlot = timeline[slotIndex];\n const maxDuration = Math.max(\n ...(currentSlot?.map(s => s.transition.duration) ?? [0]),\n );\n\n const nextIndex = isLast ? 0 : slotIndex + 1;\n const nextDelay = timeline[nextIndex]?.[0]?.transition.delay ?? 0;\n\n const timerId = window.setTimeout(() => {\n setSlotIndex(nextIndex);\n }, maxDuration + nextDelay);\n\n return () => clearTimeout(timerId);\n }, [loading, slotIndex, timeline, immediate, loop]);\n\n useEffect(() => {\n if (immediate || slotIndex < 0 || !ref.current) {\n return;\n }\n\n const container = ref.current;\n const timers: ReturnType<typeof setTimeout>[] = [];\n\n const rafId = requestAnimationFrame(() => {\n timeline[slotIndex]?.forEach(step => {\n const stepTimers = applyStep(container, step);\n\n if (stepTimers) {\n timers.push(...stepTimers);\n }\n });\n });\n\n return () => {\n cancelAnimationFrame(rafId);\n timers.forEach(clearTimeout);\n };\n }, [immediate, slotIndex, timeline]);\n\n useEffect(() => {\n if (\n immediate ||\n loop ||\n slotIndex < 0 ||\n slotIndex < timeline.length - 1 ||\n !timeline.length\n ) {\n return;\n }\n\n const lastSlot = timeline[slotIndex];\n const maxDuration = Math.max(\n ...(lastSlot?.map(s => s.transition.duration) ?? [0]),\n );\n\n const timerId = window.setTimeout(() => {\n setCompleted(true);\n }, maxDuration);\n\n return () => clearTimeout(timerId);\n }, [slotIndex, timeline, immediate, loop]);\n\n return { ref, completed };\n};\n\nexport default useTimeline;\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/hooks/use-viewport
|
|
1
|
+
//#region src/hooks/use-viewport.d.ts
|
|
2
2
|
type ViewportInfo = VisualViewport | {
|
|
3
3
|
width: number;
|
|
4
4
|
height: number;
|
|
@@ -15,4 +15,4 @@ interface Options {
|
|
|
15
15
|
declare const useViewport: (options?: Options) => ViewportInfo;
|
|
16
16
|
//#endregion
|
|
17
17
|
export { useViewport };
|
|
18
|
-
//# sourceMappingURL=
|
|
18
|
+
//# sourceMappingURL=use-viewport.d.mts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/use-viewport
|
|
3
|
+
//#region src/hooks/use-viewport.ts
|
|
4
4
|
const useViewport = (options = {}) => {
|
|
5
5
|
const { isInApp = false, debounce = 100 } = options;
|
|
6
6
|
const [viewport, setViewport] = useState({
|
|
@@ -84,4 +84,4 @@ const useViewport = (options = {}) => {
|
|
|
84
84
|
|
|
85
85
|
//#endregion
|
|
86
86
|
export { useViewport as default };
|
|
87
|
-
//# sourceMappingURL=
|
|
87
|
+
//# sourceMappingURL=use-viewport.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-viewport.mjs","names":[],"sources":["../../src/hooks/use-viewport.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\n\ntype ViewportInfo =\n | VisualViewport\n | {\n width: number;\n height: number;\n offsetLeft: number;\n offsetTop: number;\n pageLeft: number;\n pageTop: number;\n scale: number;\n };\n\ninterface Options {\n isInApp?: boolean;\n debounce?: number;\n}\n\nconst useViewport = (options: Options = {}) => {\n const { isInApp = false, debounce = 100 } = options;\n\n const [viewport, setViewport] = useState<ViewportInfo>({\n width: 0,\n height: 0,\n offsetLeft: 0,\n offsetTop: 0,\n pageLeft: 0,\n pageTop: 0,\n scale: 1,\n });\n\n const getAppViewHeight = useCallback(() => {\n const windowHeight = window.innerHeight;\n const visualHeight = window.visualViewport?.height || windowHeight;\n const documentHeight = document.documentElement.clientHeight;\n const bodyHeight = document.body.clientHeight;\n\n if (window.visualViewport && Math.abs(visualHeight - windowHeight) > 100) {\n return visualHeight;\n }\n\n return Math.max(windowHeight, documentHeight, bodyHeight);\n }, []);\n\n const readViewport = useCallback((): ViewportInfo => {\n if (window.visualViewport && !isInApp) {\n return window.visualViewport;\n }\n\n const width = window.visualViewport?.width || window.innerWidth;\n const height = isInApp\n ? getAppViewHeight()\n : window.visualViewport?.height || window.innerHeight;\n\n return {\n width,\n height,\n offsetLeft: window.visualViewport?.offsetLeft || 0,\n offsetTop: window.visualViewport?.offsetTop || 0,\n pageLeft: window.scrollX ?? window.pageXOffset ?? 0,\n pageTop: window.scrollY ?? window.pageYOffset ?? 0,\n scale: window.visualViewport?.scale || 1,\n };\n }, [isInApp, getAppViewHeight]);\n\n useEffect(() => {\n let timeoutId: ReturnType<typeof setTimeout>;\n\n const debouncedUpdate = () => {\n clearTimeout(timeoutId);\n timeoutId = setTimeout(() => {\n setViewport(readViewport());\n }, debounce);\n };\n\n const immediateUpdate = () => setViewport(readViewport());\n\n immediateUpdate();\n\n const events = ['resize', 'orientationchange'];\n\n if (isInApp) {\n events.push('focus', 'blur', 'touchstart', 'touchend');\n }\n\n events.forEach(event => {\n if (event === 'resize' || event === 'orientationchange') {\n window.addEventListener(event, debouncedUpdate);\n } else {\n window.addEventListener(event, immediateUpdate, { passive: true });\n }\n });\n\n if (window.visualViewport) {\n window.visualViewport.addEventListener('resize', immediateUpdate);\n window.visualViewport.addEventListener('scroll', immediateUpdate);\n }\n\n let intervalId: ReturnType<typeof setInterval>;\n if (isInApp) {\n let lastHeight = readViewport().height;\n intervalId = setInterval(() => {\n const currentHeight = readViewport().height;\n if (Math.abs(currentHeight - lastHeight) > 50) {\n lastHeight = currentHeight;\n immediateUpdate();\n }\n }, 500);\n }\n\n return () => {\n clearTimeout(timeoutId);\n if (intervalId) clearInterval(intervalId);\n\n events.forEach(event => {\n window.removeEventListener(\n event,\n event === 'resize' || event === 'orientationchange'\n ? debouncedUpdate\n : immediateUpdate,\n );\n });\n\n if (window.visualViewport) {\n window.visualViewport.removeEventListener('resize', immediateUpdate);\n window.visualViewport.removeEventListener('scroll', immediateUpdate);\n }\n };\n }, [readViewport, isInApp, debounce]);\n\n return viewport;\n};\n\nexport default useViewport;\n"],"mappings":";;;AAmBA,MAAM,eAAe,UAAmB,EAAE,KAAK;CAC7C,MAAM,EAAE,UAAU,OAAO,WAAW,QAAQ;CAE5C,MAAM,CAAC,UAAU,eAAe,SAAuB;EACrD,OAAO;EACP,QAAQ;EACR,YAAY;EACZ,WAAW;EACX,UAAU;EACV,SAAS;EACT,OAAO;EACR,CAAC;CAEF,MAAM,mBAAmB,kBAAkB;EACzC,MAAM,eAAe,OAAO;EAC5B,MAAM,eAAe,OAAO,gBAAgB,UAAU;EACtD,MAAM,iBAAiB,SAAS,gBAAgB;EAChD,MAAM,aAAa,SAAS,KAAK;AAEjC,MAAI,OAAO,kBAAkB,KAAK,IAAI,eAAe,aAAa,GAAG,IACnE,QAAO;AAGT,SAAO,KAAK,IAAI,cAAc,gBAAgB,WAAW;IACxD,EAAE,CAAC;CAEN,MAAM,eAAe,kBAAgC;AACnD,MAAI,OAAO,kBAAkB,CAAC,QAC5B,QAAO,OAAO;AAQhB,SAAO;GACL,OANY,OAAO,gBAAgB,SAAS,OAAO;GAOnD,QANa,UACX,kBAAkB,GAClB,OAAO,gBAAgB,UAAU,OAAO;GAK1C,YAAY,OAAO,gBAAgB,cAAc;GACjD,WAAW,OAAO,gBAAgB,aAAa;GAC/C,UAAU,OAAO,WAAW,OAAO,eAAe;GAClD,SAAS,OAAO,WAAW,OAAO,eAAe;GACjD,OAAO,OAAO,gBAAgB,SAAS;GACxC;IACA,CAAC,SAAS,iBAAiB,CAAC;AAE/B,iBAAgB;EACd,IAAI;EAEJ,MAAM,wBAAwB;AAC5B,gBAAa,UAAU;AACvB,eAAY,iBAAiB;AAC3B,gBAAY,cAAc,CAAC;MAC1B,SAAS;;EAGd,MAAM,wBAAwB,YAAY,cAAc,CAAC;AAEzD,mBAAiB;EAEjB,MAAM,SAAS,CAAC,UAAU,oBAAoB;AAE9C,MAAI,QACF,QAAO,KAAK,SAAS,QAAQ,cAAc,WAAW;AAGxD,SAAO,SAAQ,UAAS;AACtB,OAAI,UAAU,YAAY,UAAU,oBAClC,QAAO,iBAAiB,OAAO,gBAAgB;OAE/C,QAAO,iBAAiB,OAAO,iBAAiB,EAAE,SAAS,MAAM,CAAC;IAEpE;AAEF,MAAI,OAAO,gBAAgB;AACzB,UAAO,eAAe,iBAAiB,UAAU,gBAAgB;AACjE,UAAO,eAAe,iBAAiB,UAAU,gBAAgB;;EAGnE,IAAI;AACJ,MAAI,SAAS;GACX,IAAI,aAAa,cAAc,CAAC;AAChC,gBAAa,kBAAkB;IAC7B,MAAM,gBAAgB,cAAc,CAAC;AACrC,QAAI,KAAK,IAAI,gBAAgB,WAAW,GAAG,IAAI;AAC7C,kBAAa;AACb,sBAAiB;;MAElB,IAAI;;AAGT,eAAa;AACX,gBAAa,UAAU;AACvB,OAAI,WAAY,eAAc,WAAW;AAEzC,UAAO,SAAQ,UAAS;AACtB,WAAO,oBACL,OACA,UAAU,YAAY,UAAU,sBAC5B,kBACA,gBACL;KACD;AAEF,OAAI,OAAO,gBAAgB;AACzB,WAAO,eAAe,oBAAoB,UAAU,gBAAgB;AACpE,WAAO,eAAe,oBAAoB,UAAU,gBAAgB;;;IAGvE;EAAC;EAAc;EAAS;EAAS,CAAC;AAErC,QAAO"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/hooks/use-window-scroll
|
|
1
|
+
//#region src/hooks/use-window-scroll.d.ts
|
|
2
2
|
declare const useWindowScroll: () => {
|
|
3
3
|
x: number;
|
|
4
4
|
y: number;
|
|
@@ -9,4 +9,4 @@ declare const useWindowScroll: () => {
|
|
|
9
9
|
};
|
|
10
10
|
//#endregion
|
|
11
11
|
export { useWindowScroll };
|
|
12
|
-
//# sourceMappingURL=
|
|
12
|
+
//# sourceMappingURL=use-window-scroll.d.mts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/use-window-scroll
|
|
3
|
+
//#region src/hooks/use-window-scroll.ts
|
|
4
4
|
const useWindowScroll = () => {
|
|
5
5
|
const [state, setState] = useState({
|
|
6
6
|
x: 0,
|
|
@@ -57,4 +57,4 @@ const useWindowScroll = () => {
|
|
|
57
57
|
|
|
58
58
|
//#endregion
|
|
59
59
|
export { useWindowScroll as default };
|
|
60
|
-
//# sourceMappingURL=
|
|
60
|
+
//# sourceMappingURL=use-window-scroll.mjs.map
|
|
@@ -0,0 +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 = window.scrollX || 0;\n const y = window.scrollY || 0;\n\n const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);\n const visualViewport = window.visualViewport;\n\n const viewportWidth =\n isIOS && visualViewport ? visualViewport.width : window.innerWidth;\n\n const viewportHeight =\n isIOS && visualViewport ? visualViewport.height : window.innerHeight;\n\n const maxScrollX = Math.max(\n 0,\n document.documentElement.scrollWidth - viewportWidth,\n );\n const maxScrollY = Math.max(\n 0,\n document.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 window.addEventListener('scroll', onScroll, { passive: true });\n window.addEventListener('resize', onResize);\n window.addEventListener('orientationchange', onResize);\n\n if (window.visualViewport) {\n window.visualViewport.addEventListener('resize', onVisualViewportChange);\n }\n\n return () => {\n window.removeEventListener('scroll', onScroll);\n window.removeEventListener('resize', onResize);\n window.removeEventListener('orientationchange', onResize);\n\n if (window.visualViewport) {\n window.visualViewport.removeEventListener(\n 'resize',\n onVisualViewportChange,\n );\n }\n };\n }, []);\n\n return state;\n};\n\nexport default useWindowScroll;\n"],"mappings":";;;AAEA,MAAM,wBAAwB;CAC5B,MAAM,CAAC,OAAO,YAAY,SAAS;EACjC,GAAG;EACH,GAAG;EACH,SAAS;GACP,GAAG;GACH,GAAG;GACJ;EACF,CAAC;AAEF,iBAAgB;EACd,MAAM,kBAAkB;GACtB,MAAM,IAAI,OAAO,WAAW;GAC5B,MAAM,IAAI,OAAO,WAAW;GAE5B,MAAM,QAAQ,mBAAmB,KAAK,UAAU,UAAU;GAC1D,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,SAAS,gBAAgB,cAAc,cACxC;GACD,MAAM,aAAa,KAAK,IACtB,GACA,SAAS,gBAAgB,eAAe,eACzC;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,EAAE,CAAC;AAEN,QAAO"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { useDebounce } from "./hooks/use-debounce
|
|
2
|
-
import { useBodyScrollLock } from "./hooks/use-body-scroll-lock
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
1
|
+
import { useDebounce } from "./hooks/use-debounce.mjs";
|
|
2
|
+
import { useBodyScrollLock } from "./hooks/use-body-scroll-lock.mjs";
|
|
3
|
+
import { useClickOutside } from "./hooks/use-click-outside.mjs";
|
|
4
|
+
import { useElementPosition } from "./hooks/use-element-position.mjs";
|
|
5
|
+
import { useElementScroll } from "./hooks/use-element-scroll.mjs";
|
|
6
|
+
import { useResponsiveSize } from "./hooks/use-responsive-size.mjs";
|
|
7
|
+
import { useImage } from "./hooks/use-image.mjs";
|
|
8
|
+
import { useLocalStorage } from "./hooks/use-local-storage.mjs";
|
|
9
|
+
import { useRecursiveTimeout } from "./hooks/use-recursive-timeout.mjs";
|
|
10
|
+
import { useScrollToElements } from "./hooks/use-scroll-to-elements.mjs";
|
|
11
|
+
import { useThrottle } from "./hooks/use-throttle.mjs";
|
|
12
|
+
import { useTimeline } from "./hooks/use-timeline.mjs";
|
|
13
|
+
import { useWindowScroll } from "./hooks/use-window-scroll.mjs";
|
|
14
|
+
import { useViewport } from "./hooks/use-viewport.mjs";
|
|
14
15
|
import "./hooks/index.mjs";
|
|
15
|
-
export { useBodyScrollLock, useDebounce, useElementPosition, useElementScroll, useImage, useLocalStorage, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useTimeline, useViewport, useWindowScroll };
|
|
16
|
+
export { useBodyScrollLock, useClickOutside, useDebounce, useElementPosition, useElementScroll, useImage, useLocalStorage, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useTimeline, useViewport, useWindowScroll };
|
package/dist/index.mjs
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import useDebounce from "./hooks/use-debounce
|
|
2
|
-
import useBodyScrollLock from "./hooks/use-body-scroll-lock
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
1
|
+
import useDebounce from "./hooks/use-debounce.mjs";
|
|
2
|
+
import useBodyScrollLock from "./hooks/use-body-scroll-lock.mjs";
|
|
3
|
+
import useClickOutside from "./hooks/use-click-outside.mjs";
|
|
4
|
+
import useElementPosition from "./hooks/use-element-position.mjs";
|
|
5
|
+
import useElementScroll from "./hooks/use-element-scroll.mjs";
|
|
6
|
+
import useResponsiveSize from "./hooks/use-responsive-size.mjs";
|
|
7
|
+
import useImage from "./hooks/use-image.mjs";
|
|
8
|
+
import useLocalStorage from "./hooks/use-local-storage.mjs";
|
|
9
|
+
import useRecursiveTimeout from "./hooks/use-recursive-timeout.mjs";
|
|
10
|
+
import useScrollToElements from "./hooks/use-scroll-to-elements.mjs";
|
|
11
|
+
import useThrottle from "./hooks/use-throttle.mjs";
|
|
12
|
+
import useTimeline from "./hooks/use-timeline.mjs";
|
|
13
|
+
import useWindowScroll from "./hooks/use-window-scroll.mjs";
|
|
14
|
+
import useViewport from "./hooks/use-viewport.mjs";
|
|
14
15
|
import "./hooks/index.mjs";
|
|
15
16
|
|
|
16
|
-
export { useBodyScrollLock, useDebounce, useElementPosition, useElementScroll, useImage, useLocalStorage, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useTimeline, useViewport, useWindowScroll };
|
|
17
|
+
export { useBodyScrollLock, useClickOutside, useDebounce, useElementPosition, useElementScroll, useImage, 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.3.0",
|
|
4
4
|
"description": "A collection of reusable React 19 hooks for common UI and interaction patterns",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@eslint/js": "^9.33.0",
|
|
55
|
+
"@jbpark/ui-kit": "^2.7.0",
|
|
55
56
|
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
|
56
57
|
"@types/node": "^24.5.2",
|
|
57
58
|
"@types/react": "^19.1.10",
|
|
@@ -69,6 +70,7 @@
|
|
|
69
70
|
"prettier-plugin-tailwindcss": "^0.6.14",
|
|
70
71
|
"react": "^19.1.1",
|
|
71
72
|
"react-dom": "^19.1.1",
|
|
73
|
+
"react-router-dom": "^7.18.1",
|
|
72
74
|
"tsdown": "^0.20.3",
|
|
73
75
|
"typescript": "~5.8.3",
|
|
74
76
|
"typescript-eslint": "^8.39.1",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/use-body-scroll-lock/index.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\nlet lockCount = 0;\nlet originalStyles: LockedStyles | null = null;\nlet originalScrollY = 0;\n\nconst isIOS = () => /iPad|iPhone|iPod/.test(navigator.userAgent);\n\nconst lockScroll = () => {\n if (lockCount === 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 lockCount += 1;\n};\n\nconst unlockScroll = () => {\n lockCount = Math.max(0, lockCount - 1);\n\n if (lockCount > 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 (!enabled) {\n return;\n }\n\n lockScroll();\n\n if (isIOS()) {\n const preventAll = (e: Event) => {\n if (\n e.target === document.body ||\n e.target === document.documentElement ||\n e.target === window\n ) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n }\n };\n\n const preventScroll = () => {\n window.scrollTo(0, 0);\n document.body.scrollTop = 0;\n document.documentElement.scrollTop = 0;\n };\n\n const events = ['scroll', 'touchmove', 'touchstart', 'touchend'];\n events.forEach(event => {\n window.addEventListener(event, preventAll, {\n passive: false,\n capture: true,\n });\n document.addEventListener(event, preventAll, {\n passive: false,\n capture: true,\n });\n document.body.addEventListener(event, preventAll, {\n passive: false,\n capture: true,\n });\n });\n\n const scrollInterval = setInterval(preventScroll, 16);\n\n return () => {\n clearInterval(scrollInterval);\n events.forEach(event => {\n window.removeEventListener(event, preventAll, { capture: true });\n document.removeEventListener(event, preventAll, { capture: true });\n document.body.removeEventListener(event, preventAll, {\n capture: true,\n });\n });\n unlockScroll();\n };\n }\n\n return () => {\n unlockScroll();\n };\n }, [enabled]);\n};\n\nexport default useBodyScrollLock;\n"],"mappings":";;;AAsBA,IAAI,YAAY;AAChB,IAAI,iBAAsC;AAC1C,IAAI,kBAAkB;AAEtB,MAAM,cAAc,mBAAmB,KAAK,UAAU,UAAU;AAEhE,MAAM,mBAAmB;AACvB,KAAI,cAAc,GAAG;AACnB,oBAAkB,OAAO;EACzB,MAAM,iBACJ,OAAO,aAAa,SAAS,gBAAgB;EAC/C,MAAM,sBACJ,WAAW,iBAAiB,SAAS,KAAK,CAAC,aAAa,IAAI;AAE9D,mBAAiB;GACf,iBAAiB;IACf,UAAU,SAAS,gBAAgB,MAAM;IACzC,QAAQ,SAAS,gBAAgB,MAAM;IACvC,UAAU,SAAS,gBAAgB,MAAM;IACzC,OAAO,SAAS,gBAAgB,MAAM;IACvC;GACD,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,6BACD;IACF;GACF;AAED,WAAS,gBAAgB,MAAM,WAAW;AAC1C,WAAS,gBAAgB,MAAM,SAAS;AACxC,WAAS,gBAAgB,MAAM,WAAW;AAC1C,WAAS,gBAAgB,MAAM,QAAQ;AAEvC,WAAS,KAAK,MAAM,WAAW;AAC/B,WAAS,KAAK,MAAM,SAAS;AAC7B,WAAS,KAAK,MAAM,WAAW;AAC/B,WAAS,KAAK,MAAM,MAAM,IAAI,gBAAgB;AAC9C,WAAS,KAAK,MAAM,OAAO;AAC3B,WAAS,KAAK,MAAM,QAAQ;AAC5B,WAAS,KAAK,MAAM,QAAQ;AAE5B,MAAI,iBAAiB,EACnB,UAAS,KAAK,MAAM,eAAe,GAAG,sBAAsB,eAAe;AAG7E,MAAI,OAAO,CACT,UAAS,KAAK,MAAM,YAAY,8BAA8B,QAAQ;;AAI1E,cAAa;;AAGf,MAAM,qBAAqB;AACzB,aAAY,KAAK,IAAI,GAAG,YAAY,EAAE;AAEtC,KAAI,YAAY,KAAK,CAAC,eACpB;AAGF,UAAS,gBAAgB,MAAM,WAC7B,eAAe,gBAAgB;AACjC,UAAS,gBAAgB,MAAM,SAAS,eAAe,gBAAgB;AACvE,UAAS,gBAAgB,MAAM,WAC7B,eAAe,gBAAgB;AACjC,UAAS,gBAAgB,MAAM,QAAQ,eAAe,gBAAgB;AAEtE,UAAS,KAAK,MAAM,WAAW,eAAe,KAAK;AACnD,UAAS,KAAK,MAAM,SAAS,eAAe,KAAK;AACjD,UAAS,KAAK,MAAM,WAAW,eAAe,KAAK;AACnD,UAAS,KAAK,MAAM,MAAM,eAAe,KAAK;AAC9C,UAAS,KAAK,MAAM,OAAO,eAAe,KAAK;AAC/C,UAAS,KAAK,MAAM,QAAQ,eAAe,KAAK;AAChD,UAAS,KAAK,MAAM,QAAQ,eAAe,KAAK;AAChD,UAAS,KAAK,MAAM,eAAe,eAAe,KAAK;AAEvD,KAAI,eAAe,KAAK,wBACtB,UAAS,KAAK,MAAM,YAClB,8BACA,eAAe,KAAK,wBACrB;KAED,UAAS,KAAK,MAAM,eAAe,6BAA6B;AAGlE,QAAO,SAAS,GAAG,gBAAgB;AACnC,kBAAiB;;AAGnB,MAAM,qBAAqB,UAAmB,SAAS;AACrD,iBAAgB;AACd,MAAI,CAAC,QACH;AAGF,cAAY;AAEZ,MAAI,OAAO,EAAE;GACX,MAAM,cAAc,MAAa;AAC/B,QACE,EAAE,WAAW,SAAS,QACtB,EAAE,WAAW,SAAS,mBACtB,EAAE,WAAW,QACb;AACA,OAAE,gBAAgB;AAClB,OAAE,iBAAiB;AACnB,OAAE,0BAA0B;;;GAIhC,MAAM,sBAAsB;AAC1B,WAAO,SAAS,GAAG,EAAE;AACrB,aAAS,KAAK,YAAY;AAC1B,aAAS,gBAAgB,YAAY;;GAGvC,MAAM,SAAS;IAAC;IAAU;IAAa;IAAc;IAAW;AAChE,UAAO,SAAQ,UAAS;AACtB,WAAO,iBAAiB,OAAO,YAAY;KACzC,SAAS;KACT,SAAS;KACV,CAAC;AACF,aAAS,iBAAiB,OAAO,YAAY;KAC3C,SAAS;KACT,SAAS;KACV,CAAC;AACF,aAAS,KAAK,iBAAiB,OAAO,YAAY;KAChD,SAAS;KACT,SAAS;KACV,CAAC;KACF;GAEF,MAAM,iBAAiB,YAAY,eAAe,GAAG;AAErD,gBAAa;AACX,kBAAc,eAAe;AAC7B,WAAO,SAAQ,UAAS;AACtB,YAAO,oBAAoB,OAAO,YAAY,EAAE,SAAS,MAAM,CAAC;AAChE,cAAS,oBAAoB,OAAO,YAAY,EAAE,SAAS,MAAM,CAAC;AAClE,cAAS,KAAK,oBAAoB,OAAO,YAAY,EACnD,SAAS,MACV,CAAC;MACF;AACF,kBAAc;;;AAIlB,eAAa;AACX,iBAAc;;IAEf,CAAC,QAAQ,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/use-debounce/index.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\ninterface Options {\n delay?: number;\n autoInvoke?: boolean;\n}\n\nconst useDebounce = <T extends (...args: unknown[]) => unknown>(\n callback: T,\n { delay = 100, autoInvoke = true }: Options,\n deps: React.DependencyList = [],\n): T => {\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const callbackRef = useRef(callback);\n const prevDeps = useRef<React.DependencyList | undefined>(undefined);\n\n useEffect(() => {\n callbackRef.current = callback;\n });\n\n const stableDebouncedCallback = useRef<T | null>(null);\n\n if (!stableDebouncedCallback.current) {\n stableDebouncedCallback.current = ((...args: Parameters<T>) => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n timeoutRef.current = setTimeout(() => {\n callbackRef.current(...args);\n }, delay);\n }) as T;\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 if (isFirstRender) {\n callbackRef.current();\n } else if (stableDebouncedCallback.current) {\n stableDebouncedCallback.current();\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.current;\n};\n\nexport default useDebounce;\n"],"mappings":";;;AAOA,MAAM,eACJ,UACA,EAAE,QAAQ,KAAK,aAAa,QAC5B,OAA6B,EAAE,KACzB;CACN,MAAM,aAAa,OAA6C,KAAK;CACrE,MAAM,cAAc,OAAO,SAAS;CACpC,MAAM,WAAW,OAAyC,OAAU;AAEpE,iBAAgB;AACd,cAAY,UAAU;GACtB;CAEF,MAAM,0BAA0B,OAAiB,KAAK;AAEtD,KAAI,CAAC,wBAAwB,QAC3B,yBAAwB,YAAY,GAAG,SAAwB;AAC7D,MAAI,WAAW,QACb,cAAa,WAAW,QAAQ;AAGlC,aAAW,UAAU,iBAAiB;AACpC,eAAY,QAAQ,GAAG,KAAK;KAC3B,MAAM;;AAIb,iBAAgB;EACd,MAAM,cACJ,SAAS,YAAY,UACrB,SAAS,QAAQ,WAAW,KAAK,UACjC,SAAS,QAAQ,MAAM,KAAK,MAAM,QAAQ,KAAK,GAAG;AAEpD,MAAI,eAAe,WAAW,QAC5B,cAAa,WAAW,QAAQ;AAGlC,MAAI,cAAc,aAEhB;OADsB,SAAS,YAAY,OAEzC,aAAY,SAAS;YACZ,wBAAwB,QACjC,yBAAwB,SAAS;;AAIrC,WAAS,UAAU;GACnB;AAEF,iBAAgB;AACd,eAAa;AACX,OAAI,WAAW,QACb,cAAa,WAAW,QAAQ;;IAGnC,EAAE,CAAC;AAEN,QAAO,wBAAwB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/use-element-position/index.ts"],"sourcesContent":["import { type RefObject, useEffect, useState } from 'react';\n\ntype ElementReference<T> = string | RefObject<T>;\n\nconst useElementPosition = <T>(elementRef: ElementReference<T>) => {\n const [rect, setRect] = useState<DOMRect | null>(null);\n\n useEffect(() => {\n const getElement = (ref: ElementReference<T>): T | null => {\n if (typeof ref === 'string') {\n return document.querySelector(ref) as T;\n }\n return ref.current;\n };\n\n const updateRect = () => {\n const element = getElement(elementRef) as HTMLElement | null;\n if (element) {\n setRect(element.getBoundingClientRect());\n } else {\n setRect(null);\n }\n };\n\n const onUpdate = () => {\n requestAnimationFrame(updateRect);\n };\n\n updateRect();\n\n window.addEventListener('scroll', onUpdate, { passive: true });\n window.addEventListener('resize', onUpdate, { passive: true });\n\n return () => {\n window.removeEventListener('scroll', onUpdate);\n window.removeEventListener('resize', onUpdate);\n };\n }, [elementRef]);\n\n return rect;\n};\n\nexport default useElementPosition;\n"],"mappings":";;;AAIA,MAAM,sBAAyB,eAAoC;CACjE,MAAM,CAAC,MAAM,WAAW,SAAyB,KAAK;AAEtD,iBAAgB;EACd,MAAM,cAAc,QAAuC;AACzD,OAAI,OAAO,QAAQ,SACjB,QAAO,SAAS,cAAc,IAAI;AAEpC,UAAO,IAAI;;EAGb,MAAM,mBAAmB;GACvB,MAAM,UAAU,WAAW,WAAW;AACtC,OAAI,QACF,SAAQ,QAAQ,uBAAuB,CAAC;OAExC,SAAQ,KAAK;;EAIjB,MAAM,iBAAiB;AACrB,yBAAsB,WAAW;;AAGnC,cAAY;AAEZ,SAAO,iBAAiB,UAAU,UAAU,EAAE,SAAS,MAAM,CAAC;AAC9D,SAAO,iBAAiB,UAAU,UAAU,EAAE,SAAS,MAAM,CAAC;AAE9D,eAAa;AACX,UAAO,oBAAoB,UAAU,SAAS;AAC9C,UAAO,oBAAoB,UAAU,SAAS;;IAE/C,CAAC,WAAW,CAAC;AAEhB,QAAO"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/use-element-scroll/index.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\n\ninterface ScrollPosition {\n scrollY: number;\n scrollPercentage: number;\n isAtTop: boolean;\n isAtBottom: boolean;\n scrollableHeight: number;\n clientHeight: number;\n scrollHeight: number;\n}\n\nconst useElementScroll = () => {\n const [element, setElement] = useState<HTMLElement | null>(null);\n const [scrollPosition, setScrollPosition] = useState<ScrollPosition>({\n scrollY: 0,\n scrollPercentage: 0,\n isAtTop: true,\n isAtBottom: false,\n scrollableHeight: 0,\n clientHeight: 0,\n scrollHeight: 0,\n });\n\n const setRef = useCallback((el: HTMLElement | null) => {\n setElement(el);\n }, []);\n\n useEffect(() => {\n if (!element) {\n return;\n }\n\n const updateScrollPosition = () => {\n const { scrollTop, scrollHeight, clientHeight } = element;\n const scrollableHeight = scrollHeight - clientHeight;\n\n if (scrollableHeight <= 0) {\n setScrollPosition({\n scrollY: 0,\n scrollPercentage: 0,\n isAtTop: true,\n isAtBottom: true,\n scrollableHeight: 0,\n clientHeight,\n scrollHeight,\n });\n return;\n }\n\n const percentage = Math.min(\n 100,\n Math.max(0, (scrollTop / scrollableHeight) * 100),\n );\n\n setScrollPosition({\n scrollY: scrollTop,\n scrollPercentage: percentage,\n isAtTop: scrollTop <= 0,\n isAtBottom: scrollTop >= scrollableHeight - 1,\n scrollableHeight,\n clientHeight,\n scrollHeight,\n });\n };\n\n updateScrollPosition();\n\n const onScroll = () => {\n updateScrollPosition();\n };\n\n element.addEventListener('scroll', onScroll, { passive: true });\n\n const resizeObserver = new ResizeObserver(() => {\n updateScrollPosition();\n });\n\n resizeObserver.observe(element);\n\n return () => {\n element.removeEventListener('scroll', onScroll);\n resizeObserver.unobserve(element);\n };\n }, [element]);\n\n return { ...scrollPosition, element, setRef };\n};\n\nexport default useElementScroll;\n"],"mappings":";;;AAYA,MAAM,yBAAyB;CAC7B,MAAM,CAAC,SAAS,cAAc,SAA6B,KAAK;CAChE,MAAM,CAAC,gBAAgB,qBAAqB,SAAyB;EACnE,SAAS;EACT,kBAAkB;EAClB,SAAS;EACT,YAAY;EACZ,kBAAkB;EAClB,cAAc;EACd,cAAc;EACf,CAAC;CAEF,MAAM,SAAS,aAAa,OAA2B;AACrD,aAAW,GAAG;IACb,EAAE,CAAC;AAEN,iBAAgB;AACd,MAAI,CAAC,QACH;EAGF,MAAM,6BAA6B;GACjC,MAAM,EAAE,WAAW,cAAc,iBAAiB;GAClD,MAAM,mBAAmB,eAAe;AAExC,OAAI,oBAAoB,GAAG;AACzB,sBAAkB;KAChB,SAAS;KACT,kBAAkB;KAClB,SAAS;KACT,YAAY;KACZ,kBAAkB;KAClB;KACA;KACD,CAAC;AACF;;AAQF,qBAAkB;IAChB,SAAS;IACT,kBAPiB,KAAK,IACtB,KACA,KAAK,IAAI,GAAI,YAAY,mBAAoB,IAAI,CAClD;IAKC,SAAS,aAAa;IACtB,YAAY,aAAa,mBAAmB;IAC5C;IACA;IACA;IACD,CAAC;;AAGJ,wBAAsB;EAEtB,MAAM,iBAAiB;AACrB,yBAAsB;;AAGxB,UAAQ,iBAAiB,UAAU,UAAU,EAAE,SAAS,MAAM,CAAC;EAE/D,MAAM,iBAAiB,IAAI,qBAAqB;AAC9C,yBAAsB;IACtB;AAEF,iBAAe,QAAQ,QAAQ;AAE/B,eAAa;AACX,WAAQ,oBAAoB,UAAU,SAAS;AAC/C,kBAAe,UAAU,QAAQ;;IAElC,CAAC,QAAQ,CAAC;AAEb,QAAO;EAAE,GAAG;EAAgB;EAAS;EAAQ"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/use-image/index.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\n\ninterface Options {\n retryCount?: number;\n retryDelay?: number;\n}\n\nconst useImage = (src: string, options: Options = {}) => {\n const { retryCount = 0, retryDelay = 1000 } = options;\n\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | Event | null>(null);\n const [loaded, setLoaded] = useState(false);\n const [attemptCount, setAttemptCount] = useState(0);\n const [reloadToken, setReloadToken] = useState(0);\n\n useEffect(() => {\n if (!src) {\n setLoading(false);\n setLoaded(false);\n return;\n }\n\n let cancelled = false;\n let retryTimeoutId: ReturnType<typeof setTimeout> | undefined;\n\n setLoading(true);\n setError(null);\n\n const img = new Image();\n img.src = src;\n\n img.onload = () => {\n if (cancelled) {\n return;\n }\n setLoading(false);\n setLoaded(true);\n setError(null);\n };\n\n img.onerror = err => {\n if (cancelled) {\n return;\n }\n setLoading(false);\n setLoaded(false);\n setError(err);\n\n if (attemptCount < retryCount) {\n retryTimeoutId = setTimeout(() => {\n if (!cancelled) {\n setAttemptCount(prev => prev + 1);\n }\n }, retryDelay);\n }\n };\n\n return () => {\n cancelled = true;\n img.onload = null;\n img.onerror = null;\n if (retryTimeoutId) {\n clearTimeout(retryTimeoutId);\n }\n };\n }, [src, attemptCount, retryCount, retryDelay, reloadToken]);\n\n const retry = useCallback(() => {\n setAttemptCount(0);\n setReloadToken(token => token + 1);\n }, []);\n\n return {\n loading,\n error,\n loaded,\n retry,\n };\n};\n\nexport default useImage;\n"],"mappings":";;;AAOA,MAAM,YAAY,KAAa,UAAmB,EAAE,KAAK;CACvD,MAAM,EAAE,aAAa,GAAG,aAAa,QAAS;CAE9C,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,CAAC,OAAO,YAAY,SAAgC,KAAK;CAC/D,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,CAAC,cAAc,mBAAmB,SAAS,EAAE;CACnD,MAAM,CAAC,aAAa,kBAAkB,SAAS,EAAE;AAEjD,iBAAgB;AACd,MAAI,CAAC,KAAK;AACR,cAAW,MAAM;AACjB,aAAU,MAAM;AAChB;;EAGF,IAAI,YAAY;EAChB,IAAI;AAEJ,aAAW,KAAK;AAChB,WAAS,KAAK;EAEd,MAAM,MAAM,IAAI,OAAO;AACvB,MAAI,MAAM;AAEV,MAAI,eAAe;AACjB,OAAI,UACF;AAEF,cAAW,MAAM;AACjB,aAAU,KAAK;AACf,YAAS,KAAK;;AAGhB,MAAI,WAAU,QAAO;AACnB,OAAI,UACF;AAEF,cAAW,MAAM;AACjB,aAAU,MAAM;AAChB,YAAS,IAAI;AAEb,OAAI,eAAe,WACjB,kBAAiB,iBAAiB;AAChC,QAAI,CAAC,UACH,kBAAgB,SAAQ,OAAO,EAAE;MAElC,WAAW;;AAIlB,eAAa;AACX,eAAY;AACZ,OAAI,SAAS;AACb,OAAI,UAAU;AACd,OAAI,eACF,cAAa,eAAe;;IAG/B;EAAC;EAAK;EAAc;EAAY;EAAY;EAAY,CAAC;AAO5D,QAAO;EACL;EACA;EACA;EACA,OATY,kBAAkB;AAC9B,mBAAgB,EAAE;AAClB,mBAAe,UAAS,QAAQ,EAAE;KACjC,EAAE,CAAC;EAOL"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/use-local-storage/index.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nconst useLocalStorage = <T>(key: string, initialValue: T) => {\n const initialValueRef = useRef(initialValue);\n\n useEffect(() => {\n initialValueRef.current = initialValue;\n });\n\n const [storedValue, setStoredValue] = useState<T>(() => {\n if (typeof window === 'undefined') {\n return initialValue;\n }\n try {\n const item = window.localStorage.getItem(key);\n return item ? (JSON.parse(item) as T) : initialValue;\n } catch {\n return initialValue;\n }\n });\n\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n try {\n const item = window.localStorage.getItem(key);\n if (item) {\n setStoredValue(JSON.parse(item) as T);\n } else {\n window.localStorage.setItem(\n key,\n JSON.stringify(initialValueRef.current),\n );\n }\n } catch (e) {\n console.error(`Error reading localStorage key \"${key}\":`, e);\n }\n }, [key]);\n\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n const onStorage = (event: StorageEvent) => {\n if (event.key !== key) {\n return;\n }\n\n try {\n setStoredValue(\n event.newValue\n ? (JSON.parse(event.newValue) as T)\n : initialValueRef.current,\n );\n } catch (e) {\n console.error(\n `Error parsing localStorage key \"${key}\" from storage event:`,\n e,\n );\n }\n };\n\n window.addEventListener('storage', onStorage);\n return () => window.removeEventListener('storage', onStorage);\n }, [key]);\n\n const setValue = useCallback(\n (value: T | ((val: T) => T)) => {\n try {\n setStoredValue(prev => {\n const valueToStore = value instanceof Function ? value(prev) : value;\n localStorage.setItem(key, JSON.stringify(valueToStore));\n return valueToStore;\n });\n } catch (e) {\n console.error(`Error setting localStorage key \"${key}\":`, e);\n }\n },\n [key],\n );\n\n return [storedValue, setValue] as const;\n};\n\nexport default useLocalStorage;\n"],"mappings":";;;AAEA,MAAM,mBAAsB,KAAa,iBAAoB;CAC3D,MAAM,kBAAkB,OAAO,aAAa;AAE5C,iBAAgB;AACd,kBAAgB,UAAU;GAC1B;CAEF,MAAM,CAAC,aAAa,kBAAkB,eAAkB;AACtD,MAAI,OAAO,WAAW,YACpB,QAAO;AAET,MAAI;GACF,MAAM,OAAO,OAAO,aAAa,QAAQ,IAAI;AAC7C,UAAO,OAAQ,KAAK,MAAM,KAAK,GAAS;UAClC;AACN,UAAO;;GAET;AAEF,iBAAgB;AACd,MAAI,OAAO,WAAW,YACpB;AAGF,MAAI;GACF,MAAM,OAAO,OAAO,aAAa,QAAQ,IAAI;AAC7C,OAAI,KACF,gBAAe,KAAK,MAAM,KAAK,CAAM;OAErC,QAAO,aAAa,QAClB,KACA,KAAK,UAAU,gBAAgB,QAAQ,CACxC;WAEI,GAAG;AACV,WAAQ,MAAM,mCAAmC,IAAI,KAAK,EAAE;;IAE7D,CAAC,IAAI,CAAC;AAET,iBAAgB;AACd,MAAI,OAAO,WAAW,YACpB;EAGF,MAAM,aAAa,UAAwB;AACzC,OAAI,MAAM,QAAQ,IAChB;AAGF,OAAI;AACF,mBACE,MAAM,WACD,KAAK,MAAM,MAAM,SAAS,GAC3B,gBAAgB,QACrB;YACM,GAAG;AACV,YAAQ,MACN,mCAAmC,IAAI,wBACvC,EACD;;;AAIL,SAAO,iBAAiB,WAAW,UAAU;AAC7C,eAAa,OAAO,oBAAoB,WAAW,UAAU;IAC5D,CAAC,IAAI,CAAC;AAiBT,QAAO,CAAC,aAfS,aACd,UAA+B;AAC9B,MAAI;AACF,mBAAe,SAAQ;IACrB,MAAM,eAAe,iBAAiB,WAAW,MAAM,KAAK,GAAG;AAC/D,iBAAa,QAAQ,KAAK,KAAK,UAAU,aAAa,CAAC;AACvD,WAAO;KACP;WACK,GAAG;AACV,WAAQ,MAAM,mCAAmC,IAAI,KAAK,EAAE;;IAGhE,CAAC,IAAI,CACN,CAE6B"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/use-recursive-timeout/index.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\nconst useRecursiveTimeout = <T>(\n callback: () => Promise<T> | (() => void),\n delay: number | null,\n) => {\n const savedCallback = useRef(callback);\n\n useEffect(() => {\n savedCallback.current = callback;\n }, [callback]);\n\n useEffect(() => {\n let id: ReturnType<typeof setTimeout>;\n\n function tick() {\n const ret = savedCallback.current();\n\n if (ret instanceof Promise) {\n ret.then(() => {\n if (delay) {\n id = setTimeout(tick, delay);\n }\n });\n } else {\n if (delay) {\n id = setTimeout(tick, delay);\n }\n }\n }\n\n if (delay) {\n id = setTimeout(tick, delay);\n return () => {\n clearTimeout(id);\n };\n }\n }, [delay]);\n};\n\nexport default useRecursiveTimeout;\n"],"mappings":";;;AAEA,MAAM,uBACJ,UACA,UACG;CACH,MAAM,gBAAgB,OAAO,SAAS;AAEtC,iBAAgB;AACd,gBAAc,UAAU;IACvB,CAAC,SAAS,CAAC;AAEd,iBAAgB;EACd,IAAI;EAEJ,SAAS,OAAO;GACd,MAAM,MAAM,cAAc,SAAS;AAEnC,OAAI,eAAe,QACjB,KAAI,WAAW;AACb,QAAI,MACF,MAAK,WAAW,MAAM,MAAM;KAE9B;YAEE,MACF,MAAK,WAAW,MAAM,MAAM;;AAKlC,MAAI,OAAO;AACT,QAAK,WAAW,MAAM,MAAM;AAC5B,gBAAa;AACX,iBAAa,GAAG;;;IAGnB,CAAC,MAAM,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/use-responsive-size/index.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nimport useDebounce from '../use-debounce';\n\ntype Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\ninterface BreakpointInfo {\n current: Breakpoint;\n xs: boolean;\n sm: boolean;\n md: boolean;\n lg: boolean;\n xl: boolean;\n '2xl': boolean;\n}\n\ninterface Options {\n delay?: number;\n container?: HTMLElement | null;\n}\n\nconst BREAKPOINTS = {\n xs: 0, // < 640px\n sm: 640, // >= 640px\n md: 768, // >= 768px\n lg: 1024, // >= 1024px\n xl: 1280, // >= 1280px\n '2xl': 1536, // >= 1536px\n} as const;\n\nconst getBreakpointInfo = (width: number): BreakpointInfo => {\n let current: Breakpoint = 'xs';\n\n if (width >= BREAKPOINTS['2xl']) {\n current = '2xl';\n } else if (width >= BREAKPOINTS.xl) {\n current = 'xl';\n } else if (width >= BREAKPOINTS.lg) {\n current = 'lg';\n } else if (width >= BREAKPOINTS.md) {\n current = 'md';\n } else if (width >= BREAKPOINTS.sm) {\n current = 'sm';\n } else {\n current = 'xs';\n }\n\n return {\n current,\n xs: width < BREAKPOINTS.sm,\n sm: width >= BREAKPOINTS.sm && width < BREAKPOINTS.md,\n md: width >= BREAKPOINTS.md && width < BREAKPOINTS.lg,\n lg: width >= BREAKPOINTS.lg && width < BREAKPOINTS.xl,\n xl: width >= BREAKPOINTS.xl && width < BREAKPOINTS['2xl'],\n '2xl': width >= BREAKPOINTS['2xl'],\n };\n};\n\nconst useResponsiveSize = <T extends HTMLElement>(options?: Options) => {\n const { delay = 100, container } = options || {};\n const [size, setSize] = useState({ width: 0, height: 0 });\n const [breakpoint, setBreakpoint] = useState<BreakpointInfo>({\n current: 'xs',\n xs: true,\n sm: false,\n md: false,\n lg: false,\n xl: false,\n '2xl': false,\n });\n\n const [debouncedSize, setDebouncedSize] = useState({ width: 0, height: 0 });\n\n const [element, setElement] = useState<T | null>(null);\n const observerRef = useRef<ResizeObserver | null>(null);\n\n const ref = useCallback((node: T | null) => {\n setElement(node);\n }, []);\n\n useDebounce(\n () => {\n setDebouncedSize(size);\n },\n { delay },\n [size],\n );\n\n useEffect(() => {\n const updateSize = () => {\n const target = container ?? element ?? document.body;\n\n if (!target) {\n return;\n }\n\n const { offsetWidth, offsetHeight } = target;\n\n setSize(prev => {\n if (prev.width !== offsetWidth || prev.height !== offsetHeight) {\n return { width: offsetWidth, height: offsetHeight };\n }\n return prev;\n });\n\n setBreakpoint(prev => {\n const next = getBreakpointInfo(offsetWidth);\n if (prev.current !== next.current) {\n return next;\n }\n return prev;\n });\n };\n\n const connect = () => {\n const target = container ?? element ?? document.body;\n\n if (!target) {\n return;\n }\n\n updateSize();\n\n if (observerRef.current) {\n observerRef.current.disconnect();\n }\n\n observerRef.current = new ResizeObserver(() => {\n requestAnimationFrame(() => {\n updateSize();\n });\n });\n\n observerRef.current.observe(target);\n };\n\n const disconnect = () => {\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n };\n\n connect();\n\n return () => {\n disconnect();\n };\n }, [container, element]);\n\n return {\n size: debouncedSize,\n breakpoint,\n ref,\n };\n};\n\nexport default useResponsiveSize;\n"],"mappings":";;;;AAqBA,MAAM,cAAc;CAClB,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,OAAO;CACR;AAED,MAAM,qBAAqB,UAAkC;CAC3D,IAAI,UAAsB;AAE1B,KAAI,SAAS,YAAY,OACvB,WAAU;UACD,SAAS,YAAY,GAC9B,WAAU;UACD,SAAS,YAAY,GAC9B,WAAU;UACD,SAAS,YAAY,GAC9B,WAAU;UACD,SAAS,YAAY,GAC9B,WAAU;KAEV,WAAU;AAGZ,QAAO;EACL;EACA,IAAI,QAAQ,YAAY;EACxB,IAAI,SAAS,YAAY,MAAM,QAAQ,YAAY;EACnD,IAAI,SAAS,YAAY,MAAM,QAAQ,YAAY;EACnD,IAAI,SAAS,YAAY,MAAM,QAAQ,YAAY;EACnD,IAAI,SAAS,YAAY,MAAM,QAAQ,YAAY;EACnD,OAAO,SAAS,YAAY;EAC7B;;AAGH,MAAM,qBAA4C,YAAsB;CACtE,MAAM,EAAE,QAAQ,KAAK,cAAc,WAAW,EAAE;CAChD,MAAM,CAAC,MAAM,WAAW,SAAS;EAAE,OAAO;EAAG,QAAQ;EAAG,CAAC;CACzD,MAAM,CAAC,YAAY,iBAAiB,SAAyB;EAC3D,SAAS;EACT,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,OAAO;EACR,CAAC;CAEF,MAAM,CAAC,eAAe,oBAAoB,SAAS;EAAE,OAAO;EAAG,QAAQ;EAAG,CAAC;CAE3E,MAAM,CAAC,SAAS,cAAc,SAAmB,KAAK;CACtD,MAAM,cAAc,OAA8B,KAAK;CAEvD,MAAM,MAAM,aAAa,SAAmB;AAC1C,aAAW,KAAK;IACf,EAAE,CAAC;AAEN,mBACQ;AACJ,mBAAiB,KAAK;IAExB,EAAE,OAAO,EACT,CAAC,KAAK,CACP;AAED,iBAAgB;EACd,MAAM,mBAAmB;GACvB,MAAM,SAAS,aAAa,WAAW,SAAS;AAEhD,OAAI,CAAC,OACH;GAGF,MAAM,EAAE,aAAa,iBAAiB;AAEtC,YAAQ,SAAQ;AACd,QAAI,KAAK,UAAU,eAAe,KAAK,WAAW,aAChD,QAAO;KAAE,OAAO;KAAa,QAAQ;KAAc;AAErD,WAAO;KACP;AAEF,kBAAc,SAAQ;IACpB,MAAM,OAAO,kBAAkB,YAAY;AAC3C,QAAI,KAAK,YAAY,KAAK,QACxB,QAAO;AAET,WAAO;KACP;;EAGJ,MAAM,gBAAgB;GACpB,MAAM,SAAS,aAAa,WAAW,SAAS;AAEhD,OAAI,CAAC,OACH;AAGF,eAAY;AAEZ,OAAI,YAAY,QACd,aAAY,QAAQ,YAAY;AAGlC,eAAY,UAAU,IAAI,qBAAqB;AAC7C,gCAA4B;AAC1B,iBAAY;MACZ;KACF;AAEF,eAAY,QAAQ,QAAQ,OAAO;;EAGrC,MAAM,mBAAmB;AACvB,OAAI,YAAY,SAAS;AACvB,gBAAY,QAAQ,YAAY;AAChC,gBAAY,UAAU;;;AAI1B,WAAS;AAET,eAAa;AACX,eAAY;;IAEb,CAAC,WAAW,QAAQ,CAAC;AAExB,QAAO;EACL,MAAM;EACN;EACA;EACD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/use-scroll-to-elements/index.ts"],"sourcesContent":["import { useCallback, useRef } from 'react';\n\ninterface Options extends ScrollIntoViewOptions {\n offset?: number;\n}\nconst useScrollToElements = (options?: Options) => {\n const elementRefs = useRef<(HTMLElement | null)[]>([]);\n\n const scrollToElement = useCallback(\n (index: number) => {\n const element = elementRefs.current[index];\n\n if (!element) {\n return;\n }\n\n element.scrollIntoView({\n behavior: 'smooth',\n block: 'start',\n inline: 'start',\n ...options,\n });\n\n if (options?.offset) {\n const top =\n element.getBoundingClientRect().top + window.scrollY - options.offset;\n\n window.scrollTo({\n top,\n behavior: options.behavior || 'smooth',\n });\n }\n },\n [options],\n );\n\n const setElementRef = useCallback(\n (element: HTMLElement | null, index: number) => {\n elementRefs.current[index] = element;\n },\n [],\n );\n\n return { elementRefs, setElementRef, scrollToElement };\n};\n\nexport default useScrollToElements;\n"],"mappings":";;;AAKA,MAAM,uBAAuB,YAAsB;CACjD,MAAM,cAAc,OAA+B,EAAE,CAAC;CAEtD,MAAM,kBAAkB,aACrB,UAAkB;EACjB,MAAM,UAAU,YAAY,QAAQ;AAEpC,MAAI,CAAC,QACH;AAGF,UAAQ,eAAe;GACrB,UAAU;GACV,OAAO;GACP,QAAQ;GACR,GAAG;GACJ,CAAC;AAEF,MAAI,SAAS,QAAQ;GACnB,MAAM,MACJ,QAAQ,uBAAuB,CAAC,MAAM,OAAO,UAAU,QAAQ;AAEjE,UAAO,SAAS;IACd;IACA,UAAU,QAAQ,YAAY;IAC/B,CAAC;;IAGN,CAAC,QAAQ,CACV;AASD,QAAO;EAAE;EAAa,eAPA,aACnB,SAA6B,UAAkB;AAC9C,eAAY,QAAQ,SAAS;KAE/B,EAAE,CACH;EAEoC;EAAiB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/use-throttle/index.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\n\nconst useThrottle = <T>(value: T, delay = 100): T => {\n const [throttledValue, setThrottledValue] = useState(value);\n const lastExecutedRef = useRef(0);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const latestValueRef = useRef(value);\n\n useEffect(() => {\n latestValueRef.current = value;\n }, [value]);\n\n useEffect(() => {\n const now = Date.now();\n const elapsed = now - lastExecutedRef.current;\n\n if (elapsed >= delay) {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n\n lastExecutedRef.current = now;\n setThrottledValue(latestValueRef.current);\n return;\n }\n\n const remaining = delay - elapsed;\n\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n timeoutRef.current = setTimeout(() => {\n lastExecutedRef.current = Date.now();\n setThrottledValue(latestValueRef.current);\n }, remaining);\n\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n };\n }, [value, delay]);\n\n return throttledValue;\n};\n\nexport default useThrottle;\n"],"mappings":";;;AAEA,MAAM,eAAkB,OAAU,QAAQ,QAAW;CACnD,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,MAAM;CAC3D,MAAM,kBAAkB,OAAO,EAAE;CACjC,MAAM,aAAa,OAA6C,KAAK;CACrE,MAAM,iBAAiB,OAAO,MAAM;AAEpC,iBAAgB;AACd,iBAAe,UAAU;IACxB,CAAC,MAAM,CAAC;AAEX,iBAAgB;EACd,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,UAAU,MAAM,gBAAgB;AAEtC,MAAI,WAAW,OAAO;AACpB,OAAI,WAAW,SAAS;AACtB,iBAAa,WAAW,QAAQ;AAChC,eAAW,UAAU;;AAGvB,mBAAgB,UAAU;AAC1B,qBAAkB,eAAe,QAAQ;AACzC;;EAGF,MAAM,YAAY,QAAQ;AAE1B,MAAI,WAAW,QACb,cAAa,WAAW,QAAQ;AAGlC,aAAW,UAAU,iBAAiB;AACpC,mBAAgB,UAAU,KAAK,KAAK;AACpC,qBAAkB,eAAe,QAAQ;KACxC,UAAU;AAEb,eAAa;AACX,OAAI,WAAW,SAAS;AACtB,iBAAa,WAAW,QAAQ;AAChC,eAAW,UAAU;;;IAGxB,CAAC,OAAO,MAAM,CAAC;AAElB,QAAO"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/use-timeline/index.ts"],"sourcesContent":["import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';\n\nconst registeredProperties = new Set<string>();\n\nconst registerCSSProperty = (\n name: string,\n syntax = '<length>',\n initialValue = '0px',\n) => {\n if (\n registeredProperties.has(name) ||\n typeof CSS === 'undefined' ||\n !CSS.registerProperty\n ) {\n return;\n }\n\n try {\n CSS.registerProperty({\n name,\n syntax,\n inherits: false,\n initialValue,\n });\n registeredProperties.add(name);\n } catch {\n // 이미 등록된 경우 무시\n }\n};\n\nexport interface TimelineStep {\n scale?: number;\n rotate?: { x?: number; y?: number; z?: number };\n position?: { x?: number; y?: number };\n top?: number;\n left?: number;\n width?: number;\n height?: number;\n zIndex?: number;\n filter?: string;\n backgroundColor?: string;\n opacity?: number;\n selector: string;\n transition: Transition;\n}\n\ninterface Transition {\n duration: number;\n ease?: string;\n delay?: number;\n easeX?: string;\n easeY?: string;\n}\n\ninterface Options {\n steps?: TimelineStep[];\n loading?: boolean;\n immediate?: boolean;\n loop?: boolean;\n}\n\nconst buildTimeline = (steps: TimelineStep[]): TimelineStep[][] => {\n const groups: TimelineStep[][] = [];\n\n for (const step of steps) {\n if (step.transition.delay === 0 && groups.length > 0) {\n groups[groups.length - 1]?.push(step);\n } else {\n groups.push([step]);\n }\n }\n\n return groups;\n};\n\nconst buildTransform = (step: TimelineStep) => {\n const parts: string[] = [];\n\n if (step.position?.x != null || step.position?.y != null) {\n parts.push(\n `translate(${step.position.x ?? 0}px, ${step.position.y ?? 0}px)`,\n );\n }\n\n if (step.scale != null) {\n parts.push(`scale(${step.scale})`);\n }\n\n if (step.rotate?.x != null) {\n parts.push(`rotateX(${step.rotate.x}deg)`);\n }\n\n if (step.rotate?.y != null) {\n parts.push(`rotateY(${step.rotate.y}deg)`);\n }\n\n if (step.rotate?.z != null) {\n parts.push(`rotateZ(${step.rotate.z}deg)`);\n }\n\n return parts.join(' ') || undefined;\n};\n\nconst applyStep = (\n container: HTMLElement,\n step: TimelineStep,\n skipTransition?: boolean,\n) => {\n const elements = container.matches(step.selector)\n ? [container]\n : Array.from(container.querySelectorAll<HTMLElement>(step.selector));\n\n if (!elements.length) {\n return;\n }\n\n const dur = `${step.transition.duration}ms`;\n const eas = step.transition.ease ?? 'ease-out';\n const easX = step.transition.easeX;\n const easY = step.transition.easeY;\n const hasCurve = !!(easX || easY);\n const timers: ReturnType<typeof setTimeout>[] = [];\n\n elements.forEach(el => {\n if (skipTransition) {\n el.style.transition = 'none';\n } else if (hasCurve) {\n const xEase = easX ?? eas;\n const yEase = easY ?? eas;\n el.style.transition = [\n `--tx ${dur} ${xEase}`,\n `--ty ${dur} ${yEase}`,\n `--s ${dur} ${eas}`,\n `rotate ${dur} ${eas}`,\n `width ${dur} ${eas}`,\n `height ${dur} ${eas}`,\n `filter ${dur} ${eas}`,\n `opacity ${dur} ${eas}`,\n ].join(', ');\n } else {\n el.style.transition = `all ${dur} ${eas}`;\n }\n\n if (step.backgroundColor) {\n el.style.backgroundColor = step.backgroundColor;\n }\n\n if (step.opacity !== undefined) {\n el.style.opacity = String(step.opacity);\n }\n\n if (step.top !== undefined) {\n el.style.top = `${step.top}px`;\n }\n\n if (step.left !== undefined) {\n el.style.left = `${step.left}px`;\n }\n\n if (step.width !== undefined) {\n el.style.width = `${step.width}px`;\n }\n\n if (step.height !== undefined) {\n el.style.height = `${step.height}px`;\n }\n\n if (step.zIndex !== undefined) {\n if (skipTransition) {\n el.style.zIndex = String(step.zIndex);\n } else {\n const timerId = setTimeout(() => {\n el.style.zIndex = String(step.zIndex);\n }, step.transition.duration / 2);\n timers.push(timerId);\n }\n }\n\n if (step.filter !== undefined) {\n el.style.filter = step.filter;\n }\n\n if (step.rotate?.z !== undefined) {\n el.style.rotate = `${step.rotate.z}deg`;\n }\n\n if (hasCurve) {\n if (step.position?.x != null || step.position?.y != null) {\n el.style.setProperty('--tx', `${step.position.x ?? 0}px`);\n el.style.setProperty('--ty', `${step.position.y ?? 0}px`);\n }\n if (step.scale != null) {\n el.style.setProperty('--s', String(step.scale));\n }\n el.style.transform = `translate(var(--tx), var(--ty)) scale(var(--s, 1))`;\n } else {\n const transform = buildTransform(step);\n\n if (transform) {\n el.style.transform = transform;\n }\n }\n });\n\n return timers;\n};\n\nconst useTimeline = ({ steps = [], loading, immediate, loop }: Options) => {\n const [slotIndex, setSlotIndex] = useState(-1);\n const [completed, setCompleted] = useState(() => !!immediate);\n\n const ref = useRef<HTMLDivElement>(null);\n const timeline = useMemo(() => buildTimeline(steps), [steps]);\n const hasCurveSteps = useMemo(\n () => steps.some(s => s.transition.easeX || s.transition.easeY),\n [steps],\n );\n\n useLayoutEffect(() => {\n if (hasCurveSteps) {\n registerCSSProperty('--tx');\n registerCSSProperty('--ty');\n registerCSSProperty('--s', '<number>', '1');\n }\n }, [hasCurveSteps]);\n\n useLayoutEffect(() => {\n if (!immediate || !ref.current || !steps.length) {\n return;\n }\n\n const container = ref.current;\n steps.forEach(step => applyStep(container, step, true));\n }, [immediate, steps]);\n\n useEffect(() => {\n if (immediate || loading || !timeline.length) {\n return;\n }\n\n const firstDelay = timeline[0]?.[0]?.transition.delay ?? 0;\n const timerId = window.setTimeout(() => {\n requestAnimationFrame(() => setSlotIndex(0));\n }, firstDelay);\n\n return () => clearTimeout(timerId);\n }, [loading, timeline, immediate]);\n\n useEffect(() => {\n if (immediate || loading || slotIndex < 0) {\n return;\n }\n\n const isLast = slotIndex >= timeline.length - 1;\n\n if (isLast && !loop) {\n return;\n }\n\n const currentSlot = timeline[slotIndex];\n const maxDuration = Math.max(\n ...(currentSlot?.map(s => s.transition.duration) ?? [0]),\n );\n\n const nextIndex = isLast ? 0 : slotIndex + 1;\n const nextDelay = timeline[nextIndex]?.[0]?.transition.delay ?? 0;\n\n const timerId = window.setTimeout(() => {\n setSlotIndex(nextIndex);\n }, maxDuration + nextDelay);\n\n return () => clearTimeout(timerId);\n }, [loading, slotIndex, timeline, immediate, loop]);\n\n useEffect(() => {\n if (immediate || slotIndex < 0 || !ref.current) {\n return;\n }\n\n const container = ref.current;\n const timers: ReturnType<typeof setTimeout>[] = [];\n\n const rafId = requestAnimationFrame(() => {\n timeline[slotIndex]?.forEach(step => {\n const stepTimers = applyStep(container, step);\n\n if (stepTimers) {\n timers.push(...stepTimers);\n }\n });\n });\n\n return () => {\n cancelAnimationFrame(rafId);\n timers.forEach(clearTimeout);\n };\n }, [immediate, slotIndex, timeline]);\n\n useEffect(() => {\n if (\n immediate ||\n loop ||\n slotIndex < 0 ||\n slotIndex < timeline.length - 1 ||\n !timeline.length\n ) {\n return;\n }\n\n const lastSlot = timeline[slotIndex];\n const maxDuration = Math.max(\n ...(lastSlot?.map(s => s.transition.duration) ?? [0]),\n );\n\n const timerId = window.setTimeout(() => {\n setCompleted(true);\n }, maxDuration);\n\n return () => clearTimeout(timerId);\n }, [slotIndex, timeline, immediate, loop]);\n\n return { ref, completed };\n};\n\nexport default useTimeline;\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/use-viewport/index.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\n\ntype ViewportInfo =\n | VisualViewport\n | {\n width: number;\n height: number;\n offsetLeft: number;\n offsetTop: number;\n pageLeft: number;\n pageTop: number;\n scale: number;\n };\n\ninterface Options {\n isInApp?: boolean;\n debounce?: number;\n}\n\nconst useViewport = (options: Options = {}) => {\n const { isInApp = false, debounce = 100 } = options;\n\n const [viewport, setViewport] = useState<ViewportInfo>({\n width: 0,\n height: 0,\n offsetLeft: 0,\n offsetTop: 0,\n pageLeft: 0,\n pageTop: 0,\n scale: 1,\n });\n\n const getAppViewHeight = useCallback(() => {\n const windowHeight = window.innerHeight;\n const visualHeight = window.visualViewport?.height || windowHeight;\n const documentHeight = document.documentElement.clientHeight;\n const bodyHeight = document.body.clientHeight;\n\n if (window.visualViewport && Math.abs(visualHeight - windowHeight) > 100) {\n return visualHeight;\n }\n\n return Math.max(windowHeight, documentHeight, bodyHeight);\n }, []);\n\n const readViewport = useCallback((): ViewportInfo => {\n if (window.visualViewport && !isInApp) {\n return window.visualViewport;\n }\n\n const width = window.visualViewport?.width || window.innerWidth;\n const height = isInApp\n ? getAppViewHeight()\n : window.visualViewport?.height || window.innerHeight;\n\n return {\n width,\n height,\n offsetLeft: window.visualViewport?.offsetLeft || 0,\n offsetTop: window.visualViewport?.offsetTop || 0,\n pageLeft: window.scrollX ?? window.pageXOffset ?? 0,\n pageTop: window.scrollY ?? window.pageYOffset ?? 0,\n scale: window.visualViewport?.scale || 1,\n };\n }, [isInApp, getAppViewHeight]);\n\n useEffect(() => {\n let timeoutId: ReturnType<typeof setTimeout>;\n\n const debouncedUpdate = () => {\n clearTimeout(timeoutId);\n timeoutId = setTimeout(() => {\n setViewport(readViewport());\n }, debounce);\n };\n\n const immediateUpdate = () => setViewport(readViewport());\n\n immediateUpdate();\n\n const events = ['resize', 'orientationchange'];\n\n if (isInApp) {\n events.push('focus', 'blur', 'touchstart', 'touchend');\n }\n\n events.forEach(event => {\n if (event === 'resize' || event === 'orientationchange') {\n window.addEventListener(event, debouncedUpdate);\n } else {\n window.addEventListener(event, immediateUpdate, { passive: true });\n }\n });\n\n if (window.visualViewport) {\n window.visualViewport.addEventListener('resize', immediateUpdate);\n window.visualViewport.addEventListener('scroll', immediateUpdate);\n }\n\n let intervalId: ReturnType<typeof setInterval>;\n if (isInApp) {\n let lastHeight = readViewport().height;\n intervalId = setInterval(() => {\n const currentHeight = readViewport().height;\n if (Math.abs(currentHeight - lastHeight) > 50) {\n lastHeight = currentHeight;\n immediateUpdate();\n }\n }, 500);\n }\n\n return () => {\n clearTimeout(timeoutId);\n if (intervalId) clearInterval(intervalId);\n\n events.forEach(event => {\n window.removeEventListener(\n event,\n event === 'resize' || event === 'orientationchange'\n ? debouncedUpdate\n : immediateUpdate,\n );\n });\n\n if (window.visualViewport) {\n window.visualViewport.removeEventListener('resize', immediateUpdate);\n window.visualViewport.removeEventListener('scroll', immediateUpdate);\n }\n };\n }, [readViewport, isInApp, debounce]);\n\n return viewport;\n};\n\nexport default useViewport;\n"],"mappings":";;;AAmBA,MAAM,eAAe,UAAmB,EAAE,KAAK;CAC7C,MAAM,EAAE,UAAU,OAAO,WAAW,QAAQ;CAE5C,MAAM,CAAC,UAAU,eAAe,SAAuB;EACrD,OAAO;EACP,QAAQ;EACR,YAAY;EACZ,WAAW;EACX,UAAU;EACV,SAAS;EACT,OAAO;EACR,CAAC;CAEF,MAAM,mBAAmB,kBAAkB;EACzC,MAAM,eAAe,OAAO;EAC5B,MAAM,eAAe,OAAO,gBAAgB,UAAU;EACtD,MAAM,iBAAiB,SAAS,gBAAgB;EAChD,MAAM,aAAa,SAAS,KAAK;AAEjC,MAAI,OAAO,kBAAkB,KAAK,IAAI,eAAe,aAAa,GAAG,IACnE,QAAO;AAGT,SAAO,KAAK,IAAI,cAAc,gBAAgB,WAAW;IACxD,EAAE,CAAC;CAEN,MAAM,eAAe,kBAAgC;AACnD,MAAI,OAAO,kBAAkB,CAAC,QAC5B,QAAO,OAAO;AAQhB,SAAO;GACL,OANY,OAAO,gBAAgB,SAAS,OAAO;GAOnD,QANa,UACX,kBAAkB,GAClB,OAAO,gBAAgB,UAAU,OAAO;GAK1C,YAAY,OAAO,gBAAgB,cAAc;GACjD,WAAW,OAAO,gBAAgB,aAAa;GAC/C,UAAU,OAAO,WAAW,OAAO,eAAe;GAClD,SAAS,OAAO,WAAW,OAAO,eAAe;GACjD,OAAO,OAAO,gBAAgB,SAAS;GACxC;IACA,CAAC,SAAS,iBAAiB,CAAC;AAE/B,iBAAgB;EACd,IAAI;EAEJ,MAAM,wBAAwB;AAC5B,gBAAa,UAAU;AACvB,eAAY,iBAAiB;AAC3B,gBAAY,cAAc,CAAC;MAC1B,SAAS;;EAGd,MAAM,wBAAwB,YAAY,cAAc,CAAC;AAEzD,mBAAiB;EAEjB,MAAM,SAAS,CAAC,UAAU,oBAAoB;AAE9C,MAAI,QACF,QAAO,KAAK,SAAS,QAAQ,cAAc,WAAW;AAGxD,SAAO,SAAQ,UAAS;AACtB,OAAI,UAAU,YAAY,UAAU,oBAClC,QAAO,iBAAiB,OAAO,gBAAgB;OAE/C,QAAO,iBAAiB,OAAO,iBAAiB,EAAE,SAAS,MAAM,CAAC;IAEpE;AAEF,MAAI,OAAO,gBAAgB;AACzB,UAAO,eAAe,iBAAiB,UAAU,gBAAgB;AACjE,UAAO,eAAe,iBAAiB,UAAU,gBAAgB;;EAGnE,IAAI;AACJ,MAAI,SAAS;GACX,IAAI,aAAa,cAAc,CAAC;AAChC,gBAAa,kBAAkB;IAC7B,MAAM,gBAAgB,cAAc,CAAC;AACrC,QAAI,KAAK,IAAI,gBAAgB,WAAW,GAAG,IAAI;AAC7C,kBAAa;AACb,sBAAiB;;MAElB,IAAI;;AAGT,eAAa;AACX,gBAAa,UAAU;AACvB,OAAI,WAAY,eAAc,WAAW;AAEzC,UAAO,SAAQ,UAAS;AACtB,WAAO,oBACL,OACA,UAAU,YAAY,UAAU,sBAC5B,kBACA,gBACL;KACD;AAEF,OAAI,OAAO,gBAAgB;AACzB,WAAO,eAAe,oBAAoB,UAAU,gBAAgB;AACpE,WAAO,eAAe,oBAAoB,UAAU,gBAAgB;;;IAGvE;EAAC;EAAc;EAAS;EAAS,CAAC;AAErC,QAAO"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/use-window-scroll/index.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 = window.scrollX || 0;\n const y = window.scrollY || 0;\n\n const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);\n const visualViewport = window.visualViewport;\n\n const viewportWidth =\n isIOS && visualViewport ? visualViewport.width : window.innerWidth;\n\n const viewportHeight =\n isIOS && visualViewport ? visualViewport.height : window.innerHeight;\n\n const maxScrollX = Math.max(\n 0,\n document.documentElement.scrollWidth - viewportWidth,\n );\n const maxScrollY = Math.max(\n 0,\n document.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 window.addEventListener('scroll', onScroll, { passive: true });\n window.addEventListener('resize', onResize);\n window.addEventListener('orientationchange', onResize);\n\n if (window.visualViewport) {\n window.visualViewport.addEventListener('resize', onVisualViewportChange);\n }\n\n return () => {\n window.removeEventListener('scroll', onScroll);\n window.removeEventListener('resize', onResize);\n window.removeEventListener('orientationchange', onResize);\n\n if (window.visualViewport) {\n window.visualViewport.removeEventListener(\n 'resize',\n onVisualViewportChange,\n );\n }\n };\n }, []);\n\n return state;\n};\n\nexport default useWindowScroll;\n"],"mappings":";;;AAEA,MAAM,wBAAwB;CAC5B,MAAM,CAAC,OAAO,YAAY,SAAS;EACjC,GAAG;EACH,GAAG;EACH,SAAS;GACP,GAAG;GACH,GAAG;GACJ;EACF,CAAC;AAEF,iBAAgB;EACd,MAAM,kBAAkB;GACtB,MAAM,IAAI,OAAO,WAAW;GAC5B,MAAM,IAAI,OAAO,WAAW;GAE5B,MAAM,QAAQ,mBAAmB,KAAK,UAAU,UAAU;GAC1D,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,SAAS,gBAAgB,cAAc,cACxC;GACD,MAAM,aAAa,KAAK,IACtB,GACA,SAAS,gBAAgB,eAAe,eACzC;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,EAAE,CAAC;AAEN,QAAO"}
|