Skip to content

Commit 5c67f98

Browse files
committed
fix(admin): nav bar incorrectly active state (#7870)
1 parent d5ecf50 commit 5c67f98

File tree

4 files changed

+78
-73
lines changed

4 files changed

+78
-73
lines changed

packages/frontend/admin/src/app.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,12 @@ export const router = _createBrowserRouter(
9191
},
9292
{
9393
path: 'settings',
94-
lazy: () => import('./modules/settings'),
94+
children: [
95+
{
96+
path: '*',
97+
lazy: () => import('./modules/settings'),
98+
},
99+
],
95100
},
96101
],
97102
},

packages/frontend/admin/src/components/ui/accordion.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const AccordionTrigger = React.forwardRef<
2121
React.ElementRef<typeof AccordionPrimitive.Trigger>,
2222
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
2323
>(({ className, children, ...props }, ref) => (
24-
<AccordionPrimitive.Header className="flex">
24+
<AccordionPrimitive.Header className="flex w-full">
2525
<AccordionPrimitive.Trigger
2626
ref={ref}
2727
className={cn(

packages/frontend/admin/src/modules/nav/collapsible-item.tsx

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ import {
55
AccordionTrigger,
66
} from '@affine/admin/components/ui/accordion';
77
import { useCallback } from 'react';
8-
import { Link } from 'react-router-dom';
9-
10-
import { useNav } from './context';
8+
import { NavLink, useLocation } from 'react-router-dom';
119

1210
export const CollapsibleItem = ({
1311
items,
@@ -18,7 +16,9 @@ export const CollapsibleItem = ({
1816
items: string[];
1917
changeModule?: (module: string) => void;
2018
}) => {
21-
const { activeSubTab, setActiveSubTab } = useNav();
19+
const location = useLocation();
20+
const activeSubTab = location.hash.slice(1);
21+
2222
const handleClick = useCallback(
2323
(id: string) => {
2424
const targetElement = document.getElementById(id);
@@ -29,36 +29,45 @@ export const CollapsibleItem = ({
2929
});
3030
}
3131
changeModule?.(title);
32-
setActiveSubTab(id);
3332
},
34-
[changeModule, setActiveSubTab, title]
33+
[changeModule, title]
3534
);
36-
3735
return (
3836
<Accordion type="multiple" className="w-full ">
3937
<AccordionItem value="item-1" className="border-b-0">
40-
<Link to={`/admin/settings#${title}`}>
38+
<NavLink
39+
to={`/admin/settings/${title}`}
40+
className={({ isActive }) => {
41+
return isActive
42+
? 'w-full bg-zinc-100 inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50'
43+
: '';
44+
}}
45+
>
4146
<AccordionTrigger
4247
onClick={() => handleClick(title)}
43-
className={`py-2 px-3 rounded ${activeSubTab === title ? 'bg-zinc-100' : ''}`}
48+
className={`py-2 px-3 rounded`}
4449
>
4550
{title}
4651
</AccordionTrigger>
47-
</Link>
48-
<AccordionContent className=" flex flex-col gap-2">
52+
</NavLink>
53+
<AccordionContent className=" flex flex-col gap-2 py-1">
4954
{items.map((item, index) => (
50-
<Link
55+
<NavLink
5156
key={index}
52-
to={`/admin/settings#${item}`}
53-
className="px-3 overflow-hidden"
57+
to={`/admin/settings/${title}#${item}`}
58+
className={({ isActive }) => {
59+
return isActive && activeSubTab === item
60+
? `transition-all overflow-hidden w-full bg-zinc-100 inline-flex items-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50`
61+
: '';
62+
}}
5463
>
5564
<AccordionContent
5665
onClick={() => handleClick(item)}
57-
className={`py-1 px-2 rounded text-ellipsis whitespace-nowrap overflow-hidden ${activeSubTab === item ? 'bg-zinc-100' : ''}`}
66+
className={`py-1 px-2 rounded text-ellipsis whitespace-nowrap overflow-hidden`}
5867
>
5968
{item}
6069
</AccordionContent>
61-
</Link>
70+
</NavLink>
6271
))}
6372
</AccordionContent>
6473
</AccordionItem>

packages/frontend/admin/src/modules/nav/nav.tsx

Lines changed: 46 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -8,54 +8,38 @@ import { buttonVariants } from '@affine/admin/components/ui/button';
88
import { cn } from '@affine/admin/utils';
99
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
1010
import { ClipboardListIcon, SettingsIcon, UsersIcon } from 'lucide-react';
11-
import { useEffect } from 'react';
12-
import { Link } from 'react-router-dom';
11+
import { NavLink } from 'react-router-dom';
1312

1413
import { useGetServerRuntimeConfig } from '../settings/use-get-server-runtime-config';
1514
import { CollapsibleItem } from './collapsible-item';
1615
import { useNav } from './context';
1716
import { UserDropdown } from './user-dropdown';
1817

19-
const TabsMap: { [key: string]: string } = {
20-
accounts: 'Accounts',
21-
ai: 'AI',
22-
config: 'Config',
23-
settings: 'Settings',
24-
};
25-
2618
export function Nav() {
2719
const { moduleList } = useGetServerRuntimeConfig();
28-
const { activeTab, setActiveTab, setCurrentModule } = useNav();
29-
30-
useEffect(() => {
31-
const path = window.location.pathname;
32-
for (const key in TabsMap) {
33-
if (path.includes(key)) {
34-
setActiveTab(TabsMap[key]);
35-
return;
36-
}
37-
}
38-
}, [setActiveTab]);
20+
const { setCurrentModule } = useNav();
3921

4022
return (
4123
<div className="flex flex-col gap-4 py-2 justify-between flex-grow overflow-hidden">
4224
<nav className="flex flex-col gap-1 px-2 flex-grow overflow-hidden">
43-
<Link
25+
<NavLink
4426
to={'/admin/accounts'}
45-
className={cn(
46-
buttonVariants({
47-
variant: activeTab === 'Accounts' ? 'default' : 'ghost',
48-
size: 'sm',
49-
}),
50-
activeTab === 'Accounts' &&
51-
'dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white',
52-
'justify-start',
53-
'flex-none'
54-
)}
27+
className={({ isActive }) =>
28+
cn(
29+
buttonVariants({
30+
variant: isActive ? 'default' : 'ghost',
31+
size: 'sm',
32+
}),
33+
isActive &&
34+
'dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white',
35+
'justify-start',
36+
'flex-none'
37+
)
38+
}
5539
>
5640
<UsersIcon className="mr-2 h-4 w-4" />
5741
Accounts
58-
</Link>
42+
</NavLink>
5943
{/* <Link
6044
to={'/admin/ai'}
6145
className={cn(
@@ -72,48 +56,55 @@ export function Nav() {
7256
<CpuIcon className="mr-2 h-4 w-4" />
7357
AI
7458
</Link> */}
75-
<Link
59+
<NavLink
7660
to={'/admin/config'}
77-
className={cn(
78-
buttonVariants({
79-
variant: activeTab === 'Config' ? 'default' : 'ghost',
80-
size: 'sm',
81-
}),
82-
activeTab === 'Config' &&
83-
'dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white',
84-
'justify-start',
85-
'flex-none'
86-
)}
61+
className={({ isActive }) =>
62+
cn(
63+
buttonVariants({
64+
variant: isActive ? 'default' : 'ghost',
65+
size: 'sm',
66+
}),
67+
isActive &&
68+
'dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white',
69+
'justify-start',
70+
'flex-none'
71+
)
72+
}
8773
>
8874
<ClipboardListIcon className="mr-2 h-4 w-4" />
8975
Config
90-
</Link>
76+
</NavLink>
9177

9278
<Accordion type="multiple" className="w-full h-full overflow-hidden">
9379
<AccordionItem
9480
value="item-1"
95-
className="border-b-0 h-full flex flex-col gap-1"
81+
className="border-b-0 h-full flex flex-col gap-1 w-full"
9682
>
97-
<Link to={'/admin/settings'}>
98-
<AccordionTrigger
99-
className={cn(
83+
<NavLink
84+
to={'/admin/settings'}
85+
className={({ isActive }) =>
86+
cn(
10087
buttonVariants({
101-
variant: activeTab === 'Settings' ? 'default' : 'ghost',
88+
variant: isActive ? 'default' : 'ghost',
10289
size: 'sm',
10390
}),
104-
105-
activeTab === 'Settings' &&
91+
isActive &&
10692
'dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white',
107-
'justify-between',
108-
'hover:no-underline'
109-
)}
93+
'justify-start',
94+
'flex-none',
95+
'w-full'
96+
)
97+
}
98+
>
99+
<AccordionTrigger
100+
className={'flex items-center justify-between w-full'}
110101
>
111102
<div className="flex items-center">
112103
<SettingsIcon className="mr-2 h-4 w-4" />
113104
<span>Settings</span>
114105
</div>
115106
</AccordionTrigger>
116-
</Link>
107+
</NavLink>
117108

118109
<AccordionContent className="h-full overflow-hidden w-full">
119110
<ScrollAreaPrimitive.Root

0 commit comments

Comments
 (0)