diff options
| author | Haishan <[email protected]> | 2020-02-10 22:53:14 +0800 |
|---|---|---|
| committer | Haishan <[email protected]> | 2020-02-10 23:04:57 +0800 |
| commit | e85116bf717b51c0d4aa4f07c91944414f0a4b4e (patch) | |
| tree | 63a6167eaeb7b1da842caafe47947d3997a65a38 /src/components | |
| parent | bd82b8c5e3bad3efeecd9e2599b07128d290bea6 (diff) | |
refactor(Proxies): UI revamp
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/Collapsible.js | 79 | ||||
| -rw-r--r-- | src/components/CollapsibleSectionHeader.js | 34 | ||||
| -rw-r--r-- | src/components/CollapsibleSectionHeader.module.css | 30 | ||||
| -rw-r--r-- | src/components/Proxies.js | 49 | ||||
| -rw-r--r-- | src/components/Proxy.js | 15 | ||||
| -rw-r--r-- | src/components/Proxy.module.css | 18 | ||||
| -rw-r--r-- | src/components/ProxyGroup.js | 149 | ||||
| -rw-r--r-- | src/components/ProxyLatency.module.css | 1 | ||||
| -rw-r--r-- | src/components/ProxyProvider.js | 161 | ||||
| -rw-r--r-- | src/components/ProxyProvider.module.css | 19 | ||||
| -rw-r--r-- | src/components/ProxyProviderList.js | 9 | ||||
| -rw-r--r-- | src/components/StateProvider.js | 6 | ||||
| -rw-r--r-- | src/components/rtf.css | 235 | ||||
| -rw-r--r-- | src/components/shared/Basic.js | 3 |
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> ); } |
