Files
feuerwerk/src/editor/inspector/parentRow.ts
T
rootandClaude Opus 5 3b3f374e10 Split the object inspector, and sweep out the section banners
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>
2026-09-10 07:14:45 +02:00

46 lines
2.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}