Replace native select with @reach/menu-button in theme switcher

ref #706
This commit is contained in:
Haishan 2022-06-15 16:07:21 +08:00
parent d10d30baa3
commit c4346c5a95
6 changed files with 141 additions and 62 deletions

View file

@ -11,8 +11,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="application-name" content="yacd" />
<meta name="description" content="Yet Another Clash Dashboard" />
<meta name="theme-color" content="#202020" media="(prefers-color-scheme: dark)" />
<meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)" />
<title>yacd</title>
</head>
<body>

View file

@ -30,6 +30,7 @@
"dependencies": {
"@babel/runtime": "7.18.3",
"@fontsource/roboto-mono": "4.5.7",
"@reach/menu-button": "0.17.0",
"@reach/tooltip": "0.17.0",
"@reach/visually-hidden": "0.17.0",
"chart.js": "3.8.0",

View file

@ -4,6 +4,7 @@ specifiers:
'@babel/runtime': 7.18.3
'@fontsource/inter': 4.5.11
'@fontsource/roboto-mono': 4.5.7
'@reach/menu-button': 0.17.0
'@reach/tooltip': 0.17.0
'@reach/visually-hidden': 0.17.0
'@types/invariant': 2.2.35
@ -80,6 +81,7 @@ specifiers:
dependencies:
'@babel/runtime': 7.18.3
'@fontsource/roboto-mono': 4.5.7
'@reach/menu-button': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
'@reach/tooltip': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
'@reach/visually-hidden': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
chart.js: 3.8.0
@ -1741,10 +1743,69 @@ packages:
tslib: 2.4.0
dev: false
/@reach/descendants/0.17.0_ef5jwxihqo6n7gxfmzogljlgcm:
resolution: {integrity: sha512-c7lUaBfjgcmKFZiAWqhG+VnXDMEhPkI4kAav/82XKZD6NVvFjsQOTH+v3tUkskrAPV44Yuch0mFW/u5Ntifr7Q==}
peerDependencies:
react: ^16.8.0 || 17.x
react-dom: ^16.8.0 || 17.x
dependencies:
'@reach/utils': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
react: 18.1.0
react-dom: 18.1.0_react@18.1.0
tslib: 2.4.0
dev: false
/@reach/dropdown/0.17.0_ef5jwxihqo6n7gxfmzogljlgcm:
resolution: {integrity: sha512-qBTIGInhxtPHtdj4Pl2XZgZMz3e37liydh0xR3qc48syu7g71sL4nqyKjOzThykyfhA3Pb3/wFgsFJKGTSdaig==}
peerDependencies:
react: ^16.8.0 || 17.x
react-dom: ^16.8.0 || 17.x
dependencies:
'@reach/auto-id': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
'@reach/descendants': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
'@reach/popover': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
'@reach/utils': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
react: 18.1.0
react-dom: 18.1.0_react@18.1.0
tslib: 2.4.0
dev: false
/@reach/menu-button/0.17.0_ef5jwxihqo6n7gxfmzogljlgcm:
resolution: {integrity: sha512-YyuYVyMZKamPtivoEI6D0UEILYH3qZtg4kJzEAuzPmoR/aHN66NZO75Fx0gtjG1S6fZfbiARaCOZJC0VEiDOtQ==}
peerDependencies:
react: ^16.8.0 || 17.x
react-dom: ^16.8.0 || 17.x
react-is: ^16.8.0 || 17.x
dependencies:
'@reach/dropdown': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
'@reach/popover': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
'@reach/utils': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
prop-types: 15.8.1
react: 18.1.0
react-dom: 18.1.0_react@18.1.0
tiny-warning: 1.0.3
tslib: 2.4.0
dev: false
/@reach/observe-rect/1.2.0:
resolution: {integrity: sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==}
dev: false
/@reach/popover/0.17.0_ef5jwxihqo6n7gxfmzogljlgcm:
resolution: {integrity: sha512-yYbBF4fMz4Ml4LB3agobZjcZ/oPtPsNv70ZAd7lEC2h7cvhF453pA+zOBGYTPGupKaeBvgAnrMjj7RnxDU5hoQ==}
peerDependencies:
react: ^16.8.0 || 17.x
react-dom: ^16.8.0 || 17.x
dependencies:
'@reach/portal': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
'@reach/rect': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
'@reach/utils': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
react: 18.1.0
react-dom: 18.1.0_react@18.1.0
tabbable: 4.0.0
tslib: 2.4.0
dev: false
/@reach/portal/0.17.0_ef5jwxihqo6n7gxfmzogljlgcm:
resolution: {integrity: sha512-+IxsgVycOj+WOeNPL2NdgooUdHPSY285wCtj/iWID6akyr4FgGUK7sMhRM9aGFyrGpx2vzr+eggbUmAVZwOz+A==}
peerDependencies:
@ -5530,6 +5591,10 @@ packages:
stable: 0.1.8
dev: true
/tabbable/4.0.0:
resolution: {integrity: sha512-H1XoH1URcBOa/rZZWxLxHCtOdVUEev+9vo5YdYhC9tCY4wnybX+VQrCYuy9ubkg69fCBxCONJOSLGfw0DWMffQ==}
dev: false
/temp-dir/2.0.0:
resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==}
engines: {node: '>=8'}

