mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-03-01 08:10:25 +00:00
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:
@@ -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>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user