@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.
Files changed (59) hide show
  1. package/README.ko.md +17 -27
  2. package/README.md +17 -27
  3. package/dist/hooks/index.d.mts +13 -12
  4. package/dist/hooks/index.mjs +13 -12
  5. package/dist/hooks/{useBodyScrollLock → use-body-scroll-lock}/index.d.mts +1 -1
  6. package/dist/hooks/{useBodyScrollLock → use-body-scroll-lock}/index.mjs +45 -36
  7. package/dist/hooks/use-body-scroll-lock/index.mjs.map +1 -0
  8. package/dist/hooks/{useDebounce → use-debounce}/index.d.mts +1 -1
  9. package/dist/hooks/{useDebounce → use-debounce}/index.mjs +1 -5
  10. package/dist/hooks/use-debounce/index.mjs.map +1 -0
  11. package/dist/hooks/{useElementPosition → use-element-position}/index.d.mts +1 -1
  12. package/dist/hooks/{useElementPosition → use-element-position}/index.mjs +1 -1
  13. package/dist/hooks/use-element-position/index.mjs.map +1 -0
  14. package/dist/hooks/{useElementScroll → use-element-scroll}/index.d.mts +1 -1
  15. package/dist/hooks/{useElementScroll → use-element-scroll}/index.mjs +1 -1
  16. package/dist/hooks/use-element-scroll/index.mjs.map +1 -0
  17. package/dist/hooks/{useImage → use-image}/index.d.mts +1 -1
  18. package/dist/hooks/{useImage → use-image}/index.mjs +19 -11
  19. package/dist/hooks/use-image/index.mjs.map +1 -0
  20. package/dist/hooks/{useLocalStorage → use-local-storage}/index.d.mts +1 -1
  21. package/dist/hooks/{useLocalStorage → use-local-storage}/index.mjs +19 -3
  22. package/dist/hooks/use-local-storage/index.mjs.map +1 -0
  23. package/dist/hooks/{useRecursiveTimeout → use-recursive-timeout}/index.d.mts +1 -1
  24. package/dist/hooks/{useRecursiveTimeout → use-recursive-timeout}/index.mjs +4 -2
  25. package/dist/hooks/use-recursive-timeout/index.mjs.map +1 -0
  26. package/dist/hooks/{useResponsiveSize → use-responsive-size}/index.d.mts +1 -1
  27. package/dist/hooks/{useResponsiveSize → use-responsive-size}/index.mjs +2 -2
  28. package/dist/hooks/use-responsive-size/index.mjs.map +1 -0
  29. package/dist/hooks/{useScrollToElements → use-scroll-to-elements}/index.d.mts +3 -3
  30. package/dist/hooks/use-scroll-to-elements/index.mjs +34 -0
  31. package/dist/hooks/use-scroll-to-elements/index.mjs.map +1 -0
  32. package/dist/hooks/{useThrottle → use-throttle}/index.d.mts +1 -1
  33. package/dist/hooks/{useThrottle → use-throttle}/index.mjs +1 -1
  34. package/dist/hooks/use-throttle/index.mjs.map +1 -0
  35. package/dist/hooks/use-timeline/index.d.mts +50 -0
  36. package/dist/hooks/use-timeline/index.mjs +176 -0
  37. package/dist/hooks/use-timeline/index.mjs.map +1 -0
  38. package/dist/hooks/{useViewport → use-viewport}/index.d.mts +1 -1
  39. package/dist/hooks/{useViewport → use-viewport}/index.mjs +1 -1
  40. package/dist/hooks/use-viewport/index.mjs.map +1 -0
  41. package/dist/hooks/{useWindowScroll → use-window-scroll}/index.d.mts +1 -1
  42. package/dist/hooks/{useWindowScroll → use-window-scroll}/index.mjs +1 -1
  43. package/dist/hooks/use-window-scroll/index.mjs.map +1 -0
  44. package/dist/index.d.mts +14 -13
  45. package/dist/index.mjs +14 -13
  46. package/package.json +14 -10
  47. package/dist/hooks/useBodyScrollLock/index.mjs.map +0 -1
  48. package/dist/hooks/useDebounce/index.mjs.map +0 -1
  49. package/dist/hooks/useElementPosition/index.mjs.map +0 -1
  50. package/dist/hooks/useElementScroll/index.mjs.map +0 -1
  51. package/dist/hooks/useImage/index.mjs.map +0 -1
  52. package/dist/hooks/useLocalStorage/index.mjs.map +0 -1
  53. package/dist/hooks/useRecursiveTimeout/index.mjs.map +0 -1
  54. package/dist/hooks/useResponsiveSize/index.mjs.map +0 -1
  55. package/dist/hooks/useScrollToElements/index.mjs +0 -34
  56. package/dist/hooks/useScrollToElements/index.mjs.map +0 -1
  57. package/dist/hooks/useThrottle/index.mjs.map +0 -1
  58. package/dist/hooks/useViewport/index.mjs.map +0 -1
  59. 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
