summaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorHaishan <[email protected]>2020-02-10 22:53:14 +0800
committerHaishan <[email protected]>2020-02-10 23:04:57 +0800
commite85116bf717b51c0d4aa4f07c91944414f0a4b4e (patch)
tree63a6167eaeb7b1da842caafe47947d3997a65a38 /src/components
parentbd82b8c5e3bad3efeecd9e2599b07128d290bea6 (diff)
refactor(Proxies): UI revamp
Diffstat (limited to 'src/components')
-rw-r--r--src/components/Collapsible.js79
-rw-r--r--src/components/CollapsibleSectionHeader.js34
-rw-r--r--src/components/CollapsibleSectionHeader.module.css30
-rw-r--r--src/components/Proxies.js49
-rw-r--r--src/components/Proxy.js15
-rw-r--r--src/components/Proxy.module.css18
-rw-r--r--src/components/ProxyGroup.js149
-rw-r--r--src/components/ProxyLatency.module.css1
-rw-r--r--src/components/ProxyProvider.js161
-rw-r--r--src/components/ProxyProvider.module.css19
-rw-r--r--src/components/ProxyProviderList.js9
-rw-r--r--src/components/StateProvider.js6
-rw-r--r--src/components/rtf.css235
-rw-r--r--src/components/shared/Basic.js3
14 files changed, 534 insertions, 274 deletions
diff --git a/src/components/Collapsible.js b/src/components/Collapsible.js
new file mode 100644
index 0000000..22016fe
--- /dev/null
+++ b/src/components/Collapsible.js
@@ -0,0 +1,79 @@
+import React from 'react';
+import ResizeObserver from 'resize-observer-polyfill';
+import { motion } from 'framer-motion';
+
+const { memo, useState, useRef, useEffect } = React;
+
+function usePrevious(value) {
+ const ref = useRef();
+ useEffect(() => void (ref.current = value), [value]);
+ return ref.current;
+}
+
+function useMeasure() {
+ const ref = useRef();
+ const [bounds, set] = useState({ height: 0 });
+ useEffect(() => {
+ const ro = new ResizeObserver(([entry]) => set(entry.contentRect));
+ if (ref.current) ro.observe(ref.current);
+ return () => ro.disconnect();
+ }, []);
+ return [ref, bounds];
+}
+
+const variantsCollpapsibleWrap = {
+ initialOpen: {
+ height: 'auto',
+ transition: { duration: 0 }
+ },
+ open: height => ({
+ height,
+ opacity: 1,
+ visibility: 'visible',
+ transition: { duration: 0.3 }
+ }),
+ closed: {
+ height: 0,
+ opacity: 0,
+ visibility: 'hidden',
+ transition: { duration: 0.3 }
+ }
+};
+
+const variantsCollpapsibleChildContainer = {
+ open: {
+ x: 0
+ },
+ closed: {
+ x: 20
+ }
+};
+
+const Collapsible = memo(({ children, isOpen }) => {
+ const previous = usePrevious(isOpen);
+ const [refToMeature, { height }] = useMeasure();
+ return (
+ <div>
+ <motion.div
+ animate={
+ isOpen && previous === isOpen
+ ? 'initialOpen'
+ : isOpen
+ ? 'open'
+ : 'closed'
+ }
+ custom={height}
+ variants={variantsCollpapsibleWrap}
+ >
+ <motion.div
+ variants={variantsCollpapsibleChildContainer}
+ ref={refToMeature}
+ >
+ {children}
+ </motion.div>
+ </motion.div>
+ </div>
+ );
+});
+
+export default Collapsible;
diff --git a/src/components/CollapsibleSectionHeader.js b/src/components/CollapsibleSectionHeader.js
new file mode 100644
index 0000000..aebb643
--- /dev/null
+++ b/src/components/CollapsibleSectionHeader.js
@@ -0,0 +1,34 @@
+import React from 'react';
+import { ChevronDown } from 'react-feather';
+import cx from 'classnames';
+
+import { SectionNameType } from './shared/Basic';
+import Button from './Button';
+
+import s from './CollapsibleSectionHeader.module.css';
+
+type Props = {
+ name: string,
+ type: string,
+ qty?: number,
+ toggle?: () => void,
+ isOpen?: boolean
+};
+
+export default function Header({ name, type, toggle, isOpen, qty }: Props) {
+ return (
+ <div className={s.header}>
+ <div onClick={toggle} style={{ cursor: 'pointer' }}>
+ <SectionNameType name={name} type={type} />
+ </div>
+
+ {typeof qty === 'number' ? <span className={s.qty}>{qty}</span> : null}
+
+ <Button kind="minimal" onClick={toggle}>
+ <span className={cx(s.arrow, { [s.isOpen]: isOpen })}>
+ <ChevronDown size={20} />
+ </span>
+ </Button>
+ </div>
+ );
+}
diff --git a/src/components/CollapsibleSectionHeader.module.css b/src/components/CollapsibleSectionHeader.module.css
new file mode 100644
index 0000000..de24eec
--- /dev/null
+++ b/src/components/CollapsibleSectionHeader.module.css
@@ -0,0 +1,30 @@
+.header {
+ display: flex;
+ align-items: center;
+
+ .arrow {
+ display: inline-flex;
+ transform: rotate(0deg);
+ transition: transform 0.3s;
+ &.isOpen {
+ transform: rotate(180deg);
+ }
+
+ &:focus {
+ outline: var(--color-focus-blue) solid 1px;
+ }
+ }
+}
+
+/* TODO duplicate with connQty in Connections.module.css */
+.qty {
+ font-family: var(--font-normal);
+ font-size: 0.75em;
+ margin-left: 3px;
+ padding: 2px 7px;
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ background-color: var(--bg-near-transparent);
+ border-radius: 30px;
+}
diff --git a/src/components/Proxies.js b/src/components/Proxies.js
index 83d5205..acb26dd 100644
--- a/src/components/Proxies.js
+++ b/src/components/Proxies.js
@@ -1,19 +1,18 @@
import React from 'react';
-// import { useStoreState } from '../misc/store';
-import { connect } from './StateProvider';
+import { connect, useStoreActions } from './StateProvider';
import ContentHeader from './ContentHeader';
import ProxyGroup from './ProxyGroup';
-import Button from './Button';
-import { Zap, Filter } from 'react-feather';
+import { Zap, Filter, Circle } from 'react-feather';
import ProxyProviderList from './ProxyProviderList';
+import { Fab, Action } from 'react-tiny-fab';
+import './rtf.css';
import s0 from './Proxies.module.css';
import {
- getProxies,
getDelay,
getRtFilterSwitch,
getProxyGroupNames,
@@ -23,29 +22,18 @@ import {
} from '../store/proxies';
import { getClashAPIConfig } from '../store/app';
-const { useEffect, useMemo, useCallback, useRef } = React;
+const { useEffect, useCallback, useRef } = React;
function Proxies({
dispatch,
groupNames,
- proxies,
delay,
proxyProviders,
apiConfig,
filterZeroRT
}) {
const refFetchedTimestamp = useRef({});
-
- const switchRequetState = (dispath, getState) => {
- const preState = getRtFilterSwitch(getState());
-
- dispatch('store/proxies#filterZeroRTProxies', s => {
- s.filterZeroRT = !preState;
- });
- };
- const filterZeroRTFn = useCallback(() => dispatch(switchRequetState), [
- dispatch
- ]);
+ const { toggleUnavailableProxiesFilter } = useStoreActions();
const requestDelayAllFn = useCallback(
() => dispatch(requestDelayAll(apiConfig)),
[apiConfig, dispatch]
@@ -73,29 +61,16 @@ function Proxies({
window.addEventListener('focus', fn, false);
return () => window.removeEventListener('focus', fn, false);
}, [fetchProxiesHooked]);
- const icon = useMemo(() => <Zap width={16} />, []);
- const filterIcon = useMemo(() => <Filter width={16} />, []);
return (
<>
<ContentHeader title="Proxies" />
<div>
- <div className="fabgrp">
- <Button
- text="Test Latency"
- start={icon}
- onClick={requestDelayAllFn}
- />
- <Button start={filterIcon} onClick={filterZeroRTFn}>
- <span>{filterZeroRT ? 'show' : 'hide'} 0ms proxies</span>
- </Button>
- </div>
{groupNames.map(groupName => {
return (
<div className={s0.group} key={groupName}>
<ProxyGroup
name={groupName}
- proxies={proxies}
delay={delay}
apiConfig={apiConfig}
dispatch={dispatch}
@@ -106,6 +81,17 @@ function Proxies({
</div>
<ProxyProviderList items={proxyProviders} />
<div style={{ height: 60 }} />
+ <Fab icon={<Circle />}>
+ <Action text="Test Latency" onClick={requestDelayAllFn}>
+ <Zap width={16} />
+ </Action>
+ <Action
+ text={(filterZeroRT ? 'Show' : 'Hide') + ' Unavailable Proxies'}
+ onClick={toggleUnavailableProxiesFilter}
+ >
+ <Filter width={16} />
+ </Action>
+ </Fab>
</>
);
}
@@ -113,7 +99,6 @@ function Proxies({
const mapState = s => ({
apiConfig: getClashAPIConfig(s),
groupNames: getProxyGroupNames(s),
- proxies: getProxies(s),
proxyProviders: getProxyProviders(s),
delay: getDelay(s),
filterZeroRT: getRtFilterSwitch(s)
diff --git a/src/components/Proxy.js b/src/components/Proxy.js
index c485cc0..65bed4e 100644
--- a/src/components/Proxy.js
+++ b/src/components/Proxy.js
@@ -1,4 +1,3 @@
-// vim: set ft=javascript.flow :
import React from 'react';
import cx from 'classnames';
@@ -84,14 +83,14 @@ function Proxy({ now, name, proxy, latency }: ProxyProps) {
})}
>
<div className={s0.proxyName}>{name}</div>
- <span className={s0.proxyType} style={{ opacity: now ? 0.6 : 0.2 }}>
- {proxy.type}
- </span>
- {latency && latency.number ? (
- <span className={s0.proxyLatencyWrap}>
- <ProxyLatency number={latency.number} color={color} />
+ <div className={s0.row}>
+ <span className={s0.proxyType} style={{ opacity: now ? 0.6 : 0.2 }}>
+ {proxy.type}
</span>
- ) : null}
+ {latency && latency.number ? (
+ <ProxyLatency number={latency.number} color={color} />
+ ) : null}
+ </div>
</div>
);
}
diff --git a/src/components/Proxy.module.css b/src/components/Proxy.module.css
index 2aa4504..7e6e2cc 100644
--- a/src/components/Proxy.module.css
+++ b/src/components/Proxy.module.css
@@ -6,7 +6,7 @@
max-width: 280px;
@media (--breakpoint-not-small) {
- min-width: 150px;
+ min-width: 200px;
border-radius: 10px;
padding: 10px;
}
@@ -23,33 +23,29 @@
.proxyType {
font-family: var(--font-mono);
- display: inline;
- padding: 0 0.3em;
font-size: 0.6em;
@media (--breakpoint-not-small) {
font-size: 1em;
}
}
+.row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
.proxyName {
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 5px;
font-size: 0.85em;
- display: inline;
@media (--breakpoint-not-small) {
font-size: 1.1em;
}
}
-.proxyLatencyWrap {
- padding: 0 0.3em;
- height: 30px;
- /* display: flex; */
- align-items: flex-end;
-}
-
.proxySmall {
.now {
outline: pink solid 1px;
diff --git a/src/components/ProxyGroup.js b/src/components/ProxyGroup.js
index ae1fe58..fffb020 100644
--- a/src/components/ProxyGroup.js
+++ b/src/components/ProxyGroup.js
@@ -1,73 +1,49 @@
import React from 'react';
-
-import Button from './Button';
-import { ChevronsDown } from 'react-feather';
-
import cx from 'classnames';
-import { connect } from './StateProvider';
-import { getDelay, getRtFilterSwitch } from '../store/proxies';
+import memoizeOne from 'memoize-one';
+import { connect } from './StateProvider';
+import { getProxies, getRtFilterSwitch } from '../store/proxies';
+import CollapsibleSectionHeader from './CollapsibleSectionHeader';
import Proxy, { ProxySmall } from './Proxy';
-import { SectionNameType } from './shared/Basic';
+import { useToggle } from '../hooks/basic';
import s0 from './ProxyGroup.module.css';
import { switchProxy } from '../store/proxies';
-const { memo, useCallback, useMemo, useState } = React;
-
-function ProxyGroup({ name, proxies, apiConfig, dispatch }) {
- const group = proxies[name];
- const { all, type, now } = group;
+const { useCallback, useMemo } = React;
+function ProxyGroup({ name, all, type, now, apiConfig, dispatch }) {
const isSelectable = useMemo(() => type === 'Selector', [type]);
-
- const [isShow, setIsShow] = useState({
- show: false
- });
-
- const updateShow = useCallback(
- type => {
- setIsShow({
- show: !isShow.show
- });
- },
- [isShow]
- );
-
+ const [isOpen, toggle] = useToggle(true);
const itemOnTapCallback = useCallback(
proxyName => {
if (!isSelectable) return;
-
dispatch(switchProxy(apiConfig, name, proxyName));
- // switchProxyFn(name, proxyName);
},
[apiConfig, dispatch, name, isSelectable]
);
- const button = useMemo(
- () => (
- <Button
- className="btn"
- start={<ChevronsDown width={16} />}
- onClick={() => updateShow()}
- // text={isShow.show ? 'hide' : 'show'}
- />
- ),
- [updateShow]
- );
-
return (
<div className={s0.group}>
- <div className={s0.header}>
- <SectionNameType name={name} type={group.type} dropDown={button} />
- </div>
- <ProxyList
- all={isShow.show ? all : []}
- now={now}
- isSelectable={isSelectable}
- itemOnTapCallback={itemOnTapCallback}
+ <CollapsibleSectionHeader
+ name={name}
+ type={type}
+ toggle={toggle}
+ qty={all.length}
+ isOpen={isOpen}
/>
+ {isOpen ? (
+ <ProxyList
+ all={all}
+ now={now}
+ isSelectable={isSelectable}
+ itemOnTapCallback={itemOnTapCallback}
+ />
+ ) : (
+ <ProxyListSummaryView all={all} />
+ )}
</div>
);
}
@@ -79,7 +55,7 @@ type ProxyListProps = {
itemOnTapCallback?: string => void,
show?: boolean
};
-function ProxyListImpl({
+export function ProxyList({
all,
now,
isSelectable,
@@ -122,47 +98,36 @@ const getSortDelay = (d, w) => {
return w;
};
-const mapState = (s, { all }) => {
- const delay = getDelay(s);
- const filterByRt = getRtFilterSwitch(s);
-
- const groupList = [];
- const proxyList = [];
-
- let clonelist = [...all];
+function filterAvailableProxies(list, delay) {
+ return list.filter(name => {
+ const d = delay[name];
+ if (d === undefined) {
+ return true;
+ }
+ if (d.error || d.number === 0) {
+ return false;
+ } else {
+ return true;
+ }
+ });
+}
+function filterAvailableProxiesAndSortImpl(all, delay, filterByRt) {
+ // all is freezed
+ let filtered = [...all];
if (filterByRt) {
- const filterList = clonelist.filter(name => {
- const d = delay[name];
- if (d === undefined) {
- groupList.push(name);
- return true;
- }
- if (d.error || d.number === 0) {
- return false;
- } else {
- proxyList.push(name);
- return true;
- }
- });
-
- //
- if (proxyList.length > 0) {
- //not test connection yet ,,show all
- clonelist = filterList;
- }
+ filtered = filterAvailableProxies(all, delay);
}
- return {
- all: clonelist.sort((first, second) => {
- const d1 = getSortDelay(delay[first], 999999);
- const d2 = getSortDelay(delay[second], 999999);
- return d1 - d2;
- })
- };
-};
-
-export const ProxyList = connect(mapState)(ProxyListImpl);
+ return filtered.sort((first, second) => {
+ const d1 = getSortDelay(delay[first], 999999);
+ const d2 = getSortDelay(delay[second], 999999);
+ return d1 - d2;
+ });
+}
+export const filterAvailableProxiesAndSort = memoizeOne(
+ filterAvailableProxiesAndSortImpl
+);
export function ProxyListSummaryView({
all,
@@ -193,4 +158,14 @@ export function ProxyListSummaryView({
);
}
-export default memo(ProxyGroup);
+export default connect((s, { name, delay }) => {
+ const proxies = getProxies(s);
+ const filterByRt = getRtFilterSwitch(s);
+ const group = proxies[name];
+ const { all, type, now } = group;
+ return {
+ all: filterAvailableProxiesAndSort(all, delay, filterByRt),
+ type,
+ now
+ };
+})(ProxyGroup);
diff --git a/src/components/ProxyLatency.module.css b/src/components/ProxyLatency.module.css
index 1b971f1..0671982 100644
--- a/src/components/ProxyLatency.module.css
+++ b/src/components/ProxyLatency.module.css
@@ -1,6 +1,5 @@
.proxyLatency {
border-radius: 20px;
- padding: 3px 0;
color: #eee;
font-size: 0.6em;
@media (--breakpoint-not-small) {
diff --git a/src/components/ProxyProvider.js b/src/components/ProxyProvider.js
index 7a2f107..32071ab 100644
--- a/src/components/ProxyProvider.js
+++ b/src/components/ProxyProvider.js
@@ -1,69 +1,75 @@
import React from 'react';
-import { ChevronDown, RotateCw, Zap } from 'react-feather';
+import { RotateCw, Zap } from 'react-feather';
import { formatDistance } from 'date-fns';
-import ResizeObserver from 'resize-observer-polyfill';
import { motion } from 'framer-motion';
-import cx from 'classnames';
import { connect } from './StateProvider';
-import { SectionNameType } from './shared/Basic';
-import { ProxyList, ProxyListSummaryView } from './ProxyGroup';
+import Collapsible from './Collapsible';
+import CollapsibleSectionHeader from './CollapsibleSectionHeader';
+import {
+ ProxyList,
+ ProxyListSummaryView,
+ filterAvailableProxiesAndSort
+} from './ProxyGroup';
import Button from './Button';
import { getClashAPIConfig } from '../store/app';
import {
+ getDelay,
+ getRtFilterSwitch,
updateProviderByName,
healthcheckProviderByName
} from '../store/proxies';
import s from './ProxyProvider.module.css';
-const { memo, useState, useRef, useEffect, useCallback } = React;
+const { useState, useCallback } = React;
type Props = {
- item: Array<{
- name: string,
- proxies: Array<string>,
- type: 'Proxy' | 'Rule',
- vehicleType: 'HTTP' | 'File' | 'Compatible',
- updatedAt?: string
- }>,
- proxies: {
- [string]: any
- },
+ name: string,
+ proxies: Array<string>,
+ type: 'Proxy' | 'Rule',
+ vehicleType: 'HTTP' | 'File' | 'Compatible',
+ updatedAt?: string,
dispatch: any => void
};
-function ProxyProvider({ item, dispatch, apiConfig }: Props) {
+function ProxyProvider({
+ name,
+ proxies,
+ vehicleType,
+ updatedAt,
+ dispatch,
+ apiConfig
+}: Props) {
const [isHealthcheckLoading, setIsHealthcheckLoading] = useState(false);
const updateProvider = useCallback(
- () => dispatch(updateProviderByName(apiConfig, item.name)),
- [apiConfig, dispatch, item.name]
+ () => dispatch(updateProviderByName(apiConfig, name)),
+ [apiConfig, dispatch, name]
);
const healthcheckProvider = useCallback(async () => {
setIsHealthcheckLoading(true);
- await dispatch(healthcheckProviderByName(apiConfig, item.name));
+ await dispatch(healthcheckProviderByName(apiConfig, name));
setIsHealthcheckLoading(false);
- }, [apiConfig, dispatch, item.name, setIsHealthcheckLoading]);
+ }, [apiConfig, dispatch, name, setIsHealthcheckLoading]);
const [isCollapsibleOpen, setCollapsibleOpen] = useState(false);
const toggle = useCallback(() => setCollapsibleOpen(x => !x), []);
- const timeAgo = formatDistance(new Date(item.updatedAt), new Date());
+ const timeAgo = formatDistance(new Date(updatedAt), new Date());
return (
<div className={s.body}>
- <div className={s.header} onClick={toggle}>
- <SectionNameType name={item.name} type={item.vehicleType} />
- <Button kind="minimal">
- <span className={cx(s.arrow, { [s.isOpen]: isCollapsibleOpen })}>
- <ChevronDown />
- </span>
- </Button>
- </div>
+ <CollapsibleSectionHeader
+ name={name}
+ toggle={toggle}
+ type={vehicleType}
+ isOpen={isCollapsibleOpen}
+ qty={proxies.length}
+ />
<div className={s.updatedAt}>
<small>Updated {timeAgo} ago</small>
</div>
- <Collapsible2 isOpen={isCollapsibleOpen}>
- <ProxyList all={item.proxies} />
+ <Collapsible isOpen={isCollapsibleOpen}>
+ <ProxyList all={proxies} />
<div className={s.actionFooter}>
<Button text="Update" start={<Refresh />} onClick={updateProvider} />
<Button
@@ -73,10 +79,10 @@ function ProxyProvider({ item, dispatch, apiConfig }: Props) {
isLoading={isHealthcheckLoading}
/>
</div>
- </Collapsible2>
- <Collapsible2 isOpen={!isCollapsibleOpen}>
- <ProxyListSummaryView all={item.proxies} />
- </Collapsible2>
+ </Collapsible>
+ <Collapsible isOpen={!isCollapsibleOpen}>
+ <ProxyListSummaryView all={proxies} />
+ </Collapsible>
</div>
);
}
@@ -106,78 +112,17 @@ function Refresh() {
);
}
-function usePrevious(value) {
- const ref = useRef();
- useEffect(() => void (ref.current = value), [value]);
- return ref.current;
-}
-
-function useMeasure() {
- const ref = useRef();
- const [bounds, set] = useState({ height: 0 });
- useEffect(() => {
- const ro = new ResizeObserver(([entry]) => set(entry.contentRect));
- if (ref.current) ro.observe(ref.current);
- return () => ro.disconnect();
- }, []);
- return [ref, bounds];
-}
-
-const variantsCollpapsibleWrap = {
- initialOpen: {
- height: 'auto',
- transition: { duration: 0 }
- },
- open: height => ({
- height,
- opacity: 1,
- visibility: 'visible',
- transition: { duration: 0.3 }
- }),
- closed: {
- height: 0,
- opacity: 0,
- visibility: 'hidden',
- transition: { duration: 0.3 }
- }
+const mapState = (s, { proxies }) => {
+ const filterByRt = getRtFilterSwitch(s);
+ const delay = getDelay(s);
+ const apiConfig = getClashAPIConfig(s);
+ return {
+ apiConfig,
+ proxies: filterAvailableProxiesAndSort(proxies, delay, filterByRt)
+ };
};
-const variantsCollpapsibleChildContainer = {
- open: {
- x: 0
- },
- closed: {
- x: 20
- }
-};
-
-const Collapsible2 = memo(({ children, isOpen }) => {
- const previous = usePrevious(isOpen);
- const [refToMeature, { height }] = useMeasure();
- return (
- <div>
- <motion.div
- animate={
- isOpen && previous === isOpen
- ? 'initialOpen'
- : isOpen
- ? 'open'
- : 'closed'
- }
- custom={height}
- variants={variantsCollpapsibleWrap}
- >
- <motion.div
- variants={variantsCollpapsibleChildContainer}
- ref={refToMeature}
- >
- {children}
- </motion.div>
- </motion.div>
- </div>
- );
-});
-const mapState = s => ({
- apiConfig: getClashAPIConfig(s)
-});
+// const mapState = s => ({
+// apiConfig: getClashAPIConfig(s)
+// });
export default connect(mapState)(ProxyProvider);
diff --git a/src/components/ProxyProvider.module.css b/src/components/ProxyProvider.module.css
index 270cd03..534305b 100644
--- a/src/components/ProxyProvider.module.css
+++ b/src/components/ProxyProvider.module.css
@@ -1,22 +1,3 @@
-.header {
- display: flex;
- align-items: center;
- cursor: pointer;
-
- .arrow {
- display: inline-flex;
- transform: rotate(0deg);
- transition: transform 0.3s;
- &.isOpen {
- transform: rotate(180deg);
- }
-
- &:focus {
- outline: var(--color-focus-blue) solid 1px;
- }
- }
-}
-
.updatedAt {
margin-bottom: 12px;
small {
diff --git a/src/components/ProxyProviderList.js b/src/components/ProxyProviderList.js
index 9cdd7a8..4242b51 100644
--- a/src/components/ProxyProviderList.js
+++ b/src/components/ProxyProviderList.js
@@ -11,7 +11,14 @@ function ProxyProviderList({ items }) {
<ContentHeader title="Proxy Provider" />
<div>
{items.map(item => (
- <ProxyProvider key={item.name} item={item} />
+ <ProxyProvider
+ key={item.name}
+ name={item.name}
+ proxies={item.proxies}
+ type={item.type}
+ vehicleType={item.vehicleType}
+ updatedAt={item.updatedAt}
+ />
))}
</div>
</>
diff --git a/src/components/StateProvider.js b/src/components/StateProvider.js
index 23041fe..aff6147 100644
--- a/src/components/StateProvider.js
+++ b/src/components/StateProvider.js
@@ -79,11 +79,7 @@ export function connect(mapStateToProps) {
const state = useContext(StateContext);
const dispatch = useContext(DispatchContext);
const mapped = mapStateToProps(state, props);
- const nextProps = {
- ...props,
- ...mapped,
- dispatch
- };
+ const nextProps = { dispatch, ...props, ...mapped };
return <MemoComponent {...nextProps} />;
}
return Connected;
diff --git a/src/components/rtf.css b/src/components/rtf.css
new file mode 100644
index 0000000..1a68f6b
--- /dev/null
+++ b/src/components/rtf.css
@@ -0,0 +1,235 @@
+/*
+ * for react-tiny-fab
+ * based on react-tiny-fab/dist/styles.css
+ */
+.rtf {
+ box-sizing: border-box;
+ margin: 25px;
+ position: fixed;
+ white-space: nowrap;
+ z-index: 9998;
+ padding-left: 0;
+ list-style: none;
+}
+.rtf.open .rtf--mb > * {
+ transform-origin: center center;
+ transform: rotate(315deg);
+ transition: ease-in-out transform 0.2s;
+}
+.rtf.open .rtf--mb > ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+.rtf.open .rtf--ab__c:hover > span {
+ transition: ease-in-out opacity 0.2s;
+ opacity: 0.9;
+}
+.rtf.open .rtf--ab__c > span.always-show {
+ transition: ease-in-out opacity 0.2s;
+ opacity: 0.9;
+}
+.rtf.open .rtf--ab__c:nth-child(1) {
+ transform: translateY(-60px) scale(1);
+ transition-delay: 0.03s;
+}
+.rtf.open .rtf--ab__c:nth-child(1).top {
+ transform: translateY(60px) scale(1);
+}
+.rtf.open .rtf--ab__c:nth-child(2) {
+ transform: translateY(-120px) scale(1);
+ transition-delay: 0.09s;
+}
+.rtf.open .rtf--ab__c:nth-child(2).top {
+ transform: translateY(120px) scale(1);
+}
+.rtf.open .rtf--ab__c:nth-child(3) {
+ transform: translateY(-180px) scale(1);
+ transition-delay: 0.12s;
+}
+.rtf.open .rtf--ab__c:nth-child(3).top {
+ transform: translateY(180px) scale(1);
+}
+.rtf.open .rtf--ab__c:nth-child(4) {
+ transform: translateY(-240px) scale(1);
+ transition-delay: 0.15s;
+}
+.rtf.open .rtf--ab__c:nth-child(4).top {
+ transform: translateY(240px) scale(1);
+}
+.rtf.open .rtf--ab__c:nth-child(5) {
+ transform: translateY(-300px) scale(1);
+ transition-delay: 0.18s;
+}
+.rtf.open .rtf--ab__c:nth-child(5).top {
+ transform: translateY(300px) scale(1);
+}
+.rtf.open .rtf--ab__c:nth-child(6) {
+ transform: translateY(-360px) scale(1);
+ transition-delay: 0.21s;
+}
+.rtf.open .rtf--ab__c:nth-child(6).top {
+ transform: translateY(360px) scale(1);
+}
+
+.rtf--mb__c {
+ padding: 25px;
+ margin: -25px;
+}
+.rtf--mb__c *:last-child {
+ margin-bottom: 0;
+}
+.rtf--mb__c:hover > span {
+ transition: ease-in-out opacity 0.2s;
+ opacity: 0.9;
+}
+.rtf--mb__c > span.always-show {
+ transition: ease-in-out opacity 0.2s;
+ opacity: 0.9;
+}
+.rtf--mb__c > span {
+ opacity: 0;
+ transition: ease-in-out opacity 0.2s;
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ margin-right: 6px;
+ margin-left: 4px;
+ background: rgba(0, 0, 0, 0.75);
+ padding: 2px 4px;
+ border-radius: 2px;
+ color: white;
+ font-size: 13px;
+ box-shadow: 0 0 4px rgba(0, 0, 0, 0.14), 0 4px 8px rgba(0, 0, 0, 0.28);
+}
+.rtf--mb__c > span.right {
+ right: 100%;
+}
+
+.rtf--mb {
+ height: 56px;
+ width: 56px;
+ z-index: 9999;
+ /* background-color: #666666; */
+ background: #387cec;
+ /* background: var(--color-btn-bg); */
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ position: relative;
+ border: none;
+ /* border: 1px solid #555; */
+ border-radius: 50%;
+ box-shadow: 0 0 4px rgba(0, 0, 0, 0.14), 0 4px 8px rgba(0, 0, 0, 0.28);
+ cursor: pointer;
+ outline: none;
+ padding: 0;
+ -webkit-user-drag: none;
+ font-weight: bold;
+ color: #f1f1f1;
+ font-size: 18px;
+}
+.rtf--mb > * {
+ transition: ease-in-out transform 0.2s;
+}
+
+.rtf--ab__c {
+ display: block;
+ position: absolute;
+ top: 0;
+ right: 1px;
+ padding: 10px 0;
+ margin: -10px 0;
+ transition: ease-in-out transform 0.2s;
+}
+.rtf--ab__c > span {
+ opacity: 0;
+ transition: ease-in-out opacity 0.2s;
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ margin-right: 6px;
+ background: rgba(0, 0, 0, 0.75);
+ padding: 2px 4px;
+ border-radius: 2px;
+ color: white;
+ font-size: 13px;
+ box-shadow: 0 0 4px rgba(0, 0, 0, 0.14), 0 4px 8px rgba(0, 0, 0, 0.28);
+}
+.rtf--ab__c > span.right {
+ right: 100%;
+}
+.rtf--ab__c:nth-child(1) {
+ transform: translateY(-60px) scale(0);
+ transition-delay: 0.21s;
+}
+.rtf--ab__c:nth-child(1).top {
+ transform: translateY(60px) scale(0);
+}
+.rtf--ab__c:nth-child(2) {
+ transform: translateY(-120px) scale(0);
+ transition-delay: 0.18s;
+}
+.rtf--ab__c:nth-child(2).top {
+ transform: translateY(120px) scale(0);
+}
+.rtf--ab__c:nth-child(3) {
+ transform: translateY(-180px) scale(0);
+ transition-delay: 0.15s;
+}
+.rtf--ab__c:nth-child(3).top {
+ transform: translateY(180px) scale(0);
+}
+.rtf--ab__c:nth-child(4) {
+ transform: translateY(-240px) scale(0);
+ transition-delay: 0.12s;
+}
+.rtf--ab__c:nth-child(4).top {
+ transform: translateY(240px) scale(0);
+}
+.rtf--ab__c:nth-child(5) {
+ transform: translateY(-300px) scale(0);
+ transition-delay: 0.09s;
+}
+.rtf--ab__c:nth-child(5).top {
+ transform: translateY(300px) scale(0);
+}
+.rtf--ab__c:nth-child(6) {
+ transform: translateY(-360px) scale(0);
+ transition-delay: 0.03s;
+}
+.rtf--ab__c:nth-child(6).top {
+ transform: translateY(360px) scale(0);
+}
+
+.rtf--ab {
+ height: 48px;
+ width: 48px;
+ background-color: #aaaaaa;
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ position: relative;
+ border: none;
+ border-radius: 50%;
+ box-shadow: 0 0 4px rgba(0, 0, 0, 0.14), 0 4px 8px rgba(0, 0, 0, 0.28);
+ cursor: pointer;
+ outline: none;
+ padding: 0;
+ -webkit-user-drag: none;
+ font-weight: bold;
+ color: #f1f1f1;
+ margin-right: 4px;
+ font-size: 16px;
+ z-index: 10000;
+}
+
+.rtf--ab:hover {
+ background: #387cec;
+ border: 1px solid #387cec;
+ color: #fff;
+}
+
+.rtf--ab:focus {
+ border-color: var(--color-focus-blue);
+}
diff --git a/src/components/shared/Basic.js b/src/components/shared/Basic.js
index 9729411..dbd1bc7 100644
--- a/src/components/shared/Basic.js
+++ b/src/components/shared/Basic.js
@@ -2,12 +2,11 @@ import React from 'react';
import s from './Basic.module.css';
-export function SectionNameType({ name, type, dropDown }) {
+export function SectionNameType({ name, type }) {
return (
<h2 className={s.sectionNameType}>
<span>{name}</span>
<span>{type}</span>
- {dropDown}
</h2>
);
}