feat: 优化web
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
'use client';
|
||||
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { useTabStore } from '@/stores/tab-store';
|
||||
|
||||
/** 多页签条:与路由联动;左右滚动占位(后续可接完整滚动实现)。 */
|
||||
export function TabStrip() {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname() ?? '';
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
const { tabs, activeId, close, activate } = useTabStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (!pathname.startsWith('/dashboard')) {
|
||||
return;
|
||||
}
|
||||
useTabStore.getState().syncFromPath(pathname);
|
||||
}, [pathname]);
|
||||
|
||||
const onTabClick = useCallback(
|
||||
(id: string, path: string) => {
|
||||
activate(id);
|
||||
router.push(path);
|
||||
},
|
||||
[activate, router],
|
||||
);
|
||||
|
||||
const scrollBy = (delta: number) => {
|
||||
scrollRef.current?.scrollBy({ left: delta, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-end gap-0 border-b border-neutral-200 bg-white px-1 pb-0 pt-1">
|
||||
<button
|
||||
type="button"
|
||||
className="mb-px shrink-0 cursor-pointer rounded px-1 py-1 text-neutral-500 hover:bg-neutral-100 disabled:opacity-40"
|
||||
aria-label="页签向左滚动"
|
||||
onClick={() => scrollBy(-120)}
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
<div
|
||||
ref={scrollRef}
|
||||
role="tablist"
|
||||
className="flex min-w-0 flex-1 items-end gap-2 overflow-x-auto"
|
||||
>
|
||||
{tabs.map((t) => {
|
||||
const isActive = activeId === t.id;
|
||||
return (
|
||||
<button
|
||||
key={t.id}
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-selected={isActive}
|
||||
tabIndex={isActive ? 0 : -1}
|
||||
onClick={() => onTabClick(t.id, t.path)}
|
||||
className={`flex shrink-0 cursor-pointer items-end gap-1 rounded-t px-3 pb-1.5 pt-2 text-sm ${
|
||||
isActive
|
||||
? 'bg-neutral-100 font-medium text-neutral-900'
|
||||
: 'text-neutral-600'
|
||||
}`}
|
||||
>
|
||||
<span>{t.title}</span>
|
||||
{!t.pinned ? (
|
||||
<span
|
||||
className="cursor-pointer text-neutral-400 hover:text-neutral-700"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const next = close(t.id);
|
||||
if (next) router.push(next);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
const next = close(t.id);
|
||||
if (next) router.push(next);
|
||||
}
|
||||
}}
|
||||
>
|
||||
×
|
||||
</span>
|
||||
) : null}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="mb-px shrink-0 cursor-pointer rounded px-1 py-1 text-neutral-500 hover:bg-neutral-100 disabled:opacity-40"
|
||||
aria-label="页签向右滚动"
|
||||
onClick={() => scrollBy(120)}
|
||||
>
|
||||
›
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user