- │ ├── useBodyScrollLock/
110
- │ ├── useDebounce/
111
- │ ├── useElementPosition/
112
- │ ├── useElementScroll/
113
- │ ├── useImage/
114
- │ ├── useLocalStorage/
115
- │ ├── useRecursiveTimeout/
116
- │ ├── useResponsiveSize/
117
- │ ├── useScrollToElements/
118
- │ ├── useThrottle/
119
- │ ├── useViewport/
120
- │ ├── useWindowScroll/
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
- 이 프로젝트는 버전 관리를 위해 Changesets를 사용합니다:
129
+ 이 프로젝트는 `develop` → `main` 브랜치 흐름과 AI 기반 자동 릴리스 노트를 사용합니다:
131
130
 
132
- ```bash
133
- # 변경사항 기록
134
- pnpm changeset add
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
- │ ├── useBodyScrollLock/
110
- │ ├── useDebounce/
111
- │ ├── useElementPosition/
112
- │ ├── useElementScroll/
113
- │ ├── useImage/
114
- │ ├── useLocalStorage/
115
- │ ├── useRecursiveTimeout/
116
- │ ├── useResponsiveSize/
117
- │ ├── useScrollToElements/
118
- │ ├── useThrottle/
119
- │ ├── useViewport/
120
- │ ├── useWindowScroll/
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 Changesets for version management:
129
+ This project uses a `develop` → `main` branch flow with automated, AI-assisted release notes:
131
130
 
132
- ```bash
133
- # Record changes
134
- pnpm changeset add
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
 
@@ -1,12 +1,13 @@
1
- import { useDebounce } from "./useDebounce/index.mjs";
2
- import { useBodyScrollLock } from "./useBodyScrollLock/index.mjs";
3
- import { useElementPosition } from "./useElementPosition/index.mjs";
4
- import { useElementScroll } from "./useElementScroll/index.mjs";
5
- import { useResponsiveSize } from "./useResponsiveSize/index.mjs";
6
- import { useImage } from "./useImage/index.mjs";
7
- import { useLocalStorage } from "./useLocalStorage/index.mjs";
8
- import { useRecursiveTimeout } from "./useRecursiveTimeout/index.mjs";
9
- import { useScrollToElements } from "./useScrollToElements/index.mjs";
10
- import { useThrottle } from "./useThrottle/index.mjs";
11
- import { useWindowScroll } from "./useWindowScroll/index.mjs";
12
- import { useViewport } from "./useViewport/index.mjs";
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";
@@ -1,14 +1,15 @@
1
- import useDebounce from "./useDebounce/index.mjs";
2
- import useBodyScrollLock from "./useBodyScrollLock/index.mjs";
3
- import useElementPosition from "./useElementPosition/index.mjs";
4
- import useElementScroll from "./useElementScroll/index.mjs";
5
- import useResponsiveSize from "./useResponsiveSize/index.mjs";
6
- import useImage from "./useImage/index.mjs";
7
- import useLocalStorage from "./useLocalStorage/index.mjs";
8
- import useRecursiveTimeout from "./useRecursiveTimeout/index.mjs";
9
- import useScrollToElements from "./useScrollToElements/index.mjs";
10
- import useThrottle from "./useThrottle/index.mjs";
11
- import useWindowScroll from "./useWindowScroll/index.mjs";
12
- import useViewport from "./useViewport/index.mjs";
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,4 +1,4 @@
1
- //#region src/hooks/useBodyScrollLock/index.d.ts
1
+ //#region src/hooks/use-body-scroll-lock/index.d.ts
2
2
  declare const useBodyScrollLock: (enabled?: boolean) => void;
