cb96e6c6cd
统计图像和文本组件的素材槽位绑定状态。 新增绑定待处理、待检查和必须修复的循环定位。 复用识别概览的前序候选查询与导航逻辑。
112 lines
3.1 KiB
TypeScript
112 lines
3.1 KiB
TypeScript
import {
|
|
collectUiTreeNodeTargets,
|
|
isNeedReview,
|
|
type UiTreeNodeTarget,
|
|
} from './stageStatusOverview';
|
|
import type { Component } from './types/Component';
|
|
import type { SpriteAsset } from './types/SpriteAsset';
|
|
import type { UITree } from './types/UITree';
|
|
|
|
export type ComponentBindingCounts = {
|
|
componentsNeedingAssets: number;
|
|
assetSlots: number;
|
|
boundSlots: number;
|
|
pendingSlots: number;
|
|
};
|
|
|
|
export type BindingOverview = ComponentBindingCounts & {
|
|
needsAttention: number;
|
|
blocked: number;
|
|
independentAssets: number;
|
|
};
|
|
|
|
const EMPTY_COMPONENT_BINDING_COUNTS: ComponentBindingCounts = {
|
|
componentsNeedingAssets: 0,
|
|
assetSlots: 0,
|
|
boundSlots: 0,
|
|
pendingSlots: 0,
|
|
};
|
|
|
|
function countRequiredAssetSlot(isBound: boolean): ComponentBindingCounts {
|
|
return {
|
|
componentsNeedingAssets: 1,
|
|
assetSlots: 1,
|
|
boundSlots: isBound ? 1 : 0,
|
|
pendingSlots: isBound ? 0 : 1,
|
|
};
|
|
}
|
|
|
|
export function getImageBindingCounts(
|
|
component: Extract<Component, { Image: unknown }>['Image'],
|
|
): ComponentBindingCounts {
|
|
return countRequiredAssetSlot(component.target_graphic !== null);
|
|
}
|
|
|
|
export function getTextBindingCounts(
|
|
component: Extract<Component, { Text: unknown }>['Text'],
|
|
): ComponentBindingCounts {
|
|
return countRequiredAssetSlot(component.font !== null);
|
|
}
|
|
|
|
export function getComponentBindingCounts(
|
|
component: Component,
|
|
): ComponentBindingCounts {
|
|
if ('Image' in component) {
|
|
return getImageBindingCounts(component.Image);
|
|
}
|
|
if ('Text' in component) {
|
|
return getTextBindingCounts(component.Text);
|
|
}
|
|
return EMPTY_COMPONENT_BINDING_COUNTS;
|
|
}
|
|
|
|
function addBindingCounts(
|
|
overview: ComponentBindingCounts,
|
|
counts: ComponentBindingCounts,
|
|
) {
|
|
overview.componentsNeedingAssets += counts.componentsNeedingAssets;
|
|
overview.assetSlots += counts.assetSlots;
|
|
overview.boundSlots += counts.boundSlots;
|
|
overview.pendingSlots += counts.pendingSlots;
|
|
}
|
|
|
|
export function getBindingOverview(
|
|
uiTrees: UITree[],
|
|
spriteAssets: Record<string, SpriteAsset>,
|
|
): BindingOverview {
|
|
const overview: BindingOverview = {
|
|
...EMPTY_COMPONENT_BINDING_COUNTS,
|
|
needsAttention: 0,
|
|
blocked: 0,
|
|
independentAssets: Object.keys(spriteAssets).length,
|
|
};
|
|
|
|
for (const { node } of collectUiTreeNodeTargets(uiTrees)) {
|
|
const status = node.metadata.components_status;
|
|
if (status === 'Blocked') overview.blocked += 1;
|
|
if (status === 'Blocked' || isNeedReview(status)) {
|
|
overview.needsAttention += 1;
|
|
}
|
|
for (const component of node.components) {
|
|
addBindingCounts(overview, getComponentBindingCounts(component));
|
|
}
|
|
}
|
|
|
|
return overview;
|
|
}
|
|
|
|
export function nodeHasPendingBinding(target: UiTreeNodeTarget): boolean {
|
|
return target.node.components.some(
|
|
(component) => getComponentBindingCounts(component).pendingSlots > 0,
|
|
);
|
|
}
|
|
|
|
export function nodeNeedsComponentReview(target: UiTreeNodeTarget): boolean {
|
|
const status = target.node.metadata.components_status;
|
|
return status === 'Blocked' || isNeedReview(status);
|
|
}
|
|
|
|
export function nodeHasBlockedComponents(target: UiTreeNodeTarget): boolean {
|
|
return target.node.metadata.components_status === 'Blocked';
|
|
}
|