Skip to content

Commit afe626f

Browse files
committed
[@etecture/rex] Scrollbar: adds scrolling visibility with timeout and disables hover visibility on touch screens
1 parent 9b97467 commit afe626f

File tree

3 files changed

+69
-16
lines changed

3 files changed

+69
-16
lines changed

core/src/components/Scrollbar/Scrollbar.module.css

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,16 @@
4646
top: var(--rex-scrollbar-offset, 2px);
4747
bottom: var(--rex-scrollbar-offset, 2px);
4848
right: var(--rex-scrollbar-offset, 2px);
49-
width: calc(var(--rex-scrollbar-width, 10px) - calc(var(--rex-scrollbar-padding, 1px) * 2));
49+
width: calc(
50+
var(--rex-scrollbar-width, 10px) -
51+
calc(var(--rex-scrollbar-padding, 1px) * 2)
52+
);
5053

5154
& .scrollbarThumb {
52-
width: calc(var(--rex-scrollbar-width, 10px) - calc(var(--rex-scrollbar-padding, 1px) * 2));
55+
width: calc(
56+
var(--rex-scrollbar-width, 10px) -
57+
calc(var(--rex-scrollbar-padding, 1px) * 2)
58+
);
5359
height: 50%;
5460
min-height: 40px;
5561
}
@@ -59,10 +65,16 @@
5965
left: var(--rex-scrollbar-offset, 2px);
6066
bottom: var(--rex-scrollbar-offset, 2px);
6167
right: var(--rex-scrollbar-offset, 2px);
62-
height: calc(var(--rex-scrollbar-width, 10px) - calc(var(--rex-scrollbar-padding, 1px) * 2));
68+
height: calc(
69+
var(--rex-scrollbar-width, 10px) -
70+
calc(var(--rex-scrollbar-padding, 1px) * 2)
71+
);
6372

6473
& .scrollbarThumb {
65-
height: calc(var(--rex-scrollbar-width, 10px) - calc(var(--rex-scrollbar-padding, 1px) * 2));
74+
height: calc(
75+
var(--rex-scrollbar-width, 10px) -
76+
calc(var(--rex-scrollbar-padding, 1px) * 2)
77+
);
6678
width: 50%;
6779
min-width: 40px;
6880
}
@@ -88,33 +100,56 @@
88100
border-radius: 10px;
89101

90102
&:active {
91-
background: var(--rex-scrollbar-active-thumb-color, rgba(0, 0, 0, 0.95));
103+
background: var(
104+
--rex-scrollbar-active-thumb-color,
105+
rgba(0, 0, 0, 0.95)
106+
);
92107
}
93108
}
94109
}
95110

96111
&.hasVerticalScroll > .horizontalTrack {
97-
right: calc(var(--rex-scrollbar-offset, 2px) + calc(var(--rex-scrollbar-width, 10px) * 1.2));
112+
right: calc(
113+
var(--rex-scrollbar-offset, 2px) +
114+
calc(var(--rex-scrollbar-width, 10px) * 1.2)
115+
);
98116
}
99117

100118
&.hasHorizontalScroll > .verticalTrack {
101-
bottom: calc(var(--rex-scrollbar-offset, 2px) + calc(var(--rex-scrollbar-width, 10px) * 1.2));
119+
bottom: calc(
120+
var(--rex-scrollbar-offset, 2px) +
121+
calc(var(--rex-scrollbar-width, 10px) * 1.2)
122+
);
102123
}
103124

104-
&.hasVerticalScroll.visible,
105-
&.hasVerticalScroll:hover {
125+
&.hasVerticalScroll.visible {
106126
&:not(.trackHidden) > .verticalTrack {
107127
opacity: 1;
108128
visibility: visible;
109129
}
110130
}
111131

112-
&.hasHorizontalScroll.visible,
113-
&.hasHorizontalScroll:hover {
132+
&.hasHorizontalScroll.visible {
114133
&:not(.trackHidden) > .horizontalTrack {
115134
opacity: 1;
116135
visibility: visible;
117136
}
118137
}
138+
139+
@media (hover: hover) {
140+
&.hasVerticalScroll:hover {
141+
&:not(.trackHidden) > .verticalTrack {
142+
opacity: 1;
143+
visibility: visible;
144+
}
145+
}
146+
147+
&.hasHorizontalScroll:hover {
148+
&:not(.trackHidden) > .horizontalTrack {
149+
opacity: 1;
150+
visibility: visible;
151+
}
152+
}
153+
}
119154
}
120155
}

