From c78dbcf8f89072dc9c2fa8ba81e2cf5a80218cd7 Mon Sep 17 00:00:00 2001 From: Haishan Date: Sun, 13 Jun 2021 15:20:12 +0800 Subject: Support switch theme on backend config page --- src/components/APIConfig.module.scss | 7 +- src/components/APIConfig.tsx | 10 +-- src/components/APIDiscovery.module.scss | 9 ++- src/components/APIDiscovery.tsx | 19 +++-- src/components/BackendList.module.scss | 3 + src/components/Field.module.scss | 3 +- src/components/Field.tsx | 10 ++- src/components/Modal.module.scss | 4 +- src/components/Modal.tsx | 6 +- src/components/Root.scss | 20 ++++-- src/components/SideBar.module.scss | 7 -- src/components/SideBar.tsx | 88 +---------------------- src/components/SvgYacd.tsx | 17 +++-- src/components/shared/ThemeSwitcher.module.css | 28 ++++++++ src/components/shared/ThemeSwitcher.tsx | 97 ++++++++++++++++++++++++++ 15 files changed, 197 insertions(+), 131 deletions(-) create mode 100644 src/components/shared/ThemeSwitcher.module.css create mode 100644 src/components/shared/ThemeSwitcher.tsx (limited to 'src/components') diff --git a/src/components/APIConfig.module.scss b/src/components/APIConfig.module.scss index afc2d53..6581d46 100644 --- a/src/components/APIConfig.module.scss +++ b/src/components/APIConfig.module.scss @@ -10,11 +10,12 @@ align-items: center; .icon { - color: #2d2d30; - opacity: 0.4; + --stroke: #f3f3f3; + color: #20497e; + opacity: 0.7; transition: opacity 400ms; &:hover { - opacity: 0.7; + opacity: 1; } } } diff --git a/src/components/APIConfig.tsx b/src/components/APIConfig.tsx index 9b2e7e9..9bf255f 100644 --- a/src/components/APIConfig.tsx +++ b/src/components/APIConfig.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; import { fetchConfigs } from 'src/api/configs'; import { BackendList } from 'src/components/BackendList'; +import { addClashAPIConfig, getClashAPIConfig } from 'src/store/app'; +import { State } from 'src/store/types'; import { ClashAPIConfig } from 'src/types'; -import { addClashAPIConfig, getClashAPIConfig } from '../store/app'; import s0 from './APIConfig.module.scss'; import Button from './Button'; import Field from './Field'; @@ -13,7 +14,7 @@ import SvgYacd from './SvgYacd'; const { useState, useRef, useCallback } = React; const Ok = 0; -const mapState = (s) => ({ +const mapState = (s: State) => ({ apiConfig: getClashAPIConfig(s), }); @@ -73,23 +74,22 @@ function APIConfig({ dispatch }) {
- +
+ +
+ +
); } -const mapState = (s) => ({ +const mapState = (s: State) => ({ modals: s.modals, apiConfig: getClashAPIConfig(s), }); diff --git a/src/components/BackendList.module.scss b/src/components/BackendList.module.scss index 1de1972..6872d3a 100644 --- a/src/components/BackendList.module.scss +++ b/src/components/BackendList.module.scss @@ -19,6 +19,7 @@ grid-template-rows: 30px; grid-template-areas: 'close url .'; column-gap: 10px; + border: 1px solid var(--bg-near-transparent); } .li:hover { @@ -29,6 +30,7 @@ opacity: 0; grid-area: close; place-self: center; + cursor: pointer; } .li:hover .close, @@ -83,6 +85,7 @@ } .btn:hover:enabled { background-color: var(--color-focus-blue); + color: white; } .btn:active:enabled { transform: scale(0.97); diff --git a/src/components/Field.module.scss b/src/components/Field.module.scss index 9a5f1e4..72a5149 100644 --- a/src/components/Field.module.scss +++ b/src/components/Field.module.scss @@ -9,13 +9,12 @@ border-radius: 0; border-bottom: 1px solid var(--color-input-border); box-sizing: border-box; - color: #c1c1c1; + color: inherit; display: inline-block; font-size: inherit; height: 40px; outline: none; padding: 0 4px; - transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); width: 100%; &:focus { border-color: var(--color-focus-blue); diff --git a/src/components/Field.tsx b/src/components/Field.tsx index 4134d3e..a0d43cf 100644 --- a/src/components/Field.tsx +++ b/src/components/Field.tsx @@ -1,27 +1,25 @@ -import cx from 'clsx'; -import React from 'react'; +import * as React from 'react'; import s from './Field.module.scss'; const { useCallback } = React; type Props = { + name: string; value?: string | number; type?: 'text' | 'number'; onChange?: (...args: any[]) => any; id?: string; label?: string; + placeholder?: string; }; export default function Field({ id, label, value, onChange, ...props }: Props) { const valueOnChange = useCallback((e) => onChange(e), [onChange]); - const labelClassName = cx({ - [s.floatAbove]: typeof value === 'string' && value !== '', - }); return (
-
diff --git a/src/components/Modal.module.scss b/src/components/Modal.module.scss index 6192a1f..8f9807c 100644 --- a/src/components/Modal.module.scss +++ b/src/components/Modal.module.scss @@ -11,11 +11,11 @@ .content { outline: none; position: relative; - color: #ddd; + color: var(--color-text); + background: #444; top: 50%; left: 50%; transform: translate(-50%, -50%); - background: #444; padding: 20px; border-radius: 10px; } diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index fda3263..e91523c 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -1,10 +1,10 @@ import cx from 'clsx'; -import React from 'react'; -import Modal from 'react-modal'; +import * as React from 'react'; +import Modal, { Props as ReactModalProps } from 'react-modal'; import s0 from './Modal.module.scss'; -type Props = { +type Props = ReactModalProps & { isOpen: boolean; onRequestClose: (...args: any[]) => any; children: React.ReactNode; diff --git a/src/components/Root.scss b/src/components/Root.scss index 83d4171..55198ab 100644 --- a/src/components/Root.scss +++ b/src/components/Root.scss @@ -68,12 +68,12 @@ body { -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-size-adjust: 100%; -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; margin: 0; padding: 0; } -body, -body.dark { +@mixin dark { --color-background: #202020; --color-background2: rgba(32, 32, 32, 0.3); --color-bg-card: #2d2d2d; @@ -100,8 +100,7 @@ body.dark { --select-border-color: #040404; --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%23ffffff%22%20%2F%3E%0A%20%20%20%20%20%20%3Cpath%20d%3D%22M4%2017L1%2013L7%2013L4%2017Z%22%20fill%3D%22%23ffffff%22%20%2F%3E%0A%20%20%20%20%3C%2Fsvg%3E%0A%20%20); } - -body.light { +@mixin light { --color-background: #eee; --color-background2: rgba(240, 240, 240, 0.3); --color-bg-card: #fafafa; @@ -129,6 +128,19 @@ body.light { --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='dark'] { + @include dark; +} + +:root[data-theme='light'] { + @include light; +} + .flexCenter { display: flex; align-items: center; diff --git a/src/components/SideBar.module.scss b/src/components/SideBar.module.scss index 744d29d..4a06377 100644 --- a/src/components/SideBar.module.scss +++ b/src/components/SideBar.module.scss @@ -103,10 +103,3 @@ .iconWrapper:focus { border-color: var(--color-focus-blue); } - -.themeSwitchContainer { - appearance: none; - user-select: none; - background: none; - cursor: pointer; -} diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 8a6429a..dbe7f0d 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -12,13 +12,9 @@ import { FcSettings, } from 'react-icons/fc'; import { Link, useLocation } from 'react-router-dom'; +import { ThemeSwitcher } from 'src/components/shared/ThemeSwitcher'; -import { framerMotionResouce } from '../misc/motion'; -import { getTheme, switchTheme } from '../store/app'; import s from './SideBar.module.scss'; -import { connect } from './StateProvider'; - -const { useCallback } = React; const icons = { activity: FcAreaChart, @@ -85,12 +81,9 @@ const pages = [ }, ]; -function SideBar({ dispatch, theme }) { +export default function SideBar() { const { t } = useTranslation(); const location = useLocation(); - const switchThemeHooked = useCallback(() => { - dispatch(switchTheme()); - }, [dispatch]); return (
@@ -106,19 +99,7 @@ function SideBar({ dispatch, theme }) { ))}
- - - + @@ -128,66 +109,3 @@ function SideBar({ dispatch, theme }) {
); } - -function MoonA() { - const module = framerMotionResouce.read(); - const motion = module.motion; - return ( - - - - ); -} - -function Sun() { - const module = framerMotionResouce.read(); - const motion = module.motion; - - return ( - - - - - - - - - - - - - - ); -} - -const mapState = (s) => ({ theme: getTheme(s) }); -export default connect(mapState)(SideBar); diff --git a/src/components/SvgYacd.tsx b/src/components/SvgYacd.tsx index 90ad7e2..63c0bd5 100644 --- a/src/components/SvgYacd.tsx +++ b/src/components/SvgYacd.tsx @@ -1,5 +1,5 @@ import cx from 'clsx'; -import React from 'react'; +import * as React from 'react'; import s from './SvgYacd.module.scss'; @@ -9,6 +9,9 @@ type Props = { animate?: boolean; c0?: string; c1?: string; + stroke?: string; + eye?: string; + mouth?: string; }; function SvgYacd({ @@ -16,7 +19,9 @@ function SvgYacd({ height = 320, animate = false, c0 = 'currentColor', - c1 = '#eee', + stroke = '#eee', + eye = '#eee', + mouth = '#eee', }: Props) { const faceClasName = cx({ [s.path]: animate }); return ( @@ -30,16 +35,16 @@ function SvgYacd({ {/* face */} - - + + {/* mouth */} - + diff --git a/src/components/shared/ThemeSwitcher.module.css b/src/components/shared/ThemeSwitcher.module.css new file mode 100644 index 0000000..919c86c --- /dev/null +++ b/src/components/shared/ThemeSwitcher.module.css @@ -0,0 +1,28 @@ +.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.tsx b/src/components/shared/ThemeSwitcher.tsx new file mode 100644 index 0000000..fba5b0b --- /dev/null +++ b/src/components/shared/ThemeSwitcher.tsx @@ -0,0 +1,97 @@ +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'; +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'; + +export function ThemeSwitcherImpl({ theme, dispatch }) { + const { t } = useTranslation(); + + const switchThemeHooked = React.useCallback(() => { + dispatch(switchTheme()); + }, [dispatch]); + + return ( + + + + ); +} + +function MoonA() { + const module = framerMotionResouce.read(); + const motion = module.motion; + return ( + + + + ); +} + +function Sun() { + const module = framerMotionResouce.read(); + const motion = module.motion; + + return ( + + + + + + + + + + + + + + ); +} + +const mapState = (s: State) => ({ theme: getTheme(s) }); +export const ThemeSwitcher = connect(mapState)(ThemeSwitcherImpl); -- cgit v1.3.1