@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.
Files changed (45) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +28 -5
  3. package/dist/index.cjs +113 -67
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.cts +7 -74
  6. package/dist/index.d.ts +7 -74
  7. package/dist/index.js +111 -67
  8. package/dist/index.js.map +1 -1
  9. package/dist/use-infinite-scroll.cjs +53 -0
  10. package/dist/use-infinite-scroll.cjs.map +1 -0
  11. package/dist/use-infinite-scroll.d.cts +28 -0
  12. package/dist/use-infinite-scroll.d.ts +28 -0
  13. package/dist/use-infinite-scroll.js +51 -0
  14. package/dist/use-infinite-scroll.js.map +1 -0
  15. package/dist/use-live-timestamp.cjs +156 -0
  16. package/dist/use-live-timestamp.cjs.map +1 -0
  17. package/dist/use-live-timestamp.d.cts +6 -0
  18. package/dist/use-live-timestamp.d.ts +6 -0
  19. package/dist/use-live-timestamp.js +154 -0
  20. package/dist/use-live-timestamp.js.map +1 -0
  21. package/dist/use-media-query.cjs +24 -0
  22. package/dist/use-media-query.cjs.map +1 -0
  23. package/dist/use-media-query.d.cts +10 -0
  24. package/dist/use-media-query.d.ts +10 -0
  25. package/dist/use-media-query.js +22 -0
  26. package/dist/use-media-query.js.map +1 -0
  27. package/dist/use-mobile.cjs +37 -0
  28. package/dist/use-mobile.cjs.map +1 -0
  29. package/dist/use-mobile.d.cts +10 -0
  30. package/dist/use-mobile.d.ts +10 -0
  31. package/dist/use-mobile.js +34 -0
  32. package/dist/use-mobile.js.map +1 -0
  33. package/dist/use-pull-to-refresh.cjs +103 -0
  34. package/dist/use-pull-to-refresh.cjs.map +1 -0
  35. package/dist/use-pull-to-refresh.d.cts +22 -0
  36. package/dist/use-pull-to-refresh.d.ts +22 -0
  37. package/dist/use-pull-to-refresh.js +101 -0
  38. package/dist/use-pull-to-refresh.js.map +1 -0
  39. package/dist/use-scrollable-container.cjs +149 -0
  40. package/dist/use-scrollable-container.cjs.map +1 -0
  41. package/dist/use-scrollable-container.d.cts +38 -0
  42. package/dist/use-scrollable-container.d.ts +38 -0
  43. package/dist/use-scrollable-container.js +144 -0
  44. package/dist/use-scrollable-container.js.map +1 -0
  45. package/package.json +38 -5
package/dist/index.d.ts CHANGED
@@ -1,74 +1,7 @@
1
- import * as react from 'react';
2
- import { Ref, RefObject } from 'react';
3
-
4
- declare function useIsMobile(): boolean;
5
-
6
- interface UseInfiniteScrollOptions {
7
- /** Callback when intersection occurs */
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-mobile.tsx
6
- var MOBILE_BREAKPOINT = 768;
7
- var MOBILE_MEDIA_QUERY = `(max-width: ${MOBILE_BREAKPOINT - 1}px)`;
8
- function getIsMobile() {
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 useIsMobile() {
13
- const [isMobile, setIsMobile] = useState(getIsMobile);
9
+ function useMediaQuery(query, { defaultValue = false } = {}) {
10
+ const [matches, setMatches] = useState(() => getMatches(query, defaultValue));
14
11
  useEffect(() => {
15
- const mql = window.matchMedia(MOBILE_MEDIA_QUERY);
16
- const onChange = () => setIsMobile(mql.matches);
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 isMobile;
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 useInfiniteScroll({
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 [sentinelRef, setSentinelRef] = useState(null);
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 (sentinelRef) {
54
- observerRef.current.observe(sentinelRef);
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
- }, [sentinelRef, handleIntersection, rootMargin, threshold, enabled, root]);
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
- Sentinel,
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
- if (typeof window !== "undefined") {
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, elementRef) {
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 (elementRef?.current) {
288
+ if (observedElement) {
286
289
  observer = createObserver(subscriberId, callback);
287
- observer.observe(elementRef.current);
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(), elementRef]);
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 useScrollableContainer(scrollPositionKey = DEFAULT_SCROLL_POSITION_KEY) {
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 [canScrollUp, setCanScrollUp] = useState(false);
323
- const [canScrollDown, setCanScrollDown] = useState(false);
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
- sessionStorage.setItem(scrollPositionKey, String(position));
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 { scrollTop, scrollHeight, clientHeight } = containerRef.current;
340
- saveToStorage(scrollTop);
341
- setCanScrollUp(scrollTop > 0);
342
- setCanScrollDown(scrollTop < scrollHeight - clientHeight - 1);
343
- }, [saveToStorage]);
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 = sessionStorage.getItem(scrollPositionKey);
383
+ const savedPosition = storageAdapter.getItem(scrollPositionKey);
348
384
  if (savedPosition !== null) {
349
385
  const position = parseInt(savedPosition, 10);
350
- const currentPosition = containerElement.scrollTop;
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.scrollTop = position;
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
- sessionStorage.setItem(scrollPositionKey, String(containerRef.current.scrollTop));
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, SCROLL_CHECK_DELAY);
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 scrollUp = useCallback(() => {
394
- containerRef.current?.scrollBy({ top: -SCROLL_AMOUNT, behavior: "smooth" });
395
- }, []);
396
- const scrollDown = useCallback(() => {
397
- containerRef.current?.scrollBy({ top: SCROLL_AMOUNT, behavior: "smooth" });
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
- canScrollUp,
402
- canScrollDown,
403
- scrollUp,
404
- scrollDown,
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, useInfiniteScroll, useIsMobile, useLiveTimestamp, usePullToRefresh, useScrollableContainer };
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 };