core/src/components/Scrollbar/Scrollbar.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ export interface ScrollbarProps
6969
*/
7070
verticalScrollDisabled?: boolean;
7171

72+
/**
73+
* The amount of time the scrollbar remains visible after scrolling
74+
* @default 800
75+
*/
76+
visibilityTimeout?: number;
77+
7278
/**
7379
* An optional reference to the scroll container.
7480
* This is the element on which the scroll events happen.
@@ -102,6 +108,7 @@ export const Scrollbar: React.FC<ScrollbarProps> = (props) => {
102108
verticalScrollDisabled = false,
103109
horizontalScrollDisabled = false,
104110
style,
111+
visibilityTimeout = 800,
105112
...divProps
106113
} = props;
107114

@@ -118,10 +125,11 @@ export const Scrollbar: React.FC<ScrollbarProps> = (props) => {
118125
const verticalScrollbarRef = useRef<HTMLDivElement | null>(null);
119126
const horizontalScrollbarRef = useRef<HTMLDivElement | null>(null);
120127

121-
useScrollHandler({
128+
const { isScrolling } = useScrollHandler({
122129
horizontalScrollbarRef,
123130
verticalScrollbarRef,
124131
contentRef,
132+
visibilityTimeout,
125133
});
126134

127135
const { horizontalDragging, verticalDragging } = useDragHandler({
@@ -137,7 +145,7 @@ export const Scrollbar: React.FC<ScrollbarProps> = (props) => {
137145
visibility,
138146
});
139147

140-
const isTrackVisible = visibility === "always" || isDragging;
148+
const isTrackVisible = visibility === "always" || isDragging || isScrolling;
141149
const isTrackHidden = visibility === "never";
142150

143151
const containerClassNames = clsx(

core/src/components/Scrollbar/hooks/useScrollHandler.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
1-
import { RefObject, useEffect } from "react";
1+
import { RefObject, useEffect, useRef, useState } from "react";
22

33
export type UseScrollbarHandlerProps = {
44
verticalScrollbarRef: RefObject<HTMLDivElement | null>;
55
horizontalScrollbarRef: RefObject<HTMLDivElement | null>;
66
contentRef: RefObject<HTMLDivElement>;
7+
visibilityTimeout: number;
78
onScroll?: () => void;
89
};
910

1011
export const useScrollHandler = (props: UseScrollbarHandlerProps) => {
11-
const { verticalScrollbarRef, horizontalScrollbarRef, contentRef, onScroll } = props;
12+
const { verticalScrollbarRef, horizontalScrollbarRef, contentRef, visibilityTimeout, onScroll } =
13+
props;
14+
15+
const [isScrolling, setIsScrolling] = useState(false);
16+
const scrollTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
1217

1318
useEffect(() => {
1419
const handleScroll = () => {
1520
const contentWidth = contentRef.current?.clientWidth ?? 0;
1621
const contentHeight = contentRef.current?.clientHeight ?? 0;
1722

1823
onScroll?.();
24+
setIsScrolling(true);
25+
clearTimeout(scrollTimeoutRef.current);
26+
scrollTimeoutRef.current = setTimeout(() => setIsScrolling(false), visibilityTimeout);
1927

2028
if (contentRef.current && verticalScrollbarRef.current && horizontalScrollbarRef.current) {
2129
// VERTICAL
@@ -55,5 +63,7 @@ export const useScrollHandler = (props: UseScrollbarHandlerProps) => {
5563
return () => {
5664
contentRef.current?.removeEventListener("scroll", handleScroll);
5765
};
58-
}, [contentRef, verticalScrollbarRef, horizontalScrollbarRef, onScroll]);
66+
}, [contentRef, verticalScrollbarRef, horizontalScrollbarRef, visibilityTimeout, onScroll]);
67+
68+
return { isScrolling };
5969
};

0 commit comments

Comments
 (0)