@publikit/hooks 0.1.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +28 -5
- package/dist/index.cjs +113 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -74
- package/dist/index.d.ts +7 -74
- package/dist/index.js +111 -67
- package/dist/index.js.map +1 -1
- package/dist/use-infinite-scroll.cjs +53 -0
- package/dist/use-infinite-scroll.cjs.map +1 -0
- package/dist/use-infinite-scroll.d.cts +28 -0
- package/dist/use-infinite-scroll.d.ts +28 -0
- package/dist/use-infinite-scroll.js +51 -0
- package/dist/use-infinite-scroll.js.map +1 -0
- package/dist/use-live-timestamp.cjs +156 -0
- package/dist/use-live-timestamp.cjs.map +1 -0
- package/dist/use-live-timestamp.d.cts +6 -0
- package/dist/use-live-timestamp.d.ts +6 -0
- package/dist/use-live-timestamp.js +154 -0
- package/dist/use-live-timestamp.js.map +1 -0
- package/dist/use-media-query.cjs +24 -0
- package/dist/use-media-query.cjs.map +1 -0
- package/dist/use-media-query.d.cts +10 -0
- package/dist/use-media-query.d.ts +10 -0
- package/dist/use-media-query.js +22 -0
- package/dist/use-media-query.js.map +1 -0
- package/dist/use-mobile.cjs +37 -0
- package/dist/use-mobile.cjs.map +1 -0
- package/dist/use-mobile.d.cts +10 -0
- package/dist/use-mobile.d.ts +10 -0
- package/dist/use-mobile.js +34 -0
- package/dist/use-mobile.js.map +1 -0
- package/dist/use-pull-to-refresh.cjs +103 -0
- package/dist/use-pull-to-refresh.cjs.map +1 -0
- package/dist/use-pull-to-refresh.d.cts +22 -0
- package/dist/use-pull-to-refresh.d.ts +22 -0
- package/dist/use-pull-to-refresh.js +101 -0
- package/dist/use-pull-to-refresh.js.map +1 -0
- package/dist/use-scrollable-container.cjs +149 -0
- package/dist/use-scrollable-container.cjs.map +1 -0
- package/dist/use-scrollable-container.d.cts +38 -0
- package/dist/use-scrollable-container.d.ts +38 -0
- package/dist/use-scrollable-container.js +144 -0
- package/dist/use-scrollable-container.js.map +1 -0
- package/package.json +38 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,74 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
onLoadMore: () => void;
|
|
9
|
-
/** Whether more data can be loaded */
|
|
10
|
-
hasNextPage: boolean;
|
|
11
|
-
/** Whether currently fetching */
|
|
12
|
-
isFetchingNextPage: boolean;
|
|
13
|
-
/** Root margin for intersection observer */
|
|
14
|
-
rootMargin?: string;
|
|
15
|
-
/** Threshold for intersection */
|
|
16
|
-
threshold?: number;
|
|
17
|
-
/** Enable/disable the hook */
|
|
18
|
-
enabled?: boolean;
|
|
19
|
-
/** Custom root element for IntersectionObserver (e.g., ScrollArea viewport) */
|
|
20
|
-
root?: HTMLElement | null;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Hook for implementing infinite scroll using IntersectionObserver.
|
|
24
|
-
*/
|
|
25
|
-
declare function useInfiniteScroll({ onLoadMore, hasNextPage, isFetchingNextPage, rootMargin, threshold, enabled, root, }: UseInfiniteScrollOptions): {
|
|
26
|
-
Sentinel: ({ className }: {
|
|
27
|
-
className?: string;
|
|
28
|
-
}) => react.JSX.Element;
|
|
29
|
-
sentinelRef: react.Dispatch<react.SetStateAction<HTMLDivElement | null>>;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
interface UsePullToRefreshOptions {
|
|
33
|
-
onRefresh: () => Promise<void>;
|
|
34
|
-
threshold?: number;
|
|
35
|
-
resistance?: number;
|
|
36
|
-
disabled?: boolean;
|
|
37
|
-
}
|
|
38
|
-
interface UsePullToRefreshReturn {
|
|
39
|
-
pullDistance: number;
|
|
40
|
-
isRefreshing: boolean;
|
|
41
|
-
isPulling: boolean;
|
|
42
|
-
/** Callback ref — attach to the scrollable container element */
|
|
43
|
-
containerRef: Ref<HTMLDivElement | null>;
|
|
44
|
-
pullProgress: number;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Hook to add pull-to-refresh gesture for mobile devices.
|
|
48
|
-
*/
|
|
49
|
-
declare function usePullToRefresh({ onRefresh, threshold, resistance, disabled, }: UsePullToRefreshOptions): UsePullToRefreshReturn;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Live-updating timestamp hook with visibility-aware adaptive intervals.
|
|
53
|
-
*/
|
|
54
|
-
declare function useLiveTimestamp(date: Date, elementRef?: RefObject<Element>): Date;
|
|
55
|
-
|
|
56
|
-
declare const SCROLL_AMOUNT = 80;
|
|
57
|
-
declare const SCROLL_CHECK_DELAY = 100;
|
|
58
|
-
declare const DEFAULT_SCROLL_POSITION_KEY = "scrollable-container-position";
|
|
59
|
-
/**
|
|
60
|
-
* Scrollable container with position persistence and scroll button state.
|
|
61
|
-
*
|
|
62
|
-
* @param scrollPositionKey - sessionStorage key for scroll position
|
|
63
|
-
*/
|
|
64
|
-
declare function useScrollableContainer(scrollPositionKey?: string): {
|
|
65
|
-
containerRef: Ref<HTMLElement | null>;
|
|
66
|
-
canScrollUp: boolean;
|
|
67
|
-
canScrollDown: boolean;
|
|
68
|
-
scrollUp: () => void;
|
|
69
|
-
scrollDown: () => void;
|
|
70
|
-
checkScrollability: () => void;
|
|
71
|
-
preserveScrollPosition: () => void;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
export { DEFAULT_SCROLL_POSITION_KEY, SCROLL_AMOUNT, SCROLL_CHECK_DELAY, type UseInfiniteScrollOptions, type UsePullToRefreshOptions, type UsePullToRefreshReturn, useInfiniteScroll, useIsMobile, useLiveTimestamp, usePullToRefresh, useScrollableContainer };
|
|
1
|
+
export { UseMediaQueryOptions, useMediaQuery } from './use-media-query.js';
|
|
2
|
+
export { MOBILE_BREAKPOINT, UseIsMobileOptions, useIsMobile } from './use-mobile.js';
|
|
3
|
+
export { UseIntersectionLoadMoreOptions, UseIntersectionLoadMoreReturn, useIntersectionLoadMore } from './use-infinite-scroll.js';
|
|
4
|
+
export { UsePullToRefreshOptions, UsePullToRefreshReturn, usePullToRefresh } from './use-pull-to-refresh.js';
|
|
5
|
+
export { useLiveTimestamp } from './use-live-timestamp.js';
|
|
6
|
+
export { DEFAULT_SCROLL_POSITION_KEY, SCROLL_AMOUNT, SCROLL_CHECK_DELAY, ScrollAxis, ScrollStorageAdapter, UseScrollableContainerOptions, UseScrollableContainerReturn, useScrollableContainer } from './use-scrollable-container.js';
|
|
7
|
+
import 'react';
|
package/dist/index.js
CHANGED
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
import { useState, useEffect, useRef, useCallback, useMemo, useLayoutEffect } from 'react';
|
|
2
|
-
import { jsx } from 'react/jsx-runtime';
|
|
3
2
|
import { debounce } from '@publikit/utils';
|
|
4
3
|
|
|
5
|
-
// use-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
if (typeof window === "undefined") return false;
|
|
10
|
-
return window.matchMedia(MOBILE_MEDIA_QUERY).matches;
|
|
4
|
+
// use-media-query.ts
|
|
5
|
+
function getMatches(query, defaultValue) {
|
|
6
|
+
if (typeof window === "undefined") return defaultValue;
|
|
7
|
+
return window.matchMedia(query).matches;
|
|
11
8
|
}
|
|
12
|
-
function
|
|
13
|
-
const [
|
|
9
|
+
function useMediaQuery(query, { defaultValue = false } = {}) {
|
|
10
|
+
const [matches, setMatches] = useState(() => getMatches(query, defaultValue));
|
|
14
11
|
useEffect(() => {
|
|
15
|
-
const mql = window.matchMedia(
|
|
16
|
-
const onChange = () =>
|
|
12
|
+
const mql = window.matchMedia(query);
|
|
13
|
+
const onChange = () => setMatches(mql.matches);
|
|
14
|
+
setMatches(mql.matches);
|
|
17
15
|
mql.addEventListener("change", onChange);
|
|
18
16
|
return () => mql.removeEventListener("change", onChange);
|
|
19
|
-
}, []);
|
|
20
|
-
return
|
|
17
|
+
}, [query]);
|
|
18
|
+
return matches;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// use-mobile.ts
|
|
22
|
+
var MOBILE_BREAKPOINT = 768;
|
|
23
|
+
function getMobileMediaQuery(breakpoint) {
|
|
24
|
+
return `(max-width: ${breakpoint - 1}px)`;
|
|
21
25
|
}
|
|
22
|
-
function
|
|
26
|
+
function useIsMobile({
|
|
27
|
+
breakpoint = MOBILE_BREAKPOINT,
|
|
28
|
+
defaultValue = false
|
|
29
|
+
} = {}) {
|
|
30
|
+
return useMediaQuery(getMobileMediaQuery(breakpoint), { defaultValue });
|
|
31
|
+
}
|
|
32
|
+
function useIntersectionLoadMore({
|
|
23
33
|
onLoadMore,
|
|
24
34
|
hasNextPage,
|
|
25
35
|
isFetchingNextPage,
|
|
@@ -29,7 +39,7 @@ function useInfiniteScroll({
|
|
|
29
39
|
root
|
|
30
40
|
}) {
|
|
31
41
|
const observerRef = useRef(null);
|
|
32
|
-
const [
|
|
42
|
+
const [sentinelElement, setSentinelElement] = useState(null);
|
|
33
43
|
const handleIntersection = useCallback(
|
|
34
44
|
(entries) => {
|
|
35
45
|
const entry = entries[0];
|
|
@@ -50,30 +60,17 @@ function useInfiniteScroll({
|
|
|
50
60
|
rootMargin,
|
|
51
61
|
threshold
|
|
52
62
|
});
|
|
53
|
-
if (
|
|
54
|
-
observerRef.current.observe(
|
|
63
|
+
if (sentinelElement) {
|
|
64
|
+
observerRef.current.observe(sentinelElement);
|
|
55
65
|
}
|
|
56
66
|
return () => {
|
|
57
67
|
if (observerRef.current) {
|
|
58
68
|
observerRef.current.disconnect();
|
|
59
69
|
}
|
|
60
70
|
};
|
|
61
|
-
}, [
|
|
62
|
-
const Sentinel = useCallback(
|
|
63
|
-
({ className }) => /* @__PURE__ */ jsx(
|
|
64
|
-
"div",
|
|
65
|
-
{
|
|
66
|
-
ref: setSentinelRef,
|
|
67
|
-
className,
|
|
68
|
-
"aria-hidden": "true",
|
|
69
|
-
style: { height: "1px" }
|
|
70
|
-
}
|
|
71
|
-
),
|
|
72
|
-
[]
|
|
73
|
-
);
|
|
71
|
+
}, [sentinelElement, handleIntersection, rootMargin, threshold, enabled, root]);
|
|
74
72
|
return {
|
|
75
|
-
|
|
76
|
-
sentinelRef: setSentinelRef
|
|
73
|
+
sentinelRef: setSentinelElement
|
|
77
74
|
};
|
|
78
75
|
}
|
|
79
76
|
function usePullToRefresh({
|
|
@@ -177,6 +174,7 @@ var rafId = null;
|
|
|
177
174
|
var intervalId = null;
|
|
178
175
|
var isTabVisible = true;
|
|
179
176
|
var pendingUpdate = false;
|
|
177
|
+
var visibilityListenerAttached = false;
|
|
180
178
|
function getUpdateInterval(date) {
|
|
181
179
|
const ageMs = Date.now() - date.getTime();
|
|
182
180
|
const ageMinutes = ageMs / 6e4;
|
|
@@ -259,7 +257,9 @@ function restartTimer() {
|
|
|
259
257
|
stopTimer();
|
|
260
258
|
ensureTimerRunning();
|
|
261
259
|
}
|
|
262
|
-
|
|
260
|
+
function ensureVisibilityListener() {
|
|
261
|
+
if (visibilityListenerAttached) return;
|
|
262
|
+
if (typeof document === "undefined") return;
|
|
263
263
|
document.addEventListener("visibilitychange", () => {
|
|
264
264
|
isTabVisible = !document.hidden;
|
|
265
265
|
if (isTabVisible && subscribers.size > 0) {
|
|
@@ -269,12 +269,15 @@ if (typeof window !== "undefined") {
|
|
|
269
269
|
stopTimer();
|
|
270
270
|
}
|
|
271
271
|
});
|
|
272
|
+
visibilityListenerAttached = true;
|
|
272
273
|
}
|
|
273
|
-
function useLiveTimestamp(date,
|
|
274
|
+
function useLiveTimestamp(date, element) {
|
|
274
275
|
const [, setTick] = useState(0);
|
|
275
276
|
const subscriberIdRef = useRef(/* @__PURE__ */ Symbol("live-timestamp-subscriber"));
|
|
276
277
|
useEffect(() => {
|
|
278
|
+
ensureVisibilityListener();
|
|
277
279
|
const subscriberId = subscriberIdRef.current;
|
|
280
|
+
const observedElement = element ?? null;
|
|
278
281
|
const callback = () => {
|
|
279
282
|
const isVisible = visibilityMap.get(subscriberId) ?? true;
|
|
280
283
|
if (isVisible) {
|
|
@@ -282,9 +285,9 @@ function useLiveTimestamp(date, elementRef) {
|
|
|
282
285
|
}
|
|
283
286
|
};
|
|
284
287
|
let observer = null;
|
|
285
|
-
if (
|
|
288
|
+
if (observedElement) {
|
|
286
289
|
observer = createObserver(subscriberId, callback);
|
|
287
|
-
observer.observe(
|
|
290
|
+
observer.observe(observedElement);
|
|
288
291
|
visibilityMap.set(subscriberId, false);
|
|
289
292
|
} else {
|
|
290
293
|
visibilityMap.set(subscriberId, true);
|
|
@@ -308,49 +311,82 @@ function useLiveTimestamp(date, elementRef) {
|
|
|
308
311
|
restartTimer();
|
|
309
312
|
}
|
|
310
313
|
};
|
|
311
|
-
}, [date.getTime(),
|
|
314
|
+
}, [date.getTime(), element]);
|
|
312
315
|
return globalNow;
|
|
313
316
|
}
|
|
314
317
|
var SCROLL_AMOUNT = 80;
|
|
315
318
|
var SCROLL_CHECK_DELAY = 100;
|
|
316
319
|
var DEFAULT_SCROLL_POSITION_KEY = "scrollable-container-position";
|
|
317
320
|
var STORAGE_DEBOUNCE_MS = 150;
|
|
318
|
-
function
|
|
321
|
+
function resolveStorage(storage) {
|
|
322
|
+
if (storage === false) return null;
|
|
323
|
+
if (storage) return storage;
|
|
324
|
+
if (typeof sessionStorage === "undefined") return null;
|
|
325
|
+
return sessionStorage;
|
|
326
|
+
}
|
|
327
|
+
function getScrollPosition(element, axis) {
|
|
328
|
+
return axis === "x" ? element.scrollLeft : element.scrollTop;
|
|
329
|
+
}
|
|
330
|
+
function setScrollPosition(element, axis, position) {
|
|
331
|
+
if (axis === "x") {
|
|
332
|
+
element.scrollLeft = position;
|
|
333
|
+
} else {
|
|
334
|
+
element.scrollTop = position;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function getScrollSize(element, axis) {
|
|
338
|
+
return axis === "x" ? element.scrollWidth : element.scrollHeight;
|
|
339
|
+
}
|
|
340
|
+
function getClientSize(element, axis) {
|
|
341
|
+
return axis === "x" ? element.clientWidth : element.clientHeight;
|
|
342
|
+
}
|
|
343
|
+
function useScrollableContainer({
|
|
344
|
+
scrollPositionKey,
|
|
345
|
+
axis = "y",
|
|
346
|
+
scrollAmount = SCROLL_AMOUNT,
|
|
347
|
+
checkDelay = SCROLL_CHECK_DELAY,
|
|
348
|
+
storage
|
|
349
|
+
}) {
|
|
319
350
|
const containerRef = useRef(null);
|
|
320
351
|
const [containerElement, setContainerElement] = useState(null);
|
|
321
352
|
const isRestoringRef = useRef(false);
|
|
322
|
-
const [
|
|
323
|
-
const [
|
|
353
|
+
const [canScrollBackward, setCanScrollBackward] = useState(false);
|
|
354
|
+
const [canScrollForward, setCanScrollForward] = useState(false);
|
|
355
|
+
const storageAdapter = useMemo(() => resolveStorage(storage), [storage]);
|
|
324
356
|
const assignContainerRef = useCallback((node) => {
|
|
325
357
|
containerRef.current = node;
|
|
326
358
|
setContainerElement(node);
|
|
327
359
|
}, []);
|
|
328
360
|
const saveToStorage = useMemo(
|
|
329
361
|
() => debounce((position) => {
|
|
362
|
+
if (!storageAdapter) return;
|
|
330
363
|
try {
|
|
331
|
-
|
|
364
|
+
storageAdapter.setItem(scrollPositionKey, String(position));
|
|
332
365
|
} catch {
|
|
333
366
|
}
|
|
334
367
|
}, STORAGE_DEBOUNCE_MS),
|
|
335
|
-
[scrollPositionKey]
|
|
368
|
+
[scrollPositionKey, storageAdapter]
|
|
336
369
|
);
|
|
337
370
|
const checkScrollability = useCallback(() => {
|
|
338
371
|
if (!containerRef.current || isRestoringRef.current) return;
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
372
|
+
const scrollPosition = getScrollPosition(containerRef.current, axis);
|
|
373
|
+
const scrollSize = getScrollSize(containerRef.current, axis);
|
|
374
|
+
const clientSize = getClientSize(containerRef.current, axis);
|
|
375
|
+
saveToStorage(scrollPosition);
|
|
376
|
+
setCanScrollBackward(scrollPosition > 0);
|
|
377
|
+
setCanScrollForward(scrollPosition < scrollSize - clientSize - 1);
|
|
378
|
+
}, [axis, saveToStorage]);
|
|
344
379
|
useLayoutEffect(() => {
|
|
345
380
|
if (!containerElement || isRestoringRef.current) return;
|
|
381
|
+
if (!storageAdapter) return;
|
|
346
382
|
try {
|
|
347
|
-
const savedPosition =
|
|
383
|
+
const savedPosition = storageAdapter.getItem(scrollPositionKey);
|
|
348
384
|
if (savedPosition !== null) {
|
|
349
385
|
const position = parseInt(savedPosition, 10);
|
|
350
|
-
const currentPosition = containerElement
|
|
386
|
+
const currentPosition = getScrollPosition(containerElement, axis);
|
|
351
387
|
if (position > 0 && !isNaN(position) && Math.abs(currentPosition - position) > 1) {
|
|
352
388
|
isRestoringRef.current = true;
|
|
353
|
-
containerElement
|
|
389
|
+
setScrollPosition(containerElement, axis, position);
|
|
354
390
|
requestAnimationFrame(() => {
|
|
355
391
|
isRestoringRef.current = false;
|
|
356
392
|
checkScrollability();
|
|
@@ -359,15 +395,18 @@ function useScrollableContainer(scrollPositionKey = DEFAULT_SCROLL_POSITION_KEY)
|
|
|
359
395
|
}
|
|
360
396
|
} catch {
|
|
361
397
|
}
|
|
362
|
-
}, [containerElement, scrollPositionKey, checkScrollability]);
|
|
398
|
+
}, [axis, containerElement, scrollPositionKey, checkScrollability, storageAdapter]);
|
|
363
399
|
const preserveScrollPosition = useCallback(() => {
|
|
364
|
-
if (containerRef.current) {
|
|
400
|
+
if (containerRef.current && storageAdapter) {
|
|
365
401
|
try {
|
|
366
|
-
|
|
402
|
+
storageAdapter.setItem(
|
|
403
|
+
scrollPositionKey,
|
|
404
|
+
String(getScrollPosition(containerRef.current, axis))
|
|
405
|
+
);
|
|
367
406
|
} catch {
|
|
368
407
|
}
|
|
369
408
|
}
|
|
370
|
-
}, [scrollPositionKey]);
|
|
409
|
+
}, [axis, scrollPositionKey, storageAdapter]);
|
|
371
410
|
useEffect(() => {
|
|
372
411
|
return () => {
|
|
373
412
|
saveToStorage.cancel();
|
|
@@ -375,7 +414,7 @@ function useScrollableContainer(scrollPositionKey = DEFAULT_SCROLL_POSITION_KEY)
|
|
|
375
414
|
}, [saveToStorage]);
|
|
376
415
|
useEffect(() => {
|
|
377
416
|
if (!containerElement) return;
|
|
378
|
-
const timeoutId = setTimeout(checkScrollability,
|
|
417
|
+
const timeoutId = setTimeout(checkScrollability, checkDelay);
|
|
379
418
|
const handleScroll = () => {
|
|
380
419
|
checkScrollability();
|
|
381
420
|
};
|
|
@@ -389,24 +428,29 @@ function useScrollableContainer(scrollPositionKey = DEFAULT_SCROLL_POSITION_KEY)
|
|
|
389
428
|
window.removeEventListener("resize", checkScrollability);
|
|
390
429
|
resizeObserver.disconnect();
|
|
391
430
|
};
|
|
392
|
-
}, [containerElement, checkScrollability]);
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
}, []);
|
|
431
|
+
}, [checkDelay, containerElement, checkScrollability]);
|
|
432
|
+
const scrollBackward = useCallback(() => {
|
|
433
|
+
const delta = -scrollAmount;
|
|
434
|
+
containerRef.current?.scrollBy(
|
|
435
|
+
axis === "x" ? { left: delta, behavior: "smooth" } : { top: delta, behavior: "smooth" }
|
|
436
|
+
);
|
|
437
|
+
}, [axis, scrollAmount]);
|
|
438
|
+
const scrollForward = useCallback(() => {
|
|
439
|
+
containerRef.current?.scrollBy(
|
|
440
|
+
axis === "x" ? { left: scrollAmount, behavior: "smooth" } : { top: scrollAmount, behavior: "smooth" }
|
|
441
|
+
);
|
|
442
|
+
}, [axis, scrollAmount]);
|
|
399
443
|
return {
|
|
400
444
|
containerRef: assignContainerRef,
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
445
|
+
canScrollBackward,
|
|
446
|
+
canScrollForward,
|
|
447
|
+
scrollBackward,
|
|
448
|
+
scrollForward,
|
|
405
449
|
checkScrollability,
|
|
406
450
|
preserveScrollPosition
|
|
407
451
|
};
|
|
408
452
|
}
|
|
409
453
|
|
|
410
|
-
export { DEFAULT_SCROLL_POSITION_KEY, SCROLL_AMOUNT, SCROLL_CHECK_DELAY,
|
|
454
|
+
export { DEFAULT_SCROLL_POSITION_KEY, MOBILE_BREAKPOINT, SCROLL_AMOUNT, SCROLL_CHECK_DELAY, useIntersectionLoadMore, useIsMobile, useLiveTimestamp, useMediaQuery, usePullToRefresh, useScrollableContainer };
|
|
411
455
|
//# sourceMappingURL=index.js.map
|
|
412
456
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../use-mobile.tsx","../use-infinite-scroll.tsx","../use-pull-to-refresh.ts","../use-live-timestamp.ts","../use-scrollable-container.ts"],"names":["useState","useEffect","useRef","useCallback"],"mappings":"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","file":"index.js","sourcesContent":["import { useEffect, useState } from 'react';\r\n\r\nconst MOBILE_BREAKPOINT = 768;\r\nconst MOBILE_MEDIA_QUERY = `(max-width: ${MOBILE_BREAKPOINT - 1}px)` as const;\r\n\r\nfunction getIsMobile(): boolean {\r\n if (typeof window === 'undefined') return false;\r\n return window.matchMedia(MOBILE_MEDIA_QUERY).matches;\r\n}\r\n\r\nexport function useIsMobile(): boolean {\r\n const [isMobile, setIsMobile] = useState<boolean>(getIsMobile);\r\n\r\n useEffect(() => {\r\n const mql = window.matchMedia(MOBILE_MEDIA_QUERY);\r\n const onChange = () => setIsMobile(mql.matches);\r\n mql.addEventListener('change', onChange);\r\n return () => mql.removeEventListener('change', onChange);\r\n }, []);\r\n\r\n return isMobile;\r\n}\r\n","import { useEffect, useRef, useCallback, useState } from 'react';\r\n\r\nexport interface UseInfiniteScrollOptions {\r\n /** Callback when intersection occurs */\r\n onLoadMore: () => void;\r\n /** Whether more data can be loaded */\r\n hasNextPage: boolean;\r\n /** Whether currently fetching */\r\n isFetchingNextPage: boolean;\r\n /** Root margin for intersection observer */\r\n rootMargin?: string;\r\n /** Threshold for intersection */\r\n threshold?: number;\r\n /** Enable/disable the hook */\r\n enabled?: boolean;\r\n /** Custom root element for IntersectionObserver (e.g., ScrollArea viewport) */\r\n root?: HTMLElement | null;\r\n}\r\n\r\n/**\r\n * Hook for implementing infinite scroll using IntersectionObserver.\r\n */\r\nexport function useInfiniteScroll({\r\n onLoadMore,\r\n hasNextPage,\r\n isFetchingNextPage,\r\n rootMargin = '200px',\r\n threshold = 0,\r\n enabled = true,\r\n root,\r\n}: UseInfiniteScrollOptions) {\r\n const observerRef = useRef<IntersectionObserver | null>(null);\r\n const [sentinelRef, setSentinelRef] = useState<HTMLDivElement | null>(null);\r\n\r\n const handleIntersection = useCallback(\r\n (entries: IntersectionObserverEntry[]) => {\r\n const entry = entries[0];\r\n if (!entry?.isIntersecting) return;\r\n if (hasNextPage && !isFetchingNextPage && enabled) {\r\n onLoadMore();\r\n }\r\n },\r\n [onLoadMore, hasNextPage, isFetchingNextPage, enabled]\r\n );\r\n\r\n useEffect(() => {\r\n if (!enabled) return;\r\n\r\n if (observerRef.current) {\r\n observerRef.current.disconnect();\r\n }\r\n\r\n observerRef.current = new IntersectionObserver(handleIntersection, {\r\n root: root || null,\r\n rootMargin,\r\n threshold,\r\n });\r\n\r\n if (sentinelRef) {\r\n observerRef.current.observe(sentinelRef);\r\n }\r\n\r\n return () => {\r\n if (observerRef.current) {\r\n observerRef.current.disconnect();\r\n }\r\n };\r\n }, [sentinelRef, handleIntersection, rootMargin, threshold, enabled, root]);\r\n\r\n const Sentinel = useCallback(\r\n ({ className }: { className?: string }) => (\r\n <div\r\n ref={setSentinelRef}\r\n className={className}\r\n aria-hidden=\"true\"\r\n style={{ height: '1px' }}\r\n />\r\n ),\r\n []\r\n );\r\n\r\n return {\r\n Sentinel,\r\n sentinelRef: setSentinelRef,\r\n };\r\n}\r\n","import { useState, useEffect, useRef, useCallback, type Ref } from 'react';\r\n\r\nexport interface UsePullToRefreshOptions {\r\n onRefresh: () => Promise<void>;\r\n threshold?: number;\r\n resistance?: number;\r\n disabled?: boolean;\r\n}\r\n\r\nexport interface UsePullToRefreshReturn {\r\n pullDistance: number;\r\n isRefreshing: boolean;\r\n isPulling: boolean;\r\n /** Callback ref — attach to the scrollable container element */\r\n containerRef: Ref<HTMLDivElement | null>;\r\n pullProgress: number;\r\n}\r\n\r\n/**\r\n * Hook to add pull-to-refresh gesture for mobile devices.\r\n */\r\nexport function usePullToRefresh({\r\n onRefresh,\r\n threshold = 80,\r\n resistance = 2.5,\r\n disabled = false,\r\n}: UsePullToRefreshOptions): UsePullToRefreshReturn {\r\n const [pullDistance, setPullDistance] = useState(0);\r\n const [isRefreshing, setIsRefreshing] = useState(false);\r\n const [isPulling, setIsPulling] = useState(false);\r\n const [containerElement, setContainerElement] = useState<HTMLDivElement | null>(null);\r\n\r\n const containerRef = useRef<HTMLDivElement | null>(null);\r\n const startYRef = useRef<number | null>(null);\r\n const currentYRef = useRef<number | null>(null);\r\n const pullDistanceRef = useRef(0);\r\n\r\n pullDistanceRef.current = pullDistance;\r\n\r\n const assignContainerRef = useCallback((node: HTMLDivElement | null) => {\r\n containerRef.current = node;\r\n setContainerElement(node);\r\n }, []);\r\n\r\n const handleTouchStart = useCallback(\r\n (e: TouchEvent) => {\r\n if (disabled || isRefreshing) return;\r\n\r\n const container = containerRef.current;\r\n if (!container) return;\r\n\r\n if (container.scrollTop <= 0) {\r\n startYRef.current = e.touches[0].clientY;\r\n setIsPulling(true);\r\n }\r\n },\r\n [disabled, isRefreshing]\r\n );\r\n\r\n const handleTouchMove = useCallback(\r\n (e: TouchEvent) => {\r\n if (disabled || isRefreshing || startYRef.current === null) return;\r\n\r\n const container = containerRef.current;\r\n if (!container) return;\r\n\r\n if (container.scrollTop > 0) {\r\n startYRef.current = null;\r\n setPullDistance(0);\r\n setIsPulling(false);\r\n return;\r\n }\r\n\r\n currentYRef.current = e.touches[0].clientY;\r\n const diff = currentYRef.current - startYRef.current;\r\n\r\n if (diff > 0) {\r\n const distance = Math.min(diff / resistance, threshold * 1.5);\r\n setPullDistance(distance);\r\n\r\n if (distance > 5) {\r\n e.preventDefault();\r\n }\r\n }\r\n },\r\n [disabled, isRefreshing, resistance, threshold]\r\n );\r\n\r\n const handleTouchEnd = useCallback(async () => {\r\n if (disabled || isRefreshing) return;\r\n\r\n startYRef.current = null;\r\n currentYRef.current = null;\r\n setIsPulling(false);\r\n\r\n const distance = pullDistanceRef.current;\r\n\r\n if (distance >= threshold) {\r\n setIsRefreshing(true);\r\n setPullDistance(threshold * 0.5);\r\n\r\n try {\r\n await onRefresh();\r\n } finally {\r\n setIsRefreshing(false);\r\n setPullDistance(0);\r\n }\r\n } else {\r\n setPullDistance(0);\r\n }\r\n }, [disabled, isRefreshing, threshold, onRefresh]);\r\n\r\n useEffect(() => {\r\n const container = containerElement;\r\n if (!container) return;\r\n\r\n container.addEventListener('touchstart', handleTouchStart, { passive: true });\r\n container.addEventListener('touchmove', handleTouchMove, { passive: false });\r\n container.addEventListener('touchend', handleTouchEnd, { passive: true });\r\n\r\n return () => {\r\n container.removeEventListener('touchstart', handleTouchStart);\r\n container.removeEventListener('touchmove', handleTouchMove);\r\n container.removeEventListener('touchend', handleTouchEnd);\r\n };\r\n }, [containerElement, handleTouchStart, handleTouchMove, handleTouchEnd]);\r\n\r\n const pullProgress = Math.min(pullDistance / threshold, 1);\r\n\r\n return {\r\n pullDistance,\r\n isRefreshing,\r\n isPulling,\r\n containerRef: assignContainerRef,\r\n pullProgress,\r\n };\r\n}\r\n","import { useState, useEffect, useRef, type RefObject } from 'react';\r\n\r\nlet globalNow = new Date();\r\n\r\ntype SubscriberId = symbol;\r\n\r\ntype Subscriber = {\r\n callback: () => void;\r\n date: Date;\r\n observer: IntersectionObserver | null;\r\n};\r\n\r\nconst subscribers = new Map<SubscriberId, Subscriber>();\r\nconst visibilityMap = new Map<SubscriberId, boolean>();\r\n\r\nlet rafId: number | null = null;\r\nlet intervalId: ReturnType<typeof setTimeout> | null = null;\r\nlet isTabVisible = true;\r\nlet pendingUpdate = false;\r\n\r\nfunction getUpdateInterval(date: Date): number {\r\n const ageMs = Date.now() - date.getTime();\r\n const ageMinutes = ageMs / 60000;\r\n\r\n if (ageMinutes < 1) return 10000;\r\n if (ageMinutes < 60) return 30000;\r\n if (ageMinutes < 1440) return 300000;\r\n return 3600000;\r\n}\r\n\r\nfunction getMinInterval(): number {\r\n if (subscribers.size === 0) return 30000;\r\n const intervals = Array.from(subscribers.values()).map((sub) => getUpdateInterval(sub.date));\r\n return Math.max(Math.min(...intervals), 10000);\r\n}\r\n\r\nconst createObserver = (\r\n subscriberId: SubscriberId,\r\n callback: () => void\r\n): IntersectionObserver => {\r\n return new IntersectionObserver(\r\n (entries) => {\r\n entries.forEach((entry) => {\r\n visibilityMap.set(subscriberId, entry.isIntersecting);\r\n if (entry.isIntersecting) {\r\n callback();\r\n }\r\n });\r\n },\r\n {\r\n root: null,\r\n rootMargin: '50px',\r\n threshold: 0.01,\r\n }\r\n );\r\n};\r\n\r\nfunction updateVisibleSubscribers() {\r\n if (!isTabVisible || subscribers.size === 0) {\r\n pendingUpdate = false;\r\n return;\r\n }\r\n\r\n globalNow = new Date();\r\n\r\n if (!pendingUpdate) {\r\n pendingUpdate = true;\r\n rafId = requestAnimationFrame(() => {\r\n subscribers.forEach((subscriber, subscriberId) => {\r\n const isVisible = visibilityMap.get(subscriberId) ?? true;\r\n if (isVisible) {\r\n subscriber.callback();\r\n }\r\n });\r\n pendingUpdate = false;\r\n });\r\n }\r\n}\r\n\r\nfunction stopTimer() {\r\n if (intervalId) {\r\n clearTimeout(intervalId);\r\n intervalId = null;\r\n }\r\n if (rafId) {\r\n cancelAnimationFrame(rafId);\r\n rafId = null;\r\n }\r\n pendingUpdate = false;\r\n}\r\n\r\nfunction scheduleNextTick() {\r\n if (intervalId) {\r\n clearTimeout(intervalId);\r\n intervalId = null;\r\n }\r\n\r\n if (subscribers.size === 0 || !isTabVisible) {\r\n return;\r\n }\r\n\r\n intervalId = setTimeout(() => {\r\n updateVisibleSubscribers();\r\n scheduleNextTick();\r\n }, getMinInterval());\r\n}\r\n\r\nfunction ensureTimerRunning() {\r\n if (!intervalId && subscribers.size > 0 && isTabVisible) {\r\n scheduleNextTick();\r\n }\r\n}\r\n\r\nfunction restartTimer() {\r\n stopTimer();\r\n ensureTimerRunning();\r\n}\r\n\r\nif (typeof window !== 'undefined') {\r\n document.addEventListener('visibilitychange', () => {\r\n isTabVisible = !document.hidden;\r\n if (isTabVisible && subscribers.size > 0) {\r\n updateVisibleSubscribers();\r\n ensureTimerRunning();\r\n } else if (!isTabVisible) {\r\n stopTimer();\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Live-updating timestamp hook with visibility-aware adaptive intervals.\r\n */\r\nexport function useLiveTimestamp(date: Date, elementRef?: RefObject<Element>): Date {\r\n const [, setTick] = useState(0);\r\n const subscriberIdRef = useRef<SubscriberId>(Symbol('live-timestamp-subscriber'));\r\n\r\n useEffect(() => {\r\n const subscriberId = subscriberIdRef.current;\r\n\r\n const callback = () => {\r\n const isVisible = visibilityMap.get(subscriberId) ?? true;\r\n if (isVisible) {\r\n setTick((t) => t + 1);\r\n }\r\n };\r\n\r\n let observer: IntersectionObserver | null = null;\r\n if (elementRef?.current) {\r\n observer = createObserver(subscriberId, callback);\r\n observer.observe(elementRef.current);\r\n visibilityMap.set(subscriberId, false);\r\n } else {\r\n visibilityMap.set(subscriberId, true);\r\n }\r\n\r\n subscribers.set(subscriberId, {\r\n callback,\r\n date,\r\n observer,\r\n });\r\n\r\n ensureTimerRunning();\r\n\r\n return () => {\r\n const subscriber = subscribers.get(subscriberId);\r\n if (subscriber?.observer) {\r\n subscriber.observer.disconnect();\r\n }\r\n subscribers.delete(subscriberId);\r\n visibilityMap.delete(subscriberId);\r\n\r\n if (subscribers.size === 0) {\r\n stopTimer();\r\n } else {\r\n restartTimer();\r\n }\r\n };\r\n // Key on the timestamp value, not the Date identity, so callers passing a\r\n // freshly constructed Date each render don't trigger a re-subscribe.\r\n }, [date.getTime(), elementRef]);\r\n\r\n return globalNow;\r\n}\r\n","import { useRef, useEffect, useLayoutEffect, useState, useCallback, useMemo, type Ref } from 'react';\r\nimport { debounce } from '@publikit/utils';\r\n\r\nexport const SCROLL_AMOUNT = 80;\r\nexport const SCROLL_CHECK_DELAY = 100;\r\nexport const DEFAULT_SCROLL_POSITION_KEY = 'scrollable-container-position';\r\nconst STORAGE_DEBOUNCE_MS = 150;\r\n\r\n/**\r\n * Scrollable container with position persistence and scroll button state.\r\n *\r\n * @param scrollPositionKey - sessionStorage key for scroll position\r\n */\r\nexport function useScrollableContainer(\r\n scrollPositionKey: string = DEFAULT_SCROLL_POSITION_KEY\r\n) {\r\n const containerRef = useRef<HTMLElement | null>(null);\r\n const [containerElement, setContainerElement] = useState<HTMLElement | null>(null);\r\n const isRestoringRef = useRef<boolean>(false);\r\n const [canScrollUp, setCanScrollUp] = useState(false);\r\n const [canScrollDown, setCanScrollDown] = useState(false);\r\n\r\n const assignContainerRef = useCallback((node: HTMLElement | null) => {\r\n containerRef.current = node;\r\n setContainerElement(node);\r\n }, []);\r\n\r\n const saveToStorage = useMemo(\r\n () =>\r\n debounce((position: number) => {\r\n try {\r\n sessionStorage.setItem(scrollPositionKey, String(position));\r\n } catch {\r\n // Ignore storage errors\r\n }\r\n }, STORAGE_DEBOUNCE_MS),\r\n [scrollPositionKey]\r\n );\r\n\r\n const checkScrollability = useCallback(() => {\r\n if (!containerRef.current || isRestoringRef.current) return;\r\n const { scrollTop, scrollHeight, clientHeight } = containerRef.current;\r\n\r\n saveToStorage(scrollTop);\r\n\r\n setCanScrollUp(scrollTop > 0);\r\n setCanScrollDown(scrollTop < scrollHeight - clientHeight - 1);\r\n }, [saveToStorage]);\r\n\r\n useLayoutEffect(() => {\r\n if (!containerElement || isRestoringRef.current) return;\r\n\r\n try {\r\n const savedPosition = sessionStorage.getItem(scrollPositionKey);\r\n if (savedPosition !== null) {\r\n const position = parseInt(savedPosition, 10);\r\n const currentPosition = containerElement.scrollTop;\r\n\r\n if (position > 0 && !isNaN(position) && Math.abs(currentPosition - position) > 1) {\r\n isRestoringRef.current = true;\r\n containerElement.scrollTop = position;\r\n requestAnimationFrame(() => {\r\n isRestoringRef.current = false;\r\n checkScrollability();\r\n });\r\n }\r\n }\r\n } catch {\r\n // Ignore storage errors\r\n }\r\n }, [containerElement, scrollPositionKey, checkScrollability]);\r\n\r\n const preserveScrollPosition = useCallback(() => {\r\n if (containerRef.current) {\r\n try {\r\n sessionStorage.setItem(scrollPositionKey, String(containerRef.current.scrollTop));\r\n } catch {\r\n // Ignore storage errors\r\n }\r\n }\r\n }, [scrollPositionKey]);\r\n\r\n useEffect(() => {\r\n return () => {\r\n saveToStorage.cancel();\r\n };\r\n }, [saveToStorage]);\r\n\r\n useEffect(() => {\r\n if (!containerElement) return;\r\n\r\n const timeoutId = setTimeout(checkScrollability, SCROLL_CHECK_DELAY);\r\n\r\n const handleScroll = () => {\r\n checkScrollability();\r\n };\r\n\r\n containerElement.addEventListener('scroll', handleScroll, { passive: true });\r\n window.addEventListener('resize', checkScrollability);\r\n\r\n const resizeObserver = new ResizeObserver(checkScrollability);\r\n resizeObserver.observe(containerElement);\r\n\r\n return () => {\r\n clearTimeout(timeoutId);\r\n containerElement.removeEventListener('scroll', handleScroll);\r\n window.removeEventListener('resize', checkScrollability);\r\n resizeObserver.disconnect();\r\n };\r\n }, [containerElement, checkScrollability]);\r\n\r\n const scrollUp = useCallback(() => {\r\n containerRef.current?.scrollBy({ top: -SCROLL_AMOUNT, behavior: 'smooth' });\r\n }, []);\r\n\r\n const scrollDown = useCallback(() => {\r\n containerRef.current?.scrollBy({ top: SCROLL_AMOUNT, behavior: 'smooth' });\r\n }, []);\r\n\r\n return {\r\n containerRef: assignContainerRef as Ref<HTMLElement | null>,\r\n canScrollUp,\r\n canScrollDown,\r\n scrollUp,\r\n scrollDown,\r\n checkScrollability,\r\n preserveScrollPosition,\r\n };\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../use-media-query.ts","../use-mobile.ts","../use-infinite-scroll.tsx","../use-pull-to-refresh.ts","../use-live-timestamp.ts","../use-scrollable-container.ts"],"names":["useState","useEffect","useRef","useCallback"],"mappings":"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","file":"index.js","sourcesContent":["import { useEffect, useState } from 'react';\r\n\r\nexport interface UseMediaQueryOptions {\r\n /** Value returned during SSR and before the first browser measurement. */\r\n defaultValue?: boolean;\r\n}\r\n\r\nfunction getMatches(query: string, defaultValue: boolean): boolean {\r\n if (typeof window === 'undefined') return defaultValue;\r\n return window.matchMedia(query).matches;\r\n}\r\n\r\n/**\r\n * Subscribe to a CSS media query.\r\n */\r\nexport function useMediaQuery(\r\n query: string,\r\n { defaultValue = false }: UseMediaQueryOptions = {},\r\n): boolean {\r\n const [matches, setMatches] = useState<boolean>(() => getMatches(query, defaultValue));\r\n\r\n useEffect(() => {\r\n const mql = window.matchMedia(query);\r\n const onChange = () => setMatches(mql.matches);\r\n\r\n setMatches(mql.matches);\r\n mql.addEventListener('change', onChange);\r\n return () => mql.removeEventListener('change', onChange);\r\n }, [query]);\r\n\r\n return matches;\r\n}\r\n","import { useMediaQuery } from './use-media-query';\r\n\r\nexport const MOBILE_BREAKPOINT = 768;\r\n\r\nexport interface UseIsMobileOptions {\r\n /** Pixel breakpoint below which the viewport is considered mobile. */\r\n breakpoint?: number;\r\n /** Value returned during SSR and before the first browser measurement. */\r\n defaultValue?: boolean;\r\n}\r\n\r\nfunction getMobileMediaQuery(breakpoint: number): string {\r\n return `(max-width: ${breakpoint - 1}px)`;\r\n}\r\n\r\nexport function useIsMobile({\r\n breakpoint = MOBILE_BREAKPOINT,\r\n defaultValue = false,\r\n}: UseIsMobileOptions = {}): boolean {\r\n return useMediaQuery(getMobileMediaQuery(breakpoint), { defaultValue });\r\n}\r\n","import { useEffect, useRef, useCallback, useState, type RefCallback } from 'react';\r\n\r\nexport interface UseIntersectionLoadMoreOptions {\r\n /** Callback when intersection occurs */\r\n onLoadMore: () => void;\r\n /** Whether more data can be loaded */\r\n hasNextPage: boolean;\r\n /** Whether currently fetching */\r\n isFetchingNextPage: boolean;\r\n /** Root margin for intersection observer */\r\n rootMargin?: string;\r\n /** Threshold for intersection */\r\n threshold?: number;\r\n /** Enable/disable the hook */\r\n enabled?: boolean;\r\n /** Custom root element for IntersectionObserver (e.g., ScrollArea viewport) */\r\n root?: HTMLElement | null;\r\n}\r\n\r\nexport interface UseIntersectionLoadMoreReturn {\r\n /** Callback ref for the sentinel element. */\r\n sentinelRef: RefCallback<HTMLElement | null>;\r\n}\r\n\r\n/**\r\n * Headless infinite-load behaviour using IntersectionObserver.\r\n */\r\nexport function useIntersectionLoadMore({\r\n onLoadMore,\r\n hasNextPage,\r\n isFetchingNextPage,\r\n rootMargin = '200px',\r\n threshold = 0,\r\n enabled = true,\r\n root,\r\n}: UseIntersectionLoadMoreOptions): UseIntersectionLoadMoreReturn {\r\n const observerRef = useRef<IntersectionObserver | null>(null);\r\n const [sentinelElement, setSentinelElement] = useState<HTMLElement | null>(null);\r\n\r\n const handleIntersection = useCallback(\r\n (entries: IntersectionObserverEntry[]) => {\r\n const entry = entries[0];\r\n if (!entry?.isIntersecting) return;\r\n if (hasNextPage && !isFetchingNextPage && enabled) {\r\n onLoadMore();\r\n }\r\n },\r\n [onLoadMore, hasNextPage, isFetchingNextPage, enabled]\r\n );\r\n\r\n useEffect(() => {\r\n if (!enabled) return;\r\n\r\n if (observerRef.current) {\r\n observerRef.current.disconnect();\r\n }\r\n\r\n observerRef.current = new IntersectionObserver(handleIntersection, {\r\n root: root || null,\r\n rootMargin,\r\n threshold,\r\n });\r\n\r\n if (sentinelElement) {\r\n observerRef.current.observe(sentinelElement);\r\n }\r\n\r\n return () => {\r\n if (observerRef.current) {\r\n observerRef.current.disconnect();\r\n }\r\n };\r\n }, [sentinelElement, handleIntersection, rootMargin, threshold, enabled, root]);\r\n\r\n return {\r\n sentinelRef: setSentinelElement,\r\n };\r\n}\r\n","import { useState, useEffect, useRef, useCallback, type Ref } from 'react';\r\n\r\nexport interface UsePullToRefreshOptions {\r\n onRefresh: () => Promise<void>;\r\n threshold?: number;\r\n resistance?: number;\r\n disabled?: boolean;\r\n}\r\n\r\nexport interface UsePullToRefreshReturn {\r\n pullDistance: number;\r\n isRefreshing: boolean;\r\n isPulling: boolean;\r\n /** Callback ref — attach to the scrollable container element */\r\n containerRef: Ref<HTMLElement | null>;\r\n pullProgress: number;\r\n}\r\n\r\n/**\r\n * Hook to add pull-to-refresh gesture for mobile devices.\r\n */\r\nexport function usePullToRefresh({\r\n onRefresh,\r\n threshold = 80,\r\n resistance = 2.5,\r\n disabled = false,\r\n}: UsePullToRefreshOptions): UsePullToRefreshReturn {\r\n const [pullDistance, setPullDistance] = useState(0);\r\n const [isRefreshing, setIsRefreshing] = useState(false);\r\n const [isPulling, setIsPulling] = useState(false);\r\n const [containerElement, setContainerElement] = useState<HTMLElement | null>(null);\r\n\r\n const containerRef = useRef<HTMLElement | null>(null);\r\n const startYRef = useRef<number | null>(null);\r\n const currentYRef = useRef<number | null>(null);\r\n const pullDistanceRef = useRef(0);\r\n\r\n pullDistanceRef.current = pullDistance;\r\n\r\n const assignContainerRef = useCallback((node: HTMLElement | null) => {\r\n containerRef.current = node;\r\n setContainerElement(node);\r\n }, []);\r\n\r\n const handleTouchStart = useCallback(\r\n (e: TouchEvent) => {\r\n if (disabled || isRefreshing) return;\r\n\r\n const container = containerRef.current;\r\n if (!container) return;\r\n\r\n if (container.scrollTop <= 0) {\r\n startYRef.current = e.touches[0].clientY;\r\n setIsPulling(true);\r\n }\r\n },\r\n [disabled, isRefreshing]\r\n );\r\n\r\n const handleTouchMove = useCallback(\r\n (e: TouchEvent) => {\r\n if (disabled || isRefreshing || startYRef.current === null) return;\r\n\r\n const container = containerRef.current;\r\n if (!container) return;\r\n\r\n if (container.scrollTop > 0) {\r\n startYRef.current = null;\r\n setPullDistance(0);\r\n setIsPulling(false);\r\n return;\r\n }\r\n\r\n currentYRef.current = e.touches[0].clientY;\r\n const diff = currentYRef.current - startYRef.current;\r\n\r\n if (diff > 0) {\r\n const distance = Math.min(diff / resistance, threshold * 1.5);\r\n setPullDistance(distance);\r\n\r\n if (distance > 5) {\r\n e.preventDefault();\r\n }\r\n }\r\n },\r\n [disabled, isRefreshing, resistance, threshold]\r\n );\r\n\r\n const handleTouchEnd = useCallback(async () => {\r\n if (disabled || isRefreshing) return;\r\n\r\n startYRef.current = null;\r\n currentYRef.current = null;\r\n setIsPulling(false);\r\n\r\n const distance = pullDistanceRef.current;\r\n\r\n if (distance >= threshold) {\r\n setIsRefreshing(true);\r\n setPullDistance(threshold * 0.5);\r\n\r\n try {\r\n await onRefresh();\r\n } finally {\r\n setIsRefreshing(false);\r\n setPullDistance(0);\r\n }\r\n } else {\r\n setPullDistance(0);\r\n }\r\n }, [disabled, isRefreshing, threshold, onRefresh]);\r\n\r\n useEffect(() => {\r\n const container = containerElement;\r\n if (!container) return;\r\n\r\n container.addEventListener('touchstart', handleTouchStart, { passive: true });\r\n container.addEventListener('touchmove', handleTouchMove, { passive: false });\r\n container.addEventListener('touchend', handleTouchEnd, { passive: true });\r\n\r\n return () => {\r\n container.removeEventListener('touchstart', handleTouchStart);\r\n container.removeEventListener('touchmove', handleTouchMove);\r\n container.removeEventListener('touchend', handleTouchEnd);\r\n };\r\n }, [containerElement, handleTouchStart, handleTouchMove, handleTouchEnd]);\r\n\r\n const pullProgress = Math.min(pullDistance / threshold, 1);\r\n\r\n return {\r\n pullDistance,\r\n isRefreshing,\r\n isPulling,\r\n containerRef: assignContainerRef,\r\n pullProgress,\r\n };\r\n}\r\n","import { useState, useEffect, useRef } from 'react';\r\n\r\nlet globalNow = new Date();\r\n\r\ntype SubscriberId = symbol;\r\n\r\ntype Subscriber = {\r\n callback: () => void;\r\n date: Date;\r\n observer: IntersectionObserver | null;\r\n};\r\n\r\nconst subscribers = new Map<SubscriberId, Subscriber>();\r\nconst visibilityMap = new Map<SubscriberId, boolean>();\r\n\r\nlet rafId: number | null = null;\r\nlet intervalId: ReturnType<typeof setTimeout> | null = null;\r\nlet isTabVisible = true;\r\nlet pendingUpdate = false;\r\nlet visibilityListenerAttached = false;\r\n\r\nfunction getUpdateInterval(date: Date): number {\r\n const ageMs = Date.now() - date.getTime();\r\n const ageMinutes = ageMs / 60000;\r\n\r\n if (ageMinutes < 1) return 10000;\r\n if (ageMinutes < 60) return 30000;\r\n if (ageMinutes < 1440) return 300000;\r\n return 3600000;\r\n}\r\n\r\nfunction getMinInterval(): number {\r\n if (subscribers.size === 0) return 30000;\r\n const intervals = Array.from(subscribers.values()).map((sub) => getUpdateInterval(sub.date));\r\n return Math.max(Math.min(...intervals), 10000);\r\n}\r\n\r\nconst createObserver = (\r\n subscriberId: SubscriberId,\r\n callback: () => void\r\n): IntersectionObserver => {\r\n return new IntersectionObserver(\r\n (entries) => {\r\n entries.forEach((entry) => {\r\n visibilityMap.set(subscriberId, entry.isIntersecting);\r\n if (entry.isIntersecting) {\r\n callback();\r\n }\r\n });\r\n },\r\n {\r\n root: null,\r\n rootMargin: '50px',\r\n threshold: 0.01,\r\n }\r\n );\r\n};\r\n\r\nfunction updateVisibleSubscribers() {\r\n if (!isTabVisible || subscribers.size === 0) {\r\n pendingUpdate = false;\r\n return;\r\n }\r\n\r\n globalNow = new Date();\r\n\r\n if (!pendingUpdate) {\r\n pendingUpdate = true;\r\n rafId = requestAnimationFrame(() => {\r\n subscribers.forEach((subscriber, subscriberId) => {\r\n const isVisible = visibilityMap.get(subscriberId) ?? true;\r\n if (isVisible) {\r\n subscriber.callback();\r\n }\r\n });\r\n pendingUpdate = false;\r\n });\r\n }\r\n}\r\n\r\nfunction stopTimer() {\r\n if (intervalId) {\r\n clearTimeout(intervalId);\r\n intervalId = null;\r\n }\r\n if (rafId) {\r\n cancelAnimationFrame(rafId);\r\n rafId = null;\r\n }\r\n pendingUpdate = false;\r\n}\r\n\r\nfunction scheduleNextTick() {\r\n if (intervalId) {\r\n clearTimeout(intervalId);\r\n intervalId = null;\r\n }\r\n\r\n if (subscribers.size === 0 || !isTabVisible) {\r\n return;\r\n }\r\n\r\n intervalId = setTimeout(() => {\r\n updateVisibleSubscribers();\r\n scheduleNextTick();\r\n }, getMinInterval());\r\n}\r\n\r\nfunction ensureTimerRunning() {\r\n if (!intervalId && subscribers.size > 0 && isTabVisible) {\r\n scheduleNextTick();\r\n }\r\n}\r\n\r\nfunction restartTimer() {\r\n stopTimer();\r\n ensureTimerRunning();\r\n}\r\n\r\nfunction ensureVisibilityListener(): void {\r\n if (visibilityListenerAttached) return;\r\n if (typeof document === 'undefined') return;\r\n\r\n document.addEventListener('visibilitychange', () => {\r\n isTabVisible = !document.hidden;\r\n if (isTabVisible && subscribers.size > 0) {\r\n updateVisibleSubscribers();\r\n ensureTimerRunning();\r\n } else if (!isTabVisible) {\r\n stopTimer();\r\n }\r\n });\r\n visibilityListenerAttached = true;\r\n}\r\n\r\n/**\r\n * Live-updating timestamp hook with visibility-aware adaptive intervals.\r\n */\r\nexport function useLiveTimestamp(\r\n date: Date,\r\n element?: Element | null,\r\n): Date {\r\n const [, setTick] = useState(0);\r\n const subscriberIdRef = useRef<SubscriberId>(Symbol('live-timestamp-subscriber'));\r\n\r\n useEffect(() => {\r\n ensureVisibilityListener();\r\n const subscriberId = subscriberIdRef.current;\r\n const observedElement = element ?? null;\r\n\r\n const callback = () => {\r\n const isVisible = visibilityMap.get(subscriberId) ?? true;\r\n if (isVisible) {\r\n setTick((t) => t + 1);\r\n }\r\n };\r\n\r\n let observer: IntersectionObserver | null = null;\r\n if (observedElement) {\r\n observer = createObserver(subscriberId, callback);\r\n observer.observe(observedElement);\r\n visibilityMap.set(subscriberId, false);\r\n } else {\r\n visibilityMap.set(subscriberId, true);\r\n }\r\n\r\n subscribers.set(subscriberId, {\r\n callback,\r\n date,\r\n observer,\r\n });\r\n\r\n ensureTimerRunning();\r\n\r\n return () => {\r\n const subscriber = subscribers.get(subscriberId);\r\n if (subscriber?.observer) {\r\n subscriber.observer.disconnect();\r\n }\r\n subscribers.delete(subscriberId);\r\n visibilityMap.delete(subscriberId);\r\n\r\n if (subscribers.size === 0) {\r\n stopTimer();\r\n } else {\r\n restartTimer();\r\n }\r\n };\r\n // Key on the timestamp value, not the Date identity, so callers passing a\r\n // freshly constructed Date each render don't trigger a re-subscribe.\r\n }, [date.getTime(), element]);\r\n\r\n return globalNow;\r\n}\r\n","import { useRef, useEffect, useLayoutEffect, useState, useCallback, useMemo, type Ref } from 'react';\r\nimport { debounce } from '@publikit/utils';\r\n\r\nexport const SCROLL_AMOUNT = 80;\r\nexport const SCROLL_CHECK_DELAY = 100;\r\nexport const DEFAULT_SCROLL_POSITION_KEY = 'scrollable-container-position';\r\nconst STORAGE_DEBOUNCE_MS = 150;\r\n\r\nexport type ScrollAxis = 'x' | 'y';\r\n\r\nexport interface ScrollStorageAdapter {\r\n getItem(key: string): string | null;\r\n setItem(key: string, value: string): void;\r\n}\r\n\r\nexport interface UseScrollableContainerOptions {\r\n /** Session/storage key used to persist scroll position. */\r\n scrollPositionKey: string;\r\n /** Axis to measure and scroll. */\r\n axis?: ScrollAxis;\r\n /** Distance used by scroll helpers. */\r\n scrollAmount?: number;\r\n /** Delay before initial scrollability check. */\r\n checkDelay?: number;\r\n /** Optional storage adapter. Pass `false` to disable persistence. */\r\n storage?: ScrollStorageAdapter | false;\r\n}\r\n\r\nexport interface UseScrollableContainerReturn {\r\n /** Callback ref — attach to the scrollable element. */\r\n containerRef: Ref<HTMLElement | null>;\r\n canScrollBackward: boolean;\r\n canScrollForward: boolean;\r\n scrollBackward: () => void;\r\n scrollForward: () => void;\r\n checkScrollability: () => void;\r\n preserveScrollPosition: () => void;\r\n}\r\n\r\nfunction resolveStorage(\r\n storage: ScrollStorageAdapter | false | undefined,\r\n): ScrollStorageAdapter | null {\r\n if (storage === false) return null;\r\n if (storage) return storage;\r\n if (typeof sessionStorage === 'undefined') return null;\r\n return sessionStorage;\r\n}\r\n\r\nfunction getScrollPosition(element: HTMLElement, axis: ScrollAxis): number {\r\n return axis === 'x' ? element.scrollLeft : element.scrollTop;\r\n}\r\n\r\nfunction setScrollPosition(element: HTMLElement, axis: ScrollAxis, position: number): void {\r\n if (axis === 'x') {\r\n element.scrollLeft = position;\r\n } else {\r\n element.scrollTop = position;\r\n }\r\n}\r\n\r\nfunction getScrollSize(element: HTMLElement, axis: ScrollAxis): number {\r\n return axis === 'x' ? element.scrollWidth : element.scrollHeight;\r\n}\r\n\r\nfunction getClientSize(element: HTMLElement, axis: ScrollAxis): number {\r\n return axis === 'x' ? element.clientWidth : element.clientHeight;\r\n}\r\n\r\n/**\r\n * Scrollable container with position persistence and scroll button state.\r\n */\r\nexport function useScrollableContainer(\r\n {\r\n scrollPositionKey,\r\n axis = 'y',\r\n scrollAmount = SCROLL_AMOUNT,\r\n checkDelay = SCROLL_CHECK_DELAY,\r\n storage,\r\n }: UseScrollableContainerOptions,\r\n): UseScrollableContainerReturn {\r\n const containerRef = useRef<HTMLElement | null>(null);\r\n const [containerElement, setContainerElement] = useState<HTMLElement | null>(null);\r\n const isRestoringRef = useRef<boolean>(false);\r\n const [canScrollBackward, setCanScrollBackward] = useState(false);\r\n const [canScrollForward, setCanScrollForward] = useState(false);\r\n const storageAdapter = useMemo(() => resolveStorage(storage), [storage]);\r\n\r\n const assignContainerRef = useCallback((node: HTMLElement | null) => {\r\n containerRef.current = node;\r\n setContainerElement(node);\r\n }, []);\r\n\r\n const saveToStorage = useMemo(\r\n () =>\r\n debounce((position: number) => {\r\n if (!storageAdapter) return;\r\n try {\r\n storageAdapter.setItem(scrollPositionKey, String(position));\r\n } catch {\r\n // Ignore storage errors\r\n }\r\n }, STORAGE_DEBOUNCE_MS),\r\n [scrollPositionKey, storageAdapter]\r\n );\r\n\r\n const checkScrollability = useCallback(() => {\r\n if (!containerRef.current || isRestoringRef.current) return;\r\n const scrollPosition = getScrollPosition(containerRef.current, axis);\r\n const scrollSize = getScrollSize(containerRef.current, axis);\r\n const clientSize = getClientSize(containerRef.current, axis);\r\n\r\n saveToStorage(scrollPosition);\r\n\r\n setCanScrollBackward(scrollPosition > 0);\r\n setCanScrollForward(scrollPosition < scrollSize - clientSize - 1);\r\n }, [axis, saveToStorage]);\r\n\r\n useLayoutEffect(() => {\r\n if (!containerElement || isRestoringRef.current) return;\r\n if (!storageAdapter) return;\r\n\r\n try {\r\n const savedPosition = storageAdapter.getItem(scrollPositionKey);\r\n if (savedPosition !== null) {\r\n const position = parseInt(savedPosition, 10);\r\n const currentPosition = getScrollPosition(containerElement, axis);\r\n\r\n if (position > 0 && !isNaN(position) && Math.abs(currentPosition - position) > 1) {\r\n isRestoringRef.current = true;\r\n setScrollPosition(containerElement, axis, position);\r\n requestAnimationFrame(() => {\r\n isRestoringRef.current = false;\r\n checkScrollability();\r\n });\r\n }\r\n }\r\n } catch {\r\n // Ignore storage errors\r\n }\r\n }, [axis, containerElement, scrollPositionKey, checkScrollability, storageAdapter]);\r\n\r\n const preserveScrollPosition = useCallback(() => {\r\n if (containerRef.current && storageAdapter) {\r\n try {\r\n storageAdapter.setItem(\r\n scrollPositionKey,\r\n String(getScrollPosition(containerRef.current, axis)),\r\n );\r\n } catch {\r\n // Ignore storage errors\r\n }\r\n }\r\n }, [axis, scrollPositionKey, storageAdapter]);\r\n\r\n useEffect(() => {\r\n return () => {\r\n saveToStorage.cancel();\r\n };\r\n }, [saveToStorage]);\r\n\r\n useEffect(() => {\r\n if (!containerElement) return;\r\n\r\n const timeoutId = setTimeout(checkScrollability, checkDelay);\r\n\r\n const handleScroll = () => {\r\n checkScrollability();\r\n };\r\n\r\n containerElement.addEventListener('scroll', handleScroll, { passive: true });\r\n window.addEventListener('resize', checkScrollability);\r\n\r\n const resizeObserver = new ResizeObserver(checkScrollability);\r\n resizeObserver.observe(containerElement);\r\n\r\n return () => {\r\n clearTimeout(timeoutId);\r\n containerElement.removeEventListener('scroll', handleScroll);\r\n window.removeEventListener('resize', checkScrollability);\r\n resizeObserver.disconnect();\r\n };\r\n }, [checkDelay, containerElement, checkScrollability]);\r\n\r\n const scrollBackward = useCallback(() => {\r\n const delta = -scrollAmount;\r\n containerRef.current?.scrollBy(\r\n axis === 'x' ? { left: delta, behavior: 'smooth' } : { top: delta, behavior: 'smooth' },\r\n );\r\n }, [axis, scrollAmount]);\r\n\r\n const scrollForward = useCallback(() => {\r\n containerRef.current?.scrollBy(\r\n axis === 'x'\r\n ? { left: scrollAmount, behavior: 'smooth' }\r\n : { top: scrollAmount, behavior: 'smooth' },\r\n );\r\n }, [axis, scrollAmount]);\r\n\r\n return {\r\n containerRef: assignContainerRef as Ref<HTMLElement | null>,\r\n canScrollBackward,\r\n canScrollForward,\r\n scrollBackward,\r\n scrollForward,\r\n checkScrollability,\r\n preserveScrollPosition,\r\n };\r\n}\r\n"]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
|
|
5
|
+
// use-infinite-scroll.tsx
|
|
6
|
+
function useIntersectionLoadMore({
|
|
7
|
+
onLoadMore,
|
|
8
|
+
hasNextPage,
|
|
9
|
+
isFetchingNextPage,
|
|
10
|
+
rootMargin = "200px",
|
|
11
|
+
threshold = 0,
|
|
12
|
+
enabled = true,
|
|
13
|
+
root
|
|
14
|
+
}) {
|
|
15
|
+
const observerRef = react.useRef(null);
|
|
16
|
+
const [sentinelElement, setSentinelElement] = react.useState(null);
|
|
17
|
+
const handleIntersection = react.useCallback(
|
|
18
|
+
(entries) => {
|
|
19
|
+
const entry = entries[0];
|
|
20
|
+
if (!entry?.isIntersecting) return;
|
|
21
|
+
if (hasNextPage && !isFetchingNextPage && enabled) {
|
|
22
|
+
onLoadMore();
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
[onLoadMore, hasNextPage, isFetchingNextPage, enabled]
|
|
26
|
+
);
|
|
27
|
+
react.useEffect(() => {
|
|
28
|
+
if (!enabled) return;
|
|
29
|
+
if (observerRef.current) {
|
|
30
|
+
observerRef.current.disconnect();
|
|
31
|
+
}
|
|
32
|
+
observerRef.current = new IntersectionObserver(handleIntersection, {
|
|
33
|
+
root: root || null,
|
|
34
|
+
rootMargin,
|
|
35
|
+
threshold
|
|
36
|
+
});
|
|
37
|
+
if (sentinelElement) {
|
|
38
|
+
observerRef.current.observe(sentinelElement);
|
|
39
|
+
}
|
|
40
|
+
return () => {
|
|
41
|
+
if (observerRef.current) {
|
|
42
|
+
observerRef.current.disconnect();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}, [sentinelElement, handleIntersection, rootMargin, threshold, enabled, root]);
|
|
46
|
+
return {
|
|
47
|
+
sentinelRef: setSentinelElement
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
exports.useIntersectionLoadMore = useIntersectionLoadMore;
|
|
52
|
+
//# sourceMappingURL=use-infinite-scroll.cjs.map
|
|
53
|
+
//# sourceMappingURL=use-infinite-scroll.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../use-infinite-scroll.tsx"],"names":["useRef","useState","useCallback","useEffect"],"mappings":";;;;;AA2BO,SAAS,uBAAA,CAAwB;AAAA,EACtC,UAAA;AAAA,EACA,WAAA;AAAA,EACA,kBAAA;AAAA,EACA,UAAA,GAAa,OAAA;AAAA,EACb,SAAA,GAAY,CAAA;AAAA,EACZ,OAAA,GAAU,IAAA;AAAA,EACV;AACF,CAAA,EAAkE;AAChE,EAAA,MAAM,WAAA,GAAcA,aAAoC,IAAI,CAAA;AAC5D,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAIC,eAA6B,IAAI,CAAA;AAE/E,EAAA,MAAM,kBAAA,GAAqBC,iBAAA;AAAA,IACzB,CAAC,OAAA,KAAyC;AACxC,MAAA,MAAM,KAAA,GAAQ,QAAQ,CAAC,CAAA;AACvB,MAAA,IAAI,CAAC,OAAO,cAAA,EAAgB;AAC5B,MAAA,IAAI,WAAA,IAAe,CAAC,kBAAA,IAAsB,OAAA,EAAS;AACjD,QAAA,UAAA,EAAW;AAAA,MACb;AAAA,IACF,CAAA;AAAA,IACA,CAAC,UAAA,EAAY,WAAA,EAAa,kBAAA,EAAoB,OAAO;AAAA,GACvD;AAEA,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,EAAS;AAEd,IAAA,IAAI,YAAY,OAAA,EAAS;AACvB,MAAA,WAAA,CAAY,QAAQ,UAAA,EAAW;AAAA,IACjC;AAEA,IAAA,WAAA,CAAY,OAAA,GAAU,IAAI,oBAAA,CAAqB,kBAAA,EAAoB;AAAA,MACjE,MAAM,IAAA,IAAQ,IAAA;AAAA,MACd,UAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,IAAI,eAAA,EAAiB;AACnB,MAAA,WAAA,CAAY,OAAA,CAAQ,QAAQ,eAAe,CAAA;AAAA,IAC7C;AAEA,IAAA,OAAO,MAAM;AACX,MAAA,IAAI,YAAY,OAAA,EAAS;AACvB,QAAA,WAAA,CAAY,QAAQ,UAAA,EAAW;AAAA,MACjC;AAAA,IACF,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,eAAA,EAAiB,kBAAA,EAAoB,YAAY,SAAA,EAAW,OAAA,EAAS,IAAI,CAAC,CAAA;AAE9E,EAAA,OAAO;AAAA,IACL,WAAA,EAAa;AAAA,GACf;AACF","file":"use-infinite-scroll.cjs","sourcesContent":["import { useEffect, useRef, useCallback, useState, type RefCallback } from 'react';\r\n\r\nexport interface UseIntersectionLoadMoreOptions {\r\n /** Callback when intersection occurs */\r\n onLoadMore: () => void;\r\n /** Whether more data can be loaded */\r\n hasNextPage: boolean;\r\n /** Whether currently fetching */\r\n isFetchingNextPage: boolean;\r\n /** Root margin for intersection observer */\r\n rootMargin?: string;\r\n /** Threshold for intersection */\r\n threshold?: number;\r\n /** Enable/disable the hook */\r\n enabled?: boolean;\r\n /** Custom root element for IntersectionObserver (e.g., ScrollArea viewport) */\r\n root?: HTMLElement | null;\r\n}\r\n\r\nexport interface UseIntersectionLoadMoreReturn {\r\n /** Callback ref for the sentinel element. */\r\n sentinelRef: RefCallback<HTMLElement | null>;\r\n}\r\n\r\n/**\r\n * Headless infinite-load behaviour using IntersectionObserver.\r\n */\r\nexport function useIntersectionLoadMore({\r\n onLoadMore,\r\n hasNextPage,\r\n isFetchingNextPage,\r\n rootMargin = '200px',\r\n threshold = 0,\r\n enabled = true,\r\n root,\r\n}: UseIntersectionLoadMoreOptions): UseIntersectionLoadMoreReturn {\r\n const observerRef = useRef<IntersectionObserver | null>(null);\r\n const [sentinelElement, setSentinelElement] = useState<HTMLElement | null>(null);\r\n\r\n const handleIntersection = useCallback(\r\n (entries: IntersectionObserverEntry[]) => {\r\n const entry = entries[0];\r\n if (!entry?.isIntersecting) return;\r\n if (hasNextPage && !isFetchingNextPage && enabled) {\r\n onLoadMore();\r\n }\r\n },\r\n [onLoadMore, hasNextPage, isFetchingNextPage, enabled]\r\n );\r\n\r\n useEffect(() => {\r\n if (!enabled) return;\r\n\r\n if (observerRef.current) {\r\n observerRef.current.disconnect();\r\n }\r\n\r\n observerRef.current = new IntersectionObserver(handleIntersection, {\r\n root: root || null,\r\n rootMargin,\r\n threshold,\r\n });\r\n\r\n if (sentinelElement) {\r\n observerRef.current.observe(sentinelElement);\r\n }\r\n\r\n return () => {\r\n if (observerRef.current) {\r\n observerRef.current.disconnect();\r\n }\r\n };\r\n }, [sentinelElement, handleIntersection, rootMargin, threshold, enabled, root]);\r\n\r\n return {\r\n sentinelRef: setSentinelElement,\r\n };\r\n}\r\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { RefCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
interface UseIntersectionLoadMoreOptions {
|
|
4
|
+
/** Callback when intersection occurs */
|
|
5
|
+
onLoadMore: () => void;
|
|
6
|
+
/** Whether more data can be loaded */
|
|
7
|
+
hasNextPage: boolean;
|
|
8
|
+
/** Whether currently fetching */
|
|
9
|
+
isFetchingNextPage: boolean;
|
|
10
|
+
/** Root margin for intersection observer */
|
|
11
|
+
rootMargin?: string;
|
|
12
|
+
/** Threshold for intersection */
|
|
13
|
+
threshold?: number;
|
|
14
|
+
/** Enable/disable the hook */
|
|
15
|
+
enabled?: boolean;
|
|
16
|
+
/** Custom root element for IntersectionObserver (e.g., ScrollArea viewport) */
|
|
17
|
+
root?: HTMLElement | null;
|
|
18
|
+
}
|
|
19
|
+
interface UseIntersectionLoadMoreReturn {
|
|
20
|
+
/** Callback ref for the sentinel element. */
|
|
21
|
+
sentinelRef: RefCallback<HTMLElement | null>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Headless infinite-load behaviour using IntersectionObserver.
|
|
25
|
+
*/
|
|
26
|
+
declare function useIntersectionLoadMore({ onLoadMore, hasNextPage, isFetchingNextPage, rootMargin, threshold, enabled, root, }: UseIntersectionLoadMoreOptions): UseIntersectionLoadMoreReturn;
|
|
27
|
+
|
|
28
|
+
export { type UseIntersectionLoadMoreOptions, type UseIntersectionLoadMoreReturn, useIntersectionLoadMore };
|