mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2025-12-27 05:00:30 +08:00
fix: remove nested button element in NextUI Card component
This commit is contained in:
parent
e746ab4241
commit
77b67849ed
@ -26,6 +26,7 @@
|
||||
"@electron-toolkit/preload": "^3.0.1",
|
||||
"@electron-toolkit/utils": "^3.0.0",
|
||||
"@mihomo-party/sysproxy": "^2.0.7",
|
||||
"@mihomo-party/sysproxy-darwin-arm64": "^2.0.7",
|
||||
"@nextui-org/react": "2.6.10",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"adm-zip": "^0.5.16",
|
||||
@ -57,7 +58,7 @@
|
||||
"autoprefixer": "^10.4.20",
|
||||
"cron-validator": "^1.3.1",
|
||||
"driver.js": "^1.3.1",
|
||||
"electron": "^33.1.0",
|
||||
"electron": "^33.3.2",
|
||||
"electron-builder": "25.0.4",
|
||||
"electron-vite": "^2.3.0",
|
||||
"electron-window-state": "^5.0.3",
|
||||
@ -90,5 +91,6 @@
|
||||
"typescript": "^5.6.3",
|
||||
"vite": "^6.0.7",
|
||||
"vite-plugin-monaco-editor": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"packageManager": "pnpm@9.15.0+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c"
|
||||
}
|
||||
|
||||
1777
pnpm-lock.yaml
generated
1777
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -221,7 +221,8 @@ export async function createWindow(): Promise<void> {
|
||||
webPreferences: {
|
||||
preload: join(__dirname, '../preload/index.js'),
|
||||
spellcheck: false,
|
||||
sandbox: false
|
||||
sandbox: false,
|
||||
devTools: true
|
||||
}
|
||||
})
|
||||
mainWindowState.manage(mainWindow)
|
||||
@ -282,6 +283,12 @@ export async function createWindow(): Promise<void> {
|
||||
shell.openExternal(details.url)
|
||||
return { action: 'deny' }
|
||||
})
|
||||
|
||||
// 在开发模式下自动打开 DevTools
|
||||
if (is.dev) {
|
||||
mainWindow.webContents.openDevTools()
|
||||
}
|
||||
|
||||
// HMR for renderer base on electron-vite cli.
|
||||
// Load the remote URL for development or the local html file for production.
|
||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
||||
|
||||
@ -73,13 +73,11 @@ const App: React.FC = () => {
|
||||
const location = useLocation()
|
||||
const page = useRoutes(routes)
|
||||
const setTitlebar = (): void => {
|
||||
if (!useWindowFrame) {
|
||||
if (!useWindowFrame && platform !== 'darwin') {
|
||||
const options = { height: 48 } as TitleBarOverlayOptions
|
||||
try {
|
||||
if (platform !== 'darwin') {
|
||||
options.color = window.getComputedStyle(document.documentElement).backgroundColor
|
||||
options.symbolColor = window.getComputedStyle(document.documentElement).color
|
||||
}
|
||||
options.color = window.getComputedStyle(document.documentElement).backgroundColor
|
||||
options.symbolColor = window.getComputedStyle(document.documentElement).color
|
||||
setTitleBarOverlay(options)
|
||||
} catch (e) {
|
||||
// ignore
|
||||
|
||||
@ -173,141 +173,144 @@ const ProfileItem: React.FC<Props> = (props) => {
|
||||
)}
|
||||
<Card
|
||||
fullWidth
|
||||
isPressable
|
||||
onPress={() => {
|
||||
if (disableSelect) return
|
||||
setSelecting(true)
|
||||
onClick().finally(() => {
|
||||
setSelecting(false)
|
||||
})
|
||||
}}
|
||||
className={`${isCurrent ? 'bg-primary' : ''} ${selecting ? 'blur-sm' : ''}`}
|
||||
>
|
||||
<div ref={setNodeRef} {...attributes} {...listeners} className="w-full h-full">
|
||||
<CardBody className="pb-1">
|
||||
<div className="flex justify-between h-[32px]">
|
||||
<h3
|
||||
title={info?.name}
|
||||
className={`text-ellipsis whitespace-nowrap overflow-hidden text-md font-bold leading-[32px] ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
|
||||
>
|
||||
{info?.name}
|
||||
</h3>
|
||||
<div className="flex">
|
||||
{info.type === 'remote' && (
|
||||
<Tooltip placement="left" content={dayjs(info.updated).fromNow()}>
|
||||
<div
|
||||
className="w-full h-full cursor-pointer"
|
||||
onClick={() => {
|
||||
if (disableSelect) return
|
||||
setSelecting(true)
|
||||
onClick().finally(() => {
|
||||
setSelecting(false)
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div ref={setNodeRef} {...attributes} {...listeners} className="w-full h-full">
|
||||
<CardBody className="pb-1">
|
||||
<div className="flex justify-between h-[32px]">
|
||||
<h3
|
||||
title={info?.name}
|
||||
className={`text-ellipsis whitespace-nowrap overflow-hidden text-md font-bold leading-[32px] ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
|
||||
>
|
||||
{info?.name}
|
||||
</h3>
|
||||
<div className="flex">
|
||||
{info.type === 'remote' && (
|
||||
<Tooltip placement="left" content={dayjs(info.updated).fromNow()}>
|
||||
<Button
|
||||
isIconOnly
|
||||
size="sm"
|
||||
variant="light"
|
||||
color="default"
|
||||
disabled={updating}
|
||||
onPress={async () => {
|
||||
setUpdating(true)
|
||||
await addProfileItem(info)
|
||||
setUpdating(false)
|
||||
}}
|
||||
>
|
||||
<IoMdRefresh
|
||||
color="default"
|
||||
className={`${isCurrent ? 'text-primary-foreground' : 'text-foreground'} text-[24px] ${updating ? 'animate-spin' : ''}`}
|
||||
/>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<Dropdown>
|
||||
<DropdownTrigger>
|
||||
<Button isIconOnly size="sm" variant="light" color="default">
|
||||
<IoMdMore
|
||||
color="default"
|
||||
className={`text-[24px] ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
|
||||
/>
|
||||
</Button>
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu onAction={onMenuAction}>
|
||||
{menuItems.map((item) => (
|
||||
<DropdownItem
|
||||
showDivider={item.showDivider}
|
||||
key={item.key}
|
||||
color={item.color}
|
||||
className={item.className}
|
||||
>
|
||||
{item.label}
|
||||
</DropdownItem>
|
||||
))}
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
{info.type === 'remote' && extra && (
|
||||
<div
|
||||
className={`mt-2 flex justify-between ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
|
||||
>
|
||||
<small>{`${calcTraffic(usage)}/${calcTraffic(total)}`}</small>
|
||||
{profileDisplayDate === 'expire' ? (
|
||||
<Button
|
||||
isIconOnly
|
||||
size="sm"
|
||||
variant="light"
|
||||
color="default"
|
||||
disabled={updating}
|
||||
className={`h-[20px] p-1 m-0 ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
|
||||
onPress={async () => {
|
||||
setUpdating(true)
|
||||
await addProfileItem(info)
|
||||
setUpdating(false)
|
||||
await patchAppConfig({ profileDisplayDate: 'update' })
|
||||
}}
|
||||
>
|
||||
<IoMdRefresh
|
||||
color="default"
|
||||
className={`${isCurrent ? 'text-primary-foreground' : 'text-foreground'} text-[24px] ${updating ? 'animate-spin' : ''}`}
|
||||
/>
|
||||
{extra.expire ? dayjs.unix(extra.expire).format('YYYY-MM-DD') : '长期有效'}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<Dropdown>
|
||||
<DropdownTrigger>
|
||||
<Button isIconOnly size="sm" variant="light" color="default">
|
||||
<IoMdMore
|
||||
color="default"
|
||||
className={`text-[24px] ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="light"
|
||||
className={`h-[20px] p-1 m-0 ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
|
||||
onPress={async () => {
|
||||
await patchAppConfig({ profileDisplayDate: 'expire' })
|
||||
}}
|
||||
>
|
||||
{dayjs(info.updated).fromNow()}
|
||||
</Button>
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu onAction={onMenuAction}>
|
||||
{menuItems.map((item) => (
|
||||
<DropdownItem
|
||||
showDivider={item.showDivider}
|
||||
key={item.key}
|
||||
color={item.color}
|
||||
className={item.className}
|
||||
>
|
||||
{item.label}
|
||||
</DropdownItem>
|
||||
))}
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
{info.type === 'remote' && extra && (
|
||||
<div
|
||||
className={`mt-2 flex justify-between ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
|
||||
>
|
||||
<small>{`${calcTraffic(usage)}/${calcTraffic(total)}`}</small>
|
||||
{profileDisplayDate === 'expire' ? (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="light"
|
||||
className={`h-[20px] p-1 m-0 ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
|
||||
onPress={async () => {
|
||||
await patchAppConfig({ profileDisplayDate: 'update' })
|
||||
}}
|
||||
>
|
||||
{extra.expire ? dayjs.unix(extra.expire).format('YYYY-MM-DD') : '长期有效'}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="light"
|
||||
className={`h-[20px] p-1 m-0 ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
|
||||
onPress={async () => {
|
||||
await patchAppConfig({ profileDisplayDate: 'expire' })
|
||||
}}
|
||||
>
|
||||
{dayjs(info.updated).fromNow()}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</CardBody>
|
||||
<CardFooter className="pt-0">
|
||||
{info.type === 'remote' && !extra && (
|
||||
<div
|
||||
className={`w-full mt-2 flex justify-between ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
|
||||
>
|
||||
<Chip
|
||||
size="sm"
|
||||
variant="bordered"
|
||||
className={`${isCurrent ? 'text-primary-foreground border-primary-foreground' : 'border-primary text-primary'}`}
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</CardBody>
|
||||
<CardFooter className="pt-0">
|
||||
{info.type === 'remote' && !extra && (
|
||||
<div
|
||||
className={`w-full mt-2 flex justify-between ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
|
||||
>
|
||||
远程
|
||||
</Chip>
|
||||
<small>{dayjs(info.updated).fromNow()}</small>
|
||||
</div>
|
||||
)}
|
||||
{info.type === 'local' && (
|
||||
<div
|
||||
className={`mt-2 flex justify-between ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
|
||||
>
|
||||
<Chip
|
||||
size="sm"
|
||||
variant="bordered"
|
||||
className={`${isCurrent ? 'text-primary-foreground border-primary-foreground' : 'border-primary text-primary'}`}
|
||||
<Chip
|
||||
size="sm"
|
||||
variant="bordered"
|
||||
className={`${isCurrent ? 'text-primary-foreground border-primary-foreground' : 'border-primary text-primary'}`}
|
||||
>
|
||||
远程
|
||||
</Chip>
|
||||
<small>{dayjs(info.updated).fromNow()}</small>
|
||||
</div>
|
||||
)}
|
||||
{info.type === 'local' && (
|
||||
<div
|
||||
className={`mt-2 flex justify-between ${isCurrent ? 'text-primary-foreground' : 'text-foreground'}`}
|
||||
>
|
||||
本地
|
||||
</Chip>
|
||||
</div>
|
||||
)}
|
||||
{extra && (
|
||||
<Progress
|
||||
className="w-full"
|
||||
classNames={{
|
||||
indicator: isCurrent ? 'bg-primary-foreground' : 'bg-foreground'
|
||||
}}
|
||||
value={calcPercent(extra?.upload, extra?.download, extra?.total)}
|
||||
/>
|
||||
)}
|
||||
</CardFooter>
|
||||
<Chip
|
||||
size="sm"
|
||||
variant="bordered"
|
||||
className={`${isCurrent ? 'text-primary-foreground border-primary-foreground' : 'border-primary text-primary'}`}
|
||||
>
|
||||
本地
|
||||
</Chip>
|
||||
</div>
|
||||
)}
|
||||
{extra && (
|
||||
<Progress
|
||||
className="w-full"
|
||||
classNames={{
|
||||
indicator: isCurrent ? 'bg-primary-foreground' : 'bg-foreground'
|
||||
}}
|
||||
value={calcPercent(extra?.upload, extra?.download, extra?.total)}
|
||||
/>
|
||||
)}
|
||||
</CardFooter>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
@ -49,8 +49,6 @@ const ProxyItem: React.FC<Props> = (props) => {
|
||||
|
||||
return (
|
||||
<Card
|
||||
onPress={() => onSelect(group.name, proxy.name)}
|
||||
isPressable
|
||||
fullWidth
|
||||
shadow="sm"
|
||||
className={`${
|
||||
@ -62,93 +60,98 @@ const ProxyItem: React.FC<Props> = (props) => {
|
||||
}`}
|
||||
radius="sm"
|
||||
>
|
||||
<CardBody className="p-1">
|
||||
{proxyDisplayMode === 'full' ? (
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex justify-between items-center pl-1">
|
||||
<div className="text-ellipsis overflow-hidden whitespace-nowrap" title={proxy.name}>
|
||||
{proxy.name}
|
||||
</div>
|
||||
{fixed && (
|
||||
<Button
|
||||
isIconOnly
|
||||
title="取消固定"
|
||||
color="danger"
|
||||
onPress={async () => {
|
||||
await mihomoUnfixedProxy(group.name)
|
||||
mutateProxies()
|
||||
}}
|
||||
variant="light"
|
||||
className="h-[20px] p-0 text-sm"
|
||||
>
|
||||
<FaMapPin className="text-md le" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex justify-between items-center pl-1">
|
||||
<div className="flex gap-1 items-center">
|
||||
<div className="text-foreground-400 text-xs bg-default-100 px-1 rounded-md">
|
||||
{proxy.type}
|
||||
<div
|
||||
onClick={() => onSelect(group.name, proxy.name)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<CardBody className="p-1">
|
||||
{proxyDisplayMode === 'full' ? (
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex justify-between items-center pl-1">
|
||||
<div className="text-ellipsis overflow-hidden whitespace-nowrap" title={proxy.name}>
|
||||
{proxy.name}
|
||||
</div>
|
||||
{['tfo', 'udp', 'xudp', 'mptcp', 'smux'].map(protocol =>
|
||||
proxy[protocol as keyof IMihomoProxy] && (
|
||||
<div key={protocol} className="text-foreground-400 text-xs bg-default-100 px-1 rounded-md">
|
||||
{protocol}
|
||||
</div>
|
||||
)
|
||||
{fixed && (
|
||||
<Button
|
||||
isIconOnly
|
||||
title="取消固定"
|
||||
color="danger"
|
||||
onPress={async () => {
|
||||
await mihomoUnfixedProxy(group.name)
|
||||
mutateProxies()
|
||||
}}
|
||||
variant="light"
|
||||
className="h-[20px] p-0 text-sm"
|
||||
>
|
||||
<FaMapPin className="text-md le" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
isIconOnly
|
||||
title={proxy.type}
|
||||
isLoading={loading}
|
||||
color={delayColor(delay)}
|
||||
onPress={onDelay}
|
||||
variant="light"
|
||||
className="h-full text-sm ml-auto -mt-0.5"
|
||||
>
|
||||
{delayText(delay)}
|
||||
</Button>
|
||||
<div className="flex justify-between items-center pl-1">
|
||||
<div className="flex gap-1 items-center">
|
||||
<div className="text-foreground-400 text-xs bg-default-100 px-1 rounded-md">
|
||||
{proxy.type}
|
||||
</div>
|
||||
{['tfo', 'udp', 'xudp', 'mptcp', 'smux'].map(protocol =>
|
||||
proxy[protocol as keyof IMihomoProxy] && (
|
||||
<div key={protocol} className="text-foreground-400 text-xs bg-default-100 px-1 rounded-md">
|
||||
{protocol}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
isIconOnly
|
||||
title={proxy.type}
|
||||
isLoading={loading}
|
||||
color={delayColor(delay)}
|
||||
onPress={onDelay}
|
||||
variant="light"
|
||||
className="h-full text-sm ml-auto -mt-0.5"
|
||||
>
|
||||
{delayText(delay)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex justify-between items-center pl-1 p-1">
|
||||
<div className="text-ellipsis overflow-hidden whitespace-nowrap">
|
||||
<div className="flag-emoji inline" title={proxy.name}>
|
||||
{proxy.name}
|
||||
) : (
|
||||
<div className="flex justify-between items-center pl-1 p-1">
|
||||
<div className="text-ellipsis overflow-hidden whitespace-nowrap">
|
||||
<div className="flag-emoji inline" title={proxy.name}>
|
||||
{proxy.name}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
{fixed && (
|
||||
<Button
|
||||
isIconOnly
|
||||
title="取消固定"
|
||||
color="danger"
|
||||
onPress={async () => {
|
||||
await mihomoUnfixedProxy(group.name)
|
||||
mutateProxies()
|
||||
}}
|
||||
variant="light"
|
||||
className="h-[20px] p-0 text-sm"
|
||||
>
|
||||
<FaMapPin className="text-md le" />
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
isIconOnly
|
||||
title={proxy.type}
|
||||
isLoading={loading}
|
||||
color={delayColor(delay)}
|
||||
onPress={onDelay}
|
||||
variant="light"
|
||||
className="h-full p-0 text-sm"
|
||||
>
|
||||
{delayText(delay)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
{fixed && (
|
||||
<Button
|
||||
isIconOnly
|
||||
title="取消固定"
|
||||
color="danger"
|
||||
onPress={async () => {
|
||||
await mihomoUnfixedProxy(group.name)
|
||||
mutateProxies()
|
||||
}}
|
||||
variant="light"
|
||||
className="h-[20px] p-0 text-sm"
|
||||
>
|
||||
<FaMapPin className="text-md le" />
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
isIconOnly
|
||||
title={proxy.type}
|
||||
isLoading={loading}
|
||||
color={delayColor(delay)}
|
||||
onPress={onDelay}
|
||||
variant="light"
|
||||
className="h-full p-0 text-sm"
|
||||
>
|
||||
{delayText(delay)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardBody>
|
||||
)}
|
||||
</CardBody>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@ -327,118 +327,121 @@ const Proxies: React.FC = () => {
|
||||
className={`w-full pt-2 ${index === groupCounts.length - 1 && !isOpen[index] ? 'pb-2' : ''} px-2`}
|
||||
>
|
||||
<Card
|
||||
isPressable
|
||||
fullWidth
|
||||
onClick={() => {
|
||||
setIsOpen((prev) => {
|
||||
const newOpen = [...prev]
|
||||
newOpen[index] = !prev[index]
|
||||
return newOpen
|
||||
})
|
||||
}}
|
||||
>
|
||||
<CardBody className="w-full">
|
||||
<div className="flex justify-between">
|
||||
<div className="flex text-ellipsis overflow-hidden whitespace-nowrap">
|
||||
{groups[index].icon ? (
|
||||
<Avatar
|
||||
className="bg-transparent mr-2"
|
||||
size="sm"
|
||||
radius="sm"
|
||||
src={
|
||||
groups[index].icon.startsWith('<svg')
|
||||
? `data:image/svg+xml;utf8,${groups[index].icon}`
|
||||
: localStorage.getItem(groups[index].icon) || groups[index].icon
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
<div className="text-ellipsis overflow-hidden whitespace-nowrap">
|
||||
<div
|
||||
title={groups[index].name}
|
||||
className="inline flag-emoji h-[32px] text-md leading-[32px]"
|
||||
>
|
||||
{groups[index].name}
|
||||
</div>
|
||||
{proxyDisplayMode === 'full' && (
|
||||
<div
|
||||
onClick={(): void => {
|
||||
setIsOpen((prev) => {
|
||||
const newOpen = [...prev]
|
||||
newOpen[index] = !prev[index]
|
||||
return newOpen
|
||||
})
|
||||
}}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<CardBody className="w-full">
|
||||
<div className="flex justify-between">
|
||||
<div className="flex text-ellipsis overflow-hidden whitespace-nowrap">
|
||||
{groups[index].icon ? (
|
||||
<Avatar
|
||||
className="bg-transparent mr-2"
|
||||
size="sm"
|
||||
radius="sm"
|
||||
src={
|
||||
groups[index].icon.startsWith('<svg')
|
||||
? `data:image/svg+xml;utf8,${groups[index].icon}`
|
||||
: localStorage.getItem(groups[index].icon) || groups[index].icon
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
<div className="text-ellipsis overflow-hidden whitespace-nowrap">
|
||||
<div
|
||||
title={groups[index].type}
|
||||
className="inline ml-2 text-sm text-foreground-500"
|
||||
title={groups[index].name}
|
||||
className="inline flag-emoji h-[32px] text-md leading-[32px]"
|
||||
>
|
||||
{groups[index].type}
|
||||
{groups[index].name}
|
||||
</div>
|
||||
)}
|
||||
{proxyDisplayMode === 'full' && (
|
||||
<div
|
||||
title={groups[index].type}
|
||||
className="inline ml-2 text-sm text-foreground-500"
|
||||
>
|
||||
{groups[index].type}
|
||||
</div>
|
||||
)}
|
||||
{proxyDisplayMode === 'full' && (
|
||||
<div className="inline flag-emoji ml-2 text-sm text-foreground-500">
|
||||
{groups[index].now}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
{proxyDisplayMode === 'full' && (
|
||||
<div className="inline flag-emoji ml-2 text-sm text-foreground-500">
|
||||
{groups[index].now}
|
||||
</div>
|
||||
<Chip size="sm" className="my-1 mr-2">
|
||||
{groups[index].all.length}
|
||||
</Chip>
|
||||
)}
|
||||
<CollapseInput
|
||||
title="搜索节点"
|
||||
value={searchValue[index]}
|
||||
onValueChange={(v) => {
|
||||
setSearchValue((prev) => {
|
||||
const newSearchValue = [...prev]
|
||||
newSearchValue[index] = v
|
||||
return newSearchValue
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
title="定位到当前节点"
|
||||
variant="light"
|
||||
size="sm"
|
||||
isIconOnly
|
||||
onPress={() => {
|
||||
if (!isOpen[index]) {
|
||||
setIsOpen((prev) => {
|
||||
const newOpen = [...prev]
|
||||
newOpen[index] = true
|
||||
return newOpen
|
||||
})
|
||||
}
|
||||
let i = 0
|
||||
for (let j = 0; j < index; j++) {
|
||||
i += groupCounts[j]
|
||||
}
|
||||
i += Math.floor(
|
||||
allProxies[index].findIndex(
|
||||
(proxy) => proxy.name === groups[index].now
|
||||
) / cols
|
||||
)
|
||||
virtuosoRef.current?.scrollToIndex({
|
||||
index: Math.floor(i),
|
||||
align: 'start'
|
||||
})
|
||||
}}
|
||||
>
|
||||
<FaLocationCrosshairs className="text-lg text-foreground-500" />
|
||||
</Button>
|
||||
<Button
|
||||
title="延迟测试"
|
||||
variant="light"
|
||||
isLoading={delaying[index]}
|
||||
size="sm"
|
||||
isIconOnly
|
||||
onPress={() => {
|
||||
onGroupDelay(index)
|
||||
}}
|
||||
>
|
||||
<MdOutlineSpeed className="text-lg text-foreground-500" />
|
||||
</Button>
|
||||
<IoIosArrowBack
|
||||
className={`transition duration-200 ml-2 h-[32px] text-lg text-foreground-500 ${isOpen[index] ? '-rotate-90' : ''}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
{proxyDisplayMode === 'full' && (
|
||||
<Chip size="sm" className="my-1 mr-2">
|
||||
{groups[index].all.length}
|
||||
</Chip>
|
||||
)}
|
||||
<CollapseInput
|
||||
title="搜索节点"
|
||||
value={searchValue[index]}
|
||||
onValueChange={(v) => {
|
||||
setSearchValue((prev) => {
|
||||
const newSearchValue = [...prev]
|
||||
newSearchValue[index] = v
|
||||
return newSearchValue
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
title="定位到当前节点"
|
||||
variant="light"
|
||||
size="sm"
|
||||
isIconOnly
|
||||
onPress={() => {
|
||||
if (!isOpen[index]) {
|
||||
setIsOpen((prev) => {
|
||||
const newOpen = [...prev]
|
||||
newOpen[index] = true
|
||||
return newOpen
|
||||
})
|
||||
}
|
||||
let i = 0
|
||||
for (let j = 0; j < index; j++) {
|
||||
i += groupCounts[j]
|
||||
}
|
||||
i += Math.floor(
|
||||
allProxies[index].findIndex(
|
||||
(proxy) => proxy.name === groups[index].now
|
||||
) / cols
|
||||
)
|
||||
virtuosoRef.current?.scrollToIndex({
|
||||
index: Math.floor(i),
|
||||
align: 'start'
|
||||
})
|
||||
}}
|
||||
>
|
||||
<FaLocationCrosshairs className="text-lg text-foreground-500" />
|
||||
</Button>
|
||||
<Button
|
||||
title="延迟测试"
|
||||
variant="light"
|
||||
isLoading={delaying[index]}
|
||||
size="sm"
|
||||
isIconOnly
|
||||
onPress={() => {
|
||||
onGroupDelay(index)
|
||||
}}
|
||||
>
|
||||
<MdOutlineSpeed className="text-lg text-foreground-500" />
|
||||
</Button>
|
||||
<IoIosArrowBack
|
||||
className={`transition duration-200 ml-2 h-[32px] text-lg text-foreground-500 ${isOpen[index] ? '-rotate-90' : ''}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</CardBody>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user