Skip to content

Commit e1f3985

Browse files
minORCray-oxd
authored andcommitted
DBC22-4237: Updated handle scroll to use IntersectionObserver for style changes on sticky filters
1 parent 00d7fa2 commit e1f3985

3 files changed

Lines changed: 63 additions & 28 deletions

File tree

src/frontend/src/pages/CamerasListPage.js

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -256,22 +256,36 @@ export default function CamerasListPage() {
256256

257257
// Handle sticky filters on mobile
258258
useEffect(() => {
259-
const element = document.querySelector('.sticky-filters');
260-
if (!element) return;
261-
262-
const handleScroll = () => {
263-
const rect = element.getBoundingClientRect();
264-
// If the element's top is less than or equal to headerHeight, it's stuck
265-
const stuck = rect.top <= headerHeightContext;
266-
element.toggleAttribute('stuck', stuck);
259+
const sentinel = document.querySelector('.sticky-sentinel');
260+
const target = document.querySelector('.sticky-filters');
261+
if (!sentinel || !target) return;
262+
263+
let rafId = null;
264+
265+
const updateStuck = (entry) => {
266+
if (rafId) cancelAnimationFrame(rafId);
267+
rafId = requestAnimationFrame(() => {
268+
const isStuck = entry.intersectionRatio === 0;
269+
target.toggleAttribute('stuck', isStuck);
270+
});
267271
};
268272

269-
// window.addEventListener('scroll', handleScroll);
270-
// // Initial check
271-
// handleScroll();
273+
const observer = new IntersectionObserver(
274+
([entry]) => updateStuck(entry),
275+
{
276+
root: null,
277+
threshold: [0],
278+
rootMargin: '-120px 0px 0px 0px',
279+
}
280+
);
281+
282+
observer.observe(sentinel);
272283

273-
return () => window.removeEventListener('scroll', handleScroll);
274-
}, [headerHeightContext]);
284+
return () => {
285+
observer.disconnect();
286+
if (rafId) cancelAnimationFrame(rafId);
287+
};
288+
}, []);
275289

276290
return (
277291
<React.Fragment>
@@ -298,6 +312,7 @@ export default function CamerasListPage() {
298312
}
299313

300314
<div className="container--sidepanel__right">
315+
<div className="sticky-sentinel" />
301316
<div className="sticky-filters" style={{ top: `${headerHeightContext}px` }}>
302317
<div className="controls-group">
303318
<div className="controls-container">

src/frontend/src/pages/ContainerSidePanel.scss

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,20 @@
121121

122122
&__right {
123123
flex: 1 1 auto;
124+
125+
.sticky-sentinel {
126+
height: 1px;
127+
background: none;
128+
margin: -1px;
129+
}
124130

125131
.sticky-filters {
126132
position: sticky;
127133
z-index: 1;
128134
top: 0;
129135
padding: 20px 0;
130136
background: $White;
137+
will-change: transform;
131138

132139
&[stuck] {
133140
@media (max-width: 575px) {
@@ -141,8 +148,6 @@
141148
@media (max-width: 575px) {
142149
padding: 0;
143150
z-index: 20;
144-
border-radius: 8px;
145-
border: 1px solid $Input-border;
146151

147152
.controls-container {
148153
.filters-text {

src/frontend/src/pages/EventsListPage.js

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -545,22 +545,36 @@ export default function EventsListPage() {
545545

546546
// Handle sticky filters on mobile
547547
useEffect(() => {
548-
const element = document.querySelector('.sticky-filters');
549-
if (!element) return;
550-
551-
const handleScroll = () => {
552-
const rect = element.getBoundingClientRect();
553-
// If the element's top is less than or equal to headerHeight, it's stuck
554-
const stuck = rect.top <= headerHeightContext;
555-
element.toggleAttribute('stuck', stuck);
548+
const sentinel = document.querySelector('.sticky-sentinel');
549+
const target = document.querySelector('.sticky-filters');
550+
if (!sentinel || !target) return;
551+
552+
let rafId = null;
553+
554+
const updateStuck = (entry) => {
555+
if (rafId) cancelAnimationFrame(rafId);
556+
rafId = requestAnimationFrame(() => {
557+
const isStuck = entry.intersectionRatio === 0;
558+
target.toggleAttribute('stuck', isStuck);
559+
});
556560
};
557561

558-
// window.addEventListener('scroll', handleScroll);
559-
// // Initial check
560-
// handleScroll();
562+
const observer = new IntersectionObserver(
563+
([entry]) => updateStuck(entry),
564+
{
565+
root: null,
566+
threshold: [0],
567+
rootMargin: '-120px 0px 0px 0px',
568+
}
569+
);
570+
571+
observer.observe(sentinel);
561572

562-
return () => window.removeEventListener('scroll', handleScroll);
563-
}, [headerHeightContext]);
573+
return () => {
574+
observer.disconnect();
575+
if (rafId) cancelAnimationFrame(rafId);
576+
};
577+
}, []);
564578

565579
// Rendering - Main component
566580
return (
@@ -588,6 +602,7 @@ export default function EventsListPage() {
588602
}
589603

590604
<div className="container--sidepanel__right">
605+
<div className="sticky-sentinel" />
591606
<div className="sticky-filters" style={{ top: `${headerHeightContext}px` }}>
592607
<div className="controls-group">
593608
<div className="controls-container">

0 commit comments

Comments
 (0)