mirror of
https://github.com/RealKai42/qwerty-learner.git
synced 2026-04-04 22:09:04 +08:00
refactor: improve SEO and a10y
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-Hans">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-3FTEQXFKNF"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || []
|
||||
|
||||
@@ -153,11 +153,11 @@ const Footer: React.FC = () => {
|
||||
)}
|
||||
|
||||
<footer className="mt-4 flex w-full items-center justify-center pb-1 text-sm ease-in" onClick={(e) => e.currentTarget.blur()}>
|
||||
<a href="https://github.com/Kaiyiwing/qwerty-learner" target="_blank" rel="noreferrer">
|
||||
<a href="https://github.com/Kaiyiwing/qwerty-learner" target="_blank" rel="noreferrer" aria-label="前往 GitHub 项目主页">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 496 512"
|
||||
className="mr-3 inline h-4 w-4 fill-current text-gray-500 focus:outline-none dark:text-gray-400"
|
||||
className="mr-3 inline h-4 w-4 fill-current text-gray-500 focus:outline-none dark:text-gray-400"
|
||||
>
|
||||
<path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" />
|
||||
</svg>
|
||||
@@ -169,8 +169,9 @@ const Footer: React.FC = () => {
|
||||
handleOpenInfoPanel('redBook')
|
||||
e.currentTarget.blur()
|
||||
}}
|
||||
aria-label="加入我们的小红书社群"
|
||||
>
|
||||
<img src={redBookLogo} className="fill-current text-gray-500" alt="red book" />
|
||||
<img src={redBookLogo} width={16} height={16} className="fill-current text-gray-500" alt="red book" />
|
||||
</button>
|
||||
<button
|
||||
className="cursor-pointer focus:outline-none "
|
||||
@@ -179,6 +180,7 @@ const Footer: React.FC = () => {
|
||||
handleOpenInfoPanel('community')
|
||||
e.currentTarget.blur()
|
||||
}}
|
||||
aria-label="加入我们的微信用户群"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -196,6 +198,7 @@ const Footer: React.FC = () => {
|
||||
handleOpenInfoPanel('donate')
|
||||
e.currentTarget.blur()
|
||||
}}
|
||||
aria-label="考虑捐赠我们"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -213,16 +216,23 @@ const Footer: React.FC = () => {
|
||||
handleOpenInfoPanel('vsc')
|
||||
e.currentTarget.blur()
|
||||
}}
|
||||
aria-label="使用 Visual Studio Code 插件版 Qwerty Learner"
|
||||
>
|
||||
<img src={vscLogo} className="fill-current text-gray-500" alt="visual studio code" />
|
||||
<img src={vscLogo} width={14} height={14} className="fill-current text-gray-500" alt="Visual Studio Code" />
|
||||
</button>
|
||||
|
||||
<a href="mailto:me@kaiyi.cool" target="_blank" rel="noreferrer" onClick={(e) => e.currentTarget.blur()}>
|
||||
<a
|
||||
href="mailto:me@kaiyi.cool"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
onClick={(e) => e.currentTarget.blur()}
|
||||
aria-label="发送邮件到 me@kaiyi.cool"
|
||||
>
|
||||
<EnvelopeIcon className="mr-3 inline h-4 w-4 text-gray-500 dark:text-gray-400" />
|
||||
</a>
|
||||
<Tooltip content="中国大陆镜像">
|
||||
<a href="https://kaiyiwing.gitee.io/qwerty-learner" target="_self">
|
||||
<img src={cnFlag} className="mr-2 h-5 w-5 cursor-pointer" />
|
||||
<a href="https://kaiyiwing.gitee.io/qwerty-learner" target="_self" title="前往中国大陆镜像">
|
||||
<img src={cnFlag} className="mr-2 h-5 w-5 cursor-pointer" alt="中国国旗" />
|
||||
</a>
|
||||
</Tooltip>
|
||||
<button
|
||||
@@ -243,7 +253,7 @@ const Footer: React.FC = () => {
|
||||
>
|
||||
鲁ICP备2022030649号
|
||||
</a>
|
||||
<span className="ml-2 select-none rounded bg-gray-200 px-1 py-0.5 text-xs text-gray-400 dark:bg-gray-800 dark:text-gray-500">
|
||||
<span className="ml-2 select-none rounded bg-slate-200 px-1 py-0.5 text-xs text-slate-600 dark:bg-slate-800 dark:text-slate-400">
|
||||
Build <span className="select-all">{LATEST_COMMIT_HASH}</span>
|
||||
</span>
|
||||
</footer>
|
||||
|
||||
@@ -7,10 +7,10 @@ const Header: React.FC<PropsWithChildren> = ({ children }) => {
|
||||
<header className="container mx-auto w-full px-10 py-6">
|
||||
<div className="flex w-full flex-col items-center justify-between space-y-3 lg:flex-row lg:space-y-0">
|
||||
<NavLink
|
||||
className="flex items-center text-2xl font-bold text-indigo-400 no-underline hover:no-underline lg:text-4xl"
|
||||
className="flex items-center text-2xl font-bold text-indigo-500 no-underline hover:no-underline lg:text-4xl"
|
||||
to="https://qwerty.kaiyi.cool/"
|
||||
>
|
||||
<img src={logo} className="mr-3 h-16 w-16" />
|
||||
<img src={logo} className="mr-3 h-16 w-16" alt="Qwerty Learner Logo" />
|
||||
<h1>Qwerty Learner</h1>
|
||||
</NavLink>
|
||||
<nav className="card on element flex w-auto content-center items-center justify-end space-x-3 rounded-xl bg-white p-4 transition-colors duration-300 dark:bg-gray-800">
|
||||
|
||||
@@ -63,8 +63,10 @@ export default function StarCard() {
|
||||
) : (
|
||||
<div className="flex pb-0 pt-6">
|
||||
<button
|
||||
onClick={onClickWantStar}
|
||||
className="rounded-lg bg-indigo-600 px-6 py-2 text-lg text-white transition-colors duration-300 focus:outline-none"
|
||||
type="button"
|
||||
onClick={onClickWantStar}
|
||||
title="我想收藏"
|
||||
>
|
||||
我想收藏
|
||||
</button>
|
||||
@@ -94,7 +96,7 @@ export default function StarCard() {
|
||||
后自动关闭
|
||||
</span>
|
||||
)}
|
||||
<button type="button" onClick={onClickCloseStar}>
|
||||
<button type="button" onClick={onClickCloseStar} title="关闭提示" aria-label="关闭提示">
|
||||
<IconCircleX className="text-indigo-400" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -27,9 +27,9 @@ body,
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: 0px 100px 80px rgba(0, 0, 0, 0.07), 0px 41.7776px 33.4221px rgba(0, 0, 0, 0.0503198),
|
||||
0px 22.3363px 17.869px rgba(0, 0, 0, 0.0417275), 0px 12.5216px 10.0172px rgba(0, 0, 0, 0.035),
|
||||
0px 6.6501px 5.32008px rgba(0, 0, 0, 0.0282725), 0px 2.76726px 2.21381px rgba(0, 0, 0, 0.0196802);
|
||||
box-shadow: 0px 100px 80px rgba(50, 46, 129, 0.07), 0px 41.7776px 33.4221px rgba(50, 46, 129, 0.0503198),
|
||||
0px 22.3363px 17.869px rgba(50, 46, 129, 0.0417275), 0px 12.5216px 10.0172px rgba(50, 46, 129, 0.035),
|
||||
0px 6.6501px 5.32008px rgba(50, 46, 129, 0.0282725), 0px 2.76726px 2.21381px rgba(50, 46, 129, 0.0196802);
|
||||
}
|
||||
|
||||
/* Well, TailwindCSS does’t have `text-shadow` classes. */
|
||||
|
||||
@@ -39,7 +39,7 @@ export default function DictRequest() {
|
||||
<br />
|
||||
<br />
|
||||
如果您没有相关的编程基础,可以讲您的字典需求发送邮件到 如果您没有相关的编程基础,可以讲您的字典需求发送邮件到{' '}
|
||||
<a href="mailto:me@kaiyi.cool" className="px-2 text-blue-500">
|
||||
<a href="mailto:me@kaiyi.cool" className="px-2 text-blue-500" aria-label="发送邮件到 me@kaiyi.cool">
|
||||
me@kaiyi.cool
|
||||
</a>
|
||||
,或者网页底部添加我们的用户社群进行反馈。
|
||||
|
||||
@@ -8,7 +8,7 @@ interface Props {
|
||||
|
||||
function Dictionary({ dictionary, onClick }: Props) {
|
||||
return (
|
||||
<div className="flex h-40 w-80 items-center justify-center" onClick={onClick}>
|
||||
<div className="flex h-40 w-80 items-center justify-center" role="button" onClick={onClick} title="选择词典">
|
||||
<div className="h-full w-5/12 rounded-xl bg-gray-300">
|
||||
<div className="bg-gray-700"></div>
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,9 @@ export const ChapterButton: React.FC<ChapterButtonProps> = ({ index, selected, w
|
||||
<button
|
||||
ref={buttonRef}
|
||||
className="relative flex h-28 w-44 flex-col items-start justify-start overflow-hidden rounded-md border border-gray-300 bg-gray-50 p-4 text-left shadow-lg focus:outline-none dark:border-gray-500 dark:bg-gray-700 dark:bg-opacity-10"
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
title="选择章节"
|
||||
>
|
||||
<p className="w-full pb-2 text-lg text-gray-800 dark:text-white dark:text-opacity-80">Chapter {index + 1}</p>
|
||||
<p className="text-xs font-medium text-gray-600 dark:text-white dark:text-opacity-60">单词数: {wordCount}</p>
|
||||
|
||||
@@ -22,10 +22,12 @@ const DictionaryCard: React.FC<DictionaryCardProps> = ({ dictionary }) => {
|
||||
<button
|
||||
ref={buttonRef}
|
||||
className="relative w-48 overflow-hidden rounded-md border border-gray-300 bg-gray-50 p-4 text-left shadow-lg focus:outline-none dark:border-gray-500 dark:bg-gray-700 dark:bg-opacity-10 "
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setCurrentDictId(dictionary.id)
|
||||
setCurrentChapter(0)
|
||||
}}
|
||||
title="选择词典"
|
||||
>
|
||||
<p className="mb-1 text-xl text-gray-800 dark:text-white dark:text-opacity-80">{dictionary.name}</p>
|
||||
<p className="mb-1 text-xs text-gray-900 dark:text-white dark:text-opacity-90">{dictionary.description}</p>
|
||||
|
||||
@@ -98,7 +98,7 @@ const PronunciationSwitcher = () => {
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Popover.Button
|
||||
className={`flex h-8 cursor-pointer items-center justify-center rounded-md px-1 transition-colors duration-300 ease-in-out hover:bg-indigo-400 hover:text-white focus:outline-none dark:text-white dark:text-opacity-60 dark:hover:text-opacity-100 ${
|
||||
className={`flex h-8 cursor-pointer items-center justify-center rounded-md px-1 transition-colors duration-300 ease-in-out hover:bg-indigo-400 hover:text-white focus:outline-none dark:text-white dark:text-opacity-60 dark:hover:text-opacity-100 ${
|
||||
open ? 'bg-indigo-400 text-white' : 'bg-transparent'
|
||||
}`}
|
||||
onFocus={(e) => {
|
||||
|
||||
@@ -22,9 +22,16 @@ export default function WordChip({ word }: { word: Word }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div ref={refs.setReference} className="word-chip select-none" {...getReferenceProps()} onClick={onClickWord}>
|
||||
<button
|
||||
ref={refs.setReference}
|
||||
className="word-chip select-none"
|
||||
{...getReferenceProps()}
|
||||
type="button"
|
||||
onClick={onClickWord}
|
||||
title={`朗读 ${word.name}`}
|
||||
>
|
||||
<span>{word.name}</span>
|
||||
</div>
|
||||
</button>
|
||||
{showTranslation && (
|
||||
<div
|
||||
ref={refs.setFloating}
|
||||
|
||||
@@ -182,6 +182,7 @@ const ResultScreen = () => {
|
||||
}}
|
||||
className="cursor-pointer text-gray-500 dark:text-gray-400"
|
||||
type="button"
|
||||
title="加入我们的社区"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -207,7 +208,9 @@ const ResultScreen = () => {
|
||||
<Tooltip content="快捷键:shift + enter">
|
||||
<button
|
||||
className="btn-primary h-12 border-2 border-solid border-gray-300 bg-white text-base text-gray-700 dark:border-gray-700 dark:bg-gray-600 dark:text-white dark:hover:bg-gray-700"
|
||||
type="button"
|
||||
onClick={dictationButtonHandler}
|
||||
title="默写本章节"
|
||||
>
|
||||
默写本章节
|
||||
</button>
|
||||
@@ -215,7 +218,9 @@ const ResultScreen = () => {
|
||||
<Tooltip content="快捷键:space">
|
||||
<button
|
||||
className="btn-primary h-12 border-2 border-solid border-gray-300 bg-white text-base text-gray-700 dark:border-gray-700 dark:bg-gray-600 dark:text-white dark:hover:bg-gray-700"
|
||||
type="button"
|
||||
onClick={repeatButtonHandler}
|
||||
title="重复本章节"
|
||||
>
|
||||
重复本章节
|
||||
</button>
|
||||
@@ -224,7 +229,9 @@ const ResultScreen = () => {
|
||||
<Tooltip content="快捷键:enter">
|
||||
<button
|
||||
className={`btn-primary { isLastChapter ? 'cursor-not-allowed opacity-50' : ''} h-12 text-base font-bold `}
|
||||
type="button"
|
||||
onClick={nextButtonHandler}
|
||||
title="下一章节"
|
||||
>
|
||||
下一章节
|
||||
</button>
|
||||
|
||||
@@ -73,7 +73,13 @@ export default function DataSetting() {
|
||||
<span className="ml-4 w-10 text-xs font-normal text-gray-600">{`${exportProgress}%`}</span>
|
||||
</div>
|
||||
|
||||
<button className="btn-primary ml-4 disabled:bg-gray-300" onClick={onClickExport} disabled={isExporting}>
|
||||
<button
|
||||
className="btn-primary ml-4 disabled:bg-gray-300"
|
||||
type="button"
|
||||
onClick={onClickExport}
|
||||
disabled={isExporting}
|
||||
title="导出数据"
|
||||
>
|
||||
导出数据
|
||||
</button>
|
||||
</div>
|
||||
@@ -96,7 +102,13 @@ export default function DataSetting() {
|
||||
<span className="ml-4 w-10 text-xs font-normal text-gray-600">{`${importProgress}%`}</span>
|
||||
</div>
|
||||
|
||||
<button className="btn-primary ml-4 disabled:bg-gray-300" onClick={onClickImport} disabled={isImporting}>
|
||||
<button
|
||||
className="btn-primary ml-4 disabled:bg-gray-300"
|
||||
type="button"
|
||||
onClick={onClickImport}
|
||||
disabled={isImporting}
|
||||
title="导入数据"
|
||||
>
|
||||
导入数据
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -28,9 +28,10 @@ export default function Setting() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={openModal}
|
||||
className={`flex items-center justify-center rounded p-[2px] text-lg text-indigo-400 outline-none transition-colors duration-300 ease-in-out hover:bg-indigo-400 hover:text-white ${
|
||||
isOpen && 'bg-indigo-400 text-white'
|
||||
className={`flex items-center justify-center rounded p-[2px] text-lg text-indigo-500 outline-none transition-colors duration-300 ease-in-out hover:bg-indigo-400 hover:text-white ${
|
||||
isOpen && 'bg-indigo-500 text-white'
|
||||
}`}
|
||||
title="打开设置对话框"
|
||||
>
|
||||
<Cog6ToothIcon className="icon" />
|
||||
</button>
|
||||
@@ -63,7 +64,7 @@ export default function Setting() {
|
||||
<Dialog.Panel className="flex w-200 flex-col overflow-hidden rounded-2xl bg-white p-0 shadow-xl dark:bg-gray-800">
|
||||
<div className="relative flex h-22 items-end justify-between rounded-t-lg border-b border-neutral-100 bg-stone-50 px-6 py-3 dark:border-neutral-700 dark:bg-gray-900">
|
||||
<span className="text-3xl font-bold text-gray-600">设置</span>
|
||||
<button type="button" onClick={() => setIsOpen(false)}>
|
||||
<button type="button" onClick={() => setIsOpen(false)} title="关闭对话框">
|
||||
<IconX className="absolute right-7 top-5 cursor-pointer text-gray-400" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -119,7 +119,7 @@ export default function SharePicDialog({ showState, setShowState, randomChoose }
|
||||
>
|
||||
<Dialog.Panel className="relative transform overflow-hidden rounded-xl bg-white text-left shadow-xl transition-all dark:bg-gray-700">
|
||||
<div className="flex flex-col items-center justify-center pb-10 pl-20 pr-14 pt-20">
|
||||
<button className="absolute right-7 top-5" onClick={handleClose}>
|
||||
<button className="absolute right-7 top-5" type="button" onClick={handleClose} title="关闭对话框">
|
||||
<XMarkIcon className="h-6 w-6 text-gray-400" />
|
||||
</button>
|
||||
<div className="h-152 w-116">
|
||||
@@ -143,7 +143,13 @@ export default function SharePicDialog({ showState, setShowState, randomChoose }
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<button onClick={handleDownload} ref={dialogFocusRef} className="btn-primary mr-9 mt-10 h-10">
|
||||
<button
|
||||
ref={dialogFocusRef}
|
||||
className="btn-primary mr-9 mt-10 h-10"
|
||||
type="button"
|
||||
onClick={handleDownload}
|
||||
title="保存"
|
||||
>
|
||||
保存
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -27,12 +27,14 @@ export default function SoundSwitcher() {
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Popover.Button
|
||||
className={`flex items-center justify-center rounded p-[2px] text-lg text-indigo-400 outline-none transition-colors duration-300 ease-in-out hover:bg-indigo-400 hover:text-white ${
|
||||
open ? 'bg-indigo-400 text-white' : ''
|
||||
className={`flex items-center justify-center rounded p-[2px] text-lg text-indigo-500 outline-none transition-colors duration-300 ease-in-out hover:bg-indigo-400 hover:text-white ${
|
||||
open ? 'bg-indigo-500 text-white' : ''
|
||||
}`}
|
||||
onFocus={(e) => {
|
||||
e.target.blur()
|
||||
}}
|
||||
aria-label="音效设置"
|
||||
title="音效设置"
|
||||
>
|
||||
<SpeakerWaveIcon className="icon" />
|
||||
</Popover.Button>
|
||||
|
||||
@@ -77,44 +77,52 @@ export default function Switcher() {
|
||||
|
||||
<Tooltip className="h-7 w-7" content="开关单个单词循环(Ctrl + L)">
|
||||
<button
|
||||
className={`p-[2px] ${state?.isLoopSingleWord ? 'text-indigo-400' : 'text-gray-400'} text-lg focus:outline-none`}
|
||||
className={`p-[2px] ${state?.isLoopSingleWord ? 'text-indigo-500' : 'text-gray-500'} text-lg focus:outline-none`}
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
changeLoopSingleWordState()
|
||||
e.currentTarget.blur()
|
||||
}}
|
||||
aria-label="开关单个单词循环(Ctrl + L)"
|
||||
>
|
||||
{state?.isLoopSingleWord ? <IconRepeatOnce /> : <IconRepeatOff />}
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip className="h-7 w-7" content="开关英语显示(Ctrl + V)">
|
||||
<button
|
||||
className={`p-[2px] ${state?.isWordVisible ? 'text-indigo-400' : 'text-gray-400'} text-lg focus:outline-none`}
|
||||
className={`p-[2px] ${state?.isWordVisible ? 'text-indigo-500' : 'text-gray-500'} text-lg focus:outline-none`}
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
changeWordVisibleState()
|
||||
e.currentTarget.blur()
|
||||
}}
|
||||
aria-label="开关英语显示(Ctrl + V)"
|
||||
>
|
||||
{state?.isWordVisible ? <EyeIcon className="icon" /> : <EyeSlashIcon className="icon" />}
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip className="h-7 w-7" content="开关释义显示(Ctrl + T)">
|
||||
<button
|
||||
className={`p-[2px] ${state?.isTransVisible ? 'text-indigo-400' : 'text-gray-400'} text-lg focus:outline-none`}
|
||||
className={`p-[2px] ${state?.isTransVisible ? 'text-indigo-500' : 'text-gray-500'} text-lg focus:outline-none`}
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
changeTransVisibleState()
|
||||
e.currentTarget.blur()
|
||||
}}
|
||||
aria-label="开关释义显示(Ctrl + T)"
|
||||
>
|
||||
{state?.isTransVisible ? <IconLanguage /> : <IconLanguageOff />}
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip className="h-7 w-7" content="开关深色模式(Ctrl + D)">
|
||||
<button
|
||||
className={`p-[2px] text-lg text-indigo-400 focus:outline-none`}
|
||||
className={`p-[2px] text-lg text-indigo-500 focus:outline-none`}
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
changeDarkModeState()
|
||||
e.currentTarget.blur()
|
||||
}}
|
||||
aria-label="开关深色模式(Ctrl + D)"
|
||||
>
|
||||
{isOpenDarkMode ? <MoonIcon className="icon" /> : <SunIcon className="icon" />}
|
||||
</button>
|
||||
|
||||
@@ -2,11 +2,11 @@ import VolumeHighIcon from './volume-icons/VolumeHieghIcon'
|
||||
import VolumeIcon from './volume-icons/VolumeIcon'
|
||||
import VolumeLowIcon from './volume-icons/VolumeLowIcon'
|
||||
import VolumeMediumIcon from './volume-icons/VolumeMediumIcon'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import React, { MouseEventHandler, useEffect, useState } from 'react'
|
||||
|
||||
const volumeIcons = [VolumeIcon, VolumeLowIcon, VolumeMediumIcon, VolumeHighIcon]
|
||||
|
||||
export const SoundIcon = ({ duration = 500, animated = false, ...rest }: SoundIconProps) => {
|
||||
export const SoundIcon = ({ duration = 500, animated = false, onClick, ...rest }: SoundIconProps) => {
|
||||
const [animationFrameIndex, setAnimationFrameIndex] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -34,12 +34,17 @@ export const SoundIcon = ({ duration = 500, animated = false, ...rest }: SoundIc
|
||||
|
||||
const Icon = volumeIcons[animationFrameIndex]
|
||||
|
||||
return <Icon {...rest} />
|
||||
return (
|
||||
<button type="button" onClick={onClick}>
|
||||
<Icon {...rest} />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
export type SoundIconProps = {
|
||||
animated?: boolean
|
||||
duration?: number
|
||||
onClick?: MouseEventHandler<HTMLButtonElement>
|
||||
} & Omit<React.SVGProps<SVGSVGElement>, 'ref'>
|
||||
|
||||
export type SoundIconRef = {
|
||||
|
||||
@@ -141,11 +141,13 @@ const App: React.FC = () => {
|
||||
<Tooltip content="快捷键 Enter">
|
||||
<button
|
||||
className={`${
|
||||
state.isTyping ? 'bg-gray-300 dark:bg-gray-700' : 'bg-indigo-400'
|
||||
} btn-primary w-20 transition-colors duration-300`}
|
||||
state.isTyping ? 'bg-gray-400 shadow-gray-200 dark:bg-gray-700' : 'bg-indigo-600 shadow-indigo-200'
|
||||
} btn-primary w-20 shadow transition-colors duration-200`}
|
||||
type="button"
|
||||
onClick={onToggleIsTyping}
|
||||
aria-label={state.isTyping ? '暂停' : '开始'}
|
||||
>
|
||||
{state.isTyping ? 'Pause' : 'Start'}
|
||||
<span className="font-medium">{state.isTyping ? 'Pause' : 'Start'}</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip content="跳过该词">
|
||||
@@ -178,7 +180,7 @@ const App: React.FC = () => {
|
||||
<Progress />
|
||||
</>
|
||||
) : (
|
||||
<h3 className="animate-pulse select-none pb-4 text-xl text-gray-600 dark:text-gray-50">按任意键开始</h3>
|
||||
<div className="animate-pulse select-none pb-4 text-xl text-gray-600 dark:text-gray-50">按任意键开始</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user