View file

@ -5,7 +5,6 @@
left: 0;
bottom: 0;
background: #444;
z-index: 1024;
}
.content {

View file

@ -1,59 +1,58 @@
.iconWrapper {
[data-reach-menu-button] {
--sz: 40px;
width: var(--sz);
height: var(--sz);
display: flex;
display: inline-flex;
align-items: center;
justify-content: center;
align-items: center;
-webkit-appearance: none;
outline: none;
padding: 5px;
color: var(--color-text);
border-radius: 100%;
user-select: none;
cursor: pointer;
color: var(--color-btn-fg);
background: none;
border: 1px solid transparent;
}
.iconWrapper:hover {
opacity: 0.6;
}
.iconWrapper:focus {
border-color: var(--color-focus-blue);
border-radius: 20px;
&:hover {
opacity: 0.6;
}
&:focus {
border-color: var(--color-focus-blue);
}
}
.themeSwitchContainer {
--sz: 40px;
[data-reach-menu-list] {
background: var(--bg-tooltip);
color: var(--color-text);
border: 1px solid #555;
padding: 4px;
border-radius: 8px;
}
position: relative;
[data-reach-menu-item] {
padding: 5px 16px 5px 6px;
border-radius: 7px;
cursor: pointer;
display: flex;
align-items: center;
height: var(--sz);
select {
cursor: pointer;
padding-left: calc(var(--sz) - 2px);
font-size: 0;
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;
}
[data-reach-menu-item][data-selected] {
background: var(--color-focus-blue);
color: #f7f7f7;
}
.checkWrapper {
display: inline-flex;
align-items: center;
margin-right: 2px;
visibility: hidden;
&.active {
visibility: visible;
}
}

View file

@ -1,5 +1,8 @@
import { Menu, MenuButton, MenuItem, MenuList } from '@reach/menu-button';
import Tooltip from '@reach/tooltip';
import cx from 'clsx';
import * as React from 'react';
import { Check } from 'react-feather';
import { useTranslation } from 'react-i18next';
import { connect } from 'src/components/StateProvider';
import { framerMotionResouce } from 'src/misc/motion';
@ -8,7 +11,7 @@ import { State } from 'src/store/types';
import s from './ThemeSwitcher.module.scss';
export function ThemeSwitcherImpl({ theme, dispatch }) {
function ThemeSwitcherImpl({ theme, dispatch }) {
const { t } = useTranslation();
const themeIcon = React.useMemo(() => {
@ -25,22 +28,36 @@ export function ThemeSwitcherImpl({ theme, dispatch }) {
}
}, [theme]);
const onChange = React.useCallback(
(e: React.ChangeEvent<HTMLSelectElement>) => dispatch(switchTheme(e.target.value)),
[dispatch]
);
const onSelect = React.useCallback((v: string) => dispatch(switchTheme(v)), [dispatch]);
return (
<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>
<Menu>
<Tooltip label={t('switch_theme')} aria-label={'switch theme'}>
<MenuButton>{themeIcon}</MenuButton>
</Tooltip>
<MenuList>
<ThemeMenuItem value="auto" label="Auto" active={theme === 'auto'} onSelect={onSelect} />
<ThemeMenuItem value="dark" label="Dark" active={theme === 'dark'} onSelect={onSelect} />
<ThemeMenuItem value="light" label="Light" active={theme === 'light'} onSelect={onSelect} />
</MenuList>
</Menu>
);
}
function ThemeMenuItem(props: {
value: string;
label: string;
active: boolean;
onSelect: (s: string) => void;
}) {
const cls = cx(s.checkWrapper, { [s.active]: props.active });
return (
<MenuItem onSelect={() => props.onSelect(props.value)}>
<span className={cls}>
<Check size={14} />
</span>
<span>{props.label}</span>
</MenuItem>
);
}