style(code): 格式化代码缩进和布局样式
- 统一调整 admin.ts 文件中的接口定义缩进格式 - 优化 animated-theme-toggler.tsx 组件的代码结构和缩进 - 规范 app-config-modal.tsx 中 JSX 元素的嵌套格式 - 整理 app-providers.tsx 中的组件层级缩进 - 标准化 app-theme.ts 中的对象属性缩进格式
This commit is contained in:
@@ -8,259 +8,273 @@ import { useThemeStore } from "@/stores/use-theme-store";
|
||||
import { AnimatedThemeToggler } from "@/components/ui/animated-theme-toggler";
|
||||
|
||||
export function CanvasToolbar({
|
||||
selectedCount,
|
||||
canUndo,
|
||||
canRedo,
|
||||
backgroundMode,
|
||||
onAddImage,
|
||||
onAddText,
|
||||
onAddConfig,
|
||||
onUndo,
|
||||
onRedo,
|
||||
onUpload,
|
||||
onDelete,
|
||||
onClear,
|
||||
onDeselect,
|
||||
onBackgroundModeChange,
|
||||
onOpenAssetLibrary,
|
||||
onOpenMyAssets,
|
||||
selectedCount,
|
||||
canUndo,
|
||||
canRedo,
|
||||
backgroundMode,
|
||||
onAddImage,
|
||||
onAddText,
|
||||
onAddConfig,
|
||||
onUndo,
|
||||
onRedo,
|
||||
onUpload,
|
||||
onDelete,
|
||||
onClear,
|
||||
onDeselect,
|
||||
onBackgroundModeChange,
|
||||
onOpenAssetLibrary,
|
||||
onOpenMyAssets,
|
||||
}: {
|
||||
selectedCount: number;
|
||||
canUndo: boolean;
|
||||
canRedo: boolean;
|
||||
backgroundMode: CanvasBackgroundMode;
|
||||
onAddImage: () => void;
|
||||
onAddText: () => void;
|
||||
onAddConfig: () => void;
|
||||
onUndo: () => void;
|
||||
onRedo: () => void;
|
||||
onUpload: () => void;
|
||||
onDelete: () => void;
|
||||
onClear: () => void;
|
||||
onDeselect: () => void;
|
||||
onBackgroundModeChange: (mode: CanvasBackgroundMode) => void;
|
||||
onOpenAssetLibrary: () => void;
|
||||
onOpenMyAssets: () => void;
|
||||
selectedCount: number;
|
||||
canUndo: boolean;
|
||||
canRedo: boolean;
|
||||
backgroundMode: CanvasBackgroundMode;
|
||||
onAddImage: () => void;
|
||||
onAddText: () => void;
|
||||
onAddConfig: () => void;
|
||||
onUndo: () => void;
|
||||
onRedo: () => void;
|
||||
onUpload: () => void;
|
||||
onDelete: () => void;
|
||||
onClear: () => void;
|
||||
onDeselect: () => void;
|
||||
onBackgroundModeChange: (mode: CanvasBackgroundMode) => void;
|
||||
onOpenAssetLibrary: () => void;
|
||||
onOpenMyAssets: () => void;
|
||||
}) {
|
||||
const wrapRef = useRef<HTMLDivElement>(null);
|
||||
const colorTheme = useThemeStore((state) => state.theme);
|
||||
const setTheme = useThemeStore((state) => state.setTheme);
|
||||
const theme = canvasThemes[colorTheme];
|
||||
const [hovered, setHovered] = useState<string | null>(null);
|
||||
const [tipX, setTipX] = useState(0);
|
||||
const [appearanceOpen, setAppearanceOpen] = useState(false);
|
||||
const [panelX, setPanelX] = useState(0);
|
||||
const dockStyle = { background: theme.toolbar.panel, borderColor: theme.toolbar.border, color: theme.toolbar.item, boxShadow: colorTheme === "dark" ? "0 18px 45px rgba(0,0,0,.32)" : "0 16px 40px rgba(28,25,23,.12)" };
|
||||
const hoverStyle = { background: theme.toolbar.itemHover, color: theme.toolbar.activeText };
|
||||
const activeStyle = { background: theme.toolbar.activeBg, color: theme.toolbar.activeText };
|
||||
const tip = hovered ? toolLabel(hovered) : "";
|
||||
const wrapRef = useRef<HTMLDivElement>(null);
|
||||
const colorTheme = useThemeStore((state) => state.theme);
|
||||
const setTheme = useThemeStore((state) => state.setTheme);
|
||||
const theme = canvasThemes[colorTheme];
|
||||
const [hovered, setHovered] = useState<string | null>(null);
|
||||
const [tipX, setTipX] = useState(0);
|
||||
const [appearanceOpen, setAppearanceOpen] = useState(false);
|
||||
const [panelX, setPanelX] = useState(0);
|
||||
const dockStyle = { background: theme.toolbar.panel, borderColor: theme.toolbar.border, color: theme.toolbar.item, boxShadow: colorTheme === "dark" ? "0 18px 45px rgba(0,0,0,.32)" : "0 16px 40px rgba(28,25,23,.12)" };
|
||||
const hoverStyle = { background: theme.toolbar.itemHover, color: theme.toolbar.activeText };
|
||||
const activeStyle = { background: theme.toolbar.activeBg, color: theme.toolbar.activeText };
|
||||
const tip = hovered ? toolLabel(hovered) : "";
|
||||
|
||||
return (
|
||||
<div className="pointer-events-none absolute bottom-5 z-50 flex justify-center" style={{ left: 300, right: 16 }}>
|
||||
{tip ? <DockTip label={tip} x={tipX} theme={theme} /> : null}
|
||||
<div
|
||||
ref={wrapRef}
|
||||
className="thin-scrollbar pointer-events-auto flex h-14 max-w-full items-center gap-1 overflow-x-auto rounded-xl border px-2 shadow-lg backdrop-blur [&>*]:shrink-0"
|
||||
style={dockStyle}
|
||||
>
|
||||
<ToolbarButton id="tool-hand" label="移动/选择" active={!selectedCount} hovered={hovered} activeStyle={activeStyle} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onDeselect}>
|
||||
<Hand className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="tool-undo" label="撤销" disabled={!canUndo} hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onUndo}>
|
||||
<Undo2 className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="tool-redo" label="重做" disabled={!canRedo} hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onRedo}>
|
||||
<Redo2 className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<Divider theme={theme} />
|
||||
<ToolbarButton id="tool-text" label="文本" hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onAddText}>
|
||||
<Type className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="tool-image" label="图片" hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onAddImage}>
|
||||
<ImageIcon className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="tool-config" label="生成配置" hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onAddConfig}>
|
||||
<Settings2 className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="tool-upload" label="上传图片" hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onUpload}>
|
||||
<Upload className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<Divider theme={theme} />
|
||||
<ToolbarButton id="tool-library" label="素材库" hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onOpenAssetLibrary}>
|
||||
<Library className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="tool-assets" label="我的素材" hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onOpenMyAssets}>
|
||||
<FolderOpen className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
id="tool-style"
|
||||
label="画布外观"
|
||||
active={appearanceOpen}
|
||||
hovered={hovered}
|
||||
activeStyle={activeStyle}
|
||||
hoverStyle={hoverStyle}
|
||||
wrapRef={wrapRef}
|
||||
onTipX={setTipX}
|
||||
onHover={setHovered}
|
||||
onClick={(event) => {
|
||||
setPanelX(getTipX(wrapRef.current, event.currentTarget));
|
||||
setAppearanceOpen((value) => !value);
|
||||
}}
|
||||
>
|
||||
<Palette className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
{selectedCount ? (
|
||||
<>
|
||||
<Divider theme={theme} />
|
||||
<ToolbarButton id="tool-delete" label="删除选中" hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onDelete} danger>
|
||||
<Trash2 className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
</>
|
||||
) : null}
|
||||
<Divider theme={theme} />
|
||||
<ToolbarButton id="tool-clear" label="清空画布" hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onClear} danger>
|
||||
<Eraser className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
return (
|
||||
<div className="pointer-events-none absolute bottom-5 z-50 flex justify-center" style={{ left: 300, right: 16 }}>
|
||||
{tip ? <DockTip label={tip} x={tipX} theme={theme} /> : null}
|
||||
<div ref={wrapRef} className="thin-scrollbar pointer-events-auto flex h-14 max-w-full items-center gap-1 overflow-x-auto rounded-xl border px-2 shadow-lg backdrop-blur [&>*]:shrink-0" style={dockStyle}>
|
||||
<ToolbarButton id="tool-hand" label="移动/选择" active={!selectedCount} hovered={hovered} activeStyle={activeStyle} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onDeselect}>
|
||||
<Hand className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="tool-undo" label="撤销" disabled={!canUndo} hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onUndo}>
|
||||
<Undo2 className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="tool-redo" label="重做" disabled={!canRedo} hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onRedo}>
|
||||
<Redo2 className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<Divider theme={theme} />
|
||||
<ToolbarButton id="tool-text" label="文本" hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onAddText}>
|
||||
<Type className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="tool-image" label="图片" hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onAddImage}>
|
||||
<ImageIcon className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="tool-config" label="生成配置" hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onAddConfig}>
|
||||
<Settings2 className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="tool-upload" label="上传图片" hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onUpload}>
|
||||
<Upload className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<Divider theme={theme} />
|
||||
<ToolbarButton id="tool-library" label="素材库" hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onOpenAssetLibrary}>
|
||||
<Library className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="tool-assets" label="我的素材" hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onOpenMyAssets}>
|
||||
<FolderOpen className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
id="tool-style"
|
||||
label="画布外观"
|
||||
active={appearanceOpen}
|
||||
hovered={hovered}
|
||||
activeStyle={activeStyle}
|
||||
hoverStyle={hoverStyle}
|
||||
wrapRef={wrapRef}
|
||||
onTipX={setTipX}
|
||||
onHover={setHovered}
|
||||
onClick={(event) => {
|
||||
setPanelX(getTipX(wrapRef.current, event.currentTarget));
|
||||
setAppearanceOpen((value) => !value);
|
||||
}}
|
||||
>
|
||||
<Palette className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
{selectedCount ? (
|
||||
<>
|
||||
<Divider theme={theme} />
|
||||
<ToolbarButton id="tool-delete" label="删除选中" hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onDelete} danger>
|
||||
<Trash2 className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
</>
|
||||
) : null}
|
||||
<Divider theme={theme} />
|
||||
<ToolbarButton id="tool-clear" label="清空画布" hovered={hovered} hoverStyle={hoverStyle} wrapRef={wrapRef} onTipX={setTipX} onHover={setHovered} onClick={onClear} danger>
|
||||
<Eraser className="size-4.5" />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
|
||||
{appearanceOpen ? (
|
||||
<div
|
||||
className="pointer-events-auto absolute bottom-[72px] z-30 w-[248px] -translate-x-1/2 rounded-xl border p-2.5 shadow-xl backdrop-blur"
|
||||
style={{ left: panelX || "50%", background: theme.toolbar.panel, borderColor: theme.toolbar.border, color: theme.toolbar.item }}
|
||||
>
|
||||
<div className="px-1 pb-2 text-sm font-medium opacity-65">画布外观</div>
|
||||
<div className="px-1 pb-1.5 text-[11px] font-medium opacity-50">主题模式</div>
|
||||
<div className="grid grid-cols-2 gap-1 rounded-lg p-1" style={{ background: theme.toolbar.itemHover }}>
|
||||
<CanvasThemeButton colorTheme={colorTheme} targetTheme="light" onThemeChange={setTheme}>
|
||||
<Sun className="size-4" />
|
||||
浅色
|
||||
</CanvasThemeButton>
|
||||
<CanvasThemeButton colorTheme={colorTheme} targetTheme="dark" onThemeChange={setTheme}>
|
||||
<Moon className="size-4" />
|
||||
深色
|
||||
</CanvasThemeButton>
|
||||
</div>
|
||||
<div className="mt-3 px-1 pb-1.5 text-[11px] font-medium opacity-50">网格样式</div>
|
||||
<Segmented
|
||||
className="w-full !p-1 [&_.ant-segmented-group]:!flex [&_.ant-segmented-item]:!min-h-8 [&_.ant-segmented-item]:!flex-1 [&_.ant-segmented-item-label]:!min-h-8 [&_.ant-segmented-item-label]:!leading-8"
|
||||
value={backgroundMode}
|
||||
onChange={(value) => onBackgroundModeChange(value as CanvasBackgroundMode)}
|
||||
options={[
|
||||
{ value: "dots", label: <span className="inline-flex items-center gap-1.5"><CircleDot className="size-4" />点</span> },
|
||||
{ value: "lines", label: <span className="inline-flex items-center gap-1.5"><Grid2x2 className="size-4" />线</span> },
|
||||
{ value: "blank", label: <span className="inline-flex items-center gap-1.5"><Square className="size-4" />空白</span> },
|
||||
]}
|
||||
/>
|
||||
{appearanceOpen ? (
|
||||
<div
|
||||
className="pointer-events-auto absolute bottom-[72px] z-30 w-[248px] -translate-x-1/2 rounded-xl border p-2.5 shadow-xl backdrop-blur"
|
||||
style={{ left: panelX || "50%", background: theme.toolbar.panel, borderColor: theme.toolbar.border, color: theme.toolbar.item }}
|
||||
>
|
||||
<div className="px-1 pb-2 text-sm font-medium opacity-65">画布外观</div>
|
||||
<div className="px-1 pb-1.5 text-[11px] font-medium opacity-50">主题模式</div>
|
||||
<div className="grid grid-cols-2 gap-1 rounded-lg p-1" style={{ background: theme.toolbar.itemHover }}>
|
||||
<CanvasThemeButton colorTheme={colorTheme} targetTheme="light" onThemeChange={setTheme}>
|
||||
<Sun className="size-4" />
|
||||
浅色
|
||||
</CanvasThemeButton>
|
||||
<CanvasThemeButton colorTheme={colorTheme} targetTheme="dark" onThemeChange={setTheme}>
|
||||
<Moon className="size-4" />
|
||||
深色
|
||||
</CanvasThemeButton>
|
||||
</div>
|
||||
<div className="mt-3 px-1 pb-1.5 text-[11px] font-medium opacity-50">网格样式</div>
|
||||
<Segmented
|
||||
className="w-full !p-1 [&_.ant-segmented-group]:!flex [&_.ant-segmented-item]:!min-h-8 [&_.ant-segmented-item]:!flex-1 [&_.ant-segmented-item-label]:!min-h-8 [&_.ant-segmented-item-label]:!leading-8"
|
||||
value={backgroundMode}
|
||||
onChange={(value) => onBackgroundModeChange(value as CanvasBackgroundMode)}
|
||||
options={[
|
||||
{
|
||||
value: "dots",
|
||||
label: (
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<CircleDot className="size-4" />点
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: "lines",
|
||||
label: (
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<Grid2x2 className="size-4" />线
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: "blank",
|
||||
label: (
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<Square className="size-4" />
|
||||
空白
|
||||
</span>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
function ToolbarButton({
|
||||
id,
|
||||
label,
|
||||
active,
|
||||
hovered,
|
||||
activeStyle,
|
||||
hoverStyle,
|
||||
wrapRef,
|
||||
onTipX,
|
||||
onHover,
|
||||
onClick,
|
||||
disabled = false,
|
||||
danger = false,
|
||||
children,
|
||||
id,
|
||||
label,
|
||||
active,
|
||||
hovered,
|
||||
activeStyle,
|
||||
hoverStyle,
|
||||
wrapRef,
|
||||
onTipX,
|
||||
onHover,
|
||||
onClick,
|
||||
disabled = false,
|
||||
danger = false,
|
||||
children,
|
||||
}: {
|
||||
id: string;
|
||||
label: string;
|
||||
active?: boolean;
|
||||
hovered: string | null;
|
||||
activeStyle?: CSSProperties;
|
||||
hoverStyle: CSSProperties;
|
||||
wrapRef: RefObject<HTMLDivElement | null>;
|
||||
onTipX: (x: number) => void;
|
||||
onHover: (id: string | null) => void;
|
||||
onClick?: (event: ReactMouseEvent<HTMLElement>) => void;
|
||||
disabled?: boolean;
|
||||
danger?: boolean;
|
||||
children: ReactNode;
|
||||
id: string;
|
||||
label: string;
|
||||
active?: boolean;
|
||||
hovered: string | null;
|
||||
activeStyle?: CSSProperties;
|
||||
hoverStyle: CSSProperties;
|
||||
wrapRef: RefObject<HTMLDivElement | null>;
|
||||
onTipX: (x: number) => void;
|
||||
onHover: (id: string | null) => void;
|
||||
onClick?: (event: ReactMouseEvent<HTMLElement>) => void;
|
||||
disabled?: boolean;
|
||||
danger?: boolean;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const theme = canvasThemes[useThemeStore((state) => state.theme)];
|
||||
const theme = canvasThemes[useThemeStore((state) => state.theme)];
|
||||
|
||||
return (
|
||||
<Button
|
||||
type="text"
|
||||
aria-label={label}
|
||||
className="!h-8 !w-8 !min-w-8 !p-0"
|
||||
disabled={disabled}
|
||||
style={active ? activeStyle : hovered === id && !disabled ? hoverStyle : { color: danger ? "#f87171" : theme.toolbar.item, opacity: disabled ? 0.35 : 1 }}
|
||||
icon={children}
|
||||
onMouseEnter={(event) => { onHover(id); onTipX(getTipX(wrapRef.current, event.currentTarget)); }}
|
||||
onMouseLeave={() => onHover(null)}
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<Button
|
||||
type="text"
|
||||
aria-label={label}
|
||||
className="!h-8 !w-8 !min-w-8 !p-0"
|
||||
disabled={disabled}
|
||||
style={active ? activeStyle : hovered === id && !disabled ? hoverStyle : { color: danger ? "#f87171" : theme.toolbar.item, opacity: disabled ? 0.35 : 1 }}
|
||||
icon={children}
|
||||
onMouseEnter={(event) => {
|
||||
onHover(id);
|
||||
onTipX(getTipX(wrapRef.current, event.currentTarget));
|
||||
}}
|
||||
onMouseLeave={() => onHover(null)}
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function Divider({ theme }: { theme: CanvasTheme }) {
|
||||
return <div className="mx-1 h-6 w-px" style={{ background: theme.toolbar.border }} />;
|
||||
return <div className="mx-1 h-6 w-px" style={{ background: theme.toolbar.border }} />;
|
||||
}
|
||||
|
||||
function CanvasThemeButton({
|
||||
colorTheme,
|
||||
targetTheme,
|
||||
onThemeChange,
|
||||
children,
|
||||
}: {
|
||||
colorTheme: CanvasColorTheme;
|
||||
targetTheme: CanvasColorTheme;
|
||||
onThemeChange: (theme: CanvasColorTheme) => void;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const theme = canvasThemes[colorTheme];
|
||||
const active = colorTheme === targetTheme;
|
||||
const activeStyle = colorTheme === "light" ? { background: "#111111", color: "#ffffff" } : { background: theme.toolbar.activeBg, color: theme.toolbar.activeText };
|
||||
function CanvasThemeButton({ colorTheme, targetTheme, onThemeChange, children }: { colorTheme: CanvasColorTheme; targetTheme: CanvasColorTheme; onThemeChange: (theme: CanvasColorTheme) => void; children: ReactNode }) {
|
||||
const theme = canvasThemes[colorTheme];
|
||||
const active = colorTheme === targetTheme;
|
||||
const activeStyle = colorTheme === "light" ? { background: "#111111", color: "#ffffff" } : { background: theme.toolbar.activeBg, color: theme.toolbar.activeText };
|
||||
|
||||
return (
|
||||
<AnimatedThemeToggler
|
||||
theme={colorTheme}
|
||||
targetTheme={targetTheme}
|
||||
onThemeChange={onThemeChange}
|
||||
className="inline-flex h-8 min-w-0 items-center justify-center gap-1.5 rounded-md px-2 text-sm transition"
|
||||
style={active ? activeStyle : { color: theme.toolbar.item }}
|
||||
aria-label={`切换到${targetTheme === "dark" ? "深色" : "浅色"}主题`}
|
||||
title={`切换到${targetTheme === "dark" ? "深色" : "浅色"}主题`}
|
||||
>
|
||||
{children}
|
||||
</AnimatedThemeToggler>
|
||||
);
|
||||
return (
|
||||
<AnimatedThemeToggler
|
||||
theme={colorTheme}
|
||||
targetTheme={targetTheme}
|
||||
onThemeChange={onThemeChange}
|
||||
className="inline-flex h-8 min-w-0 items-center justify-center gap-1.5 rounded-md px-2 text-sm transition"
|
||||
style={active ? activeStyle : { color: theme.toolbar.item }}
|
||||
aria-label={`切换到${targetTheme === "dark" ? "深色" : "浅色"}主题`}
|
||||
title={`切换到${targetTheme === "dark" ? "深色" : "浅色"}主题`}
|
||||
>
|
||||
{children}
|
||||
</AnimatedThemeToggler>
|
||||
);
|
||||
}
|
||||
|
||||
function DockTip({ label, x, theme }: { label: string; x: number; theme: CanvasTheme }) {
|
||||
return <span className="absolute bottom-[calc(100%+8px)] -translate-x-1/2 rounded-md px-2 py-1 text-xs shadow-lg" style={{ left: x, background: theme.node.text, color: theme.node.panel }}>{label}</span>;
|
||||
return (
|
||||
<span className="absolute bottom-[calc(100%+8px)] -translate-x-1/2 rounded-md px-2 py-1 text-xs shadow-lg" style={{ left: x, background: theme.node.text, color: theme.node.panel }}>
|
||||
{label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function toolLabel(id: string) {
|
||||
if (id === "tool-hand") return "移动/选择";
|
||||
if (id === "tool-undo") return "撤销";
|
||||
if (id === "tool-redo") return "重做";
|
||||
if (id === "tool-text") return "文本";
|
||||
if (id === "tool-image") return "图片";
|
||||
if (id === "tool-config") return "生成配置";
|
||||
if (id === "tool-upload") return "上传图片";
|
||||
if (id === "tool-library") return "素材库";
|
||||
if (id === "tool-assets") return "我的素材";
|
||||
if (id === "tool-style") return "画布外观";
|
||||
if (id === "tool-delete") return "删除选中";
|
||||
if (id === "tool-clear") return "清空画布";
|
||||
return "";
|
||||
if (id === "tool-hand") return "移动/选择";
|
||||
if (id === "tool-undo") return "撤销";
|
||||
if (id === "tool-redo") return "重做";
|
||||
if (id === "tool-text") return "文本";
|
||||
if (id === "tool-image") return "图片";
|
||||
if (id === "tool-config") return "生成配置";
|
||||
if (id === "tool-upload") return "上传图片";
|
||||
if (id === "tool-library") return "素材库";
|
||||
if (id === "tool-assets") return "我的素材";
|
||||
if (id === "tool-style") return "画布外观";
|
||||
if (id === "tool-delete") return "删除选中";
|
||||
if (id === "tool-clear") return "清空画布";
|
||||
return "";
|
||||
}
|
||||
|
||||
function getTipX(wrap: HTMLDivElement | null, target: HTMLElement) {
|
||||
if (!wrap) return 0;
|
||||
const wrapBox = wrap.parentElement?.getBoundingClientRect() || wrap.getBoundingClientRect();
|
||||
const box = target.getBoundingClientRect();
|
||||
return box.left - wrapBox.left + box.width / 2;
|
||||
if (!wrap) return 0;
|
||||
const wrapBox = wrap.parentElement?.getBoundingClientRect() || wrap.getBoundingClientRect();
|
||||
const box = target.getBoundingClientRect();
|
||||
return box.left - wrapBox.left + box.width / 2;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user