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
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">
@ -42,13 +42,13 @@ const ShortcutView = (props: Props) => {
onClick={handleShortcutLinkClick}
>
<div className="truncate">
<span>{shortcut.title}</span>
<span className="dark:text-gray-400">{shortcut.title}</span>
{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="truncate">{shortcut.name}</span>
<span className="text-gray-400 dark:text-gray-500">s/</span>
<span className="truncate dark:text-gray-400">{shortcut.name}</span>
</>
)}
</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 { Button, Divider, Input } from "@mui/joy";
import { Button, CssVarsProvider, Divider, Input, Select, Option } from "@mui/joy";
import { useStorage } from "@plasmohq/storage/hook";
import { useEffect, useState } from "react";
import { Toaster, toast } from "react-hot-toast";
@ -7,6 +7,7 @@ import Icon from "./components/Icon";
import Logo from "./components/Logo";
import PullShortcutsButton from "./components/PullShortcutsButton";
import ShortcutsContainer from "./components/ShortcutsContainer";
import useColorTheme from "./hooks/useColorTheme";
import "./style.css";
interface SettingState {
@ -14,7 +15,23 @@ interface SettingState {
accessToken: string;
}
const colorThemeOptions = [
{
value: "system",
label: "System",
},
{
value: "light",
label: "Light",
},
{
value: "dark",
label: "Dark",
},
];
const IndexOptions = () => {
const { colorTheme, setColorTheme } = useColorTheme();
const [domain, setDomain] = useStorage<string>("domain", (v) => (v ? v : ""));
const [accessToken, setAccessToken] = useStorage<string>("access_token", (v) => (v ? v : ""));
const [settingState, setSettingState] = useState<SettingState>({
@ -44,91 +61,119 @@ const IndexOptions = () => {
toast.success("Setting saved");
};
const handleSelectColorTheme = async (colorTheme: string) => {
setColorTheme(colorTheme as any);
};
return (
<>
<div className="w-full">
<div className="w-full flex flex-row justify-center items-center">
<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"
href="https://github.com/boojack/slash#browser-extension"
target="_blank"
>
<Icon.HelpCircle className="w-4 h-auto" />
<span className="mx-1 text-sm">Need help? Check out the docs</span>
<Icon.ExternalLink className="w-4 h-auto" />
</a>
</div>
<div className="w-full">
<div className="w-full flex flex-row justify-center items-center">
<a
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"
target="_blank"
>
<Icon.HelpCircle className="w-4 h-auto" />
<span className="mx-1 text-sm">Need help? Check out the docs</span>
<Icon.ExternalLink className="w-4 h-auto" />
</a>
</div>
<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">
<Logo className="w-10 h-auto mr-2" />
<span>Slash</span>
<span className="mx-2 text-gray-400">/</span>
<span>Setting</span>
</h2>
<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 dark:text-gray-400">
<Logo className="w-10 h-auto mr-2" />
<span>Slash</span>
<span className="mx-2 text-gray-400">/</span>
<span>Setting</span>
</h2>
<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="mb-2 text-base w-full flex flex-row justify-between items-center">
<span>Domain</span>
{domain !== "" && (
<a
className="text-sm flex flex-row justify-start items-center hover:underline hover:text-blue-600"
href={domain}
target="_blank"
>
<span className="mr-1">Go to my Slash</span>
<Icon.ExternalLink className="w-4 h-auto" />
</a>
)}
</div>
<div className="relative w-full">
<Input
className="w-full"
type="text"
placeholder="The domain of your Slash instance"
value={settingState.domain}
onChange={(e) => setPartialSettingState({ domain: e.target.value })}
/>
</div>
<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="mb-2 text-base w-full flex flex-row justify-between items-center">
<span className="dark:text-gray-400">Domain</span>
{domain !== "" && (
<a
className="text-sm flex flex-row justify-start items-center dark:text-gray-400 hover:underline hover:text-blue-600"
href={domain}
target="_blank"
>
<span className="mr-1">Go to my Slash</span>
<Icon.ExternalLink className="w-4 h-auto" />
</a>
)}
</div>
<div className="w-full flex flex-col justify-start items-start">
<span className="mb-2 text-base">Access Token</span>
<div className="relative w-full">
<Input
className="w-full"
type="text"
placeholder="The access token of your Slash instance"
value={settingState.accessToken}
onChange={(e) => setPartialSettingState({ accessToken: e.target.value })}
/>
</div>
</div>
<div className="w-full mt-6">
<Button onClick={handleSaveSetting}>Save</Button>
<div className="relative w-full">
<Input
className="w-full"
type="text"
placeholder="The domain of your Slash instance"
value={settingState.domain}
onChange={(e) => setPartialSettingState({ domain: e.target.value })}
/>
</div>
</div>
{isInitialized && (
<>
<Divider className="!my-6" />
<div className="w-full flex flex-col justify-start items-start">
<span className="mb-2 text-base dark:text-gray-400">Access Token</span>
<div className="relative w-full">
<Input
className="w-full"
type="text"
placeholder="The access token of your Slash instance"
value={settingState.accessToken}
onChange={(e) => setPartialSettingState({ accessToken: e.target.value })}
/>
</div>
</div>
<h2 className="flex flex-row justify-start items-center mb-4">
<span className="text-lg">Shortcuts</span>
<span className="text-gray-500 mr-1">({shortcuts.length})</span>
<PullShortcutsButton />
</h2>
<ShortcutsContainer />
</>
)}
<div className="w-full mt-6 flex flex-row justify-end">
<Button onClick={handleSaveSetting}>Save</Button>
</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>
<Toaster position="top-center" />
</>
{isInitialized && (
<>
<Divider className="!my-6" />
<h2 className="flex flex-row justify-start items-center mb-4">
<span className="text-lg dark:text-gray-400">Shortcuts</span>
<span className="text-gray-500 mr-1">({shortcuts.length})</span>
<PullShortcutsButton />
</h2>
<ShortcutsContainer />
</>
)}
</div>
</div>
);
};
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 { Button, Divider, IconButton } from "@mui/joy";
import { Button, CssVarsProvider, Divider, IconButton } from "@mui/joy";
import { useStorage } from "@plasmohq/storage/hook";
import { Toaster } from "react-hot-toast";
import CreateShortcutsButton from "@/components/CreateShortcutsButton";
@ -7,9 +7,11 @@ import Icon from "@/components/Icon";
import Logo from "@/components/Logo";
import PullShortcutsButton from "@/components/PullShortcutsButton";
import ShortcutsContainer from "@/components/ShortcutsContainer";
import useColorTheme from "./hooks/useColorTheme";
import "./style.css";
const IndexPopup = () => {
useColorTheme();
const [domain] = useStorage<string>("domain", "");
const [accessToken] = useStorage<string>("access_token", "");
const [shortcuts] = useStorage<Shortcut[]>("shortcuts", []);
@ -25,86 +27,84 @@ const IndexPopup = () => {
};
return (
<>
<div className="w-full min-w-[512px] px-4 pt-4">
<div className="w-full flex flex-row justify-between items-center">
<div className="flex flex-row justify-start items-center">
<Logo className="w-6 h-auto mr-2" />
<span className="">Slash</span>
{isInitialized && (
<>
<span className="mx-1 text-gray-400">/</span>
<span>Shortcuts</span>
<span className="text-gray-500 mr-0.5">({shortcuts.length})</span>
<PullShortcutsButton />
</>
)}
</div>
<div>{isInitialized && <CreateShortcutsButton />}</div>
</div>
<div className="w-full mt-4">
{isInitialized ? (
<div className="w-full min-w-[512px] px-4 pt-4">
<div className="w-full flex flex-row justify-between items-center">
<div className="flex flex-row justify-start items-center dark:text-gray-400">
<Logo className="w-6 h-auto mr-2" />
<span className="">Slash</span>
{isInitialized && (
<>
{shortcuts.length !== 0 ? (
<ShortcutsContainer />
) : (
<div className="w-full flex flex-col justify-center items-center">
<p>No shortcut found.</p>
</div>
)}
<Divider className="!mt-4 !mb-2 opacity-40" />
<div className="w-full flex flex-row justify-between items-center mb-2">
<div className="flex flex-row justify-start items-center">
<IconButton size="sm" variant="plain" color="neutral" onClick={handleSettingButtonClick}>
<Icon.Settings className="w-5 h-auto text-gray-500" />
</IconButton>
<IconButton
size="sm"
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>
</div>
<div className="flex flex-row justify-end items-center">
<a
className="text-sm flex flex-row justify-start items-center text-gray-500 hover:underline hover:text-blue-600"
href={domain}
target="_blank"
>
<span className="mr-1">Go to my Slash</span>
<Icon.ExternalLink className="w-4 h-auto" />
</a>
</div>
</div>
<span className="mx-1 text-gray-400">/</span>
<span>Shortcuts</span>
<span className="text-gray-500 mr-0.5">({shortcuts.length})</span>
<PullShortcutsButton />
</>
) : (
<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" />
<p>Please set your domain and access token first.</p>
<div className="w-full flex flex-row justify-center items-center py-4">
<Button size="sm" color="primary" onClick={handleSettingButtonClick}>
<Icon.Settings className="w-5 h-auto mr-1" /> Setting
</Button>
<span className="mx-2">Or</span>
<Button size="sm" variant="outlined" color="neutral" onClick={handleRefreshButtonClick}>
<Icon.RefreshCcw className="w-5 h-auto mr-1" /> Refresh
</Button>
</div>
</div>
)}
</div>
<div>{isInitialized && <CreateShortcutsButton />}</div>
</div>
<Toaster position="top-right" />
</>
<div className="w-full mt-4">
{isInitialized ? (
<>
{shortcuts.length !== 0 ? (
<ShortcutsContainer />
) : (
<div className="w-full flex flex-col justify-center items-center">
<p>No shortcut found.</p>
</div>
)}
<Divider className="!mt-4 !mb-2 opacity-40" />
<div className="w-full flex flex-row justify-between items-center mb-2">
<div className="flex flex-row justify-start items-center">
<IconButton size="sm" variant="plain" color="neutral" onClick={handleSettingButtonClick}>
<Icon.Settings className="w-5 h-auto text-gray-500 dark:text-gray-400" />
</IconButton>
<IconButton size="sm" variant="plain" color="neutral" component="a" href="https://github.com/boojack/slash" target="_blank">
<Icon.Github className="w-5 h-auto text-gray-500 dark:text-gray-400" />
</IconButton>
</div>
<div className="flex flex-row justify-end items-center">
<a
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}
target="_blank"
>
<span className="mr-1">Go to my Slash</span>
<Icon.ExternalLink className="w-4 h-auto" />
</a>
</div>
</div>
</>
) : (
<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" />
<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">
<Button size="sm" color="primary" onClick={handleSettingButtonClick}>
<Icon.Settings className="w-5 h-auto mr-1" /> Setting
</Button>
<span className="mx-2 dark:text-gray-400">Or</span>
<Button size="sm" variant="outlined" color="neutral" onClick={handleRefreshButtonClick}>
<Icon.RefreshCcw className="w-5 h-auto mr-1" /> Refresh
</Button>
</div>
</div>
)}
</div>
</div>
);
};
export default IndexPopup;
const Popup = () => {
return (
<CssVarsProvider>
<IndexPopup />
<Toaster position="top-right" />
</CssVarsProvider>
);
};
export default Popup;

View File

@ -5,7 +5,7 @@
body,
html,
#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",
"WenQuanYi Micro Hei", sans-serif, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
"Noto Color Emoji";