@pelatform/ui.hook 0.1.3 → 0.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.md +40 -6
- package/dist/index.d.ts +86 -1
- package/dist/index.js +112 -30
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@pelatform/ui.hook)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
A collection of production-ready React hooks for the Pelatform UI Library. This package provides
|
|
6
|
+
A collection of production-ready React hooks for the Pelatform UI Library. This package provides 18 reusable hooks for analytics, responsive design, form handling, navigation, DOM management, and more.
|
|
7
7
|
|
|
8
8
|
## Installation
|
|
9
9
|
|
|
@@ -67,6 +67,37 @@ import { useViewport } from "@pelatform/ui.hook";
|
|
|
67
67
|
const [height, width] = useViewport();
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
#### `useIntersectionObserver`
|
|
71
|
+
|
|
72
|
+
Observe element intersection with viewport using Intersection Observer API.
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { useIntersectionObserver } from "@pelatform/ui.hook";
|
|
76
|
+
|
|
77
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
78
|
+
const { isIntersecting, entry } = useIntersectionObserver(ref, {
|
|
79
|
+
threshold: 0.5,
|
|
80
|
+
triggerOnce: true,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
<div ref={ref}>
|
|
84
|
+
{isIntersecting ? "Visible!" : "Not visible"}
|
|
85
|
+
</div>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### `useIsMac`
|
|
89
|
+
|
|
90
|
+
Detect if the user's operating system is macOS.
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { useIsMac } from "@pelatform/ui.hook";
|
|
94
|
+
|
|
95
|
+
const isMac = useIsMac();
|
|
96
|
+
|
|
97
|
+
// Useful for keyboard shortcut hints (⌘ vs Ctrl)
|
|
98
|
+
<div>Press {isMac ? "⌘" : "Ctrl"} + K to open command menu</div>
|
|
99
|
+
```
|
|
100
|
+
|
|
70
101
|
### Form & Input Management
|
|
71
102
|
|
|
72
103
|
#### `useFileUpload`
|
|
@@ -151,14 +182,14 @@ const elementScroll = useScrollPosition({ targetRef: myRef });
|
|
|
151
182
|
|
|
152
183
|
### DOM Management
|
|
153
184
|
|
|
154
|
-
#### `
|
|
185
|
+
#### `useBodyClass`
|
|
155
186
|
|
|
156
187
|
Dynamically add/remove CSS classes from the document body element.
|
|
157
188
|
|
|
158
189
|
```typescript
|
|
159
|
-
import {
|
|
190
|
+
import { useBodyClass } from "@pelatform/ui.hook";
|
|
160
191
|
|
|
161
|
-
|
|
192
|
+
useBodyClass("dark-theme overflow-hidden");
|
|
162
193
|
```
|
|
163
194
|
|
|
164
195
|
#### `useMutationObserver`
|
|
@@ -245,7 +276,8 @@ useRemoveGAParams(); // Cleans URL after GA processes linker attribution
|
|
|
245
276
|
- **Responsive Design**: `useMediaQuery`, `useIsMobile`, `useViewport`
|
|
246
277
|
- **Form Management**: `useFileUpload`, `useSliderInput`, `useCopyToClipboard`
|
|
247
278
|
- **Navigation**: `useMenu`, `useScrollPosition`
|
|
248
|
-
- **DOM Interaction**: `useMutationObserver`, `
|
|
279
|
+
- **DOM Interaction**: `useMutationObserver`, `useBodyClass`, `useIntersectionObserver`
|
|
280
|
+
- **Platform Detection**: `useIsMac`
|
|
249
281
|
- **Security**: `useRecaptchaV2`
|
|
250
282
|
- **SSR Safety**: `useMounted`, `useHydrated`, `useRemoveGAParams`
|
|
251
283
|
|
|
@@ -255,12 +287,14 @@ useRemoveGAParams(); // Cleans URL after GA processes linker attribution
|
|
|
255
287
|
|
|
256
288
|
- `useMounted`
|
|
257
289
|
- `useHydrated`
|
|
258
|
-
- `
|
|
290
|
+
- `useBodyClass`
|
|
259
291
|
- `useRemoveGAParams`
|
|
260
292
|
- `useMediaQuery`
|
|
261
293
|
- `useIsMobile`
|
|
294
|
+
- `useIsMac`
|
|
262
295
|
- `useViewport`
|
|
263
296
|
- `useScrollPosition`
|
|
297
|
+
- `useIntersectionObserver`
|
|
264
298
|
|
|
265
299
|
**Moderate** (State Management):
|
|
266
300
|
|
package/dist/index.d.ts
CHANGED
|
@@ -421,6 +421,91 @@ declare const formatBytes: (bytes: number, decimals?: number) => string;
|
|
|
421
421
|
*/
|
|
422
422
|
declare function useHydrated(): boolean;
|
|
423
423
|
|
|
424
|
+
/**
|
|
425
|
+
* Intersection observer hook for React components
|
|
426
|
+
* Efficiently observes visibility of DOM elements using a shared IntersectionObserver instance.
|
|
427
|
+
* Ideal for lazy loading, infinite scrolling, and animating elements when they enter the viewport.
|
|
428
|
+
*/
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Configuration options for the `useIntersectionObserver` hook.
|
|
432
|
+
*
|
|
433
|
+
* Extends the native `IntersectionObserverInit` options with an additional
|
|
434
|
+
* convenience flag for freezing the observer state.
|
|
435
|
+
*/
|
|
436
|
+
interface IntersectionObserverOptions extends IntersectionObserverInit {
|
|
437
|
+
/**
|
|
438
|
+
* When `true`, the hook stops observing once the element becomes visible
|
|
439
|
+
* and keeps returning `true` for subsequent renders.
|
|
440
|
+
*
|
|
441
|
+
* This is useful for one-time animations or lazy loading where you only
|
|
442
|
+
* care about the first time an element enters the viewport.
|
|
443
|
+
*/
|
|
444
|
+
freezeOnceVisible?: boolean;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* React hook that tracks whether a DOM element is currently intersecting the viewport.
|
|
448
|
+
*
|
|
449
|
+
* Features:
|
|
450
|
+
* - Uses a shared `IntersectionObserver` instance for better performance.
|
|
451
|
+
* - Supports custom `threshold`, `root`, and `rootMargin` options.
|
|
452
|
+
* - Optional `freezeOnceVisible` flag to stop observing after first intersection.
|
|
453
|
+
*
|
|
454
|
+
* @param elementRef - React ref pointing to the DOM element to observe.
|
|
455
|
+
* @param options - Optional observer configuration and behavior flags.
|
|
456
|
+
* @param options.threshold - Intersection threshold(s) for triggering visibility changes.
|
|
457
|
+
* @param options.root - Scrollable ancestor element to use as the viewport (defaults to browser viewport).
|
|
458
|
+
* @param options.rootMargin - Margin around the root, expressed in CSS units (e.g. `"0px 0px -20% 0px"`).
|
|
459
|
+
* @param options.freezeOnceVisible - When `true`, stops observing after the element first becomes visible.
|
|
460
|
+
*
|
|
461
|
+
* @returns `true` when the element is intersecting based on the given options, otherwise `false`.
|
|
462
|
+
*
|
|
463
|
+
* @example
|
|
464
|
+
* ```tsx
|
|
465
|
+
* const ref = React.useRef<HTMLDivElement | null>(null);
|
|
466
|
+
* const isVisible = useIntersectionObserver(ref, {
|
|
467
|
+
* threshold: 0.2,
|
|
468
|
+
* rootMargin: "0px 0px -10% 0px",
|
|
469
|
+
* freezeOnceVisible: true,
|
|
470
|
+
* });
|
|
471
|
+
*
|
|
472
|
+
* return (
|
|
473
|
+
* <div ref={ref} className={isVisible ? "animate-in" : "opacity-0"}>
|
|
474
|
+
* I will animate when I enter the viewport.
|
|
475
|
+
* </div>
|
|
476
|
+
* );
|
|
477
|
+
* ```
|
|
478
|
+
*/
|
|
479
|
+
declare function useIntersectionObserver(elementRef: React.RefObject<Element | null>, { threshold, root, rootMargin, freezeOnceVisible, }?: IntersectionObserverOptions): boolean;
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Platform detection hook for React components
|
|
483
|
+
* Determines whether the current user agent is running on a macOS device.
|
|
484
|
+
* Useful for rendering platform-specific keyboard shortcuts or UI variations.
|
|
485
|
+
*/
|
|
486
|
+
/**
|
|
487
|
+
* React hook that returns whether the current platform is macOS.
|
|
488
|
+
*
|
|
489
|
+
* This hook:
|
|
490
|
+
* - Runs only on the client (browser) side.
|
|
491
|
+
* - Checks `navigator.platform` and normalizes the value to uppercase.
|
|
492
|
+
* - Returns a boolean indicating if the platform contains `"MAC"`.
|
|
493
|
+
*
|
|
494
|
+
* @returns `true` if the current platform is macOS, otherwise `false`.
|
|
495
|
+
*
|
|
496
|
+
* @example
|
|
497
|
+
* ```tsx
|
|
498
|
+
* const isMac = useIsMac();
|
|
499
|
+
*
|
|
500
|
+
* return (
|
|
501
|
+
* <kbd>
|
|
502
|
+
* {isMac ? "⌘" : "Ctrl"} + K
|
|
503
|
+
* </kbd>
|
|
504
|
+
* );
|
|
505
|
+
* ```
|
|
506
|
+
*/
|
|
507
|
+
declare function useIsMac(): boolean;
|
|
508
|
+
|
|
424
509
|
/**
|
|
425
510
|
* Media query hook for responsive React components
|
|
426
511
|
* Provides real-time tracking of CSS media query matches
|
|
@@ -1108,4 +1193,4 @@ type ViewportDimensions = [number, number];
|
|
|
1108
1193
|
*/
|
|
1109
1194
|
declare const useViewport: () => ViewportDimensions;
|
|
1110
1195
|
|
|
1111
|
-
export { type FileMetadata, type FileUploadActions, type FileUploadOptions, type FileUploadState, type FileWithPreview, type GtagWindow, formatBytes, useAnalytics, useBodyClasses, useCopyToClipboard, useFileUpload, useHydrated, useIsMobile, useMediaQuery, useMenu, useMounted, useMutationObserver, useRecaptchaV2, useRemoveGAParams, useScrollPosition, useSliderInput, useViewport };
|
|
1196
|
+
export { type FileMetadata, type FileUploadActions, type FileUploadOptions, type FileUploadState, type FileWithPreview, type GtagWindow, formatBytes, useAnalytics, useBodyClasses, useCopyToClipboard, useFileUpload, useHydrated, useIntersectionObserver, useIsMac, useIsMobile, useMediaQuery, useMenu, useMounted, useMutationObserver, useRecaptchaV2, useRemoveGAParams, useScrollPosition, useSliderInput, useViewport };
|
package/dist/index.js
CHANGED
|
@@ -413,7 +413,7 @@ var formatBytes = (bytes, decimals = 2) => {
|
|
|
413
413
|
return Number.parseFloat((bytes / k ** i).toFixed(dm)) + sizes[i];
|
|
414
414
|
};
|
|
415
415
|
|
|
416
|
-
// src/use-hydrated.
|
|
416
|
+
// src/use-hydrated.ts
|
|
417
417
|
import { useSyncExternalStore } from "react";
|
|
418
418
|
function useHydrated() {
|
|
419
419
|
return useSyncExternalStore(
|
|
@@ -427,8 +427,88 @@ function subscribe() {
|
|
|
427
427
|
};
|
|
428
428
|
}
|
|
429
429
|
|
|
430
|
+
// src/use-intersection-observer.ts
|
|
431
|
+
import * as React2 from "react";
|
|
432
|
+
var SharedObserver = class {
|
|
433
|
+
observer = null;
|
|
434
|
+
callbacks = /* @__PURE__ */ new Map();
|
|
435
|
+
options;
|
|
436
|
+
constructor(options) {
|
|
437
|
+
this.options = options;
|
|
438
|
+
}
|
|
439
|
+
getObserver() {
|
|
440
|
+
if (!this.observer) {
|
|
441
|
+
this.observer = new IntersectionObserver((entries) => {
|
|
442
|
+
entries.forEach((entry) => {
|
|
443
|
+
const callback = this.callbacks.get(entry.target);
|
|
444
|
+
if (callback) {
|
|
445
|
+
callback(entry.isIntersecting);
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
}, this.options);
|
|
449
|
+
}
|
|
450
|
+
return this.observer;
|
|
451
|
+
}
|
|
452
|
+
observe(element, callback) {
|
|
453
|
+
this.callbacks.set(element, callback);
|
|
454
|
+
this.getObserver().observe(element);
|
|
455
|
+
}
|
|
456
|
+
unobserve(element) {
|
|
457
|
+
this.callbacks.delete(element);
|
|
458
|
+
this.observer?.unobserve(element);
|
|
459
|
+
}
|
|
460
|
+
disconnect() {
|
|
461
|
+
this.observer?.disconnect();
|
|
462
|
+
this.callbacks.clear();
|
|
463
|
+
this.observer = null;
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
var observers = /* @__PURE__ */ new Map();
|
|
467
|
+
function getSharedObserver(options) {
|
|
468
|
+
const key = JSON.stringify(options);
|
|
469
|
+
if (!observers.has(key)) {
|
|
470
|
+
observers.set(key, new SharedObserver(options));
|
|
471
|
+
}
|
|
472
|
+
return observers.get(key);
|
|
473
|
+
}
|
|
474
|
+
function useIntersectionObserver(elementRef, {
|
|
475
|
+
threshold = 0,
|
|
476
|
+
root = null,
|
|
477
|
+
rootMargin = "0%",
|
|
478
|
+
freezeOnceVisible = false
|
|
479
|
+
} = {}) {
|
|
480
|
+
const [isIntersecting, setIntersecting] = React2.useState(false);
|
|
481
|
+
const frozen = React2.useRef(false);
|
|
482
|
+
React2.useEffect(() => {
|
|
483
|
+
const element = elementRef?.current;
|
|
484
|
+
if (!element || freezeOnceVisible && frozen.current) return;
|
|
485
|
+
const observer = getSharedObserver({ threshold, root, rootMargin });
|
|
486
|
+
observer.observe(element, (intersecting) => {
|
|
487
|
+
setIntersecting(intersecting);
|
|
488
|
+
if (intersecting && freezeOnceVisible) {
|
|
489
|
+
frozen.current = true;
|
|
490
|
+
observer.unobserve(element);
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
return () => {
|
|
494
|
+
observer.unobserve(element);
|
|
495
|
+
};
|
|
496
|
+
}, [elementRef, threshold, root, rootMargin, freezeOnceVisible]);
|
|
497
|
+
return isIntersecting;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// src/use-is-mac.ts
|
|
501
|
+
import { useEffect as useEffect3, useState as useState4 } from "react";
|
|
502
|
+
function useIsMac() {
|
|
503
|
+
const [isMac, setIsMac] = useState4(true);
|
|
504
|
+
useEffect3(() => {
|
|
505
|
+
setIsMac(navigator.platform.toUpperCase().includes("MAC"));
|
|
506
|
+
}, []);
|
|
507
|
+
return isMac;
|
|
508
|
+
}
|
|
509
|
+
|
|
430
510
|
// src/use-media-query.ts
|
|
431
|
-
import { useEffect as
|
|
511
|
+
import { useEffect as useEffect4, useState as useState5 } from "react";
|
|
432
512
|
var getMatches = (query) => {
|
|
433
513
|
if (typeof window !== "undefined") {
|
|
434
514
|
return window.matchMedia(query).matches;
|
|
@@ -436,8 +516,8 @@ var getMatches = (query) => {
|
|
|
436
516
|
return false;
|
|
437
517
|
};
|
|
438
518
|
var useMediaQuery = (query) => {
|
|
439
|
-
const [matches, setMatches] =
|
|
440
|
-
|
|
519
|
+
const [matches, setMatches] = useState5(getMatches(query));
|
|
520
|
+
useEffect4(() => {
|
|
441
521
|
function handleChange() {
|
|
442
522
|
setMatches(getMatches(query));
|
|
443
523
|
}
|
|
@@ -543,12 +623,12 @@ var useMenu = (pathname) => {
|
|
|
543
623
|
};
|
|
544
624
|
};
|
|
545
625
|
|
|
546
|
-
// src/use-mobile.ts
|
|
547
|
-
import * as
|
|
626
|
+
// src/use-is-mobile.ts
|
|
627
|
+
import * as React3 from "react";
|
|
548
628
|
var DEFAULT_MOBILE_BREAKPOINT = 1024;
|
|
549
629
|
function useIsMobile(breakpoint = DEFAULT_MOBILE_BREAKPOINT) {
|
|
550
|
-
const [isMobile, setIsMobile] =
|
|
551
|
-
|
|
630
|
+
const [isMobile, setIsMobile] = React3.useState(void 0);
|
|
631
|
+
React3.useEffect(() => {
|
|
552
632
|
const mql = window.matchMedia(`(max-width: ${breakpoint - 1}px)`);
|
|
553
633
|
const onChange = () => {
|
|
554
634
|
setIsMobile(window.innerWidth < breakpoint);
|
|
@@ -561,17 +641,17 @@ function useIsMobile(breakpoint = DEFAULT_MOBILE_BREAKPOINT) {
|
|
|
561
641
|
}
|
|
562
642
|
|
|
563
643
|
// src/use-mounted.ts
|
|
564
|
-
import * as
|
|
644
|
+
import * as React4 from "react";
|
|
565
645
|
function useMounted() {
|
|
566
|
-
const [mounted, setMounted] =
|
|
567
|
-
|
|
646
|
+
const [mounted, setMounted] = React4.useState(false);
|
|
647
|
+
React4.useEffect(() => {
|
|
568
648
|
setMounted(true);
|
|
569
649
|
}, []);
|
|
570
650
|
return mounted;
|
|
571
651
|
}
|
|
572
652
|
|
|
573
653
|
// src/use-mutation-observer.ts
|
|
574
|
-
import * as
|
|
654
|
+
import * as React5 from "react";
|
|
575
655
|
var DEFAULT_OPTIONS = {
|
|
576
656
|
/** Watch for attribute changes */
|
|
577
657
|
attributes: true,
|
|
@@ -583,7 +663,7 @@ var DEFAULT_OPTIONS = {
|
|
|
583
663
|
subtree: true
|
|
584
664
|
};
|
|
585
665
|
var useMutationObserver = (ref, callback, options = DEFAULT_OPTIONS) => {
|
|
586
|
-
|
|
666
|
+
React5.useEffect(() => {
|
|
587
667
|
if (ref.current) {
|
|
588
668
|
const observer = new MutationObserver(callback);
|
|
589
669
|
observer.observe(ref.current, options);
|
|
@@ -595,7 +675,7 @@ var useMutationObserver = (ref, callback, options = DEFAULT_OPTIONS) => {
|
|
|
595
675
|
};
|
|
596
676
|
|
|
597
677
|
// src/use-recaptcha-v2.ts
|
|
598
|
-
import { useCallback as useCallback4, useEffect as
|
|
678
|
+
import { useCallback as useCallback4, useEffect as useEffect8, useRef as useRef3 } from "react";
|
|
599
679
|
var RECAPTCHA_SCRIPT_ID = "recaptcha-v2-script";
|
|
600
680
|
var scriptLoadPromise = null;
|
|
601
681
|
function loadRecaptchaScript() {
|
|
@@ -622,10 +702,10 @@ function loadRecaptchaScript() {
|
|
|
622
702
|
return scriptLoadPromise;
|
|
623
703
|
}
|
|
624
704
|
function useRecaptchaV2(siteKey) {
|
|
625
|
-
const widgetId =
|
|
626
|
-
const containerRef =
|
|
627
|
-
const isRendered =
|
|
628
|
-
const isInitializing =
|
|
705
|
+
const widgetId = useRef3(null);
|
|
706
|
+
const containerRef = useRef3(null);
|
|
707
|
+
const isRendered = useRef3(false);
|
|
708
|
+
const isInitializing = useRef3(false);
|
|
629
709
|
const initializeRecaptcha = useCallback4(async () => {
|
|
630
710
|
if (isInitializing.current) return;
|
|
631
711
|
if (!containerRef.current || !siteKey) return;
|
|
@@ -667,7 +747,7 @@ function useRecaptchaV2(siteKey) {
|
|
|
667
747
|
isInitializing.current = false;
|
|
668
748
|
}
|
|
669
749
|
}, [siteKey]);
|
|
670
|
-
|
|
750
|
+
useEffect8(() => {
|
|
671
751
|
if (containerRef.current) {
|
|
672
752
|
initializeRecaptcha();
|
|
673
753
|
}
|
|
@@ -719,9 +799,9 @@ function useRecaptchaV2(siteKey) {
|
|
|
719
799
|
}
|
|
720
800
|
|
|
721
801
|
// src/use-remove-ga-params.ts
|
|
722
|
-
import { useEffect as
|
|
802
|
+
import { useEffect as useEffect9 } from "react";
|
|
723
803
|
function useRemoveGAParams() {
|
|
724
|
-
|
|
804
|
+
useEffect9(() => {
|
|
725
805
|
const url = new URL(window.location.href);
|
|
726
806
|
if (url.searchParams.has("_gl")) {
|
|
727
807
|
const timer = setTimeout(() => {
|
|
@@ -734,10 +814,10 @@ function useRemoveGAParams() {
|
|
|
734
814
|
}
|
|
735
815
|
|
|
736
816
|
// src/use-scroll-position.ts
|
|
737
|
-
import { useEffect as
|
|
817
|
+
import { useEffect as useEffect10, useState as useState8 } from "react";
|
|
738
818
|
var useScrollPosition = ({ targetRef } = {}) => {
|
|
739
|
-
const [scrollPosition, setScrollPosition] =
|
|
740
|
-
|
|
819
|
+
const [scrollPosition, setScrollPosition] = useState8(0);
|
|
820
|
+
useEffect10(() => {
|
|
741
821
|
const target = targetRef?.current || document;
|
|
742
822
|
const scrollable = target === document ? window : target;
|
|
743
823
|
const updatePosition = () => {
|
|
@@ -754,10 +834,10 @@ var useScrollPosition = ({ targetRef } = {}) => {
|
|
|
754
834
|
};
|
|
755
835
|
|
|
756
836
|
// src/use-slider-input.ts
|
|
757
|
-
import { useCallback as useCallback5, useState as
|
|
837
|
+
import { useCallback as useCallback5, useState as useState9 } from "react";
|
|
758
838
|
function useSliderInput({ minValue, maxValue, initialValue }) {
|
|
759
|
-
const [sliderValues, setSliderValues] =
|
|
760
|
-
const [inputValues, setInputValues] =
|
|
839
|
+
const [sliderValues, setSliderValues] = useState9(initialValue);
|
|
840
|
+
const [inputValues, setInputValues] = useState9(initialValue);
|
|
761
841
|
const handleSliderChange = useCallback5((values) => {
|
|
762
842
|
setSliderValues(values);
|
|
763
843
|
setInputValues(values);
|
|
@@ -805,15 +885,15 @@ function useSliderInput({ minValue, maxValue, initialValue }) {
|
|
|
805
885
|
}
|
|
806
886
|
|
|
807
887
|
// src/use-viewport.ts
|
|
808
|
-
import { useEffect as
|
|
888
|
+
import { useEffect as useEffect11, useState as useState10 } from "react";
|
|
809
889
|
var useViewport = () => {
|
|
810
|
-
const [dimensions, setDimensions] =
|
|
890
|
+
const [dimensions, setDimensions] = useState10(() => {
|
|
811
891
|
if (typeof window !== "undefined") {
|
|
812
892
|
return [window.innerHeight, window.innerWidth];
|
|
813
893
|
}
|
|
814
894
|
return [0, 0];
|
|
815
895
|
});
|
|
816
|
-
|
|
896
|
+
useEffect11(() => {
|
|
817
897
|
const handleResize = () => {
|
|
818
898
|
setDimensions([window.innerHeight, window.innerWidth]);
|
|
819
899
|
};
|
|
@@ -832,6 +912,8 @@ export {
|
|
|
832
912
|
useCopyToClipboard,
|
|
833
913
|
useFileUpload,
|
|
834
914
|
useHydrated,
|
|
915
|
+
useIntersectionObserver,
|
|
916
|
+
useIsMac,
|
|
835
917
|
useIsMobile,
|
|
836
918
|
useMediaQuery,
|
|
837
919
|
useMenu,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pelatform/ui.hook",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Hook components of the Pelatform UI Library.",
|
|
5
5
|
"author": "Pelatform",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
],
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@pelatform/tsconfig": "^0.1.4",
|
|
42
|
-
"@types/node": "^25.
|
|
43
|
-
"@types/react": "^19.2.
|
|
44
|
-
"react": "^19.2.
|
|
42
|
+
"@types/node": "^25.3.3",
|
|
43
|
+
"@types/react": "^19.2.14",
|
|
44
|
+
"react": "^19.2.4",
|
|
45
45
|
"tsup": "^8.5.1",
|
|
46
46
|
"typescript": "^5.9.3"
|
|
47
47
|
},
|