Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: scroll profiles issue and right arrow displaying #1661

Merged
merged 3 commits into from
Nov 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ interface Props {
index: number;
}

export default function ProfileTabFullScreen ({ index, isHovered, orderedAccounts, selectedProfile, setSelectedProfile, text }: Props): React.ReactElement {
function ProfileTabFullScreen ({ index, isHovered, orderedAccounts, selectedProfile, setSelectedProfile, text }: Props): React.ReactElement {
const { t } = useTranslation();
const theme = useTheme();
const { alerts, notify } = useAlerts();
Expand Down Expand Up @@ -114,6 +114,7 @@ export default function ProfileTabFullScreen ({ index, isHovered, orderedAccount
cursor: 'pointer',
flexShrink: 0,
minWidth: '100px',
mr: '5px',
mt: '2px',
opacity: isDarkMode ? (visibleContent ? 1 : 0.3) : undefined,
position: 'relative',
Expand Down Expand Up @@ -151,3 +152,5 @@ export default function ProfileTabFullScreen ({ index, isHovered, orderedAccount
</Infotip2>
);
}

export default React.memo(ProfileTabFullScreen);
Original file line number Diff line number Diff line change
Expand Up @@ -19,69 +19,136 @@ interface Props {

export const HIDDEN_PERCENT = '50%';

export default function ProfileTabsFullScreen ({ orderedAccounts }: Props): React.ReactElement {
function ProfileTabsFullScreen ({ orderedAccounts }: Props): React.ReactElement {
const { defaultProfiles, userDefinedProfiles } = useProfiles();

const [selectedProfile, setSelectedProfile] = useState<string>();
const [isHovered, setIsHovered] = useState<boolean>();

const [showLeftMore, setShowLeftMore] = useState(false);
const [showRightMore, setShowRightMore] = useState(true);
const [showRightMore, setShowRightMore] = useState(false);
const [isContentReady, setIsContentReady] = useState(false);

const scrollContainerRef = useRef<HTMLDivElement>(null);
const checkTimeoutRef = useRef<ReturnType<typeof setTimeout>>();

const profilesToShow = useMemo(() => {
if (defaultProfiles.length === 0 && userDefinedProfiles.length === 0) {
return [];
}

return defaultProfiles.concat(userDefinedProfiles);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [defaultProfiles.length, userDefinedProfiles.length]);
}, [defaultProfiles, userDefinedProfiles]);

const onMouseEnter = useCallback(() => setIsHovered(true), []);
const onMouseLeave = useCallback(() => setIsHovered(false), []);

const handleScroll = () => {
if (scrollContainerRef.current) {
const { clientWidth, scrollLeft, scrollWidth } = scrollContainerRef.current;
const handleScroll = useCallback(() => {
const container = scrollContainerRef.current;

if (container && isContentReady) {
let { clientWidth, scrollLeft, scrollWidth } = container;

scrollLeft = Math.round(scrollLeft);

// Check if content is actually scrollable
const isScrollable = scrollWidth > clientWidth;
const tolerance = 10;

setShowLeftMore(scrollLeft > 0);
setShowRightMore(scrollLeft + clientWidth < scrollWidth - tolerance && isScrollable);
// Show left arrow if we've scrolled at least 1px
setShowLeftMore(scrollLeft > 1);

// Show right arrow if there's more content to scroll to
// We add a small buffer (1px) to account for rounding errors
const remainingScroll = scrollWidth - (scrollLeft + clientWidth);

setShowRightMore(isScrollable && remainingScroll > 1);
}
};
}, [isContentReady]);

const handleWheel = useCallback((event: WheelEvent) => {
if (scrollContainerRef.current) {
event.preventDefault();
scrollContainerRef.current.scrollLeft += (event.deltaY || event.deltaX);

// Make the scroll amount more controlled
const scrollAmount = event.deltaY || event.deltaX;
const normalizedScroll = Math.sign(scrollAmount) * Math.min(Math.abs(scrollAmount), 50);

scrollContainerRef.current.scrollLeft += normalizedScroll;
handleScroll();
}
}, []);
}, [handleScroll]);

// Initial setup and content ready check
useEffect(() => {
handleScroll(); // Set initial shadow states on mount
const ref = scrollContainerRef.current;
const container = scrollContainerRef.current;

if (container) {
// Clear any existing timeout
if (checkTimeoutRef.current) {
clearTimeout(checkTimeoutRef.current);
}

// Function to check if content is stable
const checkContentStability = () => {
const { clientWidth, scrollWidth } = container;

if (scrollWidth && clientWidth) {
setIsContentReady(true);
handleScroll();
} else {
// If content isn't ready, check again in a moment
checkTimeoutRef.current = setTimeout(checkContentStability, 50);
}
};

if (ref) {
ref.addEventListener('scroll', handleScroll);
ref.addEventListener('wheel', handleWheel, { passive: false });
// Start checking content stability
checkContentStability();

return () => {
ref.removeEventListener('scroll', handleScroll);
ref.removeEventListener('wheel', handleWheel);
if (checkTimeoutRef.current) {
clearTimeout(checkTimeoutRef.current);
}
};
}

return undefined;
}, [handleWheel, profilesToShow.length]);
}, [handleScroll, profilesToShow]);

// Scroll and resize handlers
useEffect(() => {
const container = scrollContainerRef.current;

if (container && isContentReady) {
// Add scroll event listener
const scrollListener = () => {
requestAnimationFrame(handleScroll);
};

container.addEventListener('scroll', scrollListener);
container.addEventListener('wheel', handleWheel, { passive: false });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Potential performance implications with non-passive wheel event listener

Setting { passive: false } for the 'wheel' event listener allows event.preventDefault(), but it can impact scrolling performance.

Review if preventing the default wheel behavior is necessary. If so, consider alternatives that minimize performance impact. If not, omit event.preventDefault() and allow the listener to remain passive.


// Check on resize
const resizeObserver = new ResizeObserver(() => {
requestAnimationFrame(handleScroll);
});

resizeObserver.observe(container);

return () => {
container.removeEventListener('scroll', scrollListener);
container.removeEventListener('wheel', handleWheel);
resizeObserver.disconnect();
};
}

return undefined;
}, [handleScroll, handleWheel, isContentReady]);

return (
<Grid container sx={{ display: 'flex', height: '30px', mb: '10px', position: 'relative' }}>
{showLeftMore && <ArrowForwardIosRoundedIcon sx={{ color: 'text.disabled', fontSize: '20px', transform: 'rotate(-180deg)', width: 'fit-content', zIndex: 1 }} />}
<Grid columnGap='5px' container item
{isHovered && isContentReady && showLeftMore && <ArrowForwardIosRoundedIcon sx={{ color: 'text.disabled', fontSize: '20px', transform: 'rotate(-180deg)', width: 'fit-content', zIndex: 1 }} />}
<Grid container display='ruby'
item
justifyContent='left'
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
Expand Down Expand Up @@ -110,7 +177,9 @@ export default function ProfileTabsFullScreen ({ orderedAccounts }: Props): Reac
))
}
</Grid>
{showRightMore && <ArrowForwardIosRoundedIcon sx={{ color: 'text.disabled', fontSize: '20px', width: 'fit-content', zIndex: 1 }} />}
{isHovered && isContentReady && showRightMore && <ArrowForwardIosRoundedIcon sx={{ color: 'text.disabled', fontSize: '20px', width: 'fit-content', zIndex: 1 }} />}
</Grid>
);
}

export default React.memo(ProfileTabsFullScreen);
Loading