Refactor extension tabs layout and styles

Remove the overflow wrapper around extension tabs and move max-w/full styling to the Tabs component. Simplify classNames (tabList, cursor) and clean up shrinking/truncation classes on tab items and plugin name to improve responsiveness and layout, while preserving the openInNewWindow click behavior.
This commit is contained in:
手瓜一十雪
2026-02-20 23:05:20 +08:00
parent 5fec649425
commit 1b73d68cbf

View File

@@ -1,7 +1,7 @@
import { Tab, Tabs } from '@heroui/tabs'; import { Tab, Tabs } from '@heroui/tabs';
import { Button } from '@heroui/button'; import { Button } from '@heroui/button';
import { Spinner } from '@heroui/spinner'; import { Spinner } from '@heroui/spinner';
import { useEffect, useState, useMemo } from 'react'; import { useEffect, useState, useMemo, useRef, useCallback } from 'react';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import { IoMdRefresh } from 'react-icons/io'; import { IoMdRefresh } from 'react-icons/io';
import { MdExtension } from 'react-icons/md'; import { MdExtension } from 'react-icons/md';
@@ -93,14 +93,45 @@ export default function ExtensionPage () {
window.open(url, '_blank'); window.open(url, '_blank');
}; };
// 拖拽滚动支持(鼠标 + 触摸)
const scrollRef = useRef<HTMLDivElement>(null);
const isDragging = useRef(false);
const startX = useRef(0);
const scrollLeft = useRef(0);
const handlePointerDown = useCallback((e: React.PointerEvent) => {
const el = scrollRef.current;
if (!el) return;
isDragging.current = true;
startX.current = e.clientX;
scrollLeft.current = el.scrollLeft;
el.setPointerCapture(e.pointerId);
el.style.cursor = 'grabbing';
el.style.userSelect = 'none';
}, []);
const handlePointerMove = useCallback((e: React.PointerEvent) => {
if (!isDragging.current || !scrollRef.current) return;
const dx = e.clientX - startX.current;
scrollRef.current.scrollLeft = scrollLeft.current - dx;
}, []);
const handlePointerUp = useCallback((e: React.PointerEvent) => {
if (!isDragging.current || !scrollRef.current) return;
isDragging.current = false;
scrollRef.current.releasePointerCapture(e.pointerId);
scrollRef.current.style.cursor = 'grab';
scrollRef.current.style.userSelect = '';
}, []);
return ( return (
<> <>
<title> - NapCat WebUI</title> <title> - NapCat WebUI</title>
<div className='p-2 md:p-4 relative h-[calc(100vh-6rem)] md:h-[calc(100vh-4rem)] flex flex-col'> <div className='p-2 md:p-4 relative h-[calc(100vh-6rem)] md:h-[calc(100vh-4rem)] flex flex-col'>
<PageLoading loading={loading} /> <PageLoading loading={loading} />
<div className='flex mb-4 items-center justify-between gap-4 flex-wrap'> <div className='flex mb-4 items-center gap-4 flex-nowrap min-w-0'>
<div className='flex items-center gap-4'> <div className='flex items-center gap-4 shrink-0'>
<div className='flex items-center gap-2 text-default-600'> <div className='flex items-center gap-2 text-default-600'>
<MdExtension size={24} /> <MdExtension size={24} />
<span className='text-lg font-medium'></span> <span className='text-lg font-medium'></span>
@@ -115,10 +146,18 @@ export default function ExtensionPage () {
</Button> </Button>
</div> </div>
{extensionPages.length > 0 && ( {extensionPages.length > 0 && (
<div className='max-w-full overflow-x-auto overflow-y-hidden pb-1 -mb-1'> <div
ref={scrollRef}
className='overflow-x-auto min-w-0 flex-1 scrollbar-thin scrollbar-thumb-default-300 scrollbar-track-transparent cursor-grab touch-pan-x'
style={{ WebkitOverflowScrolling: 'touch' }}
onPointerDown={handlePointerDown}
onPointerMove={handlePointerMove}
onPointerUp={handlePointerUp}
onPointerCancel={handlePointerUp}
>
<Tabs <Tabs
aria-label='Extension Pages' aria-label='Extension Pages'
className='min-w-max' className='w-max min-w-full'
selectedKey={selectedTab} selectedKey={selectedTab}
onSelectionChange={(key) => setSelectedTab(key as string)} onSelectionChange={(key) => setSelectedTab(key as string)}
classNames={{ classNames={{
@@ -130,12 +169,11 @@ export default function ExtensionPage () {
{tabs.map((tab) => ( {tabs.map((tab) => (
<Tab <Tab
key={tab.key} key={tab.key}
className='shrink-0'
title={ title={
<div className='flex items-center gap-2'> <div className='flex items-center gap-2 whitespace-nowrap'>
{tab.icon && <span>{tab.icon}</span>} {tab.icon && <span>{tab.icon}</span>}
<span <span
className='cursor-pointer hover:underline truncate max-w-[6rem] md:max-w-none shrink-0' className='cursor-pointer hover:underline truncate max-w-[6rem] md:max-w-none'
title={`插件:${tab.pluginName}\n点击在新窗口打开`} title={`插件:${tab.pluginName}\n点击在新窗口打开`}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
@@ -144,7 +182,7 @@ export default function ExtensionPage () {
> >
{tab.title} {tab.title}
</span> </span>
<span className='text-xs text-default-400 hidden md:inline shrink-0'>({tab.pluginName})</span> <span className='text-xs text-default-400 hidden md:inline'>({tab.pluginName})</span>
</div> </div>
} }
/> />