refactor: Upgrade to the new UI (#521)

Co-authored-by: MystiPanda <mystipanda@proton.me>
This commit is contained in:
Amnesiash
2024-03-09 21:37:21 +08:00
committed by GitHub
parent ab539081fa
commit 3a0429d049
39 changed files with 533 additions and 182 deletions

View File

@@ -21,7 +21,10 @@ export const BasePage: React.FC<Props> = (props) => {
<BaseErrorBoundary>
<div className="base-page">
<header data-windrag style={{ userSelect: "none" }}>
<Typography variant="h4" component="h1" data-windrag>
<Typography
sx={{ fontSize: "20px", fontWeight: "700 " }}
data-windrag
>
{title}
</Typography>
@@ -30,13 +33,11 @@ export const BasePage: React.FC<Props> = (props) => {
<div
className={full ? "base-container no-padding" : "base-container"}
style={{ backgroundColor: isDark ? "#090909" : "#ffffff" }}
style={{ backgroundColor: isDark ? "#1e1f27" : "#ffffff" }}
>
<section
style={{
backgroundColor: isDark
? alpha(theme.palette.primary.main, 0.1)
: "",
backgroundColor: isDark ? "#1e1f27" : "var(--background-color)",
}}
>
<div className="base-content" style={contentStyle}>

View File

@@ -0,0 +1,57 @@
import { styled } from "@mui/material/styles";
import { default as MuiSwitch, SwitchProps } from "@mui/material/Switch";
export const Switch = styled((props: SwitchProps) => (
<MuiSwitch
focusVisibleClassName=".Mui-focusVisible"
disableRipple
{...props}
/>
))(({ theme }) => ({
width: 42,
height: 26,
padding: 0,
"& .MuiSwitch-switchBase": {
padding: 0,
margin: 2,
transitionDuration: "300ms",
"&.Mui-checked": {
transform: "translateX(16px)",
color: "#fff",
"& + .MuiSwitch-track": {
backgroundColor: theme.palette.primary.main,
opacity: 1,
border: 0,
},
"&.Mui-disabled + .MuiSwitch-track": {
opacity: 0.5,
},
},
"&.Mui-focusVisible .MuiSwitch-thumb": {
color: "#33cf4d",
border: "6px solid #fff",
},
"&.Mui-disabled .MuiSwitch-thumb": {
color:
theme.palette.mode === "light"
? theme.palette.grey[100]
: theme.palette.grey[600],
},
"&.Mui-disabled + .MuiSwitch-track": {
opacity: theme.palette.mode === "light" ? 0.7 : 0.3,
},
},
"& .MuiSwitch-thumb": {
boxSizing: "border-box",
width: 22,
height: 22,
},
"& .MuiSwitch-track": {
borderRadius: 26 / 2,
backgroundColor: theme.palette.mode === "light" ? "#E9E9EA" : "#39393D",
opacity: 1,
transition: theme.transitions.create(["background-color"], {
duration: 500,
}),
},
}));

View File

@@ -4,3 +4,4 @@ export { BaseEmpty } from "./base-empty";
export { BaseLoading } from "./base-loading";
export { BaseErrorBoundary } from "./base-error-boundary";
export { Notice } from "./base-notice";
export { Switch } from "./base-switch";

View File

@@ -1,32 +1,49 @@
import { alpha, ListItem, ListItemButton, ListItemText } from "@mui/material";
import {
alpha,
ListItem,
ListItemButton,
ListItemText,
ListItemAvatar,
Avatar,
} from "@mui/material";
import { useMatch, useResolvedPath, useNavigate } from "react-router-dom";
import type { LinkProps } from "react-router-dom";
export const LayoutItem = (props: LinkProps) => {
const { to, children } = props;
interface Props {
to: string;
children: string;
img: string;
}
export const LayoutItem = (props: Props) => {
const { to, children, img } = props;
const resolved = useResolvedPath(to);
const match = useMatch({ path: resolved.pathname, end: true });
const navigate = useNavigate();
return (
<ListItem sx={{ py: 0.5, maxWidth: 250, mx: "auto", padding: "1px 0px" }}>
<ListItem sx={{ py: 0.5, maxWidth: 250, mx: "auto", padding: "4px 0px" }}>
<ListItemButton
selected={!!match}
sx={[
{
borderRadius: 3,
marginLeft: 1,
marginRight: 1,
textAlign: "center",
"& .MuiListItemText-primary": { color: "text.secondary" },
borderRadius: 2,
marginLeft: 1.5,
paddingLeft: 1,
paddingRight: 1,
marginRight: 1.5,
textAlign: "left",
"& .MuiListItemText-primary": {
color: "text.primary",
fontWeight: "700",
},
},
({ palette: { mode, primary } }) => {
const bgcolor =
mode === "light"
? alpha(primary.main, 0.15)
: alpha(primary.main, 0.35);
const color = mode === "light" ? primary.main : primary.light;
const color = mode === "light" ? "#1f1f1f" : "#ffffff";
return {
"&.Mui-selected": { bgcolor },
@@ -37,6 +54,9 @@ export const LayoutItem = (props: LinkProps) => {
]}
onClick={() => navigate(to)}
>
<ListItemAvatar sx={{ marginRight: -0.5 }}>
<Avatar src={img}></Avatar>
</ListItemAvatar>
<ListItemText primary={children} />
</ListItemButton>
</ListItem>

View File

@@ -89,7 +89,7 @@ export const LayoutTraffic = () => {
return (
<Box
width="110px"
width="188px"
position="relative"
onClick={trafficRef.current?.toggleStyle}
>

View File

@@ -1,6 +1,6 @@
import { useEffect, useMemo } from "react";
import { useRecoilState } from "recoil";
import { alpha, createTheme, Theme } from "@mui/material";
import { alpha, createTheme, Shadows, Theme } from "@mui/material";
import { appWindow } from "@tauri-apps/api/window";
import { atomThemeMode } from "@/services/states";
import { defaultTheme, defaultDarkTheme } from "@/pages/_theme";
@@ -59,6 +59,7 @@ export const useCustomTheme = () => {
paper: dt.background_color,
},
},
shadows: Array(25).fill("none") as Shadows,
typography: {
// todo
fontFamily: setting.font_family
@@ -87,11 +88,14 @@ export const useCustomTheme = () => {
}
// css
const backgroundColor = mode === "light" ? "#ffffff" : "#0B121C";
const backgroundColor = mode === "light" ? "#f0f0f0" : "#2e303d";
const selectColor = mode === "light" ? "#f5f5f5" : "#d5d5d5";
const scrollColor = mode === "light" ? "#90939980" : "#54545480";
const dividerColor =
mode === "light" ? "rgba(0, 0, 0, 0.06)" : "rgba(255, 255, 255, 0.06)";
const rootEle = document.documentElement;
rootEle.style.setProperty("--divider-color", dividerColor);
rootEle.style.setProperty("--background-color", backgroundColor);
rootEle.style.setProperty("--selection-color", selectColor);
rootEle.style.setProperty("--scroller-color", scrollColor);

View File

@@ -2,41 +2,43 @@ import { alpha, Box, styled } from "@mui/material";
export const ProfileBox = styled(Box)(
({ theme, "aria-selected": selected }) => {
const { mode, primary, text, grey, background } = theme.palette;
const { mode, primary, text } = theme.palette;
const key = `${mode}-${!!selected}`;
const backgroundColor = {
"light-true": alpha(primary.main, 0.2),
"light-false": alpha(background.paper, 0.75),
"dark-true": alpha(primary.main, 0.45),
"dark-false": alpha(grey[700], 0.45),
}[key]!;
const backgroundColor = mode === "light" ? "#ffffff" : "#282A36";
const color = {
"light-true": text.secondary,
"light-false": text.secondary,
"dark-true": alpha(text.secondary, 0.85),
"dark-true": alpha(text.secondary, 0.65),
"dark-false": alpha(text.secondary, 0.65),
}[key]!;
const h2color = {
"light-true": primary.main,
"light-false": text.primary,
"dark-true": primary.light,
"dark-true": primary.main,
"dark-false": text.primary,
}[key]!;
const borderLeft = {
"light-true": `3px solid ${primary.main}`,
"light-false": "none",
"dark-true": `3px solid ${primary.main}`,
"dark-false": "none",
}[key];
return {
position: "relative",
width: "100%",
display: "block",
cursor: "pointer",
textAlign: "left",
borderRadius: theme.shape.borderRadius,
boxShadow: theme.shadows[2],
padding: "8px 16px",
boxSizing: "border-box",
backgroundColor,
borderLeft,
borderRadius: "8px",
color,
"& h2": { color: h2color },
};

View File

@@ -230,7 +230,14 @@ export const ProfileItem = (props: Props) => {
{...attributes}
{...listeners}
>
<DragIndicator sx={{ cursor: "move", marginLeft: "-6px" }} />
<DragIndicator
sx={[
{ cursor: "move", marginLeft: "-6px" },
({ palette: { text } }) => {
return { color: text.primary };
},
]}
/>
</Box>
<Typography
@@ -303,11 +310,7 @@ export const ProfileItem = (props: Props) => {
<span title="Updated Time">{parseExpire(updated)}</span>
</Box>
)}
<LinearProgress
variant="determinate"
value={progress}
color="inherit"
/>
<LinearProgress variant="determinate" value={progress} />
</ProfileBox>
<Menu

View File

@@ -15,12 +15,11 @@ import {
InputLabel,
MenuItem,
Select,
Switch,
styled,
TextField,
} from "@mui/material";
import { createProfile, patchProfile } from "@/services/cmds";
import { BaseDialog, Notice } from "@/components/base";
import { BaseDialog, Notice, Switch } from "@/components/base";
import { version } from "@root/package.json";
import { FileInput } from "./file-input";

View File

@@ -113,12 +113,12 @@ export const ProviderButton = () => {
return (
<>
<ListItem
sx={(theme) => ({
sx={{
p: 0,
borderRadius: "10px",
boxShadow: theme.shadows[2],
border: "solid 2px var(--divider-color)",
mb: 1,
})}
}}
key={key}
>
<ListItemText
@@ -161,7 +161,6 @@ export const ProviderButton = () => {
<LinearProgress
variant="determinate"
value={progress}
color="inherit"
/>
</>
)}

View File

@@ -50,30 +50,36 @@ export const ProxyItemMini = (props: Props) => {
sx={[
{
height: 56,
borderRadius: 1,
borderRadius: 1.5,
pl: 1.5,
pr: 1,
justifyContent: "space-between",
alignItems: "center",
},
({ palette: { mode, primary } }) => {
const bgcolor =
mode === "light"
? alpha(primary.main, 0.15)
: alpha(primary.main, 0.35);
const bgcolor = mode === "light" ? "#ffffff" : "#24252f";
const color = mode === "light" ? primary.main : primary.light;
const showDelay = delay > 0;
const shadowColor =
mode === "light" ? "rgba(0,0,0,0.04)" : "rgba(255,255,255,0.08)";
const selectColor = mode === "light" ? primary.main : primary.light;
return {
"&:hover .the-check": { display: !showDelay ? "block" : "none" },
"&:hover .the-delay": { display: showDelay ? "block" : "none" },
"&:hover .the-icon": { display: "none" },
"&.Mui-selected": { bgcolor, boxShadow: `0 0 0 1px ${bgcolor}` },
"&.Mui-selected .MuiListItemText-secondary": { color },
boxShadow: `0 0 0 1px ${shadowColor}`,
"&:hover ": {
bgcolor:
mode === "light"
? alpha(primary.main, 0.15)
: alpha(primary.main, 0.35),
},
"&.Mui-selected": {
width: `calc(100% + 3px)`,
marginLeft: `-3px`,
borderLeft: `3px solid ${selectColor}`,
bgcolor,
},
// "&.Mui-selected .MuiListItemText-secondary": { color },
backgroundColor: bgcolor,
};
},
]}
@@ -82,13 +88,15 @@ export const ProxyItemMini = (props: Props) => {
<Typography
variant="body2"
component="div"
color="text.secondary"
color="text.primary"
sx={{
display: "block",
textOverflow: "ellipsis",
wordBreak: "break-all",
overflow: "hidden",
whiteSpace: "nowrap",
fontSize: "13px",
fontWeight: "700",
}}
>
{proxy.name}
@@ -114,7 +122,8 @@ export const ProxyItemMini = (props: Props) => {
wordBreak: "break-all",
overflow: "hidden",
whiteSpace: "nowrap",
fontSize: "0.75rem",
fontSize: "11px",
fontWeight: "700",
marginRight: "8px",
}}
>
@@ -191,8 +200,8 @@ export const ProxyItemMini = (props: Props) => {
};
const Widget = styled(Box)(({ theme: { typography } }) => ({
padding: "3px 6px",
fontSize: 14,
padding: "2px 4px",
fontSize: 12,
fontFamily: typography.fontFamily,
borderRadius: "4px",
}));
@@ -200,15 +209,15 @@ const Widget = styled(Box)(({ theme: { typography } }) => ({
const TypeBox = styled(Box)(({ theme: { palette, typography } }) => ({
display: "inline-block",
border: "1px solid #ccc",
borderColor: alpha(palette.text.secondary, 0.36),
color: alpha(palette.text.secondary, 0.42),
borderColor: palette.mode === "light" ? "#d9d9d9" : "#424242",
color: palette.mode === "light" ? "#8c8c8c" : "#ffffff",
borderRadius: 4,
fontSize: 10,
fontFamily: typography.fontFamily,
marginRight: "4px",
marginTop: "auto",
padding: "0 2px",
lineHeight: 1.25,
padding: "0 4px",
lineHeight: 1.5,
}));
const TypeTypo = styled(Box)(({ theme: { palette, typography } }) => ({

View File

@@ -11,6 +11,7 @@ import {
styled,
SxProps,
Theme,
Typography,
} from "@mui/material";
import { BaseLoading } from "@/components/base";
import delayManager from "@/services/delay";
@@ -78,19 +79,28 @@ export const ProxyItem = (props: Props) => {
sx={[
{ borderRadius: 1 },
({ palette: { mode, primary } }) => {
const bgcolor =
mode === "light"
? alpha(primary.main, 0.15)
: alpha(primary.main, 0.35);
const color = mode === "light" ? primary.main : primary.light;
const bgcolor = mode === "light" ? "#ffffff" : "#24252f";
const selectColor = mode === "light" ? primary.main : primary.light;
const showDelay = delay > 0;
return {
"&:hover .the-check": { display: !showDelay ? "block" : "none" },
"&:hover .the-delay": { display: showDelay ? "block" : "none" },
"&:hover .the-icon": { display: "none" },
"&.Mui-selected": { bgcolor },
"&.Mui-selected .MuiListItemText-secondary": { color },
"&:hover ": {
bgcolor:
mode === "light"
? alpha(primary.main, 0.15)
: alpha(primary.main, 0.35),
},
"&.Mui-selected": {
borderLeft: `3px solid ${selectColor}`,
bgcolor,
},
// "&.Mui-selected .MuiListItemText-secondary": { bgcolor },
backgroundColor: bgcolor,
marginBottom: "8px",
height: "40px",
};
},
]}
@@ -99,7 +109,14 @@ export const ProxyItem = (props: Props) => {
title={proxy.name}
secondary={
<>
<span style={{ marginRight: 4 }}>
<span
style={{
marginRight: "8px",
fontSize: "13px",
color: "text.primary",
fontWeight: "700",
}}
>
{proxy.name}
{showType && proxy.now && ` - ${proxy.now}`}
</span>

View File

@@ -17,6 +17,8 @@ import { ProxyItem } from "./proxy-item";
import { ProxyItemMini } from "./proxy-item-mini";
import type { IRenderItem } from "./use-render-list";
import { useVerge } from "@/hooks/use-verge";
import { useRecoilState } from "recoil";
import { atomThemeMode } from "@/services/states";
interface RenderProps {
item: IRenderItem;
@@ -33,11 +35,21 @@ export const ProxyRender = (props: RenderProps) => {
const { type, group, headState, proxy, proxyCol } = item;
const { verge } = useVerge();
const enable_group_icon = verge?.enable_group_icon ?? true;
const [mode] = useRecoilState(atomThemeMode);
console.log(mode);
const isDark = mode === "light" ? false : true;
const itembackgroundcolor = isDark ? "#282A36" : "#ffffff";
if (type === 0 && !group.hidden) {
return (
<ListItemButton
dense
style={{
background: itembackgroundcolor,
height: "64px",
margin: "8px 16px",
borderRadius: "8px",
}}
onClick={() => onHeadState(group.name, { open: !headState?.open })}
>
{enable_group_icon &&
@@ -45,8 +57,8 @@ export const ProxyRender = (props: RenderProps) => {
group.icon.trim().startsWith("http") && (
<img
src={group.icon}
height="40px"
style={{ marginRight: "8px" }}
height="32px"
style={{ marginRight: "12px", borderRadius: "6px" }}
/>
)}
{enable_group_icon &&
@@ -54,8 +66,8 @@ export const ProxyRender = (props: RenderProps) => {
group.icon.trim().startsWith("data") && (
<img
src={group.icon}
height="40px"
style={{ marginRight: "8px" }}
height="32px"
style={{ marginRight: "12px", borderRadius: "6px" }}
/>
)}
{enable_group_icon &&
@@ -63,13 +75,14 @@ export const ProxyRender = (props: RenderProps) => {
group.icon.trim().startsWith("<svg") && (
<img
src={`data:image/svg+xml;base64,${btoa(group.icon)}`}
height="40px"
height="32px"
/>
)}
<ListItemText
primary={group.name}
primary={<StyledPrimary>{group.name}</StyledPrimary>}
secondary={
<ListItemTextChild
color="text.secondary"
sx={{
overflow: "hidden",
display: "flex",
@@ -78,11 +91,18 @@ export const ProxyRender = (props: RenderProps) => {
}}
>
<StyledTypeBox>{group.type}</StyledTypeBox>
<StyledSubtitle>{group.now}</StyledSubtitle>
<StyledSubtitle
sx={{
color: isDark ? "#ffffff" : "#8c8c8c",
fontWeight: "600",
}}
>
{group.now}
</StyledSubtitle>
</ListItemTextChild>
}
secondaryTypographyProps={{
sx: { display: "flex", alignItems: "center" },
sx: { display: "flex", alignItems: "center", color: "#ccc" },
}}
/>
{headState?.open ? <ExpandLessRounded /> : <ExpandMoreRounded />}
@@ -164,8 +184,16 @@ export const ProxyRender = (props: RenderProps) => {
return null;
};
const StyledPrimary = styled("span")`
font-size: 14px;
font-weight: 700;
line-height: 1.5;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;
const StyledSubtitle = styled("span")`
font-size: 0.8rem;
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -182,7 +210,7 @@ const StyledTypeBox = styled(ListItemTextChild)(({ theme }) => ({
color: alpha(theme.palette.primary.main, 0.8),
borderRadius: 4,
fontSize: 10,
padding: "0 2px",
lineHeight: 1.25,
marginRight: "4px",
padding: "0 4px",
lineHeight: 1.5,
marginRight: "8px",
}));

View File

@@ -102,12 +102,12 @@ export const ProviderButton = () => {
return (
<>
<ListItem
sx={(theme) => ({
sx={{
p: 0,
borderRadius: "10px",
boxShadow: theme.shadows[2],
border: "solid 2px var(--divider-color)",
mb: 1,
})}
}}
key={key}
>
<ListItemText

View File

@@ -1,8 +1,8 @@
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next";
import { List, Switch, Button } from "@mui/material";
import { List, Button } from "@mui/material";
import { useVerge } from "@/hooks/use-verge";
import { BaseDialog, DialogRef, Notice } from "@/components/base";
import { BaseDialog, DialogRef, Notice, Switch } from "@/components/base";
import { SettingItem } from "./setting-comp";
import { GuardState } from "./guard-state";
import { open as openDialog } from "@tauri-apps/api/dialog";

View File

@@ -7,11 +7,10 @@ import {
ListItemText,
MenuItem,
Select,
Switch,
TextField,
} from "@mui/material";
import { useVerge } from "@/hooks/use-verge";
import { BaseDialog, DialogRef, Notice } from "@/components/base";
import { BaseDialog, DialogRef, Notice, Switch } from "@/components/base";
export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation();

View File

@@ -18,9 +18,11 @@ export const SettingItem: React.FC<ItemProps> = (props) => {
const { label, extra, children, secondary } = props;
const primary = !extra ? (
label
<Box sx={{ display: "flex", alignItems: "center", fontSize: "14px" }}>
<span>{label}</span>
</Box>
) : (
<Box sx={{ display: "flex", alignItems: "center" }}>
<Box sx={{ display: "flex", alignItems: "center", fontSize: "14px" }}>
<span>{label}</span>
{extra}
</Box>
@@ -39,7 +41,17 @@ export const SettingList: React.FC<{
children: ReactNode;
}> = (props) => (
<List>
<ListSubheader sx={{ background: "transparent" }} disableSticky>
<ListSubheader
sx={[
{ background: "transparent", fontSize: "16px", fontWeight: "700" },
({ palette }) => {
return {
color: palette.text.primary,
};
},
]}
disableSticky
>
{props.title}
</ListSubheader>

View File

@@ -8,13 +8,12 @@ import {
ListItem,
ListItemText,
styled,
Switch,
TextField,
Typography,
} from "@mui/material";
import { useVerge } from "@/hooks/use-verge";
import { getSystemProxy } from "@/services/cmds";
import { BaseDialog, DialogRef, Notice } from "@/components/base";
import { BaseDialog, DialogRef, Notice, Switch } from "@/components/base";
export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation();

View File

@@ -8,11 +8,10 @@ import {
Box,
Typography,
Button,
Switch,
TextField,
} from "@mui/material";
import { useClash } from "@/hooks/use-clash";
import { BaseDialog, DialogRef, Notice } from "@/components/base";
import { BaseDialog, DialogRef, Notice, Switch } from "@/components/base";
import { StackModeSwitch } from "./stack-mode-switch";
export const TunViewer = forwardRef<DialogRef>((props, ref) => {

View File

@@ -3,7 +3,6 @@ import { useTranslation } from "react-i18next";
import { useLockFn } from "ahooks";
import {
TextField,
Switch,
Select,
MenuItem,
Typography,
@@ -11,7 +10,7 @@ import {
Tooltip,
} from "@mui/material";
import { ArrowForward, Settings, Shuffle } from "@mui/icons-material";
import { DialogRef, Notice } from "@/components/base";
import { DialogRef, Notice, Switch } from "@/components/base";
import { useClash } from "@/hooks/use-clash";
import { GuardState } from "./mods/guard-state";
import { WebUIViewer } from "./mods/web-ui-viewer";

View File

@@ -1,11 +1,11 @@
import useSWR from "swr";
import { useRef } from "react";
import { useTranslation } from "react-i18next";
import { IconButton, Switch, Tooltip } from "@mui/material";
import { IconButton, Tooltip } from "@mui/material";
import { PrivacyTipRounded, Settings, InfoRounded } from "@mui/icons-material";
import { checkService } from "@/services/cmds";
import { useVerge } from "@/hooks/use-verge";
import { DialogRef } from "@/components/base";
import { DialogRef, Switch } from "@/components/base";
import { SettingList, SettingItem } from "./mods/setting-comp";
import { GuardState } from "./mods/guard-state";
import { ServiceViewer } from "./mods/service-viewer";

View File

@@ -1,27 +1,22 @@
import { alpha, Box, styled } from "@mui/material";
export const TestBox = styled(Box)(({ theme, "aria-selected": selected }) => {
const { mode, primary, text, grey, background } = theme.palette;
const { mode, primary, text } = theme.palette;
const key = `${mode}-${!!selected}`;
const backgroundColor = {
"light-true": alpha(primary.main, 0.2),
"light-false": alpha(background.paper, 0.75),
"dark-true": alpha(primary.main, 0.45),
"dark-false": alpha(grey[700], 0.45),
}[key]!;
const backgroundColor = mode === "light" ? "#ffffff" : "#282A36";
const color = {
"light-true": text.secondary,
"light-false": text.secondary,
"dark-true": alpha(text.secondary, 0.85),
"dark-true": alpha(text.secondary, 0.65),
"dark-false": alpha(text.secondary, 0.65),
}[key]!;
const h2color = {
"light-true": primary.main,
"light-false": text.primary,
"dark-true": primary.light,
"dark-true": primary.main,
"dark-false": text.primary,
}[key]!;