diff options
| author | yaling888 <[email protected]> | 2022-05-06 03:55:39 +0800 |
|---|---|---|
| committer | yaling888 <[email protected]> | 2022-05-06 03:55:39 +0800 |
| commit | dafd4486f17fcd72ac86578854886a807b0c4748 (patch) | |
| tree | d15d3345690f84000afda53c5c2f2ebe84cfdb8b /src/components | |
| parent | 96c16b0ae5562cbe16b311da0ed9f839da172c4e (diff) | |
| parent | 4dea888769ef153806bc5275616fd3c9d3e0a32b (diff) | |
Merge 'tracking' into master
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/Connections.tsx | 39 | ||||
| -rw-r--r-- | src/components/Logs.module.scss | 9 | ||||
| -rw-r--r-- | src/components/Logs.tsx | 63 | ||||
| -rw-r--r-- | src/components/Root.scss | 26 | ||||
| -rw-r--r-- | src/components/SideBar.module.scss | 2 | ||||
| -rw-r--r-- | src/components/shared/Basic.module.scss | 46 | ||||
| -rw-r--r-- | src/components/shared/ThemeSwitcher.module.css | 28 | ||||
| -rw-r--r-- | src/components/shared/ThemeSwitcher.module.scss | 58 | ||||
| -rw-r--r-- | src/components/shared/ThemeSwitcher.tsx | 84 | ||||
| -rw-r--r-- | src/components/shared/rtf.css | 4 |
10 files changed, 205 insertions, 154 deletions
diff --git a/src/components/Connections.tsx b/src/components/Connections.tsx index ff38004..f25e962 100644 --- a/src/components/Connections.tsx +++ b/src/components/Connections.tsx @@ -99,7 +99,7 @@ function formatConnectionDataItem( download, start: now - new Date(start).valueOf(), chains: chains.reverse().join(' / '), - rule: rule === 'GeoSite' || rule === 'GeoIP' || rule === 'RuleSet' ? `${rule} (${rulePayload})` : rule, + rule: (rulePayload == null || rulePayload === '') ? rule : (`${rule} (${rulePayload})`), ...metadata, host: `${host2}:${destinationPort}`, type: `${type}(${network})`, @@ -134,10 +134,7 @@ function Conn({ apiConfig }) { const filteredClosedConns = filterConns(closedConns, filterKeyword); const [isCloseAllModalOpen, setIsCloseAllModalOpen] = useState(false); const openCloseAllModal = useCallback(() => setIsCloseAllModalOpen(true), []); - const closeCloseAllModal = useCallback( - () => setIsCloseAllModalOpen(false), - [] - ); + const closeCloseAllModal = useCallback(() => setIsCloseAllModalOpen(false), []); const [isRefreshPaused, setIsRefreshPaused] = useState(false); const toggleIsRefreshPaused = useCallback(() => { setIsRefreshPaused((x) => !x); @@ -165,11 +162,7 @@ function Conn({ apiConfig }) { }); // if previous connections and current connections are both empty // arrays, we wont update state to avaoid rerender - if ( - x && - (x.length !== 0 || prevConnsRef.current.length !== 0) && - !isRefreshPaused - ) { + if (x && (x.length !== 0 || prevConnsRef.current.length !== 0) && !isRefreshPaused) { prevConnsRef.current = x; setConns(x); } else { @@ -222,14 +215,9 @@ function Conn({ apiConfig }) { /> </div> </div> - <div - // @ts-expect-error ts-migrate(2322) FIXME: Type 'number | MutableRefObject<any>' is not assig... Remove this comment to see the full error message - ref={refContainer} - style={{ padding: 30, paddingBottom, paddingTop: 0 }} - > + <div ref={refContainer} style={{ padding: 30, paddingBottom, paddingTop: 0 }}> <div style={{ - // @ts-expect-error ts-migrate(2362) FIXME: The left-hand side of an arithmetic operation must... Remove this comment to see the full error message height: containerHeight - paddingBottom, overflow: 'auto', }} @@ -237,24 +225,13 @@ function Conn({ apiConfig }) { <TabPanel> <>{renderTableOrPlaceholder(filteredConns)}</> <Fab - icon={ - isRefreshPaused ? <Play size={16} /> : <Pause size={16} /> - } - mainButtonStyles={ - isRefreshPaused - ? { - background: '#e74c3c', - } - : {} - } + icon={isRefreshPaused ? <Play size={16} /> : <Pause size={16} />} + mainButtonStyles={isRefreshPaused ? { background: '#e74c3c' } : {}} style={fabPosition} - text={isRefreshPaused ? 'Resume Refresh' : 'Pause Refresh'} + text={isRefreshPaused ? t('Resume Refresh') : t('Pause Refresh')} onClick={toggleIsRefreshPaused} > - <Action - text="Close All Connections" - onClick={openCloseAllModal} - > + <Action text="Close All Connections" onClick={openCloseAllModal}> <IconClose size={10} /> </Action> </Fab> diff --git a/src/components/Logs.module.scss b/src/components/Logs.module.scss index 508e9c6..16ecb7f 100644 --- a/src/components/Logs.module.scss +++ b/src/components/Logs.module.scss @@ -41,19 +41,16 @@ color: var(--color-text); :global { - li { + .log { + padding: 10px 40px; background: var(--color-background); } - li.even { + .log.even { background: var(--color-background); } } } -.log { - padding: 10px 40px; -} - /*******************/ .logPlaceholder { diff --git a/src/components/Logs.tsx b/src/components/Logs.tsx index 019edd5..d9c53d7 100644 --- a/src/components/Logs.tsx +++ b/src/components/Logs.tsx @@ -1,23 +1,21 @@ import cx from 'clsx'; import * as React from 'react'; +import { Pause, Play } from 'react-feather'; import { useTranslation } from 'react-i18next'; -import { - areEqual, - FixedSizeList as List, - ListChildComponentProps, -} from 'react-window'; -import { fetchLogs } from 'src/api/logs'; +import { areEqual, FixedSizeList as List, ListChildComponentProps } from 'react-window'; +import { fetchLogs, reconnect as reconnectLogs,stop as stopLogs } from 'src/api/logs'; import ContentHeader from 'src/components/ContentHeader'; import LogSearch from 'src/components/LogSearch'; -import { connect } from 'src/components/StateProvider'; +import { connect, useStoreActions } from 'src/components/StateProvider'; import SvgYacd from 'src/components/SvgYacd'; import useRemainingViewPortHeight from 'src/hooks/useRemainingViewPortHeight'; -import { getClashAPIConfig } from 'src/store/app'; +import { getClashAPIConfig, getLogStreamingPaused } from 'src/store/app'; import { getLogLevel } from 'src/store/configs'; import { appendLog, getLogsForDisplay } from 'src/store/logs'; import { Log, State } from 'src/store/types'; import s from './Logs.module.scss'; +import { Fab, position as fabPosition } from './shared/Fab'; const { useCallback, memo, useEffect } = React; @@ -32,7 +30,7 @@ const colors = { type LogLineProps = Partial<Log>; function LogLine({ time, even, payload, type }: LogLineProps) { - const className = cx({ even }, s.log); + const className = cx({ even }, 'log'); return ( <div className={className}> <div className={s.logMeta}> @@ -51,23 +49,24 @@ function itemKey(index: number, data: LogLineProps[]) { return item.id; } -const Row = memo( - ({ index, style, data }: ListChildComponentProps<LogLineProps>) => { - const r = data[index]; - return ( - <div style={style}> - <LogLine {...r} /> - </div> - ); - }, - areEqual -); - -function Logs({ dispatch, logLevel, apiConfig, logs }) { - const appendLogInternal = useCallback( - (log) => dispatch(appendLog(log)), - [dispatch] +const Row = memo(({ index, style, data }: ListChildComponentProps<LogLineProps>) => { + const r = data[index]; + return ( + <div style={style}> + <LogLine {...r} /> + </div> ); +}, areEqual); + +function Logs({ dispatch, logLevel, apiConfig, logs, logStreamingPaused }) { + const actions = useStoreActions(); + const toggleIsRefreshPaused = useCallback(() => { + logStreamingPaused ? reconnectLogs({ ...apiConfig, logLevel }) : stopLogs(); + // being lazy here + // ideally we should check the result of previous operation before updating this + actions.app.updateAppConfig('logStreamingPaused', !logStreamingPaused); + }, [apiConfig, logLevel, logStreamingPaused, actions.app]); + const appendLogInternal = useCallback((log) => dispatch(appendLog(log)), [dispatch]); useEffect(() => { fetchLogs({ ...apiConfig, logLevel }, appendLogInternal); }, [apiConfig, logLevel, appendLogInternal]); @@ -80,10 +79,7 @@ function Logs({ dispatch, logLevel, apiConfig, logs }) { <LogSearch /> <div ref={refLogsContainer} style={{ paddingBottom }}> {logs.length === 0 ? ( - <div - className={s.logPlaceholder} - style={{ height: containerHeight - paddingBottom }} - > + <div className={s.logPlaceholder} style={{ height: containerHeight - paddingBottom }}> <div className={s.logPlaceholderIcon}> <SvgYacd width={200} height={200} /> </div> @@ -101,6 +97,14 @@ function Logs({ dispatch, logLevel, apiConfig, logs }) { > {Row} </List> + + <Fab + icon={logStreamingPaused ? <Play size={16} /> : <Pause size={16} />} + mainButtonStyles={logStreamingPaused ? { background: '#e74c3c' } : {}} + style={fabPosition} + text={logStreamingPaused ? t('Resume Refresh') : t('Pause Refresh')} + onClick={toggleIsRefreshPaused} + ></Fab> </div> )} </div> @@ -112,6 +116,7 @@ const mapState = (s: State) => ({ logs: getLogsForDisplay(s), logLevel: getLogLevel(s), apiConfig: getClashAPIConfig(s), + logStreamingPaused: getLogStreamingPaused(s), }); export default connect(mapState)(Logs); diff --git a/src/components/Root.scss b/src/components/Root.scss index 55198ab..4ae7d5f 100644 --- a/src/components/Root.scss +++ b/src/components/Root.scss @@ -53,18 +53,14 @@ :root { --font-mono: 'Roboto Mono', Menlo, monospace; - --font-normal: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, - Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, - 'PingFang SC', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif; + // prettier-ignore + --font-normal: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, 'PingFang SC', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif; --color-focus-blue: #1a73e8; --btn-bg: #387cec; } body { - font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, - Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, - Segoe UI Symbol, 'PingFang SC', 'Microsoft YaHei', '微软雅黑', Arial, - sans-serif; + font-family: var(--font-normal); -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-size-adjust: 100%; -webkit-font-smoothing: antialiased; @@ -128,17 +124,25 @@ body { --select-bg-hover: url(data:image/svg+xml,%0A%20%20%20%20%3Csvg%20width%3D%228%22%20height%3D%2224%22%20viewBox%3D%220%200%208%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%20%20%3Cpath%20d%3D%22M4%207L7%2011H1L4%207Z%22%20fill%3D%22%23222222%22%20%2F%3E%0A%20%20%20%20%20%20%3Cpath%20d%3D%22M4%2017L1%2013L7%2013L4%2017Z%22%20fill%3D%22%23222222%22%20%2F%3E%0A%20%20%20%20%3C%2Fsvg%3E%0A%20%20); } -// we don't have a "system" or "auto" mode now -// it's just not make sense to have these yet -// @media (prefers-color-scheme: dark) {} -// @media (prefers-color-scheme: light) {} +:root[data-theme='auto'] { + @media (prefers-color-scheme: dark) { + @include dark; + color-scheme: dark; + } + @media (prefers-color-scheme: light) { + @include light; + color-scheme: light; + } +} :root[data-theme='dark'] { @include dark; + color-scheme: dark; } :root[data-theme='light'] { @include light; + color-scheme: light; } .flexCenter { diff --git a/src/components/SideBar.module.scss b/src/components/SideBar.module.scss index 4a06377..4bd2e60 100644 --- a/src/components/SideBar.module.scss +++ b/src/components/SideBar.module.scss @@ -15,7 +15,7 @@ @media (max-width: 768px) { display: flex; justify-content: space-between; - overflow: scroll; + overflow: auto; } } diff --git a/src/components/shared/Basic.module.scss b/src/components/shared/Basic.module.scss index 8e5a113..b8e0068 100644 --- a/src/components/shared/Basic.module.scss +++ b/src/components/shared/Basic.module.scss @@ -36,32 +36,30 @@ h2.sectionNameType { * */ -:global { - body.light { - /* +:root[data-theme='light'] { + /* * --loading-dot-{dot-index}-{dot-keyframe-phase} */ - --loading-dot-1-1: rgba(0, 0, 0, 0.1); - --loading-dot-1-2: rgba(0, 0, 0, 0.5); - --loading-dot-1-3: rgba(0, 0, 0, 0.3); - --loading-dot-2-1: rgba(0, 0, 0, 0.3); - --loading-dot-2-2: rgba(0, 0, 0, 0.1); - --loading-dot-2-3: rgba(0, 0, 0, 0.5); - --loading-dot-3-1: rgba(0, 0, 0, 0.5); - --loading-dot-3-2: rgba(0, 0, 0, 0.3); - --loading-dot-3-3: rgba(0, 0, 0, 0.1); - } - body.dark { - --loading-dot-1-1: rgba(255, 255, 255, 0.5); - --loading-dot-1-2: rgba(255, 255, 255, 0.1); - --loading-dot-1-3: rgba(255, 255, 255, 0.3); - --loading-dot-2-1: rgba(255, 255, 255, 0.3); - --loading-dot-2-2: rgba(255, 255, 255, 0.5); - --loading-dot-2-3: rgba(255, 255, 255, 0.1); - --loading-dot-3-1: rgba(255, 255, 255, 0.1); - --loading-dot-3-2: rgba(255, 255, 255, 0.3); - --loading-dot-3-3: rgba(255, 255, 255, 0.5); - } + --loading-dot-1-1: rgba(0, 0, 0, 0.1); + --loading-dot-1-2: rgba(0, 0, 0, 0.5); + --loading-dot-1-3: rgba(0, 0, 0, 0.3); + --loading-dot-2-1: rgba(0, 0, 0, 0.3); + --loading-dot-2-2: rgba(0, 0, 0, 0.1); + --loading-dot-2-3: rgba(0, 0, 0, 0.5); + --loading-dot-3-1: rgba(0, 0, 0, 0.5); + --loading-dot-3-2: rgba(0, 0, 0, 0.3); + --loading-dot-3-3: rgba(0, 0, 0, 0.1); +} +:root[data-theme='dark'] { + --loading-dot-1-1: rgba(255, 255, 255, 0.5); + --loading-dot-1-2: rgba(255, 255, 255, 0.1); + --loading-dot-1-3: rgba(255, 255, 255, 0.3); + --loading-dot-2-1: rgba(255, 255, 255, 0.3); + --loading-dot-2-2: rgba(255, 255, 255, 0.5); + --loading-dot-2-3: rgba(255, 255, 255, 0.1); + --loading-dot-3-1: rgba(255, 255, 255, 0.1); + --loading-dot-3-2: rgba(255, 255, 255, 0.3); + --loading-dot-3-3: rgba(255, 255, 255, 0.5); } .loadingDot, diff --git a/src/components/shared/ThemeSwitcher.module.css b/src/components/shared/ThemeSwitcher.module.css deleted file mode 100644 index 919c86c..0000000 --- a/src/components/shared/ThemeSwitcher.module.css +++ /dev/null @@ -1,28 +0,0 @@ -.iconWrapper { - --sz: 40px; - - width: var(--sz); - height: var(--sz); - display: flex; - justify-content: center; - align-items: center; - - outline: none; - padding: 5px; - color: var(--color-text); - border-radius: 100%; - border: 1px solid transparent; -} -.iconWrapper:hover { - opacity: 0.6; -} -.iconWrapper:focus { - border-color: var(--color-focus-blue); -} - -.themeSwitchContainer { - appearance: none; - user-select: none; - background: none; - cursor: pointer; -} diff --git a/src/components/shared/ThemeSwitcher.module.scss b/src/components/shared/ThemeSwitcher.module.scss new file mode 100644 index 0000000..c5de126 --- /dev/null +++ b/src/components/shared/ThemeSwitcher.module.scss @@ -0,0 +1,58 @@ +.iconWrapper { + --sz: 40px; + + width: var(--sz); + height: var(--sz); + display: flex; + justify-content: center; + align-items: center; + + outline: none; + padding: 5px; + color: var(--color-text); + border-radius: 100%; + border: 1px solid transparent; +} +.iconWrapper:hover { + opacity: 0.6; +} +.iconWrapper:focus { + border-color: var(--color-focus-blue); +} + +.themeSwitchContainer { + --sz: 40px; + + position: relative; + display: flex; + align-items: center; + height: var(--sz); + select { + cursor: pointer; + padding-left: var(--sz); + width: var(--sz); + height: var(--sz); + appearance: none; + outline: none; + border-radius: 100%; + border: 1px solid transparent; + background: var(--color-bg-sidebar); + &:focus { + border-color: var(--color-focus-blue); + } + option { + // this has effect in Firefox + // Chrome and Safari use the native menu + background: var(--color-bg-sidebar); + } + } + .iconWrapper { + pointer-events: none; + display: inline-flex; + align-items: center; + justify-content: center; + position: absolute; + left: 0; + top: 0; + } +} diff --git a/src/components/shared/ThemeSwitcher.tsx b/src/components/shared/ThemeSwitcher.tsx index fba5b0b..45b60bc 100644 --- a/src/components/shared/ThemeSwitcher.tsx +++ b/src/components/shared/ThemeSwitcher.tsx @@ -1,5 +1,4 @@ import Tooltip from '@reach/tooltip'; -import cx from 'clsx'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; import { connect } from 'src/components/StateProvider'; @@ -7,28 +6,40 @@ import { framerMotionResouce } from 'src/misc/motion'; import { getTheme, switchTheme } from 'src/store/app'; import { State } from 'src/store/types'; -import s from './ThemeSwitcher.module.css'; +import s from './ThemeSwitcher.module.scss'; export function ThemeSwitcherImpl({ theme, dispatch }) { const { t } = useTranslation(); - const switchThemeHooked = React.useCallback(() => { - dispatch(switchTheme()); - }, [dispatch]); + const themeIcon = React.useMemo(() => { + switch (theme) { + case 'dark': + return <MoonA />; + case 'auto': + return <Auto />; + case 'light': + return <Sun />; + default: + console.assert(false, 'Unknown theme'); + return <MoonA />; + } + }, [theme]); + + const onChange = React.useCallback( + (e: React.ChangeEvent<HTMLSelectElement>) => dispatch(switchTheme(e.target.value)), + [dispatch] + ); return ( - <Tooltip - label={t('theme')} - aria-label={ - 'switch to ' + (theme === 'light' ? 'dark' : 'light') + ' theme' - } - > - <button - className={cx(s.iconWrapper, s.themeSwitchContainer)} - onClick={switchThemeHooked} - > - {theme === 'light' ? <MoonA /> : <Sun />} - </button> + <Tooltip label={t('switch_theme')} aria-label={'switch theme'}> + <div className={s.themeSwitchContainer}> + <span className={s.iconWrapper}>{themeIcon}</span> + <select onChange={onChange}> + <option value="auto">Auto</option> + <option value="dark">Dark</option> + <option value="light">Light</option> + </select> + </div> </Tooltip> ); } @@ -75,11 +86,7 @@ function Sun() { strokeLinejoin="round" > <circle cx="12" cy="12" r="5"></circle> - <motion.g - initial={{ scale: 0.8 }} - animate={{ scale: 1 }} - transition={{ duration: 0.7 }} - > + <motion.g initial={{ scale: 0.7 }} animate={{ scale: 1 }} transition={{ duration: 0.5 }}> <line x1="12" y1="1" x2="12" y2="3"></line> <line x1="12" y1="21" x2="12" y2="23"></line> <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line> @@ -93,5 +100,38 @@ function Sun() { ); } +function Auto() { + const module = framerMotionResouce.read(); + const motion = module.motion; + + return ( + <svg + xmlns="http://www.w3.org/2000/svg" + width="20" + height="20" + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + strokeWidth="2" + strokeLinecap="round" + strokeLinejoin="round" + > + <circle cx="12" cy="12" r="11" /> + <clipPath id="cut-off-bottom"> + <motion.rect + x="12" + y="0" + width="12" + height="24" + initial={{ rotate: -30 }} + animate={{ rotate: 0 }} + transition={{ duration: 0.7 }} + /> + </clipPath> + <circle cx="12" cy="12" r="6" clipPath="url(#cut-off-bottom)" fill="currentColor" /> + </svg> + ); +} + const mapState = (s: State) => ({ theme: getTheme(s) }); export const ThemeSwitcher = connect(mapState)(ThemeSwitcherImpl); diff --git a/src/components/shared/rtf.css b/src/components/shared/rtf.css index da439ee..574aad1 100644 --- a/src/components/shared/rtf.css +++ b/src/components/shared/rtf.css @@ -12,8 +12,8 @@ list-style: none; } .rtf.open .rtf--mb { - box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.2), - 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12); + box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), + 0px 3px 14px 2px rgba(0, 0, 0, 0.12); } .rtf.open .rtf--mb > ul { |
