movie-web/src/pages/parts/settings/ThemePart.tsx

148 lines
5.4 KiB
TypeScript

import classNames from "classnames";
import { useTranslation } from "react-i18next";
import { Icon, Icons } from "@/components/Icon";
import { Heading1 } from "@/components/utils/Text";
const availableThemes = [
{
id: "blue",
key: "settings.appearance.themes.blue",
},
{
id: "teal",
key: "settings.appearance.themes.teal",
},
{
id: "red",
key: "settings.appearance.themes.red",
},
{
id: "gray",
key: "settings.appearance.themes.gray",
},
];
function ThemePreview(props: {
selector?: string;
active?: boolean;
name: string;
onClick?: () => void;
}) {
const { t } = useTranslation();
return (
<div
className={classNames(props.selector, "cursor-pointer group tabbable")}
onClick={props.onClick}
>
{/* Little card thing */}
<div
tabIndex={0}
onKeyUp={(e) => e.key === "Enter" && e.currentTarget.click()}
className={classNames(
"tabbable scroll-mt-32 w-full h-32 relative rounded-lg border bg-gradient-to-br from-themePreview-primary/20 to-themePreview-secondary/10 bg-clip-content transition-colors duration-150",
props.active
? "border-themePreview-primary"
: "border-transparent group-hover:border-white/20",
)}
>
{/* Dots */}
<div className="absolute top-2 left-2">
<div className="h-5 w-5 bg-themePreview-primary rounded-full" />
<div className="h-5 w-5 bg-themePreview-secondary rounded-full -mt-2" />
</div>
{/* Active check */}
<Icon
icon={Icons.CHECKMARK}
className={classNames(
"absolute top-3 right-3 text-xs text-white transition-opacity duration-150",
props.active ? "opacity-100" : "opacity-0",
)}
/>
{/* Mini movie-web. So Kawaiiiii! */}
<div className="absolute bottom-0 left-1/2 transform -translate-x-1/2 w-3/5 h-4/5 rounded-t-lg -mb-px bg-background-main overflow-hidden">
<div className="relative w-full h-full">
{/* Background color */}
<div className="bg-themePreview-primary/50 w-[130%] h-10 absolute left-1/2 -top-5 blur-xl transform -translate-x-1/2 rounded-[100%]" />
{/* Navbar */}
<div className="p-2 flex justify-between items-center">
<div className="flex space-x-1">
<div className="bg-themePreview-ghost bg-opacity-10 w-4 h-2 rounded-full" />
<div className="bg-themePreview-ghost bg-opacity-10 w-2 h-2 rounded-full" />
<div className="bg-themePreview-ghost bg-opacity-10 w-2 h-2 rounded-full" />
</div>
<div className="bg-themePreview-ghost bg-opacity-10 w-2 h-2 rounded-full" />
</div>
{/* Hero */}
<div className="mt-1 flex items-center flex-col gap-1">
{/* Title and subtitle */}
<div className="bg-themePreview-ghost bg-opacity-20 w-8 h-0.5 rounded-full" />
<div className="bg-themePreview-ghost bg-opacity-20 w-6 h-0.5 rounded-full" />
{/* Search bar */}
<div className="bg-themePreview-ghost bg-opacity-10 w-16 h-2 mt-1 rounded-full" />
</div>
{/* Media grid */}
<div className="mt-5 px-3">
{/* Title */}
<div className="flex gap-1 items-center">
<div className="bg-themePreview-ghost bg-opacity-20 w-2 h-2 rounded-full" />
<div className="bg-themePreview-ghost bg-opacity-20 w-8 h-0.5 rounded-full" />
</div>
{/* Blocks */}
<div className="flex w-full gap-1 mt-1">
<div className="bg-themePreview-ghost bg-opacity-10 w-full h-20 rounded" />
<div className="bg-themePreview-ghost bg-opacity-10 w-full h-20 rounded" />
<div className="bg-themePreview-ghost bg-opacity-10 w-full h-20 rounded" />
<div className="bg-themePreview-ghost bg-opacity-10 w-full h-20 rounded" />
</div>
</div>
</div>
</div>
</div>
<div className="mt-2 flex justify-between items-center">
<span className="font-medium text-white">{props.name}</span>
<span
className={classNames(
"inline-block px-3 py-1 leading-tight text-sm transition-opacity duration-150 rounded-full bg-pill-activeBackground text-white/85",
props.active ? "opacity-100" : "opacity-0 pointer-events-none",
)}
>
{t("settings.appearance.activeTheme")}
</span>
</div>
</div>
);
}
export function ThemePart(props: {
active: string | null;
setTheme: (theme: string | null) => void;
}) {
const { t } = useTranslation();
return (
<div>
<Heading1 border>{t("settings.appearance.title")}</Heading1>
<div className="grid grid-cols-[repeat(auto-fill,minmax(160px,1fr))] gap-6 max-w-[700px]">
{/* default theme */}
<ThemePreview
name={t("settings.appearance.themes.default")}
selector="theme-default"
active={props.active === null}
onClick={() => props.setTheme(null)}
/>
{availableThemes.map((v) => (
<ThemePreview
selector={`theme-${v.id}`}
active={props.active === v.id}
name={t(v.key)}
key={v.id}
onClick={() => props.setTheme(v.id)}
/>
))}
</div>
</div>
);
}