feat: better buttons and icons for theme import/export
This commit is contained in:
parent
18b1fe0413
commit
9b89ef58be
|
@ -308,3 +308,21 @@ export function NoEntrySignIcon(props: IconProps) {
|
||||||
</Icon>
|
</Icon>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function PasteIcon(props: IconProps) {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
{...props}
|
||||||
|
className={classes(props.className, "vc-paste-icon")}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<rect width="8" height="4" x="8" y="2" rx="1" ry="1" />
|
||||||
|
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" />
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -4,11 +4,12 @@
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useSettings } from "@api/Settings";
|
import { Settings, useSettings } from "@api/Settings";
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
|
import { CopyIcon, PasteIcon } from "@components/Icons";
|
||||||
import { copyWithToast } from "@utils/misc";
|
import { copyWithToast } from "@utils/misc";
|
||||||
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
|
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
|
||||||
import { Button, showToast, Text, Toasts } from "@webpack/common";
|
import { showToast, Text, Toasts, Tooltip } from "@webpack/common";
|
||||||
import { type ReactNode } from "react";
|
import { type ReactNode } from "react";
|
||||||
import { UserstyleHeader } from "usercss-meta";
|
import { UserstyleHeader } from "usercss-meta";
|
||||||
|
|
||||||
|
@ -19,6 +20,59 @@ interface UserCSSSettingsModalProps {
|
||||||
theme: UserstyleHeader;
|
theme: UserstyleHeader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ExportButton({ themeSettings }: { themeSettings: Settings["userCssVars"][""]; }) {
|
||||||
|
return <Tooltip text={"Copy theme settings"}>
|
||||||
|
{({ onMouseLeave, onMouseEnter }) => (
|
||||||
|
<div
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
onMouseEnter={onMouseEnter}
|
||||||
|
onMouseLeave={onMouseLeave}
|
||||||
|
|
||||||
|
onClick={() => {
|
||||||
|
copyWithToast(JSON.stringify(themeSettings), "Copied theme settings to clipboard.");
|
||||||
|
}}>
|
||||||
|
<CopyIcon />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Tooltip>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ImportButton({ themeSettings }: { themeSettings: Settings["userCssVars"][""]; }) {
|
||||||
|
return <Tooltip text={"Paste theme settings"}>
|
||||||
|
{({ onMouseLeave, onMouseEnter }) => (
|
||||||
|
<div
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
onMouseEnter={onMouseEnter}
|
||||||
|
onMouseLeave={onMouseLeave}
|
||||||
|
|
||||||
|
onClick={async () => {
|
||||||
|
const clip = (await navigator.clipboard.read())[0];
|
||||||
|
|
||||||
|
if (!clip) return showToast("Your clipboard is empty.", Toasts.Type.FAILURE);
|
||||||
|
|
||||||
|
if (!clip.types.includes("text/plain"))
|
||||||
|
return showToast("Your clipboard doesn't have valid settings data.", Toasts.Type.FAILURE);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var potentialSettings: Record<string, string> =
|
||||||
|
JSON.parse(await clip.getType("text/plain").then(b => b.text()));
|
||||||
|
} catch (e) {
|
||||||
|
return showToast("Your clipboard doesn't have valid settings data.", Toasts.Type.FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(potentialSettings)) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(themeSettings, key))
|
||||||
|
themeSettings[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
showToast("Pasted theme settings from clipboard.", Toasts.Type.SUCCESS);
|
||||||
|
}}>
|
||||||
|
<PasteIcon />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Tooltip>;
|
||||||
|
}
|
||||||
|
|
||||||
export function UserCSSSettingsModal({ modalProps, theme }: UserCSSSettingsModalProps) {
|
export function UserCSSSettingsModal({ modalProps, theme }: UserCSSSettingsModalProps) {
|
||||||
// @ts-expect-error UseSettings<> can't determine this is a valid key
|
// @ts-expect-error UseSettings<> can't determine this is a valid key
|
||||||
const themeSettings = useSettings(["userCssVars"], false).userCssVars[theme.id];
|
const themeSettings = useSettings(["userCssVars"], false).userCssVars[theme.id];
|
||||||
|
@ -105,37 +159,14 @@ export function UserCSSSettingsModal({ modalProps, theme }: UserCSSSettingsModal
|
||||||
<ModalRoot {...modalProps}>
|
<ModalRoot {...modalProps}>
|
||||||
<ModalHeader separator={false}>
|
<ModalHeader separator={false}>
|
||||||
<Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>Settings for {theme.name}</Text>
|
<Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>Settings for {theme.name}</Text>
|
||||||
|
<Flex style={{ gap: 4, marginRight: 4 }} className="vc-settings-usercss-ie-buttons">
|
||||||
|
<ExportButton themeSettings={themeSettings} />
|
||||||
|
<ImportButton themeSettings={themeSettings} />
|
||||||
|
</Flex>
|
||||||
<ModalCloseButton onClick={modalProps.onClose} />
|
<ModalCloseButton onClick={modalProps.onClose} />
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<Flex flexDirection="column" style={{ gap: 12, marginBottom: 16 }}>
|
<Flex flexDirection="column" style={{ gap: 12, marginBottom: 16 }}>
|
||||||
<div className="vc-settings-usercss-ie-grid">
|
|
||||||
<Button size={Button.Sizes.SMALL} onClick={() => {
|
|
||||||
copyWithToast(JSON.stringify(themeSettings), "Copied theme settings to clipboard.");
|
|
||||||
}}>Export</Button>
|
|
||||||
<Button size={Button.Sizes.SMALL} onClick={async () => {
|
|
||||||
const clip = (await navigator.clipboard.read())[0];
|
|
||||||
|
|
||||||
if (!clip) return showToast("Your clipboard is empty.", Toasts.Type.FAILURE);
|
|
||||||
|
|
||||||
if (!clip.types.includes("text/plain"))
|
|
||||||
return showToast("Your clipboard doesn't have valid settings data.", Toasts.Type.FAILURE);
|
|
||||||
|
|
||||||
try {
|
|
||||||
var potentialSettings: Record<string, string> =
|
|
||||||
JSON.parse(await clip.getType("text/plain").then(b => b.text()));
|
|
||||||
} catch (e) {
|
|
||||||
return showToast("Your clipboard doesn't have valid settings data.", Toasts.Type.FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(potentialSettings)) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(themeSettings, key))
|
|
||||||
themeSettings[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
showToast("Imported theme settings from clipboard.", Toasts.Type.SUCCESS);
|
|
||||||
}}>Import</Button>
|
|
||||||
</div>
|
|
||||||
{controls}
|
{controls}
|
||||||
</Flex>
|
</Flex>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
|
@ -28,8 +28,13 @@
|
||||||
content: "by ";
|
content: "by ";
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-settings-usercss-ie-grid {
|
.vc-settings-usercss-ie-buttons > div {
|
||||||
display: grid;
|
color: var(--interactive-normal);
|
||||||
grid-template-columns: repeat(2, 1fr);
|
opacity: .5;
|
||||||
grid-gap: 12px; /* matching the flex gap */
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-settings-usercss-ie-buttons > div:hover {
|
||||||
|
color: var(--interactive-hover);
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue