feat(assets): 文件夹主题 - 新增多主题皮肤并同步主线

This commit is contained in:
echoD886
2026-08-20 16:33:51 +08:00
93 changed files with 4081 additions and 1143 deletions

View File

@@ -105,6 +105,40 @@
margin-top: var(--space-16);
}
.folder-preview-lab-theme-catalog {
display: grid;
max-width: 1280px;
margin: var(--space-16) auto 0;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: var(--space-3);
}
.folder-preview-lab-theme-catalog div {
position: relative;
overflow: hidden;
min-height: 88px;
border-radius: var(--r-2xl);
background: var(--library-surface);
}
.folder-preview-lab-theme-catalog img {
width: 100%;
height: 100%;
min-height: 88px;
object-fit: cover;
}
.folder-preview-lab-theme-catalog span {
position: absolute;
left: var(--space-3);
bottom: var(--space-3);
border-radius: var(--r-full);
background: color-mix(in srgb, var(--color-neutral-950) 72%, transparent);
padding: var(--space-1) var(--space-2);
color: var(--color-neutral-0);
font-size: var(--fs-caption);
}
.folder-preview-lab-section-heading {
display: flex;
margin-bottom: var(--space-6);
@@ -286,6 +320,7 @@
.folder-preview-lab-controls,
.folder-preview-lab-section-heading { align-items: flex-start; flex-direction: column; }
.folder-preview-lab-grid { grid-template-columns: minmax(0, 1fr); }
.folder-preview-lab-theme-catalog { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.folder-preview-lab-folder { transform: scale(.86); transform-origin: top center; margin-bottom: -40px; }
.folder-preview-lab-drawer { inset: var(--space-2); padding: var(--space-4); }
.folder-preview-lab-drawer-header { grid-template-columns: 1fr 1fr; }

View File

@@ -2,8 +2,9 @@ import { useMemo, useState } from "react";
import { ChevronLeft, FolderOpen, ImagePlus, Moon, MousePointer2, Sun } from "lucide-react";
import { CanvasFolderPreview } from "@/components/canvas/canvas-folder-preview";
import { CANVAS_FOLDER_THEME_OPTIONS } from "@/lib/canvas/canvas-folder-theme";
import { useThemeStore } from "@/stores/use-theme-store";
import type { CanvasFolderStyle, CanvasNodeData } from "@/types/canvas";
import type { CanvasFolderStyle, CanvasFolderTheme, CanvasNodeData } from "@/types/canvas";
import { CanvasNodeType } from "@/types/canvas";
import "./folder-preview-lab.css";
@@ -27,12 +28,12 @@ const PRESET_GROUPS = [
{
key: "derived",
title: "风格衍生",
description: "沿用同一主题皮肤与交互合同的工作流变体。",
description: "外形与主题正交组合,六种结构都可切换四套皮肤。",
presets: STYLE_PRESETS.slice(3),
},
];
const FOLDER_THEME_COVER = "/images/canvas/folder-default-cover.png";
const THEME_SEQUENCE: CanvasFolderTheme[] = ["aurora", "pearl", "ember", "obsidian", "aurora", "pearl"];
const SAMPLE_MEDIA = [
"/short-drama-styles/fantasy-3d.jpg",
@@ -63,7 +64,7 @@ function makeFolderNode(style: CanvasFolderStyle, index: number): CanvasNodeData
width: 360,
height: 280,
metadata: {
folder: { style, createdAt: new Date(2026, 7, 19 + index).toISOString(), themeCover: FOLDER_THEME_COVER },
folder: { style, theme: THEME_SEQUENCE[index], createdAt: new Date(2026, 7, 19 + index).toISOString() },
frame: { collapsed: true, expandedWidth: 760, expandedHeight: 520 },
},
};
@@ -115,7 +116,7 @@ export default function FolderPreviewLab() {
<div>
<span className="folder-preview-lab-kicker">DEV · INTERACTIVE FOLDER LAB</span>
<h1>通用素材文件夹</h1>
<p>红蓝抽象图只作为文件夹主题皮肤;打开文件夹后,内部素材以独立内容卡片展示。悬停、单击和双击都有对应反馈。</p>
<p>文件夹外形与主题皮肤独立:四套主题都不是内部素材。打开后,真实内容以独立卡片展示;悬停、单击、双击与菜单均可交互。</p>
</div>
<div className="folder-preview-lab-controls">
<div className="folder-preview-lab-theme" aria-label="预览主题">
@@ -128,7 +129,7 @@ export default function FolderPreviewLab() {
</div>
<div className="folder-preview-lab-guide">
<MousePointer2 aria-hidden />
<span>试试 hover、双击与样式切换</span>
<span>试试 hover、双击、样式与主题切换</span>
</div>
</div>
</header>
@@ -180,6 +181,15 @@ export default function FolderPreviewLab() {
},
}))
}
onThemeChange={(nodeId, nextTheme) =>
updateFolder(nodeId, (current) => ({
...current,
metadata: {
...current.metadata,
folder: { ...current.metadata!.folder!, theme: nextTheme, themeCover: undefined },
},
}))
}
/>
</div>
<button type="button" className="folder-preview-lab-open" onClick={() => setOpenedId(folder.id)}>
@@ -192,6 +202,15 @@ export default function FolderPreviewLab() {
</section>
))}
<section className="folder-preview-lab-theme-catalog" aria-label="可用文件夹主题">
{CANVAS_FOLDER_THEME_OPTIONS.map((item) => (
<div key={item.key}>
<img src={item.cover} alt="" />
<span>{item.label}</span>
</div>
))}
</section>
{openedFolder ? (
<section className="folder-preview-lab-drawer" aria-label={`${openedFolder.title} 内容`}>
<div className="folder-preview-lab-drawer-header">