fix: Computing unsaved changes. now based on matching current preset with styles

This commit is contained in:
Sahaj Jain
2025-05-04 17:48:40 +05:30
parent 8d4557b6e4
commit faf92a12df
3 changed files with 33 additions and 37 deletions
+21 -20
View File
@@ -1,4 +1,4 @@
import { useState } from "react";
import { useMemo, useState } from "react";
import { Button } from "@/components/ui/button";
import { Copy, Check, Heart } from "lucide-react";
import { ThemeEditorState } from "@/types/editor";
@@ -38,9 +38,7 @@ const CodePanel: React.FC<CodePanelProps> = ({ themeEditorState }) => {
const setPackageManager = usePreferencesStore(
(state) => state.setPackageManager
);
const hasUnsavedChanges = useEditorStore(
(state) => state.hasThemeChangedFromCheckpoint
);
const hasUnsavedChanges = useEditorStore((state) => state.hasUnsavedChanges);
const isSavedPreset = useThemePresetStore(
(state) => preset && state.getPreset(preset)?.source === "SAVED"
@@ -101,27 +99,17 @@ const CodePanel: React.FC<CodePanelProps> = ({ themeEditorState }) => {
}
};
const showRegistryCommand = useMemo(() => {
return preset && preset !== "default" && !hasUnsavedChanges();
}, [preset, hasUnsavedChanges]);
return (
<div className="h-full flex flex-col">
<div className="flex-none mb-4">
<div className="flex items-center justify-between gap-2">
<h2 className="text-lg font-semibold">Theme Code</h2>
</div>
{preset && preset !== "default" && hasUnsavedChanges() && (
<Alert className="mt-4">
<AlertTitle>You have unsaved changes.</AlertTitle>
<AlertDescription className="flex flex-col gap-2 mt-2">
<div className="flex items-center gap-1">
<div className="flex items-center gap-1 px-2 py-0.5 border rounded-md">
<Heart className="size-3.5" />
<span>Save</span>
</div>
your theme to get the registry command.
</div>
</AlertDescription>
</Alert>
)}
{preset && preset !== "default" && !hasUnsavedChanges() && (
{showRegistryCommand ? (
<div className="mt-4 rounded-md overflow-hidden border">
<div className="flex border-b">
{(["pnpm", "npm", "yarn", "bun"] as const).map((pm) => (
@@ -158,13 +146,26 @@ const CodePanel: React.FC<CodePanelProps> = ({ themeEditorState }) => {
<ScrollArea className="w-full">
<div className="whitespace-nowrap overflow-y-hidden pb-2">
<code className="text-sm font-mono">
{getRegistryCommand(preset)}
{getRegistryCommand(preset as string)}
</code>
</div>
<ScrollBar orientation="horizontal" />
</ScrollArea>
</div>
</div>
) : (
<Alert className="mt-4">
<AlertTitle>You have unsaved changes.</AlertTitle>
<AlertDescription className="flex flex-col gap-2 mt-2">
<div className="flex items-center gap-1">
<div className="flex items-center gap-1 px-2 py-0.5 border rounded-md">
<Heart className="size-3.5" />
<span>Save</span>
</div>
your theme to get the registry command.
</div>
</AlertDescription>
</Alert>
)}
</div>
<div className="flex items-center gap-2 mb-4 ">
+4 -17
View File
@@ -36,7 +36,7 @@ import {
} from "../ui/tooltip";
import { Input } from "../ui/input";
import { Badge } from "../ui/badge";
import { cn, isDeepEqual } from "@/lib/utils";
import { cn } from "@/lib/utils";
interface ThemePresetSelectProps {
presets: Record<string, ThemePreset>;
@@ -168,7 +168,7 @@ const ThemePresetSelect: React.FC<ThemePresetSelectProps> = ({
currentPreset,
onPresetChange,
}) => {
const { themeState, hasThemeChangedFromCheckpoint } = useEditorStore();
const { themeState, hasUnsavedChanges } = useEditorStore();
const { theme, toggleTheme } = useTheme();
const mode = themeState.currentMode;
const [search, setSearch] = useState("");
@@ -256,19 +256,6 @@ const ThemePresetSelect: React.FC<ThemePresetSelectProps> = ({
);
}, [filteredPresets, isSavedTheme]);
const hasUnsavedChanges = useMemo(() => {
return (
hasThemeChangedFromCheckpoint() ||
(!currentPreset &&
!isDeepEqual(themeState.styles, presets["default"]?.styles))
);
}, [
hasThemeChangedFromCheckpoint,
currentPreset,
themeState.styles,
presets,
]);
return (
<div className="flex items-center">
<TooltipProvider>
@@ -290,7 +277,7 @@ const ThemePresetSelect: React.FC<ThemePresetSelectProps> = ({
{value !== "default" &&
value &&
isSavedTheme(value) &&
!hasUnsavedChanges && (
!hasUnsavedChanges() && (
<div className="rounded-full bg-muted p-1">
<Heart
className="size-1"
@@ -300,7 +287,7 @@ const ThemePresetSelect: React.FC<ThemePresetSelectProps> = ({
</div>
)}
<span className="capitalize font-medium">
{hasUnsavedChanges ? (
{hasUnsavedChanges() ? (
<>Custom (Unsaved)</>
) : (
presets[value || "default"]?.label || "default"
+8
View File
@@ -13,6 +13,7 @@ interface EditorStore {
saveThemeCheckpoint: () => void;
restoreThemeCheckpoint: () => void;
hasThemeChangedFromCheckpoint: () => boolean;
hasUnsavedChanges: () => boolean;
}
export const useEditorStore = create<EditorStore>()(
@@ -53,6 +54,13 @@ export const useEditorStore = create<EditorStore>()(
const checkpoint = get().themeCheckpoint;
return !isDeepEqual(get().themeState, checkpoint);
},
hasUnsavedChanges: () => {
const themeState = get().themeState;
const presetThemeStyles = getPresetThemeStyles(
themeState.preset ?? "default"
);
return !isDeepEqual(themeState.styles, presetThemeStyles);
},
}),
{
name: "editor-storage",