@choice-ui/react 1.9.4 → 1.9.7

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 (29) hide show
  1. package/dist/components/command/dist/index.d.ts +3 -0
  2. package/dist/components/command/src/components/command-list.d.ts +3 -0
  3. package/dist/components/command/src/components/command-list.js +4 -1
  4. package/dist/components/numeric-input/dist/index.d.ts +2 -0
  5. package/dist/components/numeric-input/dist/index.js +40 -13
  6. package/dist/components/numeric-input/src/context/numeric-input-context.d.ts +1 -0
  7. package/dist/components/numeric-input/src/hooks/use-input-interactions.d.ts +3 -1
  8. package/dist/components/numeric-input/src/hooks/use-numeric-input.d.ts +1 -0
  9. package/dist/components/numeric-input/src/hooks/use-numeric-input.js +36 -13
  10. package/dist/components/numeric-input/src/numeric-input.d.ts +1 -0
  11. package/dist/components/numeric-input/src/numeric-input.js +4 -0
  12. package/dist/components/scroll-area/dist/index.d.ts +4 -27
  13. package/dist/components/scroll-area/dist/index.js +96 -123
  14. package/dist/components/scroll-area/src/components/scroll-area-content.js +2 -2
  15. package/dist/components/scroll-area/src/components/scroll-area-root.js +9 -12
  16. package/dist/components/scroll-area/src/components/scroll-area-scrollbar.js +14 -4
  17. package/dist/components/scroll-area/src/components/scroll-area-viewport.js +2 -2
  18. package/dist/components/scroll-area/src/context/scroll-area-context.d.ts +17 -2
  19. package/dist/components/scroll-area/src/context/scroll-area-context.js +23 -6
  20. package/dist/components/scroll-area/src/hooks/index.d.ts +0 -1
  21. package/dist/components/scroll-area/src/hooks/use-scroll-state-and-visibility.d.ts +2 -2
  22. package/dist/components/scroll-area/src/hooks/use-scroll-state-and-visibility.js +30 -75
  23. package/dist/components/scroll-area/src/hooks/use-thumb.d.ts +1 -1
  24. package/dist/components/scroll-area/src/hooks/use-thumb.js +25 -28
  25. package/dist/components/scroll-area/src/types.d.ts +16 -4
  26. package/dist/index.js +0 -2
  27. package/package.json +1 -1
  28. package/dist/components/scroll-area/src/hooks/use-scroll-performance-monitor.d.ts +0 -23
  29. package/dist/components/scroll-area/src/hooks/use-scroll-performance-monitor.js +0 -123
@@ -1,5 +1,5 @@
1
1
  import { useState, useRef, useCallback, useEffect } from "react";
