import { useEffect, useState, type ReactNode } from "react"; import { Button, ColorPicker, Input, Segmented, Slider } from "antd"; import { CircleDot, Grid2x2, Moon, Paintbrush, RotateCcw, Save, Square, Sun } from "lucide-react"; import { canvasAppearanceForTheme, customCanvasAppearanceFromTheme, enterCustomCanvasAppearance, normalizeHexColor, type CanvasAppearance, type CanvasCustomAppearance, } from "@/lib/canvas/canvas-appearance"; import type { CanvasBackgroundMode, CanvasColorTheme, CanvasTheme } from "@/lib/canvas-theme"; const LIGHT_PRESETS = ["#F0F0F0", "#F3DCE5", "#EEE5D8", "#DFE9E6"]; const DARK_PRESETS = ["#000000", "#30272B", "#302D26", "#25302E"]; export function CanvasAppearanceControls({ appearance, backgroundMode, colorTheme, theme, onAppearanceChange, onSaveAppearanceDefault, onBackgroundModeChange, }: { appearance: CanvasAppearance; backgroundMode: CanvasBackgroundMode; colorTheme: CanvasColorTheme; theme: CanvasTheme; onAppearanceChange: (appearance: CanvasAppearance) => void; onSaveAppearanceDefault: (appearance: CanvasAppearance) => void; onBackgroundModeChange: (mode: CanvasBackgroundMode) => void; }) { const [draft, setDraft] = useState(appearance); useEffect(() => setDraft(appearance), [appearance]); const selectFixedTheme = (target: CanvasColorTheme) => { const next = canvasAppearanceForTheme(target, draft); setDraft(next); onAppearanceChange(next); }; const selectCustomTheme = () => { const next = enterCustomCanvasAppearance(draft, colorTheme); setDraft(next); onAppearanceChange(next); }; const updateCustom = (patch: Partial) => { const current = draft.mode === "custom" && draft.custom ? draft : enterCustomCanvasAppearance(draft, colorTheme); const next: CanvasAppearance = { mode: "custom", custom: { ...current.custom!, ...patch } }; setDraft(next); onAppearanceChange(next); }; const resetCustom = () => { const baseTheme = draft.custom?.baseTheme || colorTheme; const next = customCanvasAppearanceFromTheme(baseTheme); setDraft(next); onAppearanceChange(next); }; const saveAsDefault = () => { onAppearanceChange(draft); onSaveAppearanceDefault(draft); }; const presets = draft.custom?.baseTheme === "dark" ? DARK_PRESETS : LIGHT_PRESETS; return ( <>
主题模式
selectFixedTheme("light")}> selectFixedTheme("dark")}>
{draft.mode === "custom" && draft.custom ? (
背景预设 {draft.custom.baseTheme === "dark" ? "深色" : "浅色"}界面
{presets.map((color) => (
updateCustom({ baseTheme: value as CanvasColorTheme })} options={[ { value: "light", label: 浅色界面 }, { value: "dark", label: 深色界面 }, ]} /> updateCustom({ backgroundColor, backgroundBrightness: 0 })} /> updateCustom({ backgroundBrightness })} /> updateCustom({ gridColor })} /> updateCustom({ gridOpacity })} />
) : null}
空间网格
onBackgroundModeChange(value as CanvasBackgroundMode)} options={[ { value: "dots", label: 点 }, { value: "lines", label: 线 }, { value: "blank", label: 空白 }, ]} /> ); } function ThemeButton({ active, label, theme, onClick, children }: { active: boolean; label: string; theme: CanvasTheme; onClick: () => void; children: ReactNode }) { return ( ); } function ColorField({ label, value, theme, onChange }: { label: string; value: string; theme: CanvasTheme; onChange: (value: string) => void }) { const [textValue, setTextValue] = useState(value); const [editing, setEditing] = useState(false); useEffect(() => { if (!editing) setTextValue(value); }, [editing, value]); const commit = () => { const normalized = normalizeHexColor(textValue); setEditing(false); if (normalized) { setTextValue(normalized); onChange(normalized); } else { setTextValue(value); } }; return ( ); } function SliderField({ label, value, min, max, suffix, onChange }: { label: string; value: number; min: number; max: number; suffix: string; onChange: (value: number) => void }) { return (
{label} {value > 0 && min < 0 ? "+" : ""}{value}{suffix}
); }