5467ed6d7b
- 拖动落点改逐值比较 sameResourceCardReferenceDrop(条数 + 对话栏矩形四值),指针在对话栏上移动不再每帧 setState、不再每帧重渲染工作台。 - 替换面板从 stage 顶层移进画布容器 .game-resource-book-manager(本身 position: relative),top 3.2rem 排在任务开关下方;相对 stage 写死的偏移会在工具条换行时压上去,与刚修的任务开关是同一类隐患。 - 打开替换会话时收起「生成任务」面板:画布右上角同一时刻只留一块浮层(任务照旧在账本里推进)。 - 面板上方补一行「替换源素材:<显示名>」:非模态之后画布上的源素材光环会被空白点击清掉,源身份得在面板这一侧留一份可读说法。 - 测试:替换用例新增「面板锚在画布容器里、写明源素材、并与任务面板互斥」(20 passed)、落点模型新增逐值比较用例(6 passed)。 - 文档:decision-log 新增本批自查条目、待办文档补登记、验收用例 C9 注脚改为 nonModal 形态。
122 lines
4.3 KiB
TypeScript
122 lines
4.3 KiB
TypeScript
import { parseGameCreationAppAssetKind } from '../../../../../packages/shared/src/contracts/gameCreationApp';
|
||
import type { ResourceReference } from '../../features/project-workspace/resourceReferences';
|
||
import {
|
||
type ProjectResource,
|
||
projectResourceAssetCategory,
|
||
} from './resourceProjectionModel';
|
||
|
||
/** 对话栏在屏幕坐标里的矩形(拖动落点的唯一判据)。 */
|
||
export type ResourceCardReferenceDropRect = {
|
||
left: number;
|
||
top: number;
|
||
width: number;
|
||
height: number;
|
||
};
|
||
|
||
/**
|
||
* 量对话栏的落点矩形:没有元素、或元素还没量出尺寸(0 x 0,jsdom 与折叠态)时返回 null。
|
||
*
|
||
* 零面积矩形必须当「没有落点」:命中测试是 contains,一个 0 x 0 的矩形会把任何点都判成
|
||
* 在里面,于是画布内正常拖动松手也会被误判成「拖到对话」,排版写入被整段跳过。
|
||
*/
|
||
export function resourceCardReferenceDropRect(
|
||
element: Element | null,
|
||
): ResourceCardReferenceDropRect | null {
|
||
if (!element) return null;
|
||
const rect = element.getBoundingClientRect();
|
||
if (!(rect.width > 0) || !(rect.height > 0)) {
|
||
return null;
|
||
}
|
||
return {
|
||
left: rect.left,
|
||
top: rect.top,
|
||
width: rect.width,
|
||
height: rect.height,
|
||
};
|
||
}
|
||
|
||
export function resourceCardReferenceDropContains(
|
||
rect: ResourceCardReferenceDropRect,
|
||
clientX: number,
|
||
clientY: number,
|
||
) {
|
||
return (
|
||
clientX >= rect.left &&
|
||
clientX <= rect.left + rect.width &&
|
||
clientY >= rect.top &&
|
||
clientY <= rect.top + rect.height
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 两次落点状态是否**逐值相同**。
|
||
*
|
||
* 指针在对话栏上移动时每帧都会算出一个新的落点对象;只看「有没有落点」会让每一帧都 `setState`
|
||
* 一次、把工作台整棵子树重渲染一遍(`pointermove` 热路径上这是实打实的浪费)。带上这一条,
|
||
* 只有条数或对话栏矩形真的变了才重新渲染。
|
||
*/
|
||
export function sameResourceCardReferenceDrop(
|
||
left: { count: number; rect: ResourceCardReferenceDropRect },
|
||
right: { count: number; rect: ResourceCardReferenceDropRect },
|
||
) {
|
||
return (
|
||
left.count === right.count &&
|
||
left.rect.left === right.rect.left &&
|
||
left.rect.top === right.rect.top &&
|
||
left.rect.width === right.rect.width &&
|
||
left.rect.height === right.rect.height
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 拖到对话要插入的引用。
|
||
*
|
||
* 与资源卡工具条那枚「引用」按钮**同一口径**:只认已登记到 manifest 的素材
|
||
* (manifestAssetId),来源标 resource-card,kind / 分类 / 标签全部取自资源投影,
|
||
* 于是同一个素材无论从按钮还是从拖拽进来,草稿里的引用是同一枚(去重键也跟着走)。
|
||
* 未登记的卡(任务产物、附件、Agent 回执等)在这里被过滤掉,调用方按「一条都没有」给原因。
|
||
*
|
||
* 顺序即插入顺序:movedResourceIds 是本次拖动真正参与位移的那批(第 0 项是被按住的那张),
|
||
* 多选后整批引用、单选只引用按住的那张,与拖动位移的语义同源。
|
||
*/
|
||
export function resourceCardReferenceDropReferences(
|
||
movedResourceIds: readonly string[],
|
||
resources: readonly ProjectResource[],
|
||
): ResourceReference[] {
|
||
if (movedResourceIds.length === 0) {
|
||
return [];
|
||
}
|
||
const byId = new Map(resources.map((resource) => [resource.id, resource]));
|
||
const references: ResourceReference[] = [];
|
||
for (const resourceId of movedResourceIds) {
|
||
const resource = byId.get(resourceId);
|
||
if (!resource?.manifestAssetId) {
|
||
continue;
|
||
}
|
||
references.push({
|
||
type: 'resource',
|
||
resourceId: resource.manifestAssetId,
|
||
kind: parseGameCreationAppAssetKind(
|
||
resource.subtype,
|
||
'project-development.resource-card-reference-drop',
|
||
),
|
||
mediaType: resource.mediaType,
|
||
label: resource.label,
|
||
category: projectResourceAssetCategory(resource),
|
||
tags: resource.assetTags ?? [],
|
||
source: 'resource-card',
|
||
});
|
||
}
|
||
return references;
|
||
}
|
||
|
||
/**
|
||
* 落点提示文案。条数为 0 时不写「引用 0 项」,而是把「为什么一条也引用不了」说清楚:
|
||
* 用户此时已经拖到对话上了,静默什么都不发生会被读成功能坏了。
|
||
*/
|
||
export function resourceCardReferenceDropHintLabel(count: number) {
|
||
return count > 0
|
||
? `松手即可 @ 引用 ${count} 项素材`
|
||
: '这些素材还没登记为项目资源,不能 @ 引用';
|
||
}
|