refactor: use grid for proxy list layout

This commit is contained in:
Haishan 2020-06-15 23:17:50 +08:00
parent 98f0753cdc
commit 0bf2d72d05
6 changed files with 87 additions and 65 deletions

View file

@ -97,6 +97,9 @@ body {
Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji,
Segoe UI Symbol, 'PingFang SC', 'Microsoft YaHei', '微软雅黑', Arial, Segoe UI Symbol, 'PingFang SC', 'Microsoft YaHei', '微软雅黑', Arial,
sans-serif; sans-serif;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-webkit-text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }

View file

@ -4,6 +4,10 @@
border-radius: 8px; border-radius: 8px;
overflow: hidden; overflow: hidden;
display: flex;
flex-direction: column;
justify-content: space-between;
max-width: 280px; max-width: 280px;
@media (--breakpoint-not-small) { @media (--breakpoint-not-small) {
min-width: 200px; min-width: 200px;
@ -19,6 +23,13 @@
&.error { &.error {
opacity: 0.5; opacity: 0.5;
} }
&.selectable {
transition: transform 0.2s ease-in-out;
cursor: pointer;
&:hover {
transform: translateY(-2px);
}
}
} }
.proxyType { .proxyType {
@ -37,20 +48,28 @@
.proxyName { .proxyName {
width: 100%; width: 100%;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 5px; margin-bottom: 5px;
font-size: 0.85em; font-size: 0.85em;
@media (--breakpoint-not-small) { @media (--breakpoint-not-small) {
font-size: 1.1em; font-size: 1em;
} }
} }
.proxySmall { .proxySmall {
.now { width: 13px;
outline: pink solid 1px; height: 13px;
border-radius: 50%;
border: 1px solid var(--color-background);
&.now {
border-color: var(--color-text-secondary);
}
&.selectable {
transition: transform 0.1s ease-in-out;
cursor: pointer;
&:hover {
transform: scale(1.2);
}
} }
width: 12px;
height: 12px;
border-radius: 8px;
} }

View file

@ -41,9 +41,17 @@ type ProxyProps = {
now?: boolean; now?: boolean;
proxy: any; proxy: any;
latency: any; latency: any;
isSelectable?: boolean;
onClick?: (proxyName: string) => unknown;
}; };
function ProxySmallImpl({ now, name, latency }: ProxyProps) { function ProxySmallImpl({
now,
name,
latency,
isSelectable,
onClick,
}: ProxyProps) {
const color = useMemo(() => getLabelColor(latency), [latency]); const color = useMemo(() => getLabelColor(latency), [latency]);
const title = useMemo(() => { const title = useMemo(() => {
let ret = name; let ret = name;
@ -55,20 +63,37 @@ function ProxySmallImpl({ now, name, latency }: ProxyProps) {
return ( return (
<div <div
title={title} title={title}
className={cx(s0.proxySmall, { [s0.now]: now })} className={cx(s0.proxySmall, {
[s0.now]: now,
[s0.selectable]: isSelectable,
})}
style={{ backgroundColor: color }} style={{ backgroundColor: color }}
onClick={() => {
isSelectable && onClick && onClick(name);
}}
/> />
); );
} }
function ProxyImpl({ now, name, proxy, latency }: ProxyProps) { function ProxyImpl({
now,
name,
proxy,
latency,
isSelectable,
onClick,
}: ProxyProps) {
const color = useMemo(() => getLabelColor(latency), [latency]); const color = useMemo(() => getLabelColor(latency), [latency]);
return ( return (
<div <div
className={cx(s0.proxy, { className={cx(s0.proxy, {
[s0.now]: now, [s0.now]: now,
[s0.error]: latency && latency.error, [s0.error]: latency && latency.error,
[s0.selectable]: isSelectable,
})} })}
onClick={() => {
isSelectable && onClick && onClick(name);
}}
> >
<div className={s0.proxyName}>{name}</div> <div className={s0.proxyName}>{name}</div>
<div className={s0.row}> <div className={s0.row}>

View file

@ -16,7 +16,7 @@ import { ProxyList, ProxyListSummaryView } from './ProxyList';
import s0 from './ProxyGroup.module.css'; import s0 from './ProxyGroup.module.css';
const { useCallback, useMemo, useState } = React; const { createElement, useCallback, useMemo, useState } = React;
function ZapWrapper() { function ZapWrapper() {
return ( return (
@ -73,16 +73,12 @@ function ProxyGroupImpl({ name, all, type, now, isOpen, apiConfig, dispatch }) {
<ZapWrapper /> <ZapWrapper />
</Button> </Button>
</div> </div>
{isOpen ? ( {createElement(isOpen ? ProxyList : ProxyListSummaryView, {
<ProxyList all,
all={all} now,
now={now} isSelectable,
isSelectable={isSelectable} itemOnTapCallback,
itemOnTapCallback={itemOnTapCallback} })}
/>
) : (
<ProxyListSummaryView all={all} />
)}
</div> </div>
); );
} }

View file

@ -1,22 +1,13 @@
.list { .list {
display: flex;
flex-wrap: wrap;
margin-top: 8px; margin-top: 8px;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(auto, 280px));
grid-gap: 10px;
} }
.proxy { .listSummaryView {
margin-right: 5px; margin-top: 8px;
margin-bottom: 5px; display: grid;
@media (--breakpoint-not-small) { grid-template-columns: repeat(auto-fill, 13px);
margin-right: 10px; grid-gap: 10px;
margin-bottom: 10px;
}
transition: transform 0.2s ease-in-out;
&.proxySelectable {
cursor: pointer;
&:hover {
transform: translateY(-2px);
}
}
} }

View file

@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import cx from 'clsx'; /* import cx from 'clsx'; */
import { Proxy, ProxySmall } from './Proxy'; import { Proxy, ProxySmall } from './Proxy';
@ -25,20 +25,14 @@ export function ProxyList({
return ( return (
<div className={s.list}> <div className={s.list}>
{proxies.map((proxyName) => { {proxies.map((proxyName) => {
const proxyClassName = cx(s.proxy, {
[s.proxySelectable]: isSelectable,
});
return ( return (
<div <Proxy
className={proxyClassName}
key={proxyName} key={proxyName}
onClick={() => { onClick={itemOnTapCallback}
if (!isSelectable || !itemOnTapCallback) return; isSelectable={isSelectable}
itemOnTapCallback(proxyName); name={proxyName}
}} now={proxyName === now}
> />
<Proxy name={proxyName} now={proxyName === now} />
</div>
); );
})} })}
</div> </div>
@ -52,22 +46,16 @@ export function ProxyListSummaryView({
itemOnTapCallback, itemOnTapCallback,
}: ProxyListProps) { }: ProxyListProps) {
return ( return (
<div className={s.list}> <div className={s.listSummaryView}>
{all.map((proxyName) => { {all.map((proxyName) => {
const proxyClassName = cx(s.proxy, {
[s.proxySelectable]: isSelectable,
});
return ( return (
<div <ProxySmall
className={proxyClassName}
key={proxyName} key={proxyName}
onClick={() => { onClick={itemOnTapCallback}
if (!isSelectable || !itemOnTapCallback) return; isSelectable={isSelectable}
itemOnTapCallback(proxyName); name={proxyName}
}} now={proxyName === now}
> />
<ProxySmall name={proxyName} now={proxyName === now} />
</div>
); );
})} })}
</div> </div>