objectSection.ts had grown to 559 lines around three separable parts: the queries, the vector rows and the parent row. Four object commands repeated the same capture/restore preamble – applyCommands stays out, its five carry extra undo state whose restore order matters. 58 dashed banner comments are gone. The nine labels in nodes/index.ts stay: they are the only structure a flat 90-entry array has. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
46 lines
2.3 KiB
TypeScript
46 lines
2.3 KiB
TypeScript
import { SetParentCommand } from '../../core/commands/sceneObjectCommands';
|
||
import { findObject, isSelfOrAncestor, treeOrder } from '../../core/model/hierarchy';
|
||
import { findAsset, type ObjectHolder } from '../../core/model/Project';
|
||
import type { SceneObject } from '../../core/model/SceneObject';
|
||
import { Dropdown, Label, Menu, type MenuItem } from '../../ui';
|
||
import { labelledRow } from './fieldFactory';
|
||
import type { InspectorHost } from './host';
|
||
import { viewOf } from './objectQueries';
|
||
|
||
/**
|
||
* "Parent: <name> ▾" – picks another object of the same scene or container, or none. The object's own
|
||
* subtree is excluded (that would be a cycle). For behaviors the parent is the owner. Content of a
|
||
* placed container only shows its parent: where it hangs is the template's business.
|
||
*/
|
||
export function parentRow(host: InspectorHost, holder: ObjectHolder, object: SceneObject, fixed: boolean) {
|
||
const { row } = labelledRow('Parent');
|
||
const nameOf = (o: SceneObject) => o.name || findAsset(host.state.project, o.assetId)?.name || '?';
|
||
const parent = findObject(holder, object.parentId);
|
||
const name = parent ? nameOf(parent) : 'None';
|
||
if (fixed) {
|
||
row.add(new Label(name, { style: 'muted' }), 1);
|
||
return row;
|
||
}
|
||
const button = new Dropdown(name);
|
||
button.onClick = () => {
|
||
// Resolved anew on every open: `holder` is a derived copy of the scene, so one captured when
|
||
// the row was built lists the objects of that moment, not the ones there now.
|
||
const current = viewOf(host.state.project, holder.id) ?? holder;
|
||
const items: MenuItem[] = [
|
||
{ label: 'None', onSelect: () => host.state.execute(new SetParentCommand(holder.id, object.id, '')) },
|
||
];
|
||
for (const { object: candidate, depth } of treeOrder(current)) {
|
||
if (isSelfOrAncestor(current, object.id, candidate.id)) continue;
|
||
const label = `${' '.repeat(depth)}${nameOf(candidate)}`;
|
||
items.push({
|
||
label: candidate.id === object.parentId ? `● ${label}` : label,
|
||
onSelect: () => host.state.execute(new SetParentCommand(holder.id, object.id, candidate.id)),
|
||
});
|
||
}
|
||
// Searchable: this lists every object of the scene or container, which grows with the project.
|
||
new Menu(items, { searchable: true }).openBelow(button);
|
||
};
|
||
row.add(button, 1);
|
||
return row;
|
||
}
|