mirror of
https://github.com/sipeed/NanoKVM-USB.git
synced 2026-09-12 14:40:44 -05:00
optimize: update menu bar UI
This commit is contained in:
@@ -44,10 +44,8 @@ export const Direction = (): ReactElement => {
|
||||
|
||||
return (
|
||||
<Popover content={content} placement="rightTop" arrow={false} align={{ offset: [13, 0] }}>
|
||||
<div className="flex h-[30px] cursor-pointer items-center space-x-1 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
|
||||
<div className="flex h-[14px] w-[20px] items-end">
|
||||
<ArrowDownUpIcon size={16} />
|
||||
</div>
|
||||
<div className="flex h-[32px] cursor-pointer items-center space-x-2 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
|
||||
<ArrowDownUpIcon size={18} />
|
||||
<span>{t('mouse.direction')}</span>
|
||||
</div>
|
||||
</Popover>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Popover } from 'antd';
|
||||
import { Divider, Popover } from 'antd';
|
||||
import { useAtom, useSetAtom } from 'jotai';
|
||||
import { MouseIcon } from 'lucide-react';
|
||||
|
||||
@@ -59,13 +59,15 @@ export const Mouse = () => {
|
||||
}
|
||||
|
||||
const content = (
|
||||
<>
|
||||
<div className="flex flex-col space-y-0.5">
|
||||
<Style />
|
||||
<Mode />
|
||||
<Direction />
|
||||
<Speed />
|
||||
|
||||
<Divider style={{ margin: '5px 0 5px 0' }} />
|
||||
<Jiggler />
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect } from 'react';
|
||||
import { Popover } from 'antd';
|
||||
import clsx from 'clsx';
|
||||
import { useAtom } from 'jotai';
|
||||
import { MousePointerIcon } from 'lucide-react';
|
||||
import { MousePointerClickIcon } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { mouseJigglerModeAtom } from '@/jotai/mouse.ts';
|
||||
@@ -33,7 +33,7 @@ export const Jiggler = () => {
|
||||
<div
|
||||
key={mode.value}
|
||||
className={clsx(
|
||||
'my-1 flex cursor-pointer items-center space-x-1 rounded py-1 pr-5 pl-2 hover:bg-neutral-700/50',
|
||||
'my-1 flex cursor-pointer items-center space-x-1 rounded py-1 pl-2 pr-5 hover:bg-neutral-700/50',
|
||||
mode.value === jigglerMode ? 'text-blue-500' : 'text-neutral-300'
|
||||
)}
|
||||
onClick={() => update(mode.value)}
|
||||
@@ -43,12 +43,11 @@ export const Jiggler = () => {
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover content={content} placement="rightTop" arrow={false} align={{ offset: [13, 0] }}>
|
||||
<div className="flex h-[30px] cursor-pointer items-center space-x-1 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
|
||||
<div className="flex h-[14px] w-[20px] items-end">
|
||||
<MousePointerIcon size={16} />
|
||||
</div>
|
||||
<div className="flex h-[32px] cursor-pointer items-center space-x-2 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
|
||||
<MousePointerClickIcon size={18} />
|
||||
<span>{t('mouse.jiggler.title')}</span>
|
||||
</div>
|
||||
</Popover>
|
||||
|
||||
@@ -40,8 +40,8 @@ export const Mode = () => {
|
||||
|
||||
return (
|
||||
<Popover content={content} placement="rightTop" arrow={false} align={{ offset: [13, 0] }}>
|
||||
<div className="flex h-[30px] cursor-pointer items-center space-x-1 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
|
||||
<div className="flex h-[14px] w-[20px] items-end">
|
||||
<div className="flex h-[32px] cursor-pointer items-center space-x-2 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
|
||||
<div className="flex size-[18px] items-center justify-center">
|
||||
<SquareMousePointerIcon size={16} />
|
||||
</div>
|
||||
<span>{t('mouse.mode')}</span>
|
||||
|
||||
@@ -57,10 +57,8 @@ export const Speed = (): ReactElement => {
|
||||
|
||||
return (
|
||||
<Popover content={content} placement="rightTop" arrow={false} align={{ offset: [13, 0] }}>
|
||||
<div className="flex h-[30px] cursor-pointer items-center space-x-1 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
|
||||
<div className="flex h-[14px] w-[20px] items-end">
|
||||
<GaugeIcon size={16} />
|
||||
</div>
|
||||
<div className="flex h-[32px] cursor-pointer items-center space-x-2 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
|
||||
<GaugeIcon size={18} />
|
||||
<span>{t('mouse.speed')}</span>
|
||||
</div>
|
||||
</Popover>
|
||||
|
||||
@@ -34,7 +34,7 @@ export const Style = () => {
|
||||
<div
|
||||
key={style.value}
|
||||
className={clsx(
|
||||
'flex cursor-pointer select-none items-center space-x-1 rounded py-1.5 pl-3 pr-5 hover:bg-neutral-700/50',
|
||||
'flex cursor-pointer select-none items-center space-x-1 rounded py-1 pl-3 pr-5 hover:bg-neutral-700/50',
|
||||
style.value === mouseStyle ? 'text-blue-500' : 'text-neutral-300'
|
||||
)}
|
||||
onClick={() => updateStyle(style.value)}
|
||||
@@ -48,8 +48,8 @@ export const Style = () => {
|
||||
|
||||
return (
|
||||
<Popover content={content} placement="rightTop" arrow={false} align={{ offset: [13, 0] }}>
|
||||
<div className="flex h-[30px] cursor-pointer items-center space-x-2 rounded px-3 text-neutral-300 hover:bg-neutral-700">
|
||||
<MousePointerIcon size={16} />
|
||||
<div className="flex h-[32px] cursor-pointer items-center space-x-2 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
|
||||
<MousePointerIcon size={18} />
|
||||
<span className="select-none text-sm">{t('mouse.cursor.title')}</span>
|
||||
</div>
|
||||
</Popover>
|
||||
|
||||
@@ -12,11 +12,11 @@ export const Settings = () => {
|
||||
}
|
||||
|
||||
const content = (
|
||||
<div className="flex flex-col space-y-1">
|
||||
<div className="flex flex-col space-y-0.5">
|
||||
<Language />
|
||||
|
||||
<div
|
||||
className="flex h-[30px] cursor-pointer items-center space-x-2 rounded px-3 text-neutral-300 hover:bg-neutral-700"
|
||||
className="flex h-[32px] cursor-pointer items-center space-x-2 rounded px-3 text-neutral-300 hover:bg-neutral-700/50"
|
||||
onClick={() => openPage('https://wiki.sipeed.com/nanokvmusb')}
|
||||
>
|
||||
<BookIcon size={18} />
|
||||
@@ -24,7 +24,7 @@ export const Settings = () => {
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="flex h-[30px] cursor-pointer items-center space-x-2 rounded px-3 text-neutral-300 hover:bg-neutral-700"
|
||||
className="flex h-[32px] cursor-pointer items-center space-x-2 rounded px-3 text-neutral-300 hover:bg-neutral-700/50"
|
||||
onClick={() => openPage('https://github.com/sipeed/NanoKVM-USB/releases')}
|
||||
>
|
||||
<DownloadIcon size={18} />
|
||||
@@ -35,7 +35,7 @@ export const Settings = () => {
|
||||
|
||||
return (
|
||||
<Popover content={content} placement="bottomLeft" trigger="click" arrow={false}>
|
||||
<div className="flex h-[28px] w-[28px] cursor-pointer items-center justify-center rounded text-white hover:bg-neutral-700/70">
|
||||
<div className="flex h-[28px] w-[28px] cursor-pointer items-center justify-center rounded text-white hover:bg-neutral-700/50">
|
||||
<SettingsIcon size={18} />
|
||||
</div>
|
||||
</Popover>
|
||||
|
||||
@@ -35,7 +35,7 @@ export const Language = () => {
|
||||
|
||||
return (
|
||||
<Popover content={content} placement="rightTop">
|
||||
<div className="flex h-[30px] cursor-pointer items-center space-x-2 rounded px-3 text-neutral-300 hover:bg-neutral-700">
|
||||
<div className="flex h-[32px] cursor-pointer items-center space-x-2 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
|
||||
<LanguagesIcon size={18} />
|
||||
<span>{t('settings.language')}</span>
|
||||
</div>
|
||||
|
||||
@@ -78,7 +78,7 @@ export const Device = () => {
|
||||
<div
|
||||
key={device.videoId}
|
||||
className={clsx(
|
||||
'cursor-pointer rounded px-2 py-1.5 hover:bg-neutral-700/60',
|
||||
'max-w-[320px] cursor-pointer truncate rounded px-2 py-1.5 hover:bg-neutral-700/60',
|
||||
device.videoId === videoDeviceId ? 'text-blue-500' : 'text-white'
|
||||
)}
|
||||
onClick={() => selectDevice(device)}
|
||||
@@ -91,7 +91,7 @@ export const Device = () => {
|
||||
|
||||
return (
|
||||
<Popover content={content} placement="rightTop" arrow={false} align={{ offset: [13, 0] }}>
|
||||
<div className="flex h-[30px] cursor-pointer items-center space-x-2 rounded px-3 text-neutral-300 hover:bg-neutral-700">
|
||||
<div className="flex h-[32px] cursor-pointer items-center space-x-2 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
|
||||
<VideoIcon size={18} />
|
||||
<span className="select-none text-sm">{t('video.device')}</span>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Scale } from './scale.tsx';
|
||||
|
||||
export const Video = () => {
|
||||
const content = (
|
||||
<div className="flex flex-col space-y-1">
|
||||
<div className="flex flex-col space-y-0.5">
|
||||
<Resolution />
|
||||
<Scale />
|
||||
<Device />
|
||||
|
||||
@@ -102,7 +102,7 @@ export const Resolution = () => {
|
||||
<div
|
||||
key={res.width}
|
||||
className={clsx(
|
||||
'flex cursor-pointer select-none items-center space-x-1 rounded px-3 py-1.5 hover:bg-neutral-700/60',
|
||||
'flex cursor-pointer select-none items-center space-x-1 rounded px-4 py-1.5 hover:bg-neutral-700/60',
|
||||
resolution.width === res.width && resolution.height === res.height
|
||||
? 'text-blue-500'
|
||||
: 'text-white'
|
||||
@@ -119,13 +119,13 @@ export const Resolution = () => {
|
||||
|
||||
{/* custom resolution */}
|
||||
<div
|
||||
className="flex cursor-pointer select-none items-center justify-between space-x-3 rounded px-3 py-1.5 text-sm hover:bg-neutral-700/60"
|
||||
className="flex cursor-pointer select-none items-center justify-between space-x-3 rounded px-4 py-1.5 text-sm hover:bg-neutral-700/60"
|
||||
onClick={showModal}
|
||||
>
|
||||
<span>{t('video.customResolution')}</span>
|
||||
{customResolutions.length > 0 && (
|
||||
<span className="hover:text-red-500" onClick={removeCustomResolution}>
|
||||
<Trash2Icon size={16} />
|
||||
<Trash2Icon size={14} />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
@@ -134,7 +134,7 @@ export const Resolution = () => {
|
||||
<div
|
||||
key={res.width}
|
||||
className={clsx(
|
||||
'flex cursor-pointer select-none items-center space-x-1 rounded px-3 py-1.5 hover:bg-neutral-700/60',
|
||||
'flex cursor-pointer select-none items-center space-x-1 rounded px-4 py-1.5 hover:bg-neutral-700/60',
|
||||
resolution.width === res.width && resolution.height === res.height
|
||||
? 'text-blue-500'
|
||||
: 'text-white'
|
||||
@@ -152,7 +152,7 @@ export const Resolution = () => {
|
||||
return (
|
||||
<>
|
||||
<Popover content={content} placement="rightTop" arrow={false} align={{ offset: [13, 0] }}>
|
||||
<div className="flex h-[30px] cursor-pointer items-center space-x-2 rounded px-3 text-neutral-300 hover:bg-neutral-700">
|
||||
<div className="flex h-[32px] cursor-pointer items-center space-x-2 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
|
||||
<RatioIcon size={18} />
|
||||
<span className="select-none text-sm">{t('video.resolution')}</span>
|
||||
</div>
|
||||
|
||||
@@ -1,58 +1,64 @@
|
||||
import { ReactElement, useEffect } from 'react'
|
||||
import { Popover, Slider } from 'antd'
|
||||
import { useAtom } from 'jotai'
|
||||
import { ScalingIcon } from 'lucide-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ReactElement, useEffect } from 'react';
|
||||
import { Popover } from 'antd';
|
||||
import clsx from 'clsx';
|
||||
import { useAtom } from 'jotai';
|
||||
import { PercentIcon, ScalingIcon } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { videoScaleAtom } from '@/jotai/device.ts';
|
||||
import * as storage from '@/libs/storage';
|
||||
|
||||
export const Scale = (): ReactElement => {
|
||||
const { t } = useTranslation()
|
||||
const ScaleList = [
|
||||
{ label: '200', value: 2 },
|
||||
{ label: '150', value: 1.5 },
|
||||
{ label: '100', value: 1 },
|
||||
{ label: '75', value: 0.75 },
|
||||
{ label: '50', value: 0.5 }
|
||||
];
|
||||
|
||||
const [videoScale, setVideoScale] = useAtom(videoScaleAtom)
|
||||
export const Scale = (): ReactElement => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [videoScale, setVideoScale] = useAtom(videoScaleAtom);
|
||||
|
||||
useEffect(() => {
|
||||
const scale = storage.getVideoScale()
|
||||
const scale = storage.getVideoScale();
|
||||
if (scale) {
|
||||
setVideoScale(scale)
|
||||
setVideoScale(scale);
|
||||
}
|
||||
}, [])
|
||||
}, []);
|
||||
|
||||
async function updateScale(scale: number): Promise<void> {
|
||||
setVideoScale(scale)
|
||||
storage.setVideoScale(scale)
|
||||
setVideoScale(scale);
|
||||
storage.setVideoScale(scale);
|
||||
}
|
||||
|
||||
const content = (
|
||||
<div className="h-[150px] w-[60px] py-3">
|
||||
<Slider
|
||||
vertical
|
||||
marks={{
|
||||
0.5: <span>x0.5</span>,
|
||||
1: <span>x1.0</span>,
|
||||
1.5: <span>x1.5</span>,
|
||||
2: <span>x2.0</span>
|
||||
}}
|
||||
range={false}
|
||||
included={false}
|
||||
min={0.5}
|
||||
max={2}
|
||||
step={0.1}
|
||||
defaultValue={videoScale}
|
||||
onChange={updateScale}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
<>
|
||||
{ScaleList.map((item) => (
|
||||
<div
|
||||
key={item.value}
|
||||
className={clsx(
|
||||
'flex cursor-pointer select-none items-center space-x-0.5 rounded px-5 py-1.5 hover:bg-neutral-700/60',
|
||||
item.value === videoScale ? 'text-blue-500' : 'text-white'
|
||||
)}
|
||||
onClick={() => updateScale(item.value)}
|
||||
>
|
||||
<span>{item.label}</span>
|
||||
<PercentIcon size={12} />
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover content={content} placement="rightTop" arrow={false} align={{ offset: [13, 0] }}>
|
||||
<div className="flex h-[30px] cursor-pointer items-center space-x-1 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
|
||||
<div className="flex h-[14px] w-[20px] items-end">
|
||||
<div className="flex h-[32px] cursor-pointer items-center space-x-2 rounded px-3 text-neutral-300 hover:bg-neutral-700/50">
|
||||
<div className="flex size-[18px] items-center justify-center">
|
||||
<ScalingIcon size={16} />
|
||||
</div>
|
||||
<span>{t('video.scale')}</span>
|
||||
</div>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user