From edf6c5cb65cbf103fb1848760595f5c381dac723 Mon Sep 17 00:00:00 2001 From: Olivi <225673551+Olivi-9@users.noreply.github.com> Date: Wed, 15 Apr 2026 19:26:24 +0800 Subject: build(deps)!: update depends --- src/components/shared/BaseModal.tsx | 11 +++++-- src/components/shared/Basic.module.scss | 2 +- src/components/shared/Fab.tsx | 13 ++++---- src/components/shared/FeatherIcons.ts | 53 +++++++++++++++++++++++++++++++++ src/components/shared/RotateIcon.tsx | 2 +- 5 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 src/components/shared/FeatherIcons.ts (limited to 'src/components/shared') diff --git a/src/components/shared/BaseModal.tsx b/src/components/shared/BaseModal.tsx index 72dcba4..f7841f8 100644 --- a/src/components/shared/BaseModal.tsx +++ b/src/components/shared/BaseModal.tsx @@ -1,6 +1,7 @@ import cx from 'clsx'; import * as React from 'react'; -import Modal from 'react-modal'; + +import Modal from '../Modal'; import modalStyle from '../Modal.module.scss'; @@ -8,7 +9,13 @@ import s from './BaseModal.module.scss'; const { useMemo } = React; -export default function BaseModal({ isOpen, onRequestClose, children }) { +type BaseModalProps = { + isOpen: boolean; + onRequestClose: (...args: any[]) => unknown; + children: React.ReactNode; +}; + +export default function BaseModal({ isOpen, onRequestClose, children }: BaseModalProps) { const className = useMemo( () => ({ base: cx(modalStyle.content, s.cnt), diff --git a/src/components/shared/Basic.module.scss b/src/components/shared/Basic.module.scss index 79b8a16..df412e5 100644 --- a/src/components/shared/Basic.module.scss +++ b/src/components/shared/Basic.module.scss @@ -1,4 +1,4 @@ -@import '~/styles/utils/custom-media'; +@use '~/styles/utils/custom-media' as *; h2.sectionNameType { margin: 0; diff --git a/src/components/shared/Fab.tsx b/src/components/shared/Fab.tsx index 8e72432..49c9a89 100644 --- a/src/components/shared/Fab.tsx +++ b/src/components/shared/Fab.tsx @@ -18,7 +18,7 @@ export const position = { interface ABProps extends React.HTMLAttributes { text?: string; - onClick?: (e: React.FormEvent) => void; + onClick?: (e: React.MouseEvent) => unknown; 'data-testid'?: string; } @@ -46,7 +46,7 @@ interface FabProps { alwaysShowTitle?: boolean; icon?: React.ReactNode; mainButtonStyles?: React.CSSProperties; - onClick?: (e: React.FormEvent) => void; + onClick?: (e: React.MouseEvent) => unknown; text?: string; children?: React.ReactNode; } @@ -68,7 +68,7 @@ const Fab: React.FC = ({ const close = () => setIsOpen(false); const enter = () => event === 'hover' && open(); const leave = () => event === 'hover' && close(); - const toggle = (e: React.FormEvent) => { + const toggle = (e: React.MouseEvent) => { if (onClick) { return onClick(e); } @@ -76,7 +76,10 @@ const Fab: React.FC = ({ return event === 'click' ? (isOpen ? close() : open()) : null; }; - const actionOnClick = (e: React.FormEvent, userFunc: (e: React.FormEvent) => void) => { + const actionOnClick = ( + e: React.MouseEvent, + userFunc: (e: React.MouseEvent) => unknown + ) => { e.persist(); setIsOpen(false); setTimeout(() => { @@ -95,7 +98,7 @@ const Fab: React.FC = ({ 'aria-hidden': ariaHidden, tabIndex: isOpen ? 0 : -1, ...ch.props, - onClick: (e: React.FormEvent) => { + onClick: (e: React.MouseEvent) => { if (ch.props.onClick) actionOnClick(e, ch.props.onClick); }, })} diff --git a/src/components/shared/FeatherIcons.ts b/src/components/shared/FeatherIcons.ts new file mode 100644 index 0000000..745baa1 --- /dev/null +++ b/src/components/shared/FeatherIcons.ts @@ -0,0 +1,53 @@ +import * as React from 'react'; +import * as Feather from 'react-feather'; + +type FeatherCompatProps = { + color?: string; + size?: string | number; + className?: string; + style?: React.CSSProperties; + [key: string]: unknown; +}; + +type IconComponent = React.ComponentType; + +function asIcon(name: keyof typeof Feather): IconComponent { + return Feather[name] as unknown as IconComponent; +} + +export const Activity = asIcon('Activity'); +export const ArrowDown = asIcon('ArrowDown'); +export const ArrowDownCircle = asIcon('ArrowDownCircle'); +export const ArrowUp = asIcon('ArrowUp'); +export const ChevronDown = asIcon('ChevronDown'); +export const ChevronUp = asIcon('ChevronUp'); +export const Cpu = asIcon('Cpu'); +export const Database = asIcon('Database'); +export const Download = asIcon('Download'); +export const DownloadCloud = asIcon('DownloadCloud'); +export const Eye = asIcon('Eye'); +export const EyeOff = asIcon('EyeOff'); +export const FileText = asIcon('FileText'); +export const GitHub = asIcon('GitHub'); +export const Globe = asIcon('Globe'); +export const Hash = asIcon('Hash'); +export const Info = asIcon('Info'); +export const Link = asIcon('Link'); +export const LogOut = asIcon('LogOut'); +export const Menu = asIcon('Menu'); +export const Monitor = asIcon('Monitor'); +export const Pause = asIcon('Pause'); +export const Play = asIcon('Play'); +export const RefreshCcw = asIcon('RefreshCcw'); +export const RefreshCw = asIcon('RefreshCw'); +export const RotateCw = asIcon('RotateCw'); +export const Settings = asIcon('Settings'); +export const Shield = asIcon('Shield'); +export const Sliders = asIcon('Sliders'); +export const Tag = asIcon('Tag'); +export const Tool = asIcon('Tool'); +export const Trash2 = asIcon('Trash2'); +export const Upload = asIcon('Upload'); +export const X = asIcon('X'); +export const XCircle = asIcon('XCircle'); +export const Zap = asIcon('Zap'); diff --git a/src/components/shared/RotateIcon.tsx b/src/components/shared/RotateIcon.tsx index 7e3ceae..0a5a018 100644 --- a/src/components/shared/RotateIcon.tsx +++ b/src/components/shared/RotateIcon.tsx @@ -1,6 +1,6 @@ import cx from 'clsx'; import * as React from 'react'; -import { RotateCw } from 'react-feather'; +import { RotateCw } from '~/components/shared/FeatherIcons'; import s from './RotateIcon.module.scss'; -- cgit v1.3.1 From 56758999537ca0790837f446984379eebca3a44d Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sat, 18 Apr 2026 14:07:20 +0800 Subject: chore: modify chart --- src/components/Collapsible.tsx | 23 +++-- src/components/Sparkline.tsx | 24 +++-- src/components/TrafficNow.module.scss | 32 +++++-- src/components/TrafficNow.tsx | 82 +++++++++-------- src/components/proxies/ProxyProvider.tsx | 28 +++--- src/components/shared/FeatherIcons.ts | 91 ++++++++----------- src/components/shared/ThemeSwitcher.tsx | 150 +++++++++++++++---------------- 7 files changed, 226 insertions(+), 204 deletions(-) (limited to 'src/components/shared') diff --git a/src/components/Collapsible.tsx b/src/components/Collapsible.tsx index f43dbd6..6948cef 100644 --- a/src/components/Collapsible.tsx +++ b/src/components/Collapsible.tsx @@ -1,7 +1,6 @@ +import { LazyMotion, domAnimation, m } from 'framer-motion'; import React from 'react'; -import { framerMotionResouce } from '../misc/motion'; - const { memo } = React; const variantsCollpapsibleWrap = { @@ -27,17 +26,17 @@ const variantsCollpapsibleWrap = { }; const Collapsible = memo(({ children, isOpen }: { children: React.ReactNode; isOpen: boolean }) => { - const module = framerMotionResouce.read(); - const motion = module.motion; return ( - - {children} - + + + {children} + + ); }); diff --git a/src/components/Sparkline.tsx b/src/components/Sparkline.tsx index b7f35a9..79f8bf5 100644 --- a/src/components/Sparkline.tsx +++ b/src/components/Sparkline.tsx @@ -63,9 +63,19 @@ const extraChartOptions: any = { export default function Sparkline({ data: dataArray, labels, type, styleIndex = 0 }) { chartJSResource.read(); + const isMemory = type === 'inuse'; + const options = useMemo(() => { return { ...extraChartOptions, + scales: { + ...extraChartOptions.scales, + y: { + display: false, + // 内存值稳定,不从零开始,让 Y 轴自动适应数据范围以显示波动 + beginAtZero: !isMemory, + }, + }, plugins: { ...extraChartOptions.plugins, tooltip: { @@ -75,9 +85,9 @@ export default function Sparkline({ data: dataArray, labels, type, styleIndex = title: () => '', label(context) { if (context.parsed.y !== null) { - const suffix = type === 'inuse' ? '' : '/s'; - // 还原 log1p 变换后的真实值 - return prettyBytes(Math.expm1(context.parsed.y)) + suffix; + const suffix = isMemory ? '' : '/s'; + const raw = isMemory ? context.parsed.y : Math.expm1(context.parsed.y); + return prettyBytes(raw) + suffix; } return ''; }, @@ -85,7 +95,7 @@ export default function Sparkline({ data: dataArray, labels, type, styleIndex = }, }, }; - }, [type]); + }, [type, isMemory]); const data = useMemo( () => ({ @@ -93,13 +103,13 @@ export default function Sparkline({ data: dataArray, labels, type, styleIndex = { ...commonDataSetProps, ...chartStyles[styleIndex][type], - // log1p 变换:压缩大尖刺,让小流量也可见;log1p(0)=0 不会出现 -Infinity - data: dataArray.map((v, i) => ({ x: labels[i], y: Math.log1p(v) })), + // 内存用原始值(变化幅度小,不需要压缩);流量用 log1p 压缩尖刺 + data: dataArray.map((v, i) => ({ x: labels[i], y: isMemory ? v : Math.log1p(v) })), fill: true, }, ], }), - [dataArray, labels, type, styleIndex] + [dataArray, labels, type, styleIndex, isMemory], ); return ( diff --git a/src/components/TrafficNow.module.scss b/src/components/TrafficNow.module.scss index 85281e2..2b0bcdf 100644 --- a/src/components/TrafficNow.module.scss +++ b/src/components/TrafficNow.module.scss @@ -1,11 +1,12 @@ .TrafficNow { color: var(--color-text); display: flex; - flex-direction: column; grid-gap: 20px; + flex-direction: column; + grid-gap: 20px; gap: 20px; padding: 10px 0; -.overview { + .overview { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; @@ -23,19 +24,40 @@ } } + .chartsRow { + display: flex; + flex-direction: column; + gap: 20px; + + @media (min-width: 768px) { + flex-direction: row; + + & > .sec { + flex: 1; + min-width: 0; + } + } + } + .sec { padding: 20px; background-color: var(--color-bg-card); border-radius: 12px; - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); - transition: transform 0.2s ease, box-shadow 0.2s ease; + box-shadow: + 0 4px 6px -1px rgba(0, 0, 0, 0.1), + 0 2px 4px -1px rgba(0, 0, 0, 0.06); + transition: + transform 0.2s ease, + box-shadow 0.2s ease; display: flex; flex-direction: column; justify-content: space-between; min-height: 140px; &:hover { - box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + box-shadow: + 0 10px 15px -3px rgba(0, 0, 0, 0.1), + 0 4px 6px -2px rgba(0, 0, 0, 0.05); } .header { diff --git a/src/components/TrafficNow.tsx b/src/components/TrafficNow.tsx index e1b0f6d..5dc601b 100644 --- a/src/components/TrafficNow.tsx +++ b/src/components/TrafficNow.tsx @@ -1,5 +1,12 @@ import * as React from 'react'; -import { Download, ArrowDown, ArrowUp, Cpu, Link as LinkIcon, Upload } from '~/components/shared/FeatherIcons'; +import { + Download, + ArrowDown, + ArrowUp, + Cpu, + Link as LinkIcon, + Upload, +} from '~/components/shared/FeatherIcons'; import { useTranslation } from 'react-i18next'; import useMemory from '../hooks/useMemory'; @@ -51,45 +58,46 @@ export default function TrafficNow({ apiConfig, selectedChartStyleIndex }: Props -
-
- - {t('Download')} +
+
+
+ + {t('Download')} +
+
{downStr}
+
-
{downStr}
- -
-
-
- - {t('Upload')} +
+
+ + {t('Upload')} +
+
{upStr}
+
-
{upStr}
- -
- -
-
- - {t('Memory Usage')} +
+
+ + {t('Memory Usage')} +
+
{mUsage}
+
-
{mUsage}
- {' '}
); diff --git a/src/components/proxies/ProxyProvider.tsx b/src/components/proxies/ProxyProvider.tsx index bd2b4fa..47ccb8b 100644 --- a/src/components/proxies/ProxyProvider.tsx +++ b/src/components/proxies/ProxyProvider.tsx @@ -8,7 +8,7 @@ import Collapsible from '~/components/Collapsible'; import CollapsibleSectionHeader from '~/components/CollapsibleSectionHeader'; import s0 from '~/components/proxies/ProxyGroup.module.scss'; import { useStoreActions } from '~/components/StateProvider'; -import { framerMotionResouce } from '~/misc/motion'; +import { LazyMotion, domAnimation, m } from 'framer-motion'; import { useFilteredAndSorted, useUpdateProviderItem } from '~/modules/proxies/hooks'; import { healthcheckProviderByName } from '~/store/proxies'; import { DelayMapping, DispatchFn, ProxiesMapping, SubscriptionInfo } from '~/store/types'; @@ -190,19 +190,19 @@ function formatBytes(bytes, decimals = 2) { return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`; } function Refresh() { - const module = framerMotionResouce.read(); - const motion = module.motion; return ( - - - - - + + + + + + + ); } diff --git a/src/components/shared/FeatherIcons.ts b/src/components/shared/FeatherIcons.ts index 745baa1..ee1f410 100644 --- a/src/components/shared/FeatherIcons.ts +++ b/src/components/shared/FeatherIcons.ts @@ -1,53 +1,38 @@ -import * as React from 'react'; -import * as Feather from 'react-feather'; - -type FeatherCompatProps = { - color?: string; - size?: string | number; - className?: string; - style?: React.CSSProperties; - [key: string]: unknown; -}; - -type IconComponent = React.ComponentType; - -function asIcon(name: keyof typeof Feather): IconComponent { - return Feather[name] as unknown as IconComponent; -} - -export const Activity = asIcon('Activity'); -export const ArrowDown = asIcon('ArrowDown'); -export const ArrowDownCircle = asIcon('ArrowDownCircle'); -export const ArrowUp = asIcon('ArrowUp'); -export const ChevronDown = asIcon('ChevronDown'); -export const ChevronUp = asIcon('ChevronUp'); -export const Cpu = asIcon('Cpu'); -export const Database = asIcon('Database'); -export const Download = asIcon('Download'); -export const DownloadCloud = asIcon('DownloadCloud'); -export const Eye = asIcon('Eye'); -export const EyeOff = asIcon('EyeOff'); -export const FileText = asIcon('FileText'); -export const GitHub = asIcon('GitHub'); -export const Globe = asIcon('Globe'); -export const Hash = asIcon('Hash'); -export const Info = asIcon('Info'); -export const Link = asIcon('Link'); -export const LogOut = asIcon('LogOut'); -export const Menu = asIcon('Menu'); -export const Monitor = asIcon('Monitor'); -export const Pause = asIcon('Pause'); -export const Play = asIcon('Play'); -export const RefreshCcw = asIcon('RefreshCcw'); -export const RefreshCw = asIcon('RefreshCw'); -export const RotateCw = asIcon('RotateCw'); -export const Settings = asIcon('Settings'); -export const Shield = asIcon('Shield'); -export const Sliders = asIcon('Sliders'); -export const Tag = asIcon('Tag'); -export const Tool = asIcon('Tool'); -export const Trash2 = asIcon('Trash2'); -export const Upload = asIcon('Upload'); -export const X = asIcon('X'); -export const XCircle = asIcon('XCircle'); -export const Zap = asIcon('Zap'); +export { + Activity, + ArrowDown, + ArrowDownCircle, + ArrowUp, + ChevronDown, + ChevronUp, + Cpu, + Database, + Download, + DownloadCloud, + Eye, + EyeOff, + FileText, + GitHub, + Globe, + Hash, + Info, + Link, + LogOut, + Menu, + Monitor, + Pause, + Play, + RefreshCcw, + RefreshCw, + RotateCw, + Settings, + Shield, + Sliders, + Tag, + Tool, + Trash2, + Upload, + X, + XCircle, + Zap, +} from 'react-feather'; diff --git a/src/components/shared/ThemeSwitcher.tsx b/src/components/shared/ThemeSwitcher.tsx index 363d422..dfe248d 100644 --- a/src/components/shared/ThemeSwitcher.tsx +++ b/src/components/shared/ThemeSwitcher.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { useTranslation } from 'react-i18next'; import { connect } from '~/components/StateProvider'; -import { framerMotionResouce } from '~/misc/motion'; +import { LazyMotion, domAnimation, m } from 'framer-motion'; import { getTheme, switchTheme } from '~/store/app'; import { State } from '~/store/types'; @@ -28,7 +28,7 @@ export function ThemeSwitcherImpl({ theme, dispatch }) { const onChange = React.useCallback( (e: React.ChangeEvent) => dispatch(switchTheme(e.target.value)), - [dispatch] + [dispatch], ); return ( @@ -46,91 +46,89 @@ export function ThemeSwitcherImpl({ theme, dispatch }) { } function MoonA() { - const module = framerMotionResouce.read(); - const motion = module.motion; return ( - - - + + + + + ); } function Sun() { - const module = framerMotionResouce.read(); - const motion = module.motion; - return ( - - - - - - - - - - - - - + + + + + + + + + + + + + + + ); } function Auto() { - const module = framerMotionResouce.read(); - const motion = module.motion; - return ( - - - - - - - + + + + + + + + + ); } -- cgit v1.3.1