@jbpark/use-hooks 2.1.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +17 -27
- package/README.md +17 -27
- package/dist/hooks/index.d.mts +13 -12
- package/dist/hooks/index.mjs +13 -12
- package/dist/hooks/{useBodyScrollLock → use-body-scroll-lock}/index.d.mts +1 -1
- package/dist/hooks/{useBodyScrollLock → use-body-scroll-lock}/index.mjs +45 -36
- package/dist/hooks/use-body-scroll-lock/index.mjs.map +1 -0
- package/dist/hooks/{useDebounce → use-debounce}/index.d.mts +1 -1
- package/dist/hooks/{useDebounce → use-debounce}/index.mjs +1 -5
- package/dist/hooks/use-debounce/index.mjs.map +1 -0
- package/dist/hooks/{useElementPosition → use-element-position}/index.d.mts +1 -1
- package/dist/hooks/{useElementPosition → use-element-position}/index.mjs +1 -1
- package/dist/hooks/use-element-position/index.mjs.map +1 -0
- package/dist/hooks/{useElementScroll → use-element-scroll}/index.d.mts +1 -1
- package/dist/hooks/{useElementScroll → use-element-scroll}/index.mjs +1 -1
- package/dist/hooks/use-element-scroll/index.mjs.map +1 -0
- package/dist/hooks/{useImage → use-image}/index.d.mts +1 -1
- package/dist/hooks/{useImage → use-image}/index.mjs +19 -11
- package/dist/hooks/use-image/index.mjs.map +1 -0
- package/dist/hooks/{useLocalStorage → use-local-storage}/index.d.mts +1 -1
- package/dist/hooks/{useLocalStorage → use-local-storage}/index.mjs +19 -3
- package/dist/hooks/use-local-storage/index.mjs.map +1 -0
- package/dist/hooks/{useRecursiveTimeout → use-recursive-timeout}/index.d.mts +1 -1
- package/dist/hooks/{useRecursiveTimeout → use-recursive-timeout}/index.mjs +4 -2
- package/dist/hooks/use-recursive-timeout/index.mjs.map +1 -0
- package/dist/hooks/{useResponsiveSize → use-responsive-size}/index.d.mts +1 -1
- package/dist/hooks/{useResponsiveSize → use-responsive-size}/index.mjs +2 -2
- package/dist/hooks/use-responsive-size/index.mjs.map +1 -0
- package/dist/hooks/{useScrollToElements → use-scroll-to-elements}/index.d.mts +3 -3
- package/dist/hooks/use-scroll-to-elements/index.mjs +34 -0
- package/dist/hooks/use-scroll-to-elements/index.mjs.map +1 -0
- package/dist/hooks/{useThrottle → use-throttle}/index.d.mts +1 -1
- package/dist/hooks/{useThrottle → use-throttle}/index.mjs +1 -1
- package/dist/hooks/use-throttle/index.mjs.map +1 -0
- package/dist/hooks/use-timeline/index.d.mts +50 -0
- package/dist/hooks/use-timeline/index.mjs +176 -0
- package/dist/hooks/use-timeline/index.mjs.map +1 -0
- package/dist/hooks/{useViewport → use-viewport}/index.d.mts +1 -1
- package/dist/hooks/{useViewport → use-viewport}/index.mjs +1 -1
- package/dist/hooks/use-viewport/index.mjs.map +1 -0
- package/dist/hooks/{useWindowScroll → use-window-scroll}/index.d.mts +1 -1
- package/dist/hooks/{useWindowScroll → use-window-scroll}/index.mjs +1 -1
- package/dist/hooks/use-window-scroll/index.mjs.map +1 -0
- package/dist/index.d.mts +14 -13
- package/dist/index.mjs +14 -13
- package/package.json +14 -10
- package/dist/hooks/useBodyScrollLock/index.mjs.map +0 -1
- package/dist/hooks/useDebounce/index.mjs.map +0 -1
- package/dist/hooks/useElementPosition/index.mjs.map +0 -1
- package/dist/hooks/useElementScroll/index.mjs.map +0 -1
- package/dist/hooks/useImage/index.mjs.map +0 -1
- package/dist/hooks/useLocalStorage/index.mjs.map +0 -1
- package/dist/hooks/useRecursiveTimeout/index.mjs.map +0 -1
- package/dist/hooks/useResponsiveSize/index.mjs.map +0 -1
- package/dist/hooks/useScrollToElements/index.mjs +0 -34
- package/dist/hooks/useScrollToElements/index.mjs.map +0 -1
- package/dist/hooks/useThrottle/index.mjs.map +0 -1
- package/dist/hooks/useViewport/index.mjs.map +0 -1
- package/dist/hooks/useWindowScroll/index.mjs.map +0 -1
package/README.ko.md
CHANGED
|
@@ -106,42 +106,32 @@ pnpm exec prettier --write .
|
|
|
106
106
|
```
|
|
107
107
|
src/
|
|
108
108
|
├── hooks/ # 개별 훅 구현
|
|
109
|
-
│ ├──
|
|
110
|
-
│ ├──
|
|
111
|
-
│ ├──
|
|
112
|
-
│ ├──
|
|
113
|
-
│ ├──
|
|
114
|
-
│ ├──
|
|
115
|
-
│ ├──
|
|
116
|
-
│ ├──
|
|
117
|
-
│ ├──
|
|
118
|
-
│ ├──
|
|
119
|
-
│ ├──
|
|
120
|
-
│ ├──
|
|
109
|
+
│ ├── use-body-scroll-lock/
|
|
110
|
+
│ ├── use-debounce/
|
|
111
|
+
│ ├── use-element-position/
|
|
112
|
+
│ ├── use-element-scroll/
|
|
113
|
+
│ ├── use-image/
|
|
114
|
+
│ ├── use-local-storage/
|
|
115
|
+
│ ├── use-recursive-timeout/
|
|
116
|
+
│ ├── use-responsive-size/
|
|
117
|
+
│ ├── use-scroll-to-elements/
|
|
118
|
+
│ ├── use-throttle/
|
|
119
|
+
│ ├── use-viewport/
|
|
120
|
+
│ ├── use-window-scroll/
|
|
121
121
|
│ └── index.ts # 배럴 익스포트
|
|
122
122
|
└── index.ts # 패키지 진입점
|
|
123
123
|
|
|
124
124
|
dist/ # 빌드된 라이브러리 (ESM + types)
|
|
125
|
-
.changeset/ # 버저닝을 위한 Changesets
|
|
126
125
|
```
|
|
127
126
|
|
|
128
127
|
## 빌드 및 배포
|
|
129
128
|
|
|
130
|
-
이 프로젝트는
|
|
129
|
+
이 프로젝트는 `develop` → `main` 브랜치 흐름과 AI 기반 자동 릴리스 노트를 사용합니다:
|
|
131
130
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
# 버전 업데이트 및 CHANGELOG 생성
|
|
137
|
-
pnpm changeset version
|
|
138
|
-
|
|
139
|
-
# npm에 배포
|
|
140
|
-
pnpm changeset publish
|
|
141
|
-
|
|
142
|
-
# 태그 푸시
|
|
143
|
-
git push --follow-tags
|
|
144
|
-
```
|
|
131
|
+
- **기능 브랜치**는 `develop`으로 머지됩니다.
|
|
132
|
+
- `develop`에 push될 때마다 워크플로가 diff를 분석해 `CHANGELOG.md`의 `## [Unreleased]` 섹션에 항목을 누적합니다 (버전은 아직 올리지 않음).
|
|
133
|
+
- `develop`이 `main`으로 머지되면 `Unreleased` 섹션에 버전+날짜가 확정되고 `package.json` 버전이 그에 맞춰 올라간 릴리스 PR이 생성됩니다.
|
|
134
|
+
- 이 릴리스 PR을 머지하면 빌드, npm 배포, 태그 생성까지 자동으로 진행됩니다.
|
|
145
135
|
|
|
146
136
|
라이브러리는 다음과 같이 빌드됩니다:
|
|
147
137
|
|
package/README.md
CHANGED
|
@@ -106,42 +106,32 @@ pnpm exec prettier --write .
|
|
|
106
106
|
```
|
|
107
107
|
src/
|
|
108
108
|
├── hooks/ # Individual hook implementations
|
|
109
|
-
│ ├──
|
|
110
|
-
│ ├──
|
|
111
|
-
│ ├──
|
|
112
|
-
│ ├──
|
|
113
|
-
│ ├──
|
|
114
|
-
│ ├──
|
|
115
|
-
│ ├──
|
|
116
|
-
│ ├──
|
|
117
|
-
│ ├──
|
|
118
|
-
│ ├──
|
|
119
|
-
│ ├──
|
|
120
|
-
│ ├──
|
|
109
|
+
│ ├── use-body-scroll-lock/
|
|
110
|
+
│ ├── use-debounce/
|
|
111
|
+
│ ├── use-element-position/
|
|
112
|
+
│ ├── use-element-scroll/
|
|
113
|
+
│ ├── use-image/
|
|
114
|
+
│ ├── use-local-storage/
|
|
115
|
+
│ ├── use-recursive-timeout/
|
|
116
|
+
│ ├── use-responsive-size/
|
|
117
|
+
│ ├── use-scroll-to-elements/
|
|
118
|
+
│ ├── use-throttle/
|
|
119
|
+
│ ├── use-viewport/
|
|
120
|
+
│ ├── use-window-scroll/
|
|
121
121
|
│ └── index.ts # Barrel export
|
|
122
122
|
└── index.ts # Package entry point
|
|
123
123
|
|
|
124
124
|
dist/ # Built library (ESM + types)
|
|
125
|
-
.changeset/ # Changesets for versioning
|
|
126
125
|
```
|
|
127
126
|
|
|
128
127
|
## Build & Deployment
|
|
129
128
|
|
|
130
|
-
This project uses
|
|
129
|
+
This project uses a `develop` → `main` branch flow with automated, AI-assisted release notes:
|
|
131
130
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
# Update versions and generate CHANGELOG
|
|
137
|
-
pnpm changeset version
|
|
138
|
-
|
|
139
|
-
# Publish to npm
|
|
140
|
-
pnpm changeset publish
|
|
141
|
-
|
|
142
|
-
# Push tags
|
|
143
|
-
git push --follow-tags
|
|
144
|
-
```
|
|
131
|
+
- **Feature branches** merge into `develop`.
|
|
132
|
+
- On every push to `develop`, a workflow analyzes the diff and appends bullet points to the `## [Unreleased]` section of `CHANGELOG.md` (no version bump yet).
|
|
133
|
+
- When `develop` is merged into `main`, that `Unreleased` section is stamped with a version + date and `package.json` is bumped to match, opened as a release PR.
|
|
134
|
+
- Merging the release PR builds, publishes to npm, and tags the release.
|
|
145
135
|
|
|
146
136
|
The library is built as:
|
|
147
137
|
|
package/dist/hooks/index.d.mts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { useDebounce } from "./
|
|
2
|
-
import { useBodyScrollLock } from "./
|
|
3
|
-
import { useElementPosition } from "./
|
|
4
|
-
import { useElementScroll } from "./
|
|
5
|
-
import { useResponsiveSize } from "./
|
|
6
|
-
import { useImage } from "./
|
|
7
|
-
import { useLocalStorage } from "./
|
|
8
|
-
import { useRecursiveTimeout } from "./
|
|
9
|
-
import { useScrollToElements } from "./
|
|
10
|
-
import { useThrottle } from "./
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
1
|
+
import { useDebounce } from "./use-debounce/index.mjs";
|
|
2
|
+
import { useBodyScrollLock } from "./use-body-scroll-lock/index.mjs";
|
|
3
|
+
import { useElementPosition } from "./use-element-position/index.mjs";
|
|
4
|
+
import { useElementScroll } from "./use-element-scroll/index.mjs";
|
|
5
|
+
import { useResponsiveSize } from "./use-responsive-size/index.mjs";
|
|
6
|
+
import { useImage } from "./use-image/index.mjs";
|
|
7
|
+
import { useLocalStorage } from "./use-local-storage/index.mjs";
|
|
8
|
+
import { useRecursiveTimeout } from "./use-recursive-timeout/index.mjs";
|
|
9
|
+
import { useScrollToElements } from "./use-scroll-to-elements/index.mjs";
|
|
10
|
+
import { useThrottle } from "./use-throttle/index.mjs";
|
|
11
|
+
import { useTimeline } from "./use-timeline/index.mjs";
|
|
12
|
+
import { useWindowScroll } from "./use-window-scroll/index.mjs";
|
|
13
|
+
import { useViewport } from "./use-viewport/index.mjs";
|
package/dist/hooks/index.mjs
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import useDebounce from "./
|
|
2
|
-
import useBodyScrollLock from "./
|
|
3
|
-
import useElementPosition from "./
|
|
4
|
-
import useElementScroll from "./
|
|
5
|
-
import useResponsiveSize from "./
|
|
6
|
-
import useImage from "./
|
|
7
|
-
import useLocalStorage from "./
|
|
8
|
-
import useRecursiveTimeout from "./
|
|
9
|
-
import useScrollToElements from "./
|
|
10
|
-
import useThrottle from "./
|
|
11
|
-
import
|
|
12
|
-
import
|
|
1
|
+
import useDebounce from "./use-debounce/index.mjs";
|
|
2
|
+
import useBodyScrollLock from "./use-body-scroll-lock/index.mjs";
|
|
3
|
+
import useElementPosition from "./use-element-position/index.mjs";
|
|
4
|
+
import useElementScroll from "./use-element-scroll/index.mjs";
|
|
5
|
+
import useResponsiveSize from "./use-responsive-size/index.mjs";
|
|
6
|
+
import useImage from "./use-image/index.mjs";
|
|
7
|
+
import useLocalStorage from "./use-local-storage/index.mjs";
|
|
8
|
+
import useRecursiveTimeout from "./use-recursive-timeout/index.mjs";
|
|
9
|
+
import useScrollToElements from "./use-scroll-to-elements/index.mjs";
|
|
10
|
+
import useThrottle from "./use-throttle/index.mjs";
|
|
11
|
+
import useTimeline from "./use-timeline/index.mjs";
|
|
12
|
+
import useWindowScroll from "./use-window-scroll/index.mjs";
|
|
13
|
+
import useViewport from "./use-viewport/index.mjs";
|
|
13
14
|
|
|
14
15
|
export { };
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { useEffect } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
//#region src/hooks/use-body-scroll-lock/index.ts
|
|
4
|
+
let lockCount = 0;
|
|
5
|
+
let originalStyles = null;
|
|
6
|
+
let originalScrollY = 0;
|
|
7
|
+
const isIOS = () => /iPad|iPhone|iPod/.test(navigator.userAgent);
|
|
8
|
+
const lockScroll = () => {
|
|
9
|
+
if (lockCount === 0) {
|
|
10
|
+
originalScrollY = window.scrollY;
|
|
11
|
+
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
|
|
12
|
+
const currentPaddingRight = parseFloat(getComputedStyle(document.body).paddingRight) || 0;
|
|
13
|
+
originalStyles = {
|
|
10
14
|
documentElement: {
|
|
11
15
|
overflow: document.documentElement.style.overflow,
|
|
12
16
|
height: document.documentElement.style.height,
|
|
@@ -21,6 +25,7 @@ const useBodyScrollLock = (enabled = true) => {
|
|
|
21
25
|
left: document.body.style.left,
|
|
22
26
|
right: document.body.style.right,
|
|
23
27
|
width: document.body.style.width,
|
|
28
|
+
paddingRight: document.body.style.paddingRight,
|
|
24
29
|
webkitOverflowScrolling: document.body.style.getPropertyValue("-webkit-overflow-scrolling")
|
|
25
30
|
}
|
|
26
31
|
};
|
|
@@ -31,12 +36,40 @@ const useBodyScrollLock = (enabled = true) => {
|
|
|
31
36
|
document.body.style.overflow = "hidden";
|
|
32
37
|
document.body.style.height = "100%";
|
|
33
38
|
document.body.style.position = "fixed";
|
|
34
|
-
document.body.style.top = `-${
|
|
39
|
+
document.body.style.top = `-${originalScrollY}px`;
|
|
35
40
|
document.body.style.left = "0";
|
|
36
41
|
document.body.style.right = "0";
|
|
37
42
|
document.body.style.width = "100%";
|
|
38
|
-
if (
|
|
39
|
-
|
|
43
|
+
if (scrollbarWidth > 0) document.body.style.paddingRight = `${currentPaddingRight + scrollbarWidth}px`;
|
|
44
|
+
if (isIOS()) document.body.style.setProperty("-webkit-overflow-scrolling", "touch");
|
|
45
|
+
}
|
|
46
|
+
lockCount += 1;
|
|
47
|
+
};
|
|
48
|
+
const unlockScroll = () => {
|
|
49
|
+
lockCount = Math.max(0, lockCount - 1);
|
|
50
|
+
if (lockCount > 0 || !originalStyles) return;
|
|
51
|
+
document.documentElement.style.overflow = originalStyles.documentElement.overflow;
|
|
52
|
+
document.documentElement.style.height = originalStyles.documentElement.height;
|
|
53
|
+
document.documentElement.style.position = originalStyles.documentElement.position;
|
|
54
|
+
document.documentElement.style.width = originalStyles.documentElement.width;
|
|
55
|
+
document.body.style.overflow = originalStyles.body.overflow;
|
|
56
|
+
document.body.style.height = originalStyles.body.height;
|
|
57
|
+
document.body.style.position = originalStyles.body.position;
|
|
58
|
+
document.body.style.top = originalStyles.body.top;
|
|
59
|
+
document.body.style.left = originalStyles.body.left;
|
|
60
|
+
document.body.style.right = originalStyles.body.right;
|
|
61
|
+
document.body.style.width = originalStyles.body.width;
|
|
62
|
+
document.body.style.paddingRight = originalStyles.body.paddingRight;
|
|
63
|
+
if (originalStyles.body.webkitOverflowScrolling) document.body.style.setProperty("-webkit-overflow-scrolling", originalStyles.body.webkitOverflowScrolling);
|
|
64
|
+
else document.body.style.removeProperty("-webkit-overflow-scrolling");
|
|
65
|
+
window.scrollTo(0, originalScrollY);
|
|
66
|
+
originalStyles = null;
|
|
67
|
+
};
|
|
68
|
+
const useBodyScrollLock = (enabled = true) => {
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (!enabled) return;
|
|
71
|
+
lockScroll();
|
|
72
|
+
if (isIOS()) {
|
|
40
73
|
const preventAll = (e) => {
|
|
41
74
|
if (e.target === document.body || e.target === document.documentElement || e.target === window) {
|
|
42
75
|
e.preventDefault();
|
|
@@ -77,35 +110,11 @@ const useBodyScrollLock = (enabled = true) => {
|
|
|
77
110
|
document.removeEventListener(event, preventAll, { capture: true });
|
|
78
111
|
document.body.removeEventListener(event, preventAll, { capture: true });
|
|
79
112
|
});
|
|
80
|
-
|
|
81
|
-
document.documentElement.style.height = originalStyles.documentElement.height;
|
|
82
|
-
document.documentElement.style.position = originalStyles.documentElement.position;
|
|
83
|
-
document.documentElement.style.width = originalStyles.documentElement.width;
|
|
84
|
-
document.body.style.overflow = originalStyles.body.overflow;
|
|
85
|
-
document.body.style.height = originalStyles.body.height;
|
|
86
|
-
document.body.style.position = originalStyles.body.position;
|
|
87
|
-
document.body.style.top = originalStyles.body.top;
|
|
88
|
-
document.body.style.left = originalStyles.body.left;
|
|
89
|
-
document.body.style.right = originalStyles.body.right;
|
|
90
|
-
document.body.style.width = originalStyles.body.width;
|
|
91
|
-
if (originalStyles.body.webkitOverflowScrolling) document.body.style.setProperty("-webkit-overflow-scrolling", originalStyles.body.webkitOverflowScrolling);
|
|
92
|
-
else document.body.style.removeProperty("-webkit-overflow-scrolling");
|
|
93
|
-
window.scrollTo(0, scrollY);
|
|
113
|
+
unlockScroll();
|
|
94
114
|
};
|
|
95
115
|
}
|
|
96
116
|
return () => {
|
|
97
|
-
|
|
98
|
-
document.documentElement.style.height = originalStyles.documentElement.height;
|
|
99
|
-
document.documentElement.style.position = originalStyles.documentElement.position;
|
|
100
|
-
document.documentElement.style.width = originalStyles.documentElement.width;
|
|
101
|
-
document.body.style.overflow = originalStyles.body.overflow;
|
|
102
|
-
document.body.style.height = originalStyles.body.height;
|
|
103
|
-
document.body.style.position = originalStyles.body.position;
|
|
104
|
-
document.body.style.top = originalStyles.body.top;
|
|
105
|
-
document.body.style.left = originalStyles.body.left;
|
|
106
|
-
document.body.style.right = originalStyles.body.right;
|
|
107
|
-
document.body.style.width = originalStyles.body.width;
|
|
108
|
-
window.scrollTo(0, scrollY);
|
|
117
|
+
unlockScroll();
|
|
109
118
|
};
|
|
110
119
|
}, [enabled]);
|
|
111
120
|
};
|
|
@@ -0,0 +1 @@
|
|
|
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":"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"}
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import { useEffect, useRef } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/
|
|
3
|
+
//#region src/hooks/use-debounce/index.ts
|
|
4
4
|
const useDebounce = (callback, { delay = 100, autoInvoke = true }, deps = []) => {
|
|
5
5
|
const timeoutRef = useRef(null);
|
|
6
6
|
const callbackRef = useRef(callback);
|
|
7
|
-
const depsRef = useRef(deps);
|
|
8
7
|
const prevDeps = useRef(void 0);
|
|
9
8
|
useEffect(() => {
|
|
10
9
|
callbackRef.current = callback;
|
|
11
10
|
});
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
depsRef.current = deps;
|
|
14
|
-
});
|
|
15
11
|
const stableDebouncedCallback = useRef(null);
|
|
16
12
|
if (!stableDebouncedCallback.current) stableDebouncedCallback.current = ((...args) => {
|
|
17
13
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
@@ -0,0 +1 @@
|
|
|
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,6 +1,6 @@
|
|
|
1
1
|
import { RefObject } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/
|
|
3
|
+
//#region src/hooks/use-element-position/index.d.ts
|
|
4
4
|
type ElementReference<T> = string | RefObject<T>;
|
|
5
5
|
declare const useElementPosition: <T>(elementRef: ElementReference<T>) => DOMRect | null;
|
|
6
6
|
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
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,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/
|
|
3
|
+
//#region src/hooks/use-element-scroll/index.ts
|
|
4
4
|
const useElementScroll = () => {
|
|
5
5
|
const [element, setElement] = useState(null);
|
|
6
6
|
const [scrollPosition, setScrollPosition] = useState({
|
|
@@ -0,0 +1 @@
|
|
|
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,53 +1,61 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/
|
|
3
|
+
//#region src/hooks/use-image/index.ts
|
|
4
4
|
const useImage = (src, options = {}) => {
|
|
5
5
|
const { retryCount = 0, retryDelay = 1e3 } = options;
|
|
6
6
|
const [loading, setLoading] = useState(true);
|
|
7
7
|
const [error, setError] = useState(null);
|
|
8
8
|
const [loaded, setLoaded] = useState(false);
|
|
9
9
|
const [attemptCount, setAttemptCount] = useState(0);
|
|
10
|
-
const
|
|
10
|
+
const [reloadToken, setReloadToken] = useState(0);
|
|
11
|
+
useEffect(() => {
|
|
11
12
|
if (!src) {
|
|
12
13
|
setLoading(false);
|
|
13
14
|
setLoaded(false);
|
|
14
15
|
return;
|
|
15
16
|
}
|
|
17
|
+
let cancelled = false;
|
|
18
|
+
let retryTimeoutId;
|
|
16
19
|
setLoading(true);
|
|
17
20
|
setError(null);
|
|
18
21
|
const img = new Image();
|
|
19
22
|
img.src = src;
|
|
20
23
|
img.onload = () => {
|
|
24
|
+
if (cancelled) return;
|
|
21
25
|
setLoading(false);
|
|
22
26
|
setLoaded(true);
|
|
23
27
|
setError(null);
|
|
24
|
-
setAttemptCount(0);
|
|
25
28
|
};
|
|
26
29
|
img.onerror = (err) => {
|
|
30
|
+
if (cancelled) return;
|
|
27
31
|
setLoading(false);
|
|
28
32
|
setLoaded(false);
|
|
29
33
|
setError(err);
|
|
30
|
-
if (attemptCount < retryCount) setTimeout(() => {
|
|
31
|
-
setAttemptCount((prev) => prev + 1);
|
|
34
|
+
if (attemptCount < retryCount) retryTimeoutId = setTimeout(() => {
|
|
35
|
+
if (!cancelled) setAttemptCount((prev) => prev + 1);
|
|
32
36
|
}, retryDelay);
|
|
33
37
|
};
|
|
38
|
+
return () => {
|
|
39
|
+
cancelled = true;
|
|
40
|
+
img.onload = null;
|
|
41
|
+
img.onerror = null;
|
|
42
|
+
if (retryTimeoutId) clearTimeout(retryTimeoutId);
|
|
43
|
+
};
|
|
34
44
|
}, [
|
|
35
45
|
src,
|
|
36
46
|
attemptCount,
|
|
37
47
|
retryCount,
|
|
38
|
-
retryDelay
|
|
48
|
+
retryDelay,
|
|
49
|
+
reloadToken
|
|
39
50
|
]);
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
loadImage();
|
|
42
|
-
}, [loadImage]);
|
|
43
51
|
return {
|
|
44
52
|
loading,
|
|
45
53
|
error,
|
|
46
54
|
loaded,
|
|
47
55
|
retry: useCallback(() => {
|
|
48
56
|
setAttemptCount(0);
|
|
49
|
-
|
|
50
|
-
}, [
|
|
57
|
+
setReloadToken((token) => token + 1);
|
|
58
|
+
}, [])
|
|
51
59
|
};
|
|
52
60
|
};
|
|
53
61
|
|
|
@@ -0,0 +1 @@
|
|
|
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,8 +1,11 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/
|
|
3
|
+
//#region src/hooks/use-local-storage/index.ts
|
|
4
4
|
const useLocalStorage = (key, initialValue) => {
|
|
5
|
-
const
|
|
5
|
+
const initialValueRef = useRef(initialValue);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
initialValueRef.current = initialValue;
|
|
8
|
+
});
|
|
6
9
|
const [storedValue, setStoredValue] = useState(() => {
|
|
7
10
|
if (typeof window === "undefined") return initialValue;
|
|
8
11
|
try {
|
|
@@ -17,11 +20,24 @@ const useLocalStorage = (key, initialValue) => {
|
|
|
17
20
|
try {
|
|
18
21
|
const item = window.localStorage.getItem(key);
|
|
19
22
|
if (item) setStoredValue(JSON.parse(item));
|
|
20
|
-
else window.localStorage.setItem(key, JSON.stringify(
|
|
23
|
+
else window.localStorage.setItem(key, JSON.stringify(initialValueRef.current));
|
|
21
24
|
} catch (e) {
|
|
22
25
|
console.error(`Error reading localStorage key "${key}":`, e);
|
|
23
26
|
}
|
|
24
27
|
}, [key]);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (typeof window === "undefined") return;
|
|
30
|
+
const onStorage = (event) => {
|
|
31
|
+
if (event.key !== key) return;
|
|
32
|
+
try {
|
|
33
|
+
setStoredValue(event.newValue ? JSON.parse(event.newValue) : initialValueRef.current);
|
|
34
|
+
} catch (e) {
|
|
35
|
+
console.error(`Error parsing localStorage key "${key}" from storage event:`, e);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
window.addEventListener("storage", onStorage);
|
|
39
|
+
return () => window.removeEventListener("storage", onStorage);
|
|
40
|
+
}, [key]);
|
|
25
41
|
return [storedValue, useCallback((value) => {
|
|
26
42
|
try {
|
|
27
43
|
setStoredValue((prev) => {
|
|
@@ -0,0 +1 @@
|
|
|
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,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useRef } from "react";
|
|
2
2
|
|
|
3
|
-
//#region src/hooks/
|
|
3
|
+
//#region src/hooks/use-recursive-timeout/index.ts
|
|
4
4
|
const useRecursiveTimeout = (callback, delay) => {
|
|
5
5
|
const savedCallback = useRef(callback);
|
|
6
6
|
useEffect(() => {
|
|
@@ -17,7 +17,9 @@ const useRecursiveTimeout = (callback, delay) => {
|
|
|
17
17
|
}
|
|
18
18
|
if (delay) {
|
|
19
19
|
id = setTimeout(tick, delay);
|
|
20
|
-
return () =>
|
|
20
|
+
return () => {
|
|
21
|
+
clearTimeout(id);
|
|
22
|
+
};
|
|
21
23
|
}
|
|
22
24
|
}, [delay]);
|
|
23
25
|
};
|
|
@@ -0,0 +1 @@
|
|
|
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,7 +1,7 @@
|
|
|
1
|
-
import useDebounce from "../
|
|
1
|
+
import useDebounce from "../use-debounce/index.mjs";
|
|
2
2
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
3
|
|
|
4
|
-
//#region src/hooks/
|
|
4
|
+
//#region src/hooks/use-responsive-size/index.ts
|
|
5
5
|
const BREAKPOINTS = {
|
|
6
6
|
xs: 0,
|
|
7
7
|
sm: 640,
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|