mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-11 11:58:41 +00:00
fix(admin): nav bar incorrectly active state (#7870)
This commit is contained in:
@@ -91,7 +91,12 @@ export const router = _createBrowserRouter(
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
lazy: () => import('./modules/settings'),
|
||||
children: [
|
||||
{
|
||||
path: '*',
|
||||
lazy: () => import('./modules/settings'),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@ const AccordionTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<AccordionPrimitive.Header className="flex">
|
||||
<AccordionPrimitive.Header className="flex w-full">
|
||||
<AccordionPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
|
||||
@@ -5,9 +5,7 @@ import {
|
||||
AccordionTrigger,
|
||||
} from '@affine/admin/components/ui/accordion';
|
||||
import { useCallback } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { useNav } from './context';
|
||||
import { NavLink, useLocation } from 'react-router-dom';
|
||||
|
||||
export const CollapsibleItem = ({
|
||||
items,
|
||||
@@ -18,7 +16,9 @@ export const CollapsibleItem = ({
|
||||
items: string[];
|
||||
changeModule?: (module: string) => void;
|
||||
}) => {
|
||||
const { activeSubTab, setActiveSubTab } = useNav();
|
||||
const location = useLocation();
|
||||
const activeSubTab = location.hash.slice(1);
|
||||
|
||||
const handleClick = useCallback(
|
||||
(id: string) => {
|
||||
const targetElement = document.getElementById(id);
|
||||
@@ -29,36 +29,45 @@ export const CollapsibleItem = ({
|
||||
});
|
||||
}
|
||||
changeModule?.(title);
|
||||
setActiveSubTab(id);
|
||||
},
|
||||
[changeModule, setActiveSubTab, title]
|
||||
[changeModule, title]
|
||||
);
|
||||
|
||||
return (
|
||||
<Accordion type="multiple" className="w-full ">
|
||||
<AccordionItem value="item-1" className="border-b-0">
|
||||
<Link to={`/admin/settings#${title}`}>
|
||||
<NavLink
|
||||
to={`/admin/settings/${title}`}
|
||||
className={({ isActive }) => {
|
||||
return isActive
|
||||
? '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'
|
||||
: '';
|
||||
}}
|
||||
>
|
||||
<AccordionTrigger
|
||||
onClick={() => handleClick(title)}
|
||||
className={`py-2 px-3 rounded ${activeSubTab === title ? 'bg-zinc-100' : ''}`}
|
||||
className={`py-2 px-3 rounded`}
|
||||
>
|
||||
{title}
|
||||
</AccordionTrigger>
|
||||
</Link>
|
||||
<AccordionContent className=" flex flex-col gap-2">
|
||||
</NavLink>
|
||||
<AccordionContent className=" flex flex-col gap-2 py-1">
|
||||
{items.map((item, index) => (
|
||||
<Link
|
||||
<NavLink
|
||||
key={index}
|
||||
to={`/admin/settings#${item}`}
|
||||
className="px-3 overflow-hidden"
|
||||
to={`/admin/settings/${title}#${item}`}
|
||||
className={({ isActive }) => {
|
||||
return isActive && activeSubTab === item
|
||||
? `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`
|
||||
: '';
|
||||
}}
|
||||
>
|
||||
<AccordionContent
|
||||
onClick={() => handleClick(item)}
|
||||
className={`py-1 px-2 rounded text-ellipsis whitespace-nowrap overflow-hidden ${activeSubTab === item ? 'bg-zinc-100' : ''}`}
|
||||
className={`py-1 px-2 rounded text-ellipsis whitespace-nowrap overflow-hidden`}
|
||||
>
|
||||
{item}
|
||||
</AccordionContent>
|
||||
</Link>
|
||||
</NavLink>
|
||||
))}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
@@ -8,54 +8,38 @@ import { buttonVariants } from '@affine/admin/components/ui/button';
|
||||
import { cn } from '@affine/admin/utils';
|
||||
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
|
||||
import { ClipboardListIcon, SettingsIcon, UsersIcon } from 'lucide-react';
|
||||
import { useEffect } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
|
||||
import { useGetServerRuntimeConfig } from '../settings/use-get-server-runtime-config';
|
||||
import { CollapsibleItem } from './collapsible-item';
|
||||
import { useNav } from './context';
|
||||
import { UserDropdown } from './user-dropdown';
|
||||
|
||||
const TabsMap: { [key: string]: string } = {
|
||||
accounts: 'Accounts',
|
||||
ai: 'AI',
|
||||
config: 'Config',
|
||||
settings: 'Settings',
|
||||
};
|
||||
|
||||
export function Nav() {
|
||||
const { moduleList } = useGetServerRuntimeConfig();
|
||||
const { activeTab, setActiveTab, setCurrentModule } = useNav();
|
||||
|
||||
useEffect(() => {
|
||||
const path = window.location.pathname;
|
||||
for (const key in TabsMap) {
|
||||
if (path.includes(key)) {
|
||||
setActiveTab(TabsMap[key]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}, [setActiveTab]);
|
||||
const { setCurrentModule } = useNav();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 py-2 justify-between flex-grow overflow-hidden">
|
||||
<nav className="flex flex-col gap-1 px-2 flex-grow overflow-hidden">
|
||||
<Link
|
||||
<NavLink
|
||||
to={'/admin/accounts'}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
variant: activeTab === 'Accounts' ? 'default' : 'ghost',
|
||||
size: 'sm',
|
||||
}),
|
||||
activeTab === 'Accounts' &&
|
||||
'dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white',
|
||||
'justify-start',
|
||||
'flex-none'
|
||||
)}
|
||||
className={({ isActive }) =>
|
||||
cn(
|
||||
buttonVariants({
|
||||
variant: isActive ? 'default' : 'ghost',
|
||||
size: 'sm',
|
||||
}),
|
||||
isActive &&
|
||||
'dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white',
|
||||
'justify-start',
|
||||
'flex-none'
|
||||
)
|
||||
}
|
||||
>
|
||||
<UsersIcon className="mr-2 h-4 w-4" />
|
||||
Accounts
|
||||
</Link>
|
||||
</NavLink>
|
||||
{/* <Link
|
||||
to={'/admin/ai'}
|
||||
className={cn(
|
||||
@@ -72,48 +56,55 @@ export function Nav() {
|
||||
<CpuIcon className="mr-2 h-4 w-4" />
|
||||
AI
|
||||
</Link> */}
|
||||
<Link
|
||||
<NavLink
|
||||
to={'/admin/config'}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
variant: activeTab === 'Config' ? 'default' : 'ghost',
|
||||
size: 'sm',
|
||||
}),
|
||||
activeTab === 'Config' &&
|
||||
'dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white',
|
||||
'justify-start',
|
||||
'flex-none'
|
||||
)}
|
||||
className={({ isActive }) =>
|
||||
cn(
|
||||
buttonVariants({
|
||||
variant: isActive ? 'default' : 'ghost',
|
||||
size: 'sm',
|
||||
}),
|
||||
isActive &&
|
||||
'dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white',
|
||||
'justify-start',
|
||||
'flex-none'
|
||||
)
|
||||
}
|
||||
>
|
||||
<ClipboardListIcon className="mr-2 h-4 w-4" />
|
||||
Config
|
||||
</Link>
|
||||
</NavLink>
|
||||
|
||||
<Accordion type="multiple" className="w-full h-full overflow-hidden">
|
||||
<AccordionItem
|
||||
value="item-1"
|
||||
className="border-b-0 h-full flex flex-col gap-1"
|
||||
className="border-b-0 h-full flex flex-col gap-1 w-full"
|
||||
>
|
||||
<Link to={'/admin/settings'}>
|
||||
<AccordionTrigger
|
||||
className={cn(
|
||||
<NavLink
|
||||
to={'/admin/settings'}
|
||||
className={({ isActive }) =>
|
||||
cn(
|
||||
buttonVariants({
|
||||
variant: activeTab === 'Settings' ? 'default' : 'ghost',
|
||||
variant: isActive ? 'default' : 'ghost',
|
||||
size: 'sm',
|
||||
}),
|
||||
|
||||
activeTab === 'Settings' &&
|
||||
isActive &&
|
||||
'dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white',
|
||||
'justify-between',
|
||||
'hover:no-underline'
|
||||
)}
|
||||
'justify-start',
|
||||
'flex-none',
|
||||
'w-full'
|
||||
)
|
||||
}
|
||||
>
|
||||
<AccordionTrigger
|
||||
className={'flex items-center justify-between w-full'}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<SettingsIcon className="mr-2 h-4 w-4" />
|
||||
<span>Settings</span>
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
</Link>
|
||||
</NavLink>
|
||||
|
||||
<AccordionContent className="h-full overflow-hidden w-full">
|
||||
<ScrollAreaPrimitive.Root
|
||||
|
||||
Reference in New Issue
Block a user