@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
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
|
|
5
|
+
// use-pull-to-refresh.ts
|
|
6
|
+
function usePullToRefresh({
|
|
7
|
+
onRefresh,
|
|
8
|
+
threshold = 80,
|
|
9
|
+
resistance = 2.5,
|
|
10
|
+
disabled = false
|
|
11
|
+
}) {
|
|
12
|
+
const [pullDistance, setPullDistance] = react.useState(0);
|
|
13
|
+
const [isRefreshing, setIsRefreshing] = react.useState(false);
|
|
14
|
+
const [isPulling, setIsPulling] = react.useState(false);
|
|
15
|
+
const [containerElement, setContainerElement] = react.useState(null);
|
|
16
|
+
const containerRef = react.useRef(null);
|
|
17
|
+
const startYRef = react.useRef(null);
|
|
18
|
+
const currentYRef = react.useRef(null);
|
|
19
|
+
const pullDistanceRef = react.useRef(0);
|
|
20
|
+
pullDistanceRef.current = pullDistance;
|
|
21
|
+
const assignContainerRef = react.useCallback((node) => {
|
|
22
|
+
containerRef.current = node;
|
|
23
|
+
setContainerElement(node);
|
|
24
|
+
}, []);
|
|
25
|
+
const handleTouchStart = react.useCallback(
|
|
26
|
+
(e) => {
|
|
27
|
+
if (disabled || isRefreshing) return;
|
|
28
|
+
const container = containerRef.current;
|
|
29
|
+
if (!container) return;
|
|
30
|
+
if (container.scrollTop <= 0) {
|
|
31
|
+
startYRef.current = e.touches[0].clientY;
|
|
32
|
+
setIsPulling(true);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
[disabled, isRefreshing]
|
|
36
|
+
);
|
|
37
|
+
const handleTouchMove = react.useCallback(
|
|
38
|
+
(e) => {
|
|
39
|
+
if (disabled || isRefreshing || startYRef.current === null) return;
|
|
40
|
+
const container = containerRef.current;
|
|
41
|
+
if (!container) return;
|
|
42
|
+
if (container.scrollTop > 0) {
|
|
43
|
+
startYRef.current = null;
|
|
44
|
+
setPullDistance(0);
|
|
45
|
+
setIsPulling(false);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
currentYRef.current = e.touches[0].clientY;
|
|
49
|
+
const diff = currentYRef.current - startYRef.current;
|
|
50
|
+
if (diff > 0) {
|
|
51
|
+
const distance = Math.min(diff / resistance, threshold * 1.5);
|
|
52
|
+
setPullDistance(distance);
|
|
53
|
+
if (distance > 5) {
|
|
54
|
+
e.preventDefault();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
[disabled, isRefreshing, resistance, threshold]
|
|
59
|
+
);
|
|
60
|
+
const handleTouchEnd = react.useCallback(async () => {
|
|
61
|
+
if (disabled || isRefreshing) return;
|
|
62
|
+
startYRef.current = null;
|
|
63
|
+
currentYRef.current = null;
|
|
64
|
+
setIsPulling(false);
|
|
65
|
+
const distance = pullDistanceRef.current;
|
|
66
|
+
if (distance >= threshold) {
|
|
67
|
+
setIsRefreshing(true);
|
|
68
|
+
setPullDistance(threshold * 0.5);
|
|
69
|
+
try {
|
|
70
|
+
await onRefresh();
|
|
71
|
+
} finally {
|
|
72
|
+
setIsRefreshing(false);
|
|
73
|
+
setPullDistance(0);
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
setPullDistance(0);
|
|
77
|
+
}
|
|
78
|
+
}, [disabled, isRefreshing, threshold, onRefresh]);
|
|
79
|
+
react.useEffect(() => {
|
|
80
|
+
const container = containerElement;
|
|
81
|
+
if (!container) return;
|
|
82
|
+
container.addEventListener("touchstart", handleTouchStart, { passive: true });
|
|
83
|
+
container.addEventListener("touchmove", handleTouchMove, { passive: false });
|
|
84
|
+
container.addEventListener("touchend", handleTouchEnd, { passive: true });
|
|
85
|
+
return () => {
|
|
86
|
+
container.removeEventListener("touchstart", handleTouchStart);
|
|
87
|
+
container.removeEventListener("touchmove", handleTouchMove);
|
|
88
|
+
container.removeEventListener("touchend", handleTouchEnd);
|
|
89
|
+
};
|
|
90
|
+
}, [containerElement, handleTouchStart, handleTouchMove, handleTouchEnd]);
|
|
91
|
+
const pullProgress = Math.min(pullDistance / threshold, 1);
|
|
92
|
+
return {
|
|
93
|
+
pullDistance,
|
|
94
|
+
isRefreshing,
|
|
95
|
+
isPulling,
|
|
96
|
+
containerRef: assignContainerRef,
|
|
97
|
+
pullProgress
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
exports.usePullToRefresh = usePullToRefresh;
|
|
102
|
+
//# sourceMappingURL=use-pull-to-refresh.cjs.map
|
|
103
|
+
//# sourceMappingURL=use-pull-to-refresh.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../use-pull-to-refresh.ts"],"names":["useState","useRef","useCallback","useEffect"],"mappings":"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","file":"use-pull-to-refresh.cjs","sourcesContent":["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"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Ref } from 'react';
|
|
2
|
+
|
|
3
|
+
interface UsePullToRefreshOptions {
|
|
4
|
+
onRefresh: () => Promise<void>;
|
|
5
|
+
threshold?: number;
|
|
6
|
+
resistance?: number;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface UsePullToRefreshReturn {
|
|
10
|
+
pullDistance: number;
|
|
11
|
+
isRefreshing: boolean;
|
|
12
|
+
isPulling: boolean;
|
|
13
|
+
/** Callback ref — attach to the scrollable container element */
|
|
14
|
+
containerRef: Ref<HTMLElement | null>;
|
|
15
|
+
pullProgress: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Hook to add pull-to-refresh gesture for mobile devices.
|
|
19
|
+
*/
|
|
20
|
+
declare function usePullToRefresh({ onRefresh, threshold, resistance, disabled, }: UsePullToRefreshOptions): UsePullToRefreshReturn;
|
|
21
|
+
|
|
22
|
+
export { type UsePullToRefreshOptions, type UsePullToRefreshReturn, usePullToRefresh };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Ref } from 'react';
|
|
2
|
+
|
|
3
|
+
interface UsePullToRefreshOptions {
|
|
4
|
+
onRefresh: () => Promise<void>;
|
|
5
|
+
threshold?: number;
|
|
6
|
+
resistance?: number;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface UsePullToRefreshReturn {
|
|
10
|
+
pullDistance: number;
|
|
11
|
+
isRefreshing: boolean;
|
|
12
|
+
isPulling: boolean;
|
|
13
|
+
/** Callback ref — attach to the scrollable container element */
|
|
14
|
+
containerRef: Ref<HTMLElement | null>;
|
|
15
|
+
pullProgress: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Hook to add pull-to-refresh gesture for mobile devices.
|
|
19
|
+
*/
|
|
20
|
+
declare function usePullToRefresh({ onRefresh, threshold, resistance, disabled, }: UsePullToRefreshOptions): UsePullToRefreshReturn;
|
|
21
|
+
|
|
22
|
+
export { type UsePullToRefreshOptions, type UsePullToRefreshReturn, usePullToRefresh };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
// use-pull-to-refresh.ts
|
|
4
|
+
function usePullToRefresh({
|
|
5
|
+
onRefresh,
|
|
6
|
+
threshold = 80,
|
|
7
|
+
resistance = 2.5,
|
|
8
|
+
disabled = false
|
|
9
|
+
}) {
|
|
10
|
+
const [pullDistance, setPullDistance] = useState(0);
|
|
11
|
+
const [isRefreshing, setIsRefreshing] = useState(false);
|
|
12
|
+
const [isPulling, setIsPulling] = useState(false);
|
|
13
|
+
const [containerElement, setContainerElement] = useState(null);
|
|
14
|
+
const containerRef = useRef(null);
|
|
15
|
+
const startYRef = useRef(null);
|
|
16
|
+
const currentYRef = useRef(null);
|
|
17
|
+
const pullDistanceRef = useRef(0);
|
|
18
|
+
pullDistanceRef.current = pullDistance;
|
|
19
|
+
const assignContainerRef = useCallback((node) => {
|
|
20
|
+
containerRef.current = node;
|
|
21
|
+
setContainerElement(node);
|
|
22
|
+
}, []);
|
|
23
|
+
const handleTouchStart = useCallback(
|
|
24
|
+
(e) => {
|
|
25
|
+
if (disabled || isRefreshing) return;
|
|
26
|
+
const container = containerRef.current;
|
|
27
|
+
if (!container) return;
|
|
28
|
+
if (container.scrollTop <= 0) {
|
|
29
|
+
startYRef.current = e.touches[0].clientY;
|
|
30
|
+
setIsPulling(true);
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
[disabled, isRefreshing]
|
|
34
|
+
);
|
|
35
|
+
const handleTouchMove = useCallback(
|
|
36
|
+
(e) => {
|
|
37
|
+
if (disabled || isRefreshing || startYRef.current === null) return;
|
|
38
|
+
const container = containerRef.current;
|
|
39
|
+
if (!container) return;
|
|
40
|
+
if (container.scrollTop > 0) {
|
|
41
|
+
startYRef.current = null;
|
|
42
|
+
setPullDistance(0);
|
|
43
|
+
setIsPulling(false);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
currentYRef.current = e.touches[0].clientY;
|
|
47
|
+
const diff = currentYRef.current - startYRef.current;
|
|
48
|
+
if (diff > 0) {
|
|
49
|
+
const distance = Math.min(diff / resistance, threshold * 1.5);
|
|
50
|
+
setPullDistance(distance);
|
|
51
|
+
if (distance > 5) {
|
|
52
|
+
e.preventDefault();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
[disabled, isRefreshing, resistance, threshold]
|
|
57
|
+
);
|
|
58
|
+
const handleTouchEnd = useCallback(async () => {
|
|
59
|
+
if (disabled || isRefreshing) return;
|
|
60
|
+
startYRef.current = null;
|
|
61
|
+
currentYRef.current = null;
|
|
62
|
+
setIsPulling(false);
|
|
63
|
+
const distance = pullDistanceRef.current;
|
|
64
|
+
if (distance >= threshold) {
|
|
65
|
+
setIsRefreshing(true);
|
|
66
|
+
setPullDistance(threshold * 0.5);
|
|
67
|
+
try {
|
|
68
|
+
await onRefresh();
|
|
69
|
+
} finally {
|
|
70
|
+
setIsRefreshing(false);
|
|
71
|
+
setPullDistance(0);
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
setPullDistance(0);
|
|
75
|
+
}
|
|
76
|
+
}, [disabled, isRefreshing, threshold, onRefresh]);
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
const container = containerElement;
|
|
79
|
+
if (!container) return;
|
|
80
|
+
container.addEventListener("touchstart", handleTouchStart, { passive: true });
|
|
81
|
+
container.addEventListener("touchmove", handleTouchMove, { passive: false });
|
|
82
|
+
container.addEventListener("touchend", handleTouchEnd, { passive: true });
|
|
83
|
+
return () => {
|
|
84
|
+
container.removeEventListener("touchstart", handleTouchStart);
|
|
85
|
+
container.removeEventListener("touchmove", handleTouchMove);
|
|
86
|
+
container.removeEventListener("touchend", handleTouchEnd);
|
|
87
|
+
};
|
|
88
|
+
}, [containerElement, handleTouchStart, handleTouchMove, handleTouchEnd]);
|
|
89
|
+
const pullProgress = Math.min(pullDistance / threshold, 1);
|
|
90
|
+
return {
|
|
91
|
+
pullDistance,
|
|
92
|
+
isRefreshing,
|
|
93
|
+
isPulling,
|
|
94
|
+
containerRef: assignContainerRef,
|
|
95
|
+
pullProgress
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { usePullToRefresh };
|
|
100
|
+
//# sourceMappingURL=use-pull-to-refresh.js.map
|
|
101
|
+
//# sourceMappingURL=use-pull-to-refresh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../use-pull-to-refresh.ts"],"names":[],"mappings":"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","file":"use-pull-to-refresh.js","sourcesContent":["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"]}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var utils = require('@publikit/utils');
|
|
5
|
+
|
|
6
|
+
// use-scrollable-container.ts
|
|
7
|
+
var SCROLL_AMOUNT = 80;
|
|
8
|
+
var SCROLL_CHECK_DELAY = 100;
|
|
9
|
+
var DEFAULT_SCROLL_POSITION_KEY = "scrollable-container-position";
|
|
10
|
+
var STORAGE_DEBOUNCE_MS = 150;
|
|
11
|
+
function resolveStorage(storage) {
|
|
12
|
+
if (storage === false) return null;
|
|
13
|
+
if (storage) return storage;
|
|
14
|
+
if (typeof sessionStorage === "undefined") return null;
|
|
15
|
+
return sessionStorage;
|
|
16
|
+
}
|
|
17
|
+
function getScrollPosition(element, axis) {
|
|
18
|
+
return axis === "x" ? element.scrollLeft : element.scrollTop;
|
|
19
|
+
}
|
|
20
|
+
function setScrollPosition(element, axis, position) {
|
|
21
|
+
if (axis === "x") {
|
|
22
|
+
element.scrollLeft = position;
|
|
23
|
+
} else {
|
|
24
|
+
element.scrollTop = position;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function getScrollSize(element, axis) {
|
|
28
|
+
return axis === "x" ? element.scrollWidth : element.scrollHeight;
|
|
29
|
+
}
|
|
30
|
+
function getClientSize(element, axis) {
|
|
31
|
+
return axis === "x" ? element.clientWidth : element.clientHeight;
|
|
32
|
+
}
|
|
33
|
+
function useScrollableContainer({
|
|
34
|
+
scrollPositionKey,
|
|
35
|
+
axis = "y",
|
|
36
|
+
scrollAmount = SCROLL_AMOUNT,
|
|
37
|
+
checkDelay = SCROLL_CHECK_DELAY,
|
|
38
|
+
storage
|
|
39
|
+
}) {
|
|
40
|
+
const containerRef = react.useRef(null);
|
|
41
|
+
const [containerElement, setContainerElement] = react.useState(null);
|
|
42
|
+
const isRestoringRef = react.useRef(false);
|
|
43
|
+
const [canScrollBackward, setCanScrollBackward] = react.useState(false);
|
|
44
|
+
const [canScrollForward, setCanScrollForward] = react.useState(false);
|
|
45
|
+
const storageAdapter = react.useMemo(() => resolveStorage(storage), [storage]);
|
|
46
|
+
const assignContainerRef = react.useCallback((node) => {
|
|
47
|
+
containerRef.current = node;
|
|
48
|
+
setContainerElement(node);
|
|
49
|
+
}, []);
|
|
50
|
+
const saveToStorage = react.useMemo(
|
|
51
|
+
() => utils.debounce((position) => {
|
|
52
|
+
if (!storageAdapter) return;
|
|
53
|
+
try {
|
|
54
|
+
storageAdapter.setItem(scrollPositionKey, String(position));
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
}, STORAGE_DEBOUNCE_MS),
|
|
58
|
+
[scrollPositionKey, storageAdapter]
|
|
59
|
+
);
|
|
60
|
+
const checkScrollability = react.useCallback(() => {
|
|
61
|
+
if (!containerRef.current || isRestoringRef.current) return;
|
|
62
|
+
const scrollPosition = getScrollPosition(containerRef.current, axis);
|
|
63
|
+
const scrollSize = getScrollSize(containerRef.current, axis);
|
|
64
|
+
const clientSize = getClientSize(containerRef.current, axis);
|
|
65
|
+
saveToStorage(scrollPosition);
|
|
66
|
+
setCanScrollBackward(scrollPosition > 0);
|
|
67
|
+
setCanScrollForward(scrollPosition < scrollSize - clientSize - 1);
|
|
68
|
+
}, [axis, saveToStorage]);
|
|
69
|
+
react.useLayoutEffect(() => {
|
|
70
|
+
if (!containerElement || isRestoringRef.current) return;
|
|
71
|
+
if (!storageAdapter) return;
|
|
72
|
+
try {
|
|
73
|
+
const savedPosition = storageAdapter.getItem(scrollPositionKey);
|
|
74
|
+
if (savedPosition !== null) {
|
|
75
|
+
const position = parseInt(savedPosition, 10);
|
|
76
|
+
const currentPosition = getScrollPosition(containerElement, axis);
|
|
77
|
+
if (position > 0 && !isNaN(position) && Math.abs(currentPosition - position) > 1) {
|
|
78
|
+
isRestoringRef.current = true;
|
|
79
|
+
setScrollPosition(containerElement, axis, position);
|
|
80
|
+
requestAnimationFrame(() => {
|
|
81
|
+
isRestoringRef.current = false;
|
|
82
|
+
checkScrollability();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
}, [axis, containerElement, scrollPositionKey, checkScrollability, storageAdapter]);
|
|
89
|
+
const preserveScrollPosition = react.useCallback(() => {
|
|
90
|
+
if (containerRef.current && storageAdapter) {
|
|
91
|
+
try {
|
|
92
|
+
storageAdapter.setItem(
|
|
93
|
+
scrollPositionKey,
|
|
94
|
+
String(getScrollPosition(containerRef.current, axis))
|
|
95
|
+
);
|
|
96
|
+
} catch {
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}, [axis, scrollPositionKey, storageAdapter]);
|
|
100
|
+
react.useEffect(() => {
|
|
101
|
+
return () => {
|
|
102
|
+
saveToStorage.cancel();
|
|
103
|
+
};
|
|
104
|
+
}, [saveToStorage]);
|
|
105
|
+
react.useEffect(() => {
|
|
106
|
+
if (!containerElement) return;
|
|
107
|
+
const timeoutId = setTimeout(checkScrollability, checkDelay);
|
|
108
|
+
const handleScroll = () => {
|
|
109
|
+
checkScrollability();
|
|
110
|
+
};
|
|
111
|
+
containerElement.addEventListener("scroll", handleScroll, { passive: true });
|
|
112
|
+
window.addEventListener("resize", checkScrollability);
|
|
113
|
+
const resizeObserver = new ResizeObserver(checkScrollability);
|
|
114
|
+
resizeObserver.observe(containerElement);
|
|
115
|
+
return () => {
|
|
116
|
+
clearTimeout(timeoutId);
|
|
117
|
+
containerElement.removeEventListener("scroll", handleScroll);
|
|
118
|
+
window.removeEventListener("resize", checkScrollability);
|
|
119
|
+
resizeObserver.disconnect();
|
|
120
|
+
};
|
|
121
|
+
}, [checkDelay, containerElement, checkScrollability]);
|
|
122
|
+
const scrollBackward = react.useCallback(() => {
|
|
123
|
+
const delta = -scrollAmount;
|
|
124
|
+
containerRef.current?.scrollBy(
|
|
125
|
+
axis === "x" ? { left: delta, behavior: "smooth" } : { top: delta, behavior: "smooth" }
|
|
126
|
+
);
|
|
127
|
+
}, [axis, scrollAmount]);
|
|
128
|
+
const scrollForward = react.useCallback(() => {
|
|
129
|
+
containerRef.current?.scrollBy(
|
|
130
|
+
axis === "x" ? { left: scrollAmount, behavior: "smooth" } : { top: scrollAmount, behavior: "smooth" }
|
|
131
|
+
);
|
|
132
|
+
}, [axis, scrollAmount]);
|
|
133
|
+
return {
|
|
134
|
+
containerRef: assignContainerRef,
|
|
135
|
+
canScrollBackward,
|
|
136
|
+
canScrollForward,
|
|
137
|
+
scrollBackward,
|
|
138
|
+
scrollForward,
|
|
139
|
+
checkScrollability,
|
|
140
|
+
preserveScrollPosition
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
exports.DEFAULT_SCROLL_POSITION_KEY = DEFAULT_SCROLL_POSITION_KEY;
|
|
145
|
+
exports.SCROLL_AMOUNT = SCROLL_AMOUNT;
|
|
146
|
+
exports.SCROLL_CHECK_DELAY = SCROLL_CHECK_DELAY;
|
|
147
|
+
exports.useScrollableContainer = useScrollableContainer;
|
|
148
|
+
//# sourceMappingURL=use-scrollable-container.cjs.map
|
|
149
|
+
//# sourceMappingURL=use-scrollable-container.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../use-scrollable-container.ts"],"names":["useRef","useState","useMemo","useCallback","debounce","useLayoutEffect","useEffect"],"mappings":"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","file":"use-scrollable-container.cjs","sourcesContent":["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,38 @@
|
|
|
1
|
+
import { Ref } from 'react';
|
|
2
|
+
|
|
3
|
+
declare const SCROLL_AMOUNT = 80;
|
|
4
|
+
declare const SCROLL_CHECK_DELAY = 100;
|
|
5
|
+
declare const DEFAULT_SCROLL_POSITION_KEY = "scrollable-container-position";
|
|
6
|
+
type ScrollAxis = 'x' | 'y';
|
|
7
|
+
interface ScrollStorageAdapter {
|
|
8
|
+
getItem(key: string): string | null;
|
|
9
|
+
setItem(key: string, value: string): void;
|
|
10
|
+
}
|
|
11
|
+
interface UseScrollableContainerOptions {
|
|
12
|
+
/** Session/storage key used to persist scroll position. */
|
|
13
|
+
scrollPositionKey: string;
|
|
14
|
+
/** Axis to measure and scroll. */
|
|
15
|
+
axis?: ScrollAxis;
|
|
16
|
+
/** Distance used by scroll helpers. */
|
|
17
|
+
scrollAmount?: number;
|
|
18
|
+
/** Delay before initial scrollability check. */
|
|
19
|
+
checkDelay?: number;
|
|
20
|
+
/** Optional storage adapter. Pass `false` to disable persistence. */
|
|
21
|
+
storage?: ScrollStorageAdapter | false;
|
|
22
|
+
}
|
|
23
|
+
interface UseScrollableContainerReturn {
|
|
24
|
+
/** Callback ref — attach to the scrollable element. */
|
|
25
|
+
containerRef: Ref<HTMLElement | null>;
|
|
26
|
+
canScrollBackward: boolean;
|
|
27
|
+
canScrollForward: boolean;
|
|
28
|
+
scrollBackward: () => void;
|
|
29
|
+
scrollForward: () => void;
|
|
30
|
+
checkScrollability: () => void;
|
|
31
|
+
preserveScrollPosition: () => void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Scrollable container with position persistence and scroll button state.
|
|
35
|
+
*/
|
|
36
|
+
declare function useScrollableContainer({ scrollPositionKey, axis, scrollAmount, checkDelay, storage, }: UseScrollableContainerOptions): UseScrollableContainerReturn;
|
|
37
|
+
|
|
38
|
+
export { DEFAULT_SCROLL_POSITION_KEY, SCROLL_AMOUNT, SCROLL_CHECK_DELAY, type ScrollAxis, type ScrollStorageAdapter, type UseScrollableContainerOptions, type UseScrollableContainerReturn, useScrollableContainer };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Ref } from 'react';
|
|
2
|
+
|
|
3
|
+
declare const SCROLL_AMOUNT = 80;
|
|
4
|
+
declare const SCROLL_CHECK_DELAY = 100;
|
|
5
|
+
declare const DEFAULT_SCROLL_POSITION_KEY = "scrollable-container-position";
|
|
6
|
+
type ScrollAxis = 'x' | 'y';
|
|
7
|
+
interface ScrollStorageAdapter {
|
|
8
|
+
getItem(key: string): string | null;
|
|
9
|
+
setItem(key: string, value: string): void;
|
|
10
|
+
}
|
|
11
|
+
interface UseScrollableContainerOptions {
|
|
12
|
+
/** Session/storage key used to persist scroll position. */
|
|
13
|
+
scrollPositionKey: string;
|
|
14
|
+
/** Axis to measure and scroll. */
|
|
15
|
+
axis?: ScrollAxis;
|
|
16
|
+
/** Distance used by scroll helpers. */
|
|
17
|
+
scrollAmount?: number;
|
|
18
|
+
/** Delay before initial scrollability check. */
|
|
19
|
+
checkDelay?: number;
|
|
20
|
+
/** Optional storage adapter. Pass `false` to disable persistence. */
|
|
21
|
+
storage?: ScrollStorageAdapter | false;
|
|
22
|
+
}
|
|
23
|
+
interface UseScrollableContainerReturn {
|
|
24
|
+
/** Callback ref — attach to the scrollable element. */
|
|
25
|
+
containerRef: Ref<HTMLElement | null>;
|
|
26
|
+
canScrollBackward: boolean;
|
|
27
|
+
canScrollForward: boolean;
|
|
28
|
+
scrollBackward: () => void;
|
|
29
|
+
scrollForward: () => void;
|
|
30
|
+
checkScrollability: () => void;
|
|
31
|
+
preserveScrollPosition: () => void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Scrollable container with position persistence and scroll button state.
|
|
35
|
+
*/
|
|
36
|
+
declare function useScrollableContainer({ scrollPositionKey, axis, scrollAmount, checkDelay, storage, }: UseScrollableContainerOptions): UseScrollableContainerReturn;
|
|
37
|
+
|
|
38
|
+
export { DEFAULT_SCROLL_POSITION_KEY, SCROLL_AMOUNT, SCROLL_CHECK_DELAY, type ScrollAxis, type ScrollStorageAdapter, type UseScrollableContainerOptions, type UseScrollableContainerReturn, useScrollableContainer };
|