feat(assets): 素材库与画布 - 新增可交互通用文件夹
This commit is contained in:
1 parent
9ee71d4650
commit
935108edca
35 files changed
+2010
-155
No files matched your search
@@ -4,15 +4,29 @@ export const FRAME_HEADER_HEIGHT = 36;
|
||||
export const FRAME_PADDING = 24;
|
||||
export const FRAME_COLLAPSED_WIDTH = 240;
|
||||
export const FRAME_COLLAPSED_HEIGHT = 144;
|
||||
export const FOLDER_COLLAPSED_WIDTH = 360;
|
||||
export const FOLDER_COLLAPSED_HEIGHT = 280;
|
||||
|
||||
export function isFrameNode(node?: CanvasNodeData | null): node is CanvasNodeData & { type: CanvasNodeType.Frame } {
|
||||
return node?.type === CanvasNodeType.Frame;
|
||||
}
|
||||
|
||||
export function isCanvasFolderNode(node?: CanvasNodeData | null) {
|
||||
return isFrameNode(node) && Boolean(node.metadata?.folder);
|
||||
}
|
||||
|
||||
export function canFrameContain(node: CanvasNodeData) {
|
||||
return node.type === CanvasNodeType.Image || node.type === CanvasNodeType.Text || node.type === CanvasNodeType.Drawing || node.type === CanvasNodeType.Script || node.type === CanvasNodeType.Video;
|
||||
}
|
||||
|
||||
export function canFolderContain(node: CanvasNodeData) {
|
||||
return node.type !== CanvasNodeType.Frame;
|
||||
}
|
||||
|
||||
export function canLinkedFolderArchive(node: CanvasNodeData) {
|
||||
return (node.type === CanvasNodeType.Image || node.type === CanvasNodeType.Video || node.type === CanvasNodeType.Audio || node.type === CanvasNodeType.Text) && Boolean(node.metadata?.assetId || node.metadata?.content?.trim());
|
||||
}
|
||||
|
||||
export function getFrameChildren(frameId: string, nodes: CanvasNodeData[]) {
|
||||
return nodes.filter((node) => node.parentId === frameId);
|
||||
}
|
||||
@@ -32,16 +46,18 @@ export function isNodeHiddenByCollapsedFrame(node: CanvasNodeData, nodes: Canvas
|
||||
}
|
||||
|
||||
export function findFrameDropTarget(nodes: CanvasNodeData[], draggedNodeIds: Set<string>) {
|
||||
const dragged = nodes.filter((node) => draggedNodeIds.has(node.id) && canFrameContain(node));
|
||||
const dragged = nodes.filter((node) => draggedNodeIds.has(node.id));
|
||||
if (!dragged.length) return null;
|
||||
|
||||
return (
|
||||
[...nodes]
|
||||
.reverse()
|
||||
.find((frame) => {
|
||||
if (!isFrameNode(frame) || frame.metadata?.frame?.collapsed || draggedNodeIds.has(frame.id)) return false;
|
||||
if (!isFrameNode(frame) || (frame.metadata?.frame?.collapsed && !isCanvasFolderNode(frame)) || draggedNodeIds.has(frame.id)) return false;
|
||||
const canContain = frame.metadata?.folder?.assetFolderId ? canLinkedFolderArchive : isCanvasFolderNode(frame) ? canFolderContain : canFrameContain;
|
||||
if (!dragged.every(canContain)) return false;
|
||||
const left = frame.position.x;
|
||||
const top = frame.position.y + FRAME_HEADER_HEIGHT;
|
||||
const top = frame.position.y + (isCanvasFolderNode(frame) && frame.metadata?.frame?.collapsed ? 0 : FRAME_HEADER_HEIGHT);
|
||||
const right = frame.position.x + frame.width;
|
||||
const bottom = frame.position.y + frame.height;
|
||||
return dragged.every((node) => {
|
||||
@@ -54,7 +70,13 @@ export function findFrameDropTarget(nodes: CanvasNodeData[], draggedNodeIds: Set
|
||||
}
|
||||
|
||||
export function applyFrameDrop(nodes: CanvasNodeData[], draggedNodeIds: Set<string>, frameId: string | null) {
|
||||
const next = nodes.map((node) => (draggedNodeIds.has(node.id) && canFrameContain(node) ? { ...node, parentId: frameId || undefined } : node));
|
||||
const target = frameId ? nodes.find((node) => node.id === frameId && isFrameNode(node)) : null;
|
||||
const canContain = target?.metadata?.folder?.assetFolderId ? canLinkedFolderArchive : target && isCanvasFolderNode(target) ? canFolderContain : canFrameContain;
|
||||
const next = nodes.map((node) => {
|
||||
if (!draggedNodeIds.has(node.id)) return node;
|
||||
if (!frameId) return node.parentId ? { ...node, parentId: undefined } : node;
|
||||
return target && canContain(node) ? { ...node, parentId: frameId } : node;
|
||||
});
|
||||
if (!frameId) return next;
|
||||
|
||||
const children = getFrameChildren(frameId, next);
|
||||
@@ -62,6 +84,10 @@ export function applyFrameDrop(nodes: CanvasNodeData[], draggedNodeIds: Set<stri
|
||||
const frame = next.find((node) => node.id === frameId);
|
||||
if (!frame || !isFrameNode(frame)) return next;
|
||||
|
||||
if (isCanvasFolderNode(frame) && frame.metadata?.frame?.collapsed) {
|
||||
return layoutCollapsedFolderChildren(next, frame);
|
||||
}
|
||||
|
||||
const left = Math.min(frame.position.x, ...children.map((node) => node.position.x - FRAME_PADDING));
|
||||
const top = Math.min(frame.position.y, ...children.map((node) => node.position.y - FRAME_HEADER_HEIGHT - FRAME_PADDING));
|
||||
const right = Math.max(frame.position.x + frame.width, ...children.map((node) => node.position.x + node.width + FRAME_PADDING));
|
||||
@@ -87,6 +113,43 @@ export function applyFrameDrop(nodes: CanvasNodeData[], draggedNodeIds: Set<stri
|
||||
);
|
||||
}
|
||||
|
||||
function layoutCollapsedFolderChildren(nodes: CanvasNodeData[], folder: CanvasNodeData) {
|
||||
const children = getFrameChildren(folder.id, nodes);
|
||||
if (!children.length) return nodes;
|
||||
const gap = 24;
|
||||
const padding = FRAME_PADDING;
|
||||
const columns = Math.min(3, Math.ceil(Math.sqrt(children.length)));
|
||||
const rows = Math.ceil(children.length / columns);
|
||||
const cellWidth = Math.max(...children.map((node) => node.width));
|
||||
const cellHeight = Math.max(...children.map((node) => node.height));
|
||||
const expandedWidth = padding * 2 + columns * cellWidth + Math.max(columns - 1, 0) * gap;
|
||||
const expandedHeight = FRAME_HEADER_HEIGHT + padding * 2 + rows * cellHeight + Math.max(rows - 1, 0) * gap;
|
||||
const indexById = new Map(children.map((node, index) => [node.id, index]));
|
||||
|
||||
return nodes.map((node) => {
|
||||
if (node.id === folder.id) {
|
||||
return {
|
||||
...node,
|
||||
metadata: {
|
||||
...node.metadata,
|
||||
frame: { collapsed: true, expandedWidth, expandedHeight },
|
||||
},
|
||||
};
|
||||
}
|
||||
const index = indexById.get(node.id);
|
||||
if (index === undefined) return node;
|
||||
const column = index % columns;
|
||||
const row = Math.floor(index / columns);
|
||||
return {
|
||||
...node,
|
||||
position: {
|
||||
x: folder.position.x + padding + column * (cellWidth + gap) + (cellWidth - node.width) / 2,
|
||||
y: folder.position.y + FRAME_HEADER_HEIGHT + padding + row * (cellHeight + gap) + (cellHeight - node.height) / 2,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function resolveFrameConnection(connection: CanvasConnection, nodes: CanvasNodeData[]) {
|
||||
const from = nodes.find((node) => node.id === connection.fromNodeId);
|
||||
const to = nodes.find((node) => node.id === connection.toNodeId);
|
||||
|
||||
Reference in new issue
Block a user