feat: impl dark mode for extension

This commit is contained in:
Steven 2023-09-30 01:22:40 +08:00
parent 6126701025
commit 8c753e9557
5 changed files with 245 additions and 157 deletions

View File

@ -22,7 +22,7 @@ const ShortcutView = (props: Props) => {
<> <>
<div <div
className={classNames( className={classNames(
"group w-full px-3 py-2 flex flex-col justify-start items-start border rounded-lg hover:bg-gray-100 hover:shadow" "group w-full px-3 py-2 flex flex-col justify-start items-start border rounded-lg hover:bg-gray-100 hover:shadow dark:border-zinc-800 dark:hover:bg-zinc-800"
)} )}
> >
<div className="w-full flex flex-row justify-start items-center"> <div className="w-full flex flex-row justify-start items-center">
@ -42,13 +42,13 @@ const ShortcutView = (props: Props) => {
onClick={handleShortcutLinkClick} onClick={handleShortcutLinkClick}
> >
<div className="truncate"> <div className="truncate">
<span>{shortcut.title}</span> <span className="dark:text-gray-400">{shortcut.title}</span>
{shortcut.title ? ( {shortcut.title ? (
<span className="text-gray-400">(s/{shortcut.name})</span> <span className="text-gray-500">(s/{shortcut.name})</span>
) : ( ) : (
<> <>
<span className="text-gray-400">s/</span> <span className="text-gray-400 dark:text-gray-500">s/</span>
<span className="truncate">{shortcut.name}</span> <span className="truncate dark:text-gray-400">{shortcut.name}</span>
</> </>
)} )}
</div> </div>

View File

@ -0,0 +1,43 @@
import { useColorScheme } from "@mui/joy";
import { useEffect } from "react";
const useColorTheme = () => {
const { mode: colorTheme, setMode: setColorTheme } = useColorScheme();
useEffect(() => {
const root = document.documentElement;
if (colorTheme === "light") {
root.classList.remove("dark");
} else if (colorTheme === "dark") {
root.classList.add("dark");
} else {
const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
if (darkMediaQuery.matches) {
root.classList.add("dark");
} else {
root.classList.remove("dark");
}
const handleColorSchemeChange = (e: MediaQueryListEvent) => {
if (e.matches) {
root.classList.add("dark");
} else {
root.classList.remove("dark");
}
};
try {
darkMediaQuery.addEventListener("change", handleColorSchemeChange);
} catch (error) {
console.error("failed to initial color scheme listener", error);
}
return () => {
darkMediaQuery.removeEventListener("change", handleColorSchemeChange);
};
}
}, [colorTheme]);
return { colorTheme, setColorTheme };
};
export default useColorTheme;

View File

@ -1,5 +1,5 @@
import type { Shortcut } from "@/types/proto/api/v2/shortcut_service"; import type { Shortcut } from "@/types/proto/api/v2/shortcut_service";
import { Button, Divider, Input } from "@mui/joy"; import { Button, CssVarsProvider, Divider, Input, Select, Option } from "@mui/joy";
import { useStorage } from "@plasmohq/storage/hook"; import { useStorage } from "@plasmohq/storage/hook";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Toaster, toast } from "react-hot-toast"; import { Toaster, toast } from "react-hot-toast";
@ -7,6 +7,7 @@ import Icon from "./components/Icon";
import Logo from "./components/Logo"; import Logo from "./components/Logo";
import PullShortcutsButton from "./components/PullShortcutsButton"; import PullShortcutsButton from "./components/PullShortcutsButton";
import ShortcutsContainer from "./components/ShortcutsContainer"; import ShortcutsContainer from "./components/ShortcutsContainer";
import useColorTheme from "./hooks/useColorTheme";
import "./style.css"; import "./style.css";
interface SettingState { interface SettingState {
@ -14,7 +15,23 @@ interface SettingState {
accessToken: string; accessToken: string;
} }
const colorThemeOptions = [
{
value: "system",
label: "System",
},
{
value: "light",
label: "Light",
},
{
value: "dark",
label: "Dark",
},
];
const IndexOptions = () => { const IndexOptions = () => {
const { colorTheme, setColorTheme } = useColorTheme();
const [domain, setDomain] = useStorage<string>("domain", (v) => (v ? v : "")); const [domain, setDomain] = useStorage<string>("domain", (v) => (v ? v : ""));
const [accessToken, setAccessToken] = useStorage<string>("access_token", (v) => (v ? v : "")); const [accessToken, setAccessToken] = useStorage<string>("access_token", (v) => (v ? v : ""));
const [settingState, setSettingState] = useState<SettingState>({ const [settingState, setSettingState] = useState<SettingState>({
@ -44,12 +61,15 @@ const IndexOptions = () => {
toast.success("Setting saved"); toast.success("Setting saved");
}; };
const handleSelectColorTheme = async (colorTheme: string) => {
setColorTheme(colorTheme as any);
};
return ( return (
<>
<div className="w-full"> <div className="w-full">
<div className="w-full flex flex-row justify-center items-center"> <div className="w-full flex flex-row justify-center items-center">
<a <a
className="bg-yellow-100 mt-12 py-2 px-3 rounded-full border flex flex-row justify-start items-center cursor-pointer shadow hover:underline hover:text-blue-600" className="bg-yellow-100 dark:bg-yellow-500 dark:opacity-70 mt-12 py-2 px-3 rounded-full border dark:border-yellow-600 flex flex-row justify-start items-center cursor-pointer shadow hover:underline hover:text-blue-600"
href="https://github.com/boojack/slash#browser-extension" href="https://github.com/boojack/slash#browser-extension"
target="_blank" target="_blank"
> >
@ -60,7 +80,7 @@ const IndexOptions = () => {
</div> </div>
<div className="w-full max-w-lg mx-auto flex flex-col justify-start items-start mt-12"> <div className="w-full max-w-lg mx-auto flex flex-col justify-start items-start mt-12">
<h2 className="flex flex-row justify-start items-center mb-6 text-2xl"> <h2 className="flex flex-row justify-start items-center mb-6 text-2xl dark:text-gray-400">
<Logo className="w-10 h-auto mr-2" /> <Logo className="w-10 h-auto mr-2" />
<span>Slash</span> <span>Slash</span>
<span className="mx-2 text-gray-400">/</span> <span className="mx-2 text-gray-400">/</span>
@ -70,10 +90,10 @@ const IndexOptions = () => {
<div className="w-full flex flex-col justify-start items-start"> <div className="w-full flex flex-col justify-start items-start">
<div className="w-full flex flex-col justify-start items-start mb-4"> <div className="w-full flex flex-col justify-start items-start mb-4">
<div className="mb-2 text-base w-full flex flex-row justify-between items-center"> <div className="mb-2 text-base w-full flex flex-row justify-between items-center">
<span>Domain</span> <span className="dark:text-gray-400">Domain</span>
{domain !== "" && ( {domain !== "" && (
<a <a
className="text-sm flex flex-row justify-start items-center hover:underline hover:text-blue-600" className="text-sm flex flex-row justify-start items-center dark:text-gray-400 hover:underline hover:text-blue-600"
href={domain} href={domain}
target="_blank" target="_blank"
> >
@ -94,7 +114,7 @@ const IndexOptions = () => {
</div> </div>
<div className="w-full flex flex-col justify-start items-start"> <div className="w-full flex flex-col justify-start items-start">
<span className="mb-2 text-base">Access Token</span> <span className="mb-2 text-base dark:text-gray-400">Access Token</span>
<div className="relative w-full"> <div className="relative w-full">
<Input <Input
className="w-full" className="w-full"
@ -106,9 +126,28 @@ const IndexOptions = () => {
</div> </div>
</div> </div>
<div className="w-full mt-6"> <div className="w-full mt-6 flex flex-row justify-end">
<Button onClick={handleSaveSetting}>Save</Button> <Button onClick={handleSaveSetting}>Save</Button>
</div> </div>
<Divider className="!my-6" />
<p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">Preference</p>
<div className="w-full flex flex-row justify-between items-center">
<div className="flex flex-row justify-start items-center gap-x-1">
<span className="dark:text-gray-400">Color Theme</span>
</div>
<Select defaultValue={colorTheme} onChange={(_, value) => handleSelectColorTheme(value)}>
{colorThemeOptions.map((option) => {
return (
<Option key={option.value} value={option.value}>
{option.label}
</Option>
);
})}
</Select>
</div>
</div> </div>
{isInitialized && ( {isInitialized && (
@ -116,7 +155,7 @@ const IndexOptions = () => {
<Divider className="!my-6" /> <Divider className="!my-6" />
<h2 className="flex flex-row justify-start items-center mb-4"> <h2 className="flex flex-row justify-start items-center mb-4">
<span className="text-lg">Shortcuts</span> <span className="text-lg dark:text-gray-400">Shortcuts</span>
<span className="text-gray-500 mr-1">({shortcuts.length})</span> <span className="text-gray-500 mr-1">({shortcuts.length})</span>
<PullShortcutsButton /> <PullShortcutsButton />
</h2> </h2>
@ -125,10 +164,16 @@ const IndexOptions = () => {
)} )}
</div> </div>
</div> </div>
<Toaster position="top-center" />
</>
); );
}; };
export default IndexOptions; const Options = () => {
return (
<CssVarsProvider>
<IndexOptions />
<Toaster position="top-right" />
</CssVarsProvider>
);
};
export default Options;

View File

@ -1,5 +1,5 @@
import type { Shortcut } from "@/types/proto/api/v2/shortcut_service"; import type { Shortcut } from "@/types/proto/api/v2/shortcut_service";
import { Button, Divider, IconButton } from "@mui/joy"; import { Button, CssVarsProvider, Divider, IconButton } from "@mui/joy";
import { useStorage } from "@plasmohq/storage/hook"; import { useStorage } from "@plasmohq/storage/hook";
import { Toaster } from "react-hot-toast"; import { Toaster } from "react-hot-toast";
import CreateShortcutsButton from "@/components/CreateShortcutsButton"; import CreateShortcutsButton from "@/components/CreateShortcutsButton";
@ -7,9 +7,11 @@ import Icon from "@/components/Icon";
import Logo from "@/components/Logo"; import Logo from "@/components/Logo";
import PullShortcutsButton from "@/components/PullShortcutsButton"; import PullShortcutsButton from "@/components/PullShortcutsButton";
import ShortcutsContainer from "@/components/ShortcutsContainer"; import ShortcutsContainer from "@/components/ShortcutsContainer";
import useColorTheme from "./hooks/useColorTheme";
import "./style.css"; import "./style.css";
const IndexPopup = () => { const IndexPopup = () => {
useColorTheme();
const [domain] = useStorage<string>("domain", ""); const [domain] = useStorage<string>("domain", "");
const [accessToken] = useStorage<string>("access_token", ""); const [accessToken] = useStorage<string>("access_token", "");
const [shortcuts] = useStorage<Shortcut[]>("shortcuts", []); const [shortcuts] = useStorage<Shortcut[]>("shortcuts", []);
@ -25,10 +27,9 @@ const IndexPopup = () => {
}; };
return ( return (
<>
<div className="w-full min-w-[512px] px-4 pt-4"> <div className="w-full min-w-[512px] px-4 pt-4">
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">
<div className="flex flex-row justify-start items-center"> <div className="flex flex-row justify-start items-center dark:text-gray-400">
<Logo className="w-6 h-auto mr-2" /> <Logo className="w-6 h-auto mr-2" />
<span className="">Slash</span> <span className="">Slash</span>
{isInitialized && ( {isInitialized && (
@ -59,22 +60,15 @@ const IndexPopup = () => {
<div className="w-full flex flex-row justify-between items-center mb-2"> <div className="w-full flex flex-row justify-between items-center mb-2">
<div className="flex flex-row justify-start items-center"> <div className="flex flex-row justify-start items-center">
<IconButton size="sm" variant="plain" color="neutral" onClick={handleSettingButtonClick}> <IconButton size="sm" variant="plain" color="neutral" onClick={handleSettingButtonClick}>
<Icon.Settings className="w-5 h-auto text-gray-500" /> <Icon.Settings className="w-5 h-auto text-gray-500 dark:text-gray-400" />
</IconButton> </IconButton>
<IconButton <IconButton size="sm" variant="plain" color="neutral" component="a" href="https://github.com/boojack/slash" target="_blank">
size="sm" <Icon.Github className="w-5 h-auto text-gray-500 dark:text-gray-400" />
variant="plain"
color="neutral"
component="a"
href="https://github.com/boojack/slash"
target="_blank"
>
<Icon.Github className="w-5 h-auto text-gray-500" />
</IconButton> </IconButton>
</div> </div>
<div className="flex flex-row justify-end items-center"> <div className="flex flex-row justify-end items-center">
<a <a
className="text-sm flex flex-row justify-start items-center text-gray-500 hover:underline hover:text-blue-600" className="text-sm flex flex-row justify-start items-center text-gray-500 dark:text-gray-400 hover:underline hover:text-blue-600"
href={domain} href={domain}
target="_blank" target="_blank"
> >
@ -87,12 +81,12 @@ const IndexPopup = () => {
) : ( ) : (
<div className="w-full flex flex-col justify-start items-center"> <div className="w-full flex flex-col justify-start items-center">
<Icon.Cookie strokeWidth={1} className="w-20 h-auto mb-4 text-gray-400" /> <Icon.Cookie strokeWidth={1} className="w-20 h-auto mb-4 text-gray-400" />
<p>Please set your domain and access token first.</p> <p className="dark:text-gray-400">Please set your domain and access token first.</p>
<div className="w-full flex flex-row justify-center items-center py-4"> <div className="w-full flex flex-row justify-center items-center py-4">
<Button size="sm" color="primary" onClick={handleSettingButtonClick}> <Button size="sm" color="primary" onClick={handleSettingButtonClick}>
<Icon.Settings className="w-5 h-auto mr-1" /> Setting <Icon.Settings className="w-5 h-auto mr-1" /> Setting
</Button> </Button>
<span className="mx-2">Or</span> <span className="mx-2 dark:text-gray-400">Or</span>
<Button size="sm" variant="outlined" color="neutral" onClick={handleRefreshButtonClick}> <Button size="sm" variant="outlined" color="neutral" onClick={handleRefreshButtonClick}>
<Icon.RefreshCcw className="w-5 h-auto mr-1" /> Refresh <Icon.RefreshCcw className="w-5 h-auto mr-1" /> Refresh
</Button> </Button>
@ -101,10 +95,16 @@ const IndexPopup = () => {
)} )}
</div> </div>
</div> </div>
<Toaster position="top-right" />
</>
); );
}; };
export default IndexPopup; const Popup = () => {
return (
<CssVarsProvider>
<IndexPopup />
<Toaster position="top-right" />
</CssVarsProvider>
);
};
export default Popup;

View File

@ -5,7 +5,7 @@
body, body,
html, html,
#root { #root {
@apply text-base; @apply text-base dark:bg-zinc-900;
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Noto Sans", "Noto Sans CJK SC", "Microsoft YaHei UI", "Microsoft YaHei", font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Noto Sans", "Noto Sans CJK SC", "Microsoft YaHei UI", "Microsoft YaHei",
"WenQuanYi Micro Hei", sans-serif, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "WenQuanYi Micro Hei", sans-serif, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
"Noto Color Emoji"; "Noto Color Emoji";