3
3
  //#endregion
4
4
  export { useBodyScrollLock };
@@ -1,12 +1,16 @@
1
1
  import { useEffect } from "react";
2
2
 
3
- //#region src/hooks/useBodyScrollLock/index.ts
4
- const useBodyScrollLock = (enabled = true) => {
5
- useEffect(() => {
6
- if (!enabled) return;
7
- const scrollY = window.scrollY;
8
- const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
9
- const originalStyles = {
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 = `-${scrollY}px`;
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 (isIOS) {
39
- document.body.style.setProperty("-webkit-overflow-scrolling", "touch");
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
- document.documentElement.style.overflow = originalStyles.documentElement.overflow;
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
- document.documentElement.style.overflow = originalStyles.documentElement.overflow;
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,4 +1,4 @@
1
- //#region src/hooks/useDebounce/index.d.ts
1
+ //#region src/hooks/use-debounce/index.d.ts
2
2
  interface Options {
3
3
  delay?: number;
4
4
  autoInvoke?: boolean;
@@ -1,17 +1,13 @@
1
1
  import { useEffect, useRef } from "react";
2
2
 
3
- //#region src/hooks/useDebounce/index.ts
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/useElementPosition/index.d.ts
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
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useState } from "react";
2
2
 
3
- //#region src/hooks/useElementPosition/index.ts
3
+ //#region src/hooks/use-element-position/index.ts
4
4
  const useElementPosition = (elementRef) => {
5
5
  const [rect, setRect] = useState(null);
6
6
  useEffect(() => {
@@ -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,4 +1,4 @@
1
- //#region src/hooks/useElementScroll/index.d.ts
1
+ //#region src/hooks/use-element-scroll/index.d.ts
2
2
  declare const useElementScroll: () => {
3
3
  element: HTMLElement | null;
4
4
  setRef: (el: HTMLElement | null) => void;
@@ -1,6 +1,6 @@
1
1
  import { useCallback, useEffect, useState } from "react";
2
2
 
3
- //#region src/hooks/useElementScroll/index.ts
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,4 +1,4 @@
1
- //#region src/hooks/useImage/index.d.ts
1
+ //#region src/hooks/use-image/index.d.ts
2
2
  interface Options {
3
3
  retryCount?: number;
4
4
  retryDelay?: number;
@@ -1,53 +1,61 @@
1
1
  import { useCallback, useEffect, useState } from "react";
2
2
 
3
- //#region src/hooks/useImage/index.ts
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 loadImage = useCallback(() => {
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
- loadImage();
50
- }, [loadImage])
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,4 +1,4 @@
1
- //#region src/hooks/useLocalStorage/index.d.ts
1
+ //#region src/hooks/use-local-storage/index.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 };
@@ -1,8 +1,11 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
2
 
3
- //#region src/hooks/useLocalStorage/index.ts
3
+ //#region src/hooks/use-local-storage/index.ts
4
4
  const useLocalStorage = (key, initialValue) => {
5
- const initialRef = useRef(initialValue);
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(initialRef.current));
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,4 +1,4 @@
1
- //#region src/hooks/useRecursiveTimeout/index.d.ts
1
+ //#region src/hooks/use-recursive-timeout/index.d.ts
2
2
  declare const useRecursiveTimeout: <T>(callback: () => Promise<T> | (() => void), delay: number | null) => void;
3
3
  //#endregion
4
4
  export { useRecursiveTimeout };
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useRef } from "react";
2
2
 
3
- //#region src/hooks/useRecursiveTimeout/index.ts
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 () => id && clearTimeout(id);
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,4 +1,4 @@
1
- //#region src/hooks/useResponsiveSize/index.d.ts
1
+ //#region src/hooks/use-responsive-size/index.d.ts
2
2
  type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
3
3
  interface BreakpointInfo {
4
4
  current: Breakpoint;
@@ -1,7 +1,7 @@
1
- import useDebounce from "../useDebounce/index.mjs";
1
+ import useDebounce from "../use-debounce/index.mjs";
2
2
  import { useCallback, useEffect, useRef, useState } from "react";
3
3
 
4
- //#region src/hooks/useResponsiveSize/index.ts
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"}