2
- function useScrollStateAndVisibility(viewport) {
2
+ function useScrollStateAndVisibility(viewport, content) {
3
3
  const [scrollState, setScrollState] = useState({
4
4
  scrollLeft: 0,
5
5
  scrollTop: 0,
@@ -13,8 +13,6 @@ function useScrollStateAndVisibility(viewport) {
13
13
  const scrollTimeoutRef = useRef();
14
14
  const rafRef = useRef();
15
15
  const resizeObserverRef = useRef();
16
- const mutationObserverRef = useRef();
17
- const mutationTimeoutRef = useRef();
18
16
  const lastUpdateTimeRef = useRef(0);
19
17
  const minUpdateIntervalRef = useRef(16);
20
18
  const updateScrollState = useCallback(() => {
@@ -26,36 +24,36 @@ function useScrollStateAndVisibility(viewport) {
26
24
  cancelAnimationFrame(rafRef.current);
27
25
  }
28
26
  rafRef.current = requestAnimationFrame(() => {
27
+ rafRef.current = void 0;
29
28
  updateScrollState();
30
29
  });
31
30
  return;
32
31
  }
33
32
  if (rafRef.current) {
34
33
  cancelAnimationFrame(rafRef.current);
34
+ rafRef.current = void 0;
35
35
  }
36
- rafRef.current = requestAnimationFrame(() => {
37
- const newState = {
38
- scrollLeft: viewport.scrollLeft,
39
- scrollTop: viewport.scrollTop,
40
- scrollWidth: viewport.scrollWidth,
41
- scrollHeight: viewport.scrollHeight,
42
- clientWidth: viewport.clientWidth,
43
- clientHeight: viewport.clientHeight
44
- };
45
- setScrollState((prevState) => {
46
- const scrollLeftChanged = Math.abs(prevState.scrollLeft - newState.scrollLeft) > 0.5;
47
- const scrollTopChanged = Math.abs(prevState.scrollTop - newState.scrollTop) > 0.5;
48
- const scrollWidthChanged = prevState.scrollWidth !== newState.scrollWidth;
49
- const scrollHeightChanged = prevState.scrollHeight !== newState.scrollHeight;
50
- const clientWidthChanged = prevState.clientWidth !== newState.clientWidth;
51
- const clientHeightChanged = prevState.clientHeight !== newState.clientHeight;
52
- const hasChanges = scrollLeftChanged || scrollTopChanged || scrollWidthChanged || scrollHeightChanged || clientWidthChanged || clientHeightChanged;
53
- if (hasChanges) {
54
- lastUpdateTimeRef.current = now;
55
- return newState;
56
- }
57
- return prevState;
58
- });
36
+ const newState = {
37
+ scrollLeft: viewport.scrollLeft,
38
+ scrollTop: viewport.scrollTop,
39
+ scrollWidth: viewport.scrollWidth,
40
+ scrollHeight: viewport.scrollHeight,
41
+ clientWidth: viewport.clientWidth,
42
+ clientHeight: viewport.clientHeight
43
+ };
44
+ setScrollState((prevState) => {
45
+ const scrollLeftChanged = Math.abs(prevState.scrollLeft - newState.scrollLeft) > 0.5;
46
+ const scrollTopChanged = Math.abs(prevState.scrollTop - newState.scrollTop) > 0.5;
47
+ const scrollWidthChanged = prevState.scrollWidth !== newState.scrollWidth;
48
+ const scrollHeightChanged = prevState.scrollHeight !== newState.scrollHeight;
49
+ const clientWidthChanged = prevState.clientWidth !== newState.clientWidth;
50
+ const clientHeightChanged = prevState.clientHeight !== newState.clientHeight;
51
+ const hasChanges = scrollLeftChanged || scrollTopChanged || scrollWidthChanged || scrollHeightChanged || clientWidthChanged || clientHeightChanged;
52
+ if (hasChanges) {
53
+ lastUpdateTimeRef.current = now;
54
+ return newState;
55
+ }
56
+ return prevState;
59
57
  });
60
58
  }, [viewport]);
61
59
  const delayedUpdateScrollState = useCallback(() => {
@@ -92,54 +90,19 @@ function useScrollStateAndVisibility(viewport) {
92
90
  passive: true,
93
91
  signal,
94
92
  capture: false
95
- // Avoid unnecessary event capture
96
93
  });
97
94
  window.addEventListener("resize", handleResize, {
98
95
  passive: true,
99
96
  signal
100
97
  });
101
98
  if (window.ResizeObserver) {
102
- resizeObserverRef.current = new ResizeObserver((entries) => {
103
- for (const entry of entries) {
104
- if (entry.target === viewport) {
105
- updateScrollState();
106
- break;
107
- }
108
- }
99
+ resizeObserverRef.current = new ResizeObserver(() => {
100
+ updateScrollState();
109
101
  });
110
102
  resizeObserverRef.current.observe(viewport);
111
- }
112
- if (window.MutationObserver) {
113
- mutationObserverRef.current = new MutationObserver((mutations) => {
114
- const hasLayoutChanges = mutations.some((mutation) => {
115
- if (mutation.type === "childList") {
116
- return mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0;
117
- }
118
- if (mutation.type === "attributes") {
119
- const attr = mutation.attributeName;
120
- return attr === "style" || attr === "class";
121
- }
122
- return mutation.type === "characterData";
123
- });
124
- if (!hasLayoutChanges) return;
125
- if (mutationTimeoutRef.current) {
126
- clearTimeout(mutationTimeoutRef.current);
127
- }
128
- mutationTimeoutRef.current = window.setTimeout(() => {
129
- updateScrollState();
130
- }, 16);
131
- });
132
- mutationObserverRef.current.observe(viewport, {
133
- childList: true,
134
- subtree: true,
135
- attributes: true,
136
- attributeFilter: ["style", "class"],
137
- // Only listen to attributes that affect layout
138
- characterData: true,
139
- characterDataOldValue: false,
140
- // No need for old value, improve performance
141
- attributeOldValue: false
142
- });
103
+ if (content) {
104
+ resizeObserverRef.current.observe(content);
105
+ }
143
106
  }
144
107
  delayedUpdateScrollState();
145
108
  return () => {
@@ -148,10 +111,6 @@ function useScrollStateAndVisibility(viewport) {
148
111
  clearTimeout(scrollTimeoutRef.current);
149
112
  scrollTimeoutRef.current = void 0;
150
113
  }
151
- if (mutationTimeoutRef.current) {
152
- clearTimeout(mutationTimeoutRef.current);
153
- mutationTimeoutRef.current = void 0;
154
- }
155
114
  if (rafRef.current) {
156
115
  cancelAnimationFrame(rafRef.current);
157
116
  rafRef.current = void 0;
@@ -160,12 +119,8 @@ function useScrollStateAndVisibility(viewport) {
160
119
  resizeObserverRef.current.disconnect();
161
120
  resizeObserverRef.current = void 0;
162
121
  }
163
- if (mutationObserverRef.current) {
164
- mutationObserverRef.current.disconnect();
165
- mutationObserverRef.current = void 0;
166
- }
167
122
  };
168
- }, [viewport, handleScroll, delayedUpdateScrollState]);
123
+ }, [viewport, content, handleScroll, delayedUpdateScrollState, updateScrollState]);
169
124
  const handleMouseEnter = useCallback(() => setIsHovering(true), []);
170
125
  const handleMouseLeave = useCallback(() => setIsHovering(false), []);
171
126
  return {
@@ -14,7 +14,7 @@ export declare function useThumbStyle(scrollState: ScrollState, orientation: "ve
14
14
  top?: undefined;
15
15
  };
16
16
  /**
17
- * 🚀 High-performance thumb drag hook - optimize drag responsiveness and performance
17
+ * High-performance thumb drag hook
18
18
  */
19
19
  export declare function useThumbDrag(viewport: HTMLDivElement | null, scrollState: ScrollState, orientation: "vertical" | "horizontal"): {
20
20
  isDragging: boolean;
@@ -42,16 +42,13 @@ function useThumbDrag(viewport, scrollState, orientation) {
42
42
  const isDragging = useRef(false);
43
43
  const startPos = useRef(0);
44
44
  const startScroll = useRef(0);
45
- const rafId = useRef();
46
45
  const cleanupRef = useRef(null);
46
+ const scrollStateRef = useRef(scrollState);
47
+ scrollStateRef.current = scrollState;
47
48
  const dragContextRef = useRef(null);
48
49
  useEffect(() => {
49
50
  return () => {
50
51
  isDragging.current = false;
51
- if (rafId.current) {
52
- cancelAnimationFrame(rafId.current);
53
- rafId.current = void 0;
54
- }
55
52
  if (cleanupRef.current) {
56
53
  cleanupRef.current();
57
54
  cleanupRef.current = null;
@@ -61,13 +58,21 @@ function useThumbDrag(viewport, scrollState, orientation) {
61
58
  const handleMouseDown = useCallback(
62
59
  (e) => {
63
60
  if (!viewport) return;
61
+ const currentScrollState = scrollStateRef.current;
64
62
  const target = e.currentTarget;
65
63
  const scrollbar = target.closest('[role="scrollbar"]');
66
64
  if (!scrollbar) return;
67
65
  const scrollbarRect = scrollbar.getBoundingClientRect();
68
- const scrollableRange = orientation === "vertical" ? Math.max(0, scrollState.scrollHeight - scrollState.clientHeight) : Math.max(0, scrollState.scrollWidth - scrollState.clientWidth);
66
+ const scrollableRange = orientation === "vertical" ? Math.max(0, currentScrollState.scrollHeight - currentScrollState.clientHeight) : Math.max(0, currentScrollState.scrollWidth - currentScrollState.clientWidth);
69
67
  const scrollbarRange = orientation === "vertical" ? scrollbarRect.height : scrollbarRect.width;
70
68
  if (scrollableRange <= 0 || scrollbarRange <= 0) return;
69
+ const thumbFraction = Math.max(
70
+ 0.1,
71
+ orientation === "vertical" ? currentScrollState.clientHeight / currentScrollState.scrollHeight : currentScrollState.clientWidth / currentScrollState.scrollWidth
72
+ );
73
+ const thumbSizePixels = scrollbarRange * thumbFraction;
74
+ const effectiveTrackRange = scrollbarRange - thumbSizePixels;
75
+ if (effectiveTrackRange <= 0) return;
71
76
  dragContextRef.current = {
72
77
  scrollbarRect,
73
78
  scrollableRange,
@@ -75,34 +80,26 @@ function useThumbDrag(viewport, scrollState, orientation) {
75
80
  };
76
81
  isDragging.current = true;
77
82
  startPos.current = orientation === "vertical" ? e.clientY : e.clientX;
78
- startScroll.current = orientation === "vertical" ? scrollState.scrollTop : scrollState.scrollLeft;
79
- const scrollRatio = scrollableRange / scrollbarRange;
83
+ startScroll.current = orientation === "vertical" ? currentScrollState.scrollTop : currentScrollState.scrollLeft;
84
+ const scrollRatio = scrollableRange / effectiveTrackRange;
80
85
  const handleMouseMove = (e2) => {
81
86
  if (!isDragging.current || !viewport || !dragContextRef.current) return;
82
- if (rafId.current) {
83
- cancelAnimationFrame(rafId.current);
87
+ const currentPos = orientation === "vertical" ? e2.clientY : e2.clientX;
88
+ const delta = currentPos - startPos.current;
89
+ const scrollDelta = delta * scrollRatio;
90
+ const newScrollValue = Math.max(
91
+ 0,
92
+ Math.min(startScroll.current + scrollDelta, dragContextRef.current.scrollableRange)
93
+ );
94
+ if (orientation === "vertical") {
95
+ viewport.scrollTop = newScrollValue;
96
+ } else {
97
+ viewport.scrollLeft = newScrollValue;
84
98
  }
85
- rafId.current = requestAnimationFrame(() => {
86
- const currentPos = orientation === "vertical" ? e2.clientY : e2.clientX;
87
- const delta = currentPos - startPos.current;
88
- const scrollDelta = delta * scrollRatio;
89
- const newScrollValue = Math.max(
90
- 0,
91
- Math.min(startScroll.current + scrollDelta, dragContextRef.current.scrollableRange)
92
- );
93
- if (orientation === "vertical") {
94
- viewport.scrollTop = newScrollValue;
95
- } else {
96
- viewport.scrollLeft = newScrollValue;
97
- }
98
- });
99
99
  };
100
100
  const handleMouseUp = () => {
101
101
  isDragging.current = false;
102
102
  dragContextRef.current = null;
103
- if (rafId.current) {
104
- cancelAnimationFrame(rafId.current);
105
- }
106
103
  document.removeEventListener("mousemove", handleMouseMove);
107
104
  document.removeEventListener("mouseup", handleMouseUp);
108
105
  cleanupRef.current = null;
@@ -116,7 +113,7 @@ function useThumbDrag(viewport, scrollState, orientation) {
116
113
  document.addEventListener("mouseup", handleMouseUp, { passive: true });
117
114
  e.preventDefault();
118
115
  },
119
- [viewport, orientation, scrollState]
116
+ [viewport, orientation]
120
117
  );
121
118
  return {
122
119
  isDragging: isDragging.current,
@@ -24,14 +24,22 @@ export interface ScrollPosition {
24
24
  * Render prop function type
25
25
  */
26
26
  export type ScrollAreaRenderProp = (position: ScrollPosition) => React.ReactNode;
27
- export interface ScrollAreaContextType {
28
- content: HTMLDivElement | null;
29
- hoverBoundary: HoverBoundary;
27
+ /**
28
+ * Frequently changing state — triggers re-renders on scroll
29
+ */
30
+ export interface ScrollAreaStateContextType {
30
31
  isHovering: boolean;
31
32
  isScrolling: boolean;
33
+ scrollState: ScrollState;
34
+ }
35
+ /**
36
+ * Rarely changing config, refs, setters — does NOT change on scroll
37
+ */
38
+ export interface ScrollAreaConfigContextType {
39
+ content: HTMLDivElement | null;
40
+ hoverBoundary: HoverBoundary;
32
41
  orientation: ScrollOrientation;
33
42
  rootId: string;
34
- scrollState: ScrollState;
35
43
  scrollbarMode: ScrollbarMode;
36
44
  scrollbarX: HTMLDivElement | null;
37
45
  scrollbarXId: string;
@@ -50,6 +58,10 @@ export interface ScrollAreaContextType {
50
58
  viewport: HTMLDivElement | null;
51
59
  viewportId: string;
52
60
  }
61
+ /**
62
+ * Combined context type for components that need both state and config
63
+ */
64
+ export type ScrollAreaContextType = ScrollAreaStateContextType & ScrollAreaConfigContextType;
53
65
  export interface ScrollAreaProps extends Omit<React.ComponentPropsWithoutRef<"div">, "children"> {
54
66
  /** Accessible name */
55
67
  "aria-label"?: string;
package/dist/index.js CHANGED
@@ -233,7 +233,6 @@ import { ScrollArea } from "./components/scroll-area/src/scroll-area.js";
233
233
  import { useScrollStateAndVisibility } from "./components/scroll-area/src/hooks/use-scroll-state-and-visibility.js";
234
234
  import { useThumbDrag, useThumbStyle } from "./components/scroll-area/src/hooks/use-thumb.js";
235
235
  import { useHasOverflow, useScrollbarShouldShow } from "./components/scroll-area/src/hooks/use-scrollbar.js";
236
- import { useScrollPerformanceMonitor } from "./components/scroll-area/src/hooks/use-scroll-performance-monitor.js";
237
236
  import { SearchInput } from "./components/search-input/src/search-input.js";
238
237
  import { Segmented } from "./components/segmented/src/segmented.js";
239
238
  import { Select } from "./components/select/src/select.js";
@@ -770,7 +769,6 @@ export {
770
769
  useRangeContext,
771
770
  useRangeTupleContext,
772
771
  useRenderData,
773
- useScrollPerformanceMonitor,
774
772
  useScrollStateAndVisibility,
775
773
  useScrollbarShouldShow,
776
774
  useSlot,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@choice-ui/react",
3
- "version": "1.9.4",
3
+ "version": "1.9.7",
4
4
  "description": "A desktop-first React UI component library built for professional desktop applications with comprehensive documentation",
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -1,23 +0,0 @@
1
- interface PerformanceMetrics {
2
- averageFrameTime: number;
3
- droppedFrames: number;
4
- maxFrameTime: number;
5
- scrollEventFrequency: number;
6
- updateFrequency: number;
7
- }
8
- interface PerformanceMonitorOptions {
9
- enabled?: boolean;
10
- frameTimeThreshold?: number;
11
- logInterval?: number;
12
- }
13
- /**
14
- * 🔍 ScrollArea performance monitoring Hook
15
- *
16
- * Used to monitor and diagnose scroll performance issues, including:
17
- * - Frame rate monitoring
18
- * - Event frequency statistics
19
- * - Performance bottleneck detection
20
- * - Real-time performance reporting
21
- */
22
- export declare function useScrollPerformanceMonitor(viewport: HTMLDivElement | null, options?: PerformanceMonitorOptions): PerformanceMetrics | null;
23
- export {};
@@ -1,123 +0,0 @@
1
- import { useState, useRef, useEffect } from "react";
2
- function useScrollPerformanceMonitor(viewport, options = {}) {
3
- const {
4
- enabled = false,
5
- // Default disabled, only enabled in development
6
- logInterval = 5e3,
7
- // Report every 5 seconds
8
- frameTimeThreshold = 16.67
9
- // 60fps threshold
10
- } = options;
11
- const [metrics, setMetrics] = useState({
12
- averageFrameTime: 0,
13
- maxFrameTime: 0,
14
- droppedFrames: 0,
15
- scrollEventFrequency: 0,
16
- updateFrequency: 0
17
- });
18
- const countersRef = useRef({
19
- frameCount: 0,
20
- totalFrameTime: 0,
21
- scrollEventCount: 0,
22
- updateCount: 0,
23
- lastReportTime: 0,
24
- maxFrameTime: 0,
25
- droppedFrames: 0
26
- });
27
- const lastFrameTimeRef = useRef(0);
28
- const reportIntervalRef = useRef();
29
- const updateIntervalRef = useRef();
30
- useEffect(() => {
31
- if (!enabled || !viewport) return;
32
- const startTime = performance.now();
33
- countersRef.current.lastReportTime = startTime;
34
- const handleScroll = () => {
35
- countersRef.current.scrollEventCount++;
36
- };
37
- const monitorFrame = () => {
38
- const now = performance.now();
39
- if (lastFrameTimeRef.current > 0) {
40
- const frameTime = now - lastFrameTimeRef.current;
41
- countersRef.current.totalFrameTime += frameTime;
42
- countersRef.current.frameCount++;
43
- if (frameTime > countersRef.current.maxFrameTime) {
44
- countersRef.current.maxFrameTime = frameTime;
45
- }
46
- if (frameTime > frameTimeThreshold) {
47
- countersRef.current.droppedFrames++;
48
- }
49
- }
50
- lastFrameTimeRef.current = now;
51
- countersRef.current.updateCount++;
52
- requestAnimationFrame(monitorFrame);
53
- };
54
- viewport.addEventListener("scroll", handleScroll, { passive: true });
55
- requestAnimationFrame(monitorFrame);
56
- updateIntervalRef.current = window.setInterval(() => {
57
- const now = performance.now();
58
- const timeElapsed = Math.max(1, now - (countersRef.current.lastReportTime || startTime));
59
- const currentMetrics = {
60
- averageFrameTime: countersRef.current.frameCount > 0 ? countersRef.current.totalFrameTime / countersRef.current.frameCount : 0,
61
- maxFrameTime: countersRef.current.maxFrameTime,
62
- droppedFrames: countersRef.current.droppedFrames,
63
- scrollEventFrequency: countersRef.current.scrollEventCount / timeElapsed * 1e3,
64
- updateFrequency: countersRef.current.updateCount / timeElapsed * 1e3
65
- };
66
- setMetrics(currentMetrics);
67
- }, 500);
68
- reportIntervalRef.current = window.setInterval(() => {
69
- const now = performance.now();
70
- const timeElapsed = now - countersRef.current.lastReportTime;
71
- const reportMetrics = {
72
- averageFrameTime: countersRef.current.frameCount > 0 ? countersRef.current.totalFrameTime / countersRef.current.frameCount : 0,
73
- maxFrameTime: countersRef.current.maxFrameTime,
74
- droppedFrames: countersRef.current.droppedFrames,
75
- scrollEventFrequency: countersRef.current.scrollEventCount / timeElapsed * 1e3,
76
- updateFrequency: countersRef.current.updateCount / timeElapsed * 1e3
77
- };
78
- console.group("🔍 ScrollArea Performance Report");
79
- console.log("📊 Frame Performance:");
80
- console.log(` • Average frame time: ${reportMetrics.averageFrameTime.toFixed(2)}ms`);
81
- console.log(` • Max frame time: ${reportMetrics.maxFrameTime.toFixed(2)}ms`);
82
- console.log(` • Dropped frames: ${reportMetrics.droppedFrames}`);
83
- console.log(` • Current FPS: ${(1e3 / reportMetrics.averageFrameTime).toFixed(1)}`);
84
- console.log("⚡ Event Frequency:");
85
- console.log(` • Scroll events/sec: ${reportMetrics.scrollEventFrequency.toFixed(1)}`);
86
- console.log(` • Updates/sec: ${reportMetrics.updateFrequency.toFixed(1)}`);
87
- if (reportMetrics.averageFrameTime > frameTimeThreshold) {
88
- console.warn("⚠️ Performance Warning: Average frame time exceeds 60fps threshold");
89
- }
90
- if (reportMetrics.droppedFrames > 10) {
91
- console.warn("⚠️ Performance Warning: High number of dropped frames detected");
92
- }
93
- if (reportMetrics.scrollEventFrequency > 200) {
94
- console.warn(
95
- "⚠️ Performance Warning: Very high scroll event frequency, consider throttling"
96
- );
97
- }
98
- console.groupEnd();
99
- countersRef.current = {
100
- frameCount: 0,
101
- totalFrameTime: 0,
102
- scrollEventCount: 0,
103
- updateCount: 0,
104
- lastReportTime: now,
105
- maxFrameTime: 0,
106
- droppedFrames: 0
107
- };
108
- }, logInterval);
109
- return () => {
110
- viewport.removeEventListener("scroll", handleScroll);
111
- if (reportIntervalRef.current) {
112
- clearInterval(reportIntervalRef.current);
113
- }
114
- if (updateIntervalRef.current) {
115
- clearInterval(updateIntervalRef.current);
116
- }
117
- };
118
- }, [enabled, viewport, logInterval, frameTimeThreshold]);
119
- return enabled ? metrics : null;
120
- }
121
- export {
122
- useScrollPerformanceMonitor
123
- };