Skip to content

Commit 8f7e0fb

Browse files
fix: scroll active sidebar items into view (#4965)
* Add centering of settings sidebar link when isActive * abstract redundant logic into a reusable hook * add jsdocs | refactor hook to consume behavior and block args * remove unused import * dev --------- Co-authored-by: Timothy Carambat <rambat1010@gmail.com>
1 parent 71cfff8 commit 8f7e0fb

File tree

4 files changed

+52
-7
lines changed

4 files changed

+52
-7
lines changed

.github/workflows/dev-build.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ concurrency:
66

77
on:
88
push:
9-
branches: ["web-push-notifications-bootstrap"] # put your current branch to create a build. Core team only.
9+
branches: ["4963-sidebar-selection-srcoll-into-view"] # put your current branch to create a build. Core team only.
1010
paths-ignore:
1111
- "**.md"
1212
- "cloud-deployments/*"

frontend/src/components/SettingsSidebar/MenuOption/index.jsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react";
22
import { CaretRight } from "@phosphor-icons/react";
33
import { Link, useLocation } from "react-router-dom";
44
import { safeJsonParse } from "@/utils/request";
5+
import useScrollActiveItemIntoView from "@/hooks/useScrollActiveItemIntoView";
56

67
export default function MenuOption({
78
btnText,
@@ -25,6 +26,18 @@ export default function MenuOption({
2526
location: location.pathname,
2627
});
2728

29+
const isActive = hasChildren
30+
? (!isExpanded &&
31+
childOptions.some((child) => child.href === location.pathname)) ||
32+
location.pathname === href
33+
: location.pathname === href;
34+
35+
const { ref } = useScrollActiveItemIntoView({
36+
isActive,
37+
behavior: "instant",
38+
block: "center",
39+
});
40+
2841
if (hidden) return null;
2942

3043
// If this option is a parent level option
@@ -43,12 +56,6 @@ export default function MenuOption({
4356
if (flex && !!user && !roles.includes(user?.role)) return null;
4457
}
4558

46-
const isActive = hasChildren
47-
? (!isExpanded &&
48-
childOptions.some((child) => child.href === location.pathname)) ||
49-
location.pathname === href
50-
: location.pathname === href;
51-
5259
const handleClick = (e) => {
5360
if (hasChildren) {
5461
e.preventDefault();
@@ -73,6 +80,7 @@ export default function MenuOption({
7380
`}
7481
>
7582
<Link
83+
ref={ref}
7684
to={href}
7785
className={`flex flex-grow items-center px-[12px] h-[32px] font-medium ${
7886
isChild ? "hover:text-white" : "text-white light:text-black"

frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import useScrollActiveItemIntoView from "@/hooks/useScrollActiveItemIntoView";
12
import Workspace from "@/models/workspace";
23
import paths from "@/utils/paths";
34
import showToast from "@/utils/toast";
@@ -30,6 +31,11 @@ export default function ThreadItem({
3031
? paths.workspace.chat(slug)
3132
: paths.workspace.thread(slug, thread.slug);
3233

34+
const { ref } = useScrollActiveItemIntoView({
35+
isActive,
36+
behavior: "instant",
37+
block: "center",
38+
});
3339
return (
3440
<div
3541
className="w-full relative flex h-[38px] items-center border-none rounded-lg"
@@ -88,6 +94,7 @@ export default function ThreadItem({
8894
</div>
8995
) : (
9096
<a
97+
ref={ref}
9198
href={
9299
window.location.pathname === linkTo || ctrlPressed ? "#" : linkTo
93100
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { useEffect, useRef } from "react";
2+
3+
/**
4+
* Hook that scrolls an element into view when it becomes active.
5+
* @param {Object} options - The options for the hook.
6+
* @param {boolean} options.isActive - Whether the element is currently active.
7+
* @param {"smooth" | "instant" | "auto"} options.behavior - The scroll behavior.
8+
* @param {"start" | "center" | "end" | "nearest"} options.block - The vertical alignment of the element within the scrollable container.
9+
* @returns {{ ref: React.RefObject<HTMLElement> }} An object containing the ref to attach to the target element.
10+
*/
11+
export default function useScrollActiveItemIntoView({
12+
isActive,
13+
behavior,
14+
block,
15+
}) {
16+
const ref = useRef(null);
17+
18+
useEffect(() => {
19+
if (isActive) {
20+
ref.current.scrollIntoView({
21+
behavior,
22+
block,
23+
});
24+
}
25+
}, [isActive]);
26+
27+
return {
28+
ref,
29+
};
30+
}

0 commit comments

Comments
 (0)