Skip to content

Commit 031d067

Browse files
authored
Add metadata to objects (#727)
1 parent 7da5166 commit 031d067

File tree

10 files changed

+143
-0
lines changed

10 files changed

+143
-0
lines changed

editor/src/editor/layout/inspector/camera/arc-rotate.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { EditorInspectorStringField } from "../fields/string";
1313
import { EditorInspectorSectionField } from "../fields/section";
1414

1515
import { ScriptInspectorComponent } from "../script/script";
16+
import { CustomMetadataInspector } from "../metadata/custom-metadata";
1617

1718
import { CameraModeInspector } from "./utils/mode";
1819
import { FocalLengthInspector } from "./utils/focal";
@@ -58,6 +59,8 @@ export class EditorArcRotateCameraInspector extends Component<IEditorInspectorIm
5859
</EditorInspectorSectionField>
5960

6061
<ScriptInspectorComponent editor={this.props.editor} object={this.props.object} />
62+
63+
<CustomMetadataInspector object={this.props.object} />
6164
</>
6265
);
6366
}

editor/src/editor/layout/inspector/camera/editor.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { IEditorInspectorImplementationProps } from "../inspector";
99
import { EditorInspectorNumberField } from "../fields/number";
1010
import { EditorInspectorSectionField } from "../fields/section";
1111

12+
import { CustomMetadataInspector } from "../metadata/custom-metadata";
13+
1214
import { FocalLengthInspector } from "./utils/focal";
1315

1416
export class EditorCameraInspector extends Component<IEditorInspectorImplementationProps<EditorCamera>> {
@@ -54,6 +56,8 @@ export class EditorCameraInspector extends Component<IEditorInspectorImplementat
5456
Configure in preferences...
5557
</Button>
5658
</EditorInspectorSectionField>
59+
60+
<CustomMetadataInspector object={this.props.object} />
5761
</>
5862
);
5963
}

editor/src/editor/layout/inspector/camera/free.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { EditorInspectorStringField } from "../fields/string";
1818
import { EditorInspectorSectionField } from "../fields/section";
1919

2020
import { ScriptInspectorComponent } from "../script/script";
21+
import { CustomMetadataInspector } from "../metadata/custom-metadata";
2122

2223
import { CameraModeInspector } from "./utils/mode";
2324
import { FocalLengthInspector } from "./utils/focal";
@@ -80,6 +81,8 @@ export class EditorFreeCameraInspector extends Component<IEditorInspectorImpleme
8081
<EditorInspectorKeyField value={this.props.object.keysUpward[0]?.toString() ?? ""} onChange={(v) => (this.props.object.keysUpward = [v])} label="Up" />
8182
<EditorInspectorKeyField value={this.props.object.keysDownward[0]?.toString() ?? ""} onChange={(v) => (this.props.object.keysDownward = [v])} label="Down" />
8283
</EditorInspectorSectionField>
84+
85+
<CustomMetadataInspector object={this.props.object} />
8386
</>
8487
);
8588
}

editor/src/editor/layout/inspector/light/directional.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { EditorInspectorNumberField } from "../fields/number";
1616
import { EditorInspectorSectionField } from "../fields/section";
1717

1818
import { ScriptInspectorComponent } from "../script/script";
19+
import { CustomMetadataInspector } from "../metadata/custom-metadata";
1920

2021
import { EditorLightShadowsInspector } from "./shadows";
2122

@@ -79,6 +80,8 @@ export class EditorDirectionalLightInspector extends Component<IEditorInspectorI
7980
<ScriptInspectorComponent editor={this.props.editor} object={this.props.object} />
8081

8182
<EditorLightShadowsInspector light={this.props.object} />
83+
84+
<CustomMetadataInspector object={this.props.object} />
8285
</>
8386
);
8487
}

editor/src/editor/layout/inspector/light/hemispheric.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { EditorInspectorNumberField } from "../fields/number";
1515
import { EditorInspectorSectionField } from "../fields/section";
1616

1717
import { ScriptInspectorComponent } from "../script/script";
18+
import { CustomMetadataInspector } from "../metadata/custom-metadata";
1819

1920
export class EditorHemisphericLightInspector extends Component<IEditorInspectorImplementationProps<HemisphericLight>> {
2021
/**
@@ -58,6 +59,8 @@ export class EditorHemisphericLightInspector extends Component<IEditorInspectorI
5859
</EditorInspectorSectionField>
5960

6061
<ScriptInspectorComponent editor={this.props.editor} object={this.props.object} />
62+
63+
<CustomMetadataInspector object={this.props.object} />
6164
</>
6265
);
6366
}

editor/src/editor/layout/inspector/light/point.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { EditorInspectorNumberField } from "../fields/number";
1616
import { EditorInspectorSectionField } from "../fields/section";
1717

1818
import { ScriptInspectorComponent } from "../script/script";
19+
import { CustomMetadataInspector } from "../metadata/custom-metadata";
1920

2021
import { EditorLightShadowsInspector } from "./shadows";
2122

@@ -82,6 +83,8 @@ export class EditorPointLightInspector extends Component<IEditorInspectorImpleme
8283
<ScriptInspectorComponent editor={this.props.editor} object={this.props.object} />
8384

8485
<EditorLightShadowsInspector light={this.props.object} />
86+
87+
<CustomMetadataInspector object={this.props.object} />
8588
</>
8689
);
8790
}

editor/src/editor/layout/inspector/light/spot.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { EditorInspectorSectionField } from "../fields/section";
1717
// import { EditorInspectorTextureField } from "../fields/texture";
1818

1919
import { ScriptInspectorComponent } from "../script/script";
20+
import { CustomMetadataInspector } from "../metadata/custom-metadata";
2021

2122
import { EditorLightShadowsInspector } from "./shadows";
2223

@@ -103,6 +104,8 @@ export class EditorSpotLightInspector extends Component<IEditorInspectorImplemen
103104
<EditorLightShadowsInspector light={this.props.object}>
104105
<EditorInspectorNumberField label="Angle" object={this.props.object} property="shadowAngleScale" min={0} max={Math.PI * 2} />
105106
</EditorLightShadowsInspector>
107+
108+
<CustomMetadataInspector object={this.props.object} />
106109
</>
107110
);
108111
}

editor/src/editor/layout/inspector/mesh/mesh.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import { EditorInspectorNumberField } from "../fields/number";
4545
import { EditorInspectorSectionField } from "../fields/section";
4646

4747
import { ScriptInspectorComponent } from "../script/script";
48+
import { CustomMetadataInspector } from "../metadata/custom-metadata";
4849

4950
import { onGizmoNodeChangedObservable } from "../../preview/gizmo";
5051

@@ -200,6 +201,8 @@ export class EditorMeshInspector extends Component<IEditorInspectorImplementatio
200201
<EditorInspectorSwitchField label="Always Select As Active Mesh" object={this.props.object} property="alwaysSelectAsActiveMesh" />
201202
</EditorInspectorSectionField>
202203
)}
204+
205+
<CustomMetadataInspector object={this.props.object} />
203206
</>
204207
);
205208
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { Component, ReactNode } from "react";
2+
3+
import { IoAddSharp, IoCloseOutline } from "react-icons/io5";
4+
5+
import { Node } from "babylonjs";
6+
7+
import { showAlert, showPrompt } from "../../../../ui/dialog";
8+
import { Button } from "../../../../ui/shadcn/ui/button";
9+
10+
import { EditorInspectorStringField } from "../fields/string";
11+
import { EditorInspectorSectionField } from "../fields/section";
12+
13+
export interface ICustomMetadataInspectorProps {
14+
object: Node;
15+
}
16+
17+
/**
18+
* Custom metadata inspector component that allows users to add/edit/remove key-value pairs
19+
* for custom metadata on nodes.
20+
*/
21+
export class CustomMetadataInspector extends Component<ICustomMetadataInspectorProps> {
22+
public render(): ReactNode {
23+
24+
const keys = Object.keys(this.props.object.metadata?.customMetadata ?? {});
25+
26+
return (
27+
<EditorInspectorSectionField title="Metadata">
28+
{keys.length === 0 && (
29+
<div className="text-center text-white/50 py-2">
30+
No metadata found. Click "Add" to create a new key-value pair.
31+
</div>
32+
)}
33+
34+
{keys.map((key, index) => (
35+
<div key={index} className="flex items-center gap-2">
36+
<div className="w-full">
37+
<EditorInspectorStringField
38+
label={key}
39+
object={this.props.object.metadata?.customMetadata ?? {}}
40+
property={key}
41+
onChange={() => this.forceUpdate()}
42+
/>
43+
</div>
44+
45+
<Button
46+
variant="secondary"
47+
className="p-2"
48+
onClick={() => this._handleRemoveKey(key)}
49+
>
50+
<IoCloseOutline className="w-4 h-4" />
51+
</Button>
52+
</div>
53+
))}
54+
55+
<Button
56+
variant="secondary"
57+
className="flex items-center gap-2 w-full"
58+
onClick={() => this._handleAddKey()}
59+
>
60+
<IoAddSharp className="w-6 h-6" /> Add
61+
</Button>
62+
</EditorInspectorSectionField>
63+
);
64+
}
65+
66+
private async _handleAddKey(): Promise<void> {
67+
const key = await showPrompt("Add Custom Metadata", "Enter the key name for the new metadata:");
68+
69+
if (!key) {
70+
return;
71+
}
72+
73+
// Validate key name
74+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
75+
showAlert("Invalid Key Name", "Key must start with a letter or underscore and contain only letters, numbers, and underscores.");
76+
return;
77+
}
78+
79+
// Ensure metadata exists
80+
if (!this.props.object.metadata) {
81+
this.props.object.metadata = {};
82+
}
83+
84+
// Ensure customMetadata exists
85+
if (!this.props.object.metadata.customMetadata) {
86+
this.props.object.metadata.customMetadata = {};
87+
}
88+
89+
// Check if key already exists
90+
if (key in this.props.object.metadata.customMetadata) {
91+
showAlert("Duplicate Key", `Key "${key}" already exists.`);
92+
return;
93+
}
94+
95+
// Add new key with empty string value
96+
this.props.object.metadata.customMetadata[key] = "";
97+
98+
this.forceUpdate();
99+
}
100+
101+
private _handleRemoveKey(key: string): void {
102+
if (!this.props.object.metadata?.customMetadata) {
103+
return;
104+
}
105+
106+
delete this.props.object.metadata.customMetadata[key];
107+
108+
// Clean up empty customMetadata object
109+
if (Object.keys(this.props.object.metadata.customMetadata).length === 0) {
110+
delete this.props.object.metadata.customMetadata;
111+
}
112+
113+
this.forceUpdate();
114+
}
115+
}

editor/src/editor/layout/inspector/transform.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { EditorInspectorVectorField } from "./fields/vector";
1111
import { EditorInspectorSectionField } from "./fields/section";
1212

1313
import { ScriptInspectorComponent } from "./script/script";
14+
import { CustomMetadataInspector } from "./metadata/custom-metadata";
1415

1516
import { onGizmoNodeChangedObservable } from "../preview/gizmo";
1617

@@ -45,6 +46,8 @@ export class EditorTransformNodeInspector extends Component<IEditorInspectorImpl
4546
</EditorInspectorSectionField>
4647

4748
<ScriptInspectorComponent editor={this.props.editor} object={this.props.object} />
49+
50+
<CustomMetadataInspector object={this.props.object} />
4851
</>
4952
);
5053
}

0 commit comments

Comments
 (0)