Replace native select with @reach/menu-button in theme switcher
ref #706
This commit is contained in:
parent
d10d30baa3
commit
c4346c5a95
6 changed files with 141 additions and 62 deletions
|
@ -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>
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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'}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
left: 0;
|
||||
bottom: 0;
|
||||
background: #444;
|
||||
z-index: 1024;
|
||||
}
|
||||
|
||||
.content {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue