react-three-game 0.0.18 → 0.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,110 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
3
  import { getComponent } from './components/ComponentRegistry';
4
+ import { base, tree, menu } from './styles';
5
+ import { findNode, findParent, deleteNode, cloneNode } from './utils';
4
6
  export default function EditorTree({ prefabData, setPrefabData, selectedId, setSelectedId }) {
5
7
  const [contextMenu, setContextMenu] = useState(null);
6
8
  const [draggedId, setDraggedId] = useState(null);
7
9
  const [collapsedIds, setCollapsedIds] = useState(new Set());
8
- const [isTreeCollapsed, setIsTreeCollapsed] = useState(false);
9
- const styles = {
10
- panel: {
11
- background: "rgba(0,0,0,0.55)",
12
- color: "rgba(255,255,255,0.9)",
13
- border: "1px solid rgba(255,255,255,0.12)",
14
- borderRadius: 6,
15
- overflow: "hidden",
16
- maxHeight: "85vh",
17
- display: "flex",
18
- flexDirection: "column",
19
- backdropFilter: "blur(6px)",
20
- WebkitBackdropFilter: "blur(6px)",
21
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace",
22
- fontSize: 11,
23
- lineHeight: 1.2,
24
- userSelect: "none",
25
- WebkitUserSelect: "none",
26
- },
27
- panelHeader: {
28
- padding: "4px 6px",
29
- borderBottom: "1px solid rgba(255,255,255,0.10)",
30
- display: "flex",
31
- gap: 8,
32
- alignItems: "center",
33
- justifyContent: "space-between",
34
- cursor: "pointer",
35
- background: "rgba(255,255,255,0.05)",
36
- textTransform: "uppercase",
37
- letterSpacing: "0.08em",
38
- fontSize: 10,
39
- color: "rgba(255,255,255,0.7)",
40
- },
41
- scroll: {
42
- overflowY: "auto",
43
- },
44
- row: {
45
- display: "flex",
46
- alignItems: "center",
47
- padding: "2px 6px",
48
- borderBottom: "1px solid rgba(255,255,255,0.07)",
49
- cursor: "pointer",
50
- whiteSpace: "nowrap",
51
- },
52
- rowSelected: {
53
- background: "rgba(255,255,255,0.10)",
54
- },
55
- chevron: {
56
- width: 12,
57
- textAlign: "center",
58
- opacity: 0.55,
59
- fontSize: 10,
60
- marginRight: 4,
61
- cursor: "pointer",
62
- },
63
- idText: {
64
- fontSize: 11,
65
- overflow: "hidden",
66
- textOverflow: "ellipsis",
67
- },
68
- dragHandle: {
69
- width: 14,
70
- height: 14,
71
- display: "flex",
72
- alignItems: "center",
73
- justifyContent: "center",
74
- marginRight: 4,
75
- opacity: 0.4,
76
- cursor: "grab",
77
- fontSize: 10,
78
- },
79
- contextMenu: {
80
- position: "fixed",
81
- zIndex: 50,
82
- minWidth: 120,
83
- background: "rgba(0,0,0,0.82)",
84
- border: "1px solid rgba(255,255,255,0.16)",
85
- borderRadius: 6,
86
- overflow: "hidden",
87
- boxShadow: "0 12px 32px rgba(0,0,0,0.45)",
88
- backdropFilter: "blur(6px)",
89
- WebkitBackdropFilter: "blur(6px)",
90
- },
91
- menuItem: {
92
- width: "100%",
93
- textAlign: "left",
94
- padding: "6px 8px",
95
- background: "transparent",
96
- border: "none",
97
- color: "rgba(255,255,255,0.9)",
98
- font: "inherit",
99
- cursor: "pointer",
100
- },
101
- menuItemDanger: {
102
- color: "rgba(255,120,120,0.95)",
103
- },
104
- menuDivider: {
105
- borderTop: "1px solid rgba(255,255,255,0.10)",
106
- }
107
- };
10
+ const [collapsed, setCollapsed] = useState(false);
108
11
  if (!prefabData || !setPrefabData)
109
12
  return null;
110
13
  const handleContextMenu = (e, nodeId) => {
@@ -112,15 +15,11 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
112
15
  e.stopPropagation();
113
16
  setContextMenu({ x: e.clientX, y: e.clientY, nodeId });
114
17
  };
115
- const closeContextMenu = () => setContextMenu(null);
116
18
  const toggleCollapse = (e, id) => {
117
19
  e.stopPropagation();
118
20
  setCollapsedIds(prev => {
119
21
  const next = new Set(prev);
120
- if (next.has(id))
121
- next.delete(id);
122
- else
123
- next.add(id);
22
+ next.has(id) ? next.delete(id) : next.add(id);
124
23
  return next;
125
24
  });
126
25
  };
@@ -137,7 +36,7 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
137
36
  }
138
37
  };
139
38
  setPrefabData(prev => {
140
- const newRoot = JSON.parse(JSON.stringify(prev.root)); // Deep clone for safety
39
+ const newRoot = JSON.parse(JSON.stringify(prev.root));
141
40
  const parent = findNode(newRoot, parentId);
142
41
  if (parent) {
143
42
  parent.children = parent.children || [];
@@ -145,11 +44,11 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
145
44
  }
146
45
  return Object.assign(Object.assign({}, prev), { root: newRoot });
147
46
  });
148
- closeContextMenu();
47
+ setContextMenu(null);
149
48
  };
150
49
  const handleDuplicate = (nodeId) => {
151
50
  if (nodeId === prefabData.root.id)
152
- return; // Cannot duplicate root
51
+ return;
153
52
  setPrefabData(prev => {
154
53
  const newRoot = JSON.parse(JSON.stringify(prev.root));
155
54
  const parent = findParent(newRoot, nodeId);
@@ -161,18 +60,15 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
161
60
  }
162
61
  return Object.assign(Object.assign({}, prev), { root: newRoot });
163
62
  });
164
- closeContextMenu();
63
+ setContextMenu(null);
165
64
  };
166
65
  const handleDelete = (nodeId) => {
167
66
  if (nodeId === prefabData.root.id)
168
- return; // Cannot delete root
169
- setPrefabData(prev => {
170
- const newRoot = deleteNodeFromTree(JSON.parse(JSON.stringify(prev.root)), nodeId);
171
- return Object.assign(Object.assign({}, prev), { root: newRoot });
172
- });
67
+ return;
68
+ setPrefabData(prev => (Object.assign(Object.assign({}, prev), { root: deleteNode(prev.root, nodeId) })));
173
69
  if (selectedId === nodeId)
174
70
  setSelectedId(null);
175
- closeContextMenu();
71
+ setContextMenu(null);
176
72
  };
177
73
  // Drag and Drop
178
74
  const handleDragStart = (e, id) => {
@@ -181,12 +77,8 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
181
77
  return;
182
78
  }
183
79
  e.dataTransfer.effectAllowed = "move";
184
- e.dataTransfer.setData("text/plain", id);
185
80
  setDraggedId(id);
186
81
  };
187
- const handleDragEnd = () => {
188
- setDraggedId(null);
189
- };
190
82
  const handleDragOver = (e, targetId) => {
191
83
  if (!draggedId || draggedId === targetId)
192
84
  return;
@@ -194,7 +86,6 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
194
86
  if (draggedNode && findNode(draggedNode, targetId))
195
87
  return;
196
88
  e.preventDefault();
197
- e.dataTransfer.dropEffect = "move";
198
89
  };
199
90
  const handleDrop = (e, targetId) => {
200
91
  if (!draggedId || draggedId === targetId)
@@ -228,79 +119,14 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
228
119
  const isSelected = node.id === selectedId;
229
120
  const isCollapsed = collapsedIds.has(node.id);
230
121
  const hasChildren = node.children && node.children.length > 0;
231
- return (_jsxs("div", { children: [_jsxs("div", { style: Object.assign(Object.assign(Object.assign({}, styles.row), (isSelected ? styles.rowSelected : null)), { paddingLeft: `${depth * 10 + 6}px`, cursor: node.id !== prefabData.root.id ? "grab" : "pointer" }), draggable: node.id !== prefabData.root.id, onClick: (e) => { e.stopPropagation(); setSelectedId(node.id); }, onContextMenu: (e) => handleContextMenu(e, node.id), onDragStart: (e) => handleDragStart(e, node.id), onDragEnd: handleDragEnd, onDragOver: (e) => handleDragOver(e, node.id), onDrop: (e) => handleDrop(e, node.id), onPointerEnter: (e) => {
232
- if (!isSelected)
233
- e.currentTarget.style.background = "rgba(255,255,255,0.06)";
234
- }, onPointerLeave: (e) => {
235
- if (!isSelected)
236
- e.currentTarget.style.background = "transparent";
237
- }, children: [_jsx("span", { style: Object.assign(Object.assign({}, styles.chevron), { visibility: hasChildren ? 'visible' : 'hidden' }), onClick: (e) => hasChildren && toggleCollapse(e, node.id), onPointerEnter: (e) => {
238
- e.currentTarget.style.opacity = "0.9";
239
- }, onPointerLeave: (e) => {
240
- e.currentTarget.style.opacity = "0.55";
241
- }, children: isCollapsed ? '▶' : '▼' }), node.id !== prefabData.root.id && (_jsx("span", { style: styles.dragHandle, onPointerEnter: (e) => {
242
- e.currentTarget.style.opacity = "0.9";
243
- }, onPointerLeave: (e) => {
244
- e.currentTarget.style.opacity = "0.4";
245
- }, children: "\u22EE\u22EE" })), _jsx("span", { style: styles.idText, children: node.id })] }), !isCollapsed && node.children && (_jsx("div", { children: node.children.map(child => renderNode(child, depth + 1)) }))] }, node.id));
122
+ const isRoot = node.id === prefabData.root.id;
123
+ return (_jsxs("div", { children: [_jsxs("div", { style: Object.assign(Object.assign(Object.assign({}, tree.row), (isSelected ? tree.selected : {})), { paddingLeft: `${depth * 12 + 6}px` }), draggable: !isRoot, onClick: (e) => { e.stopPropagation(); setSelectedId(node.id); }, onContextMenu: (e) => handleContextMenu(e, node.id), onDragStart: (e) => handleDragStart(e, node.id), onDragEnd: () => setDraggedId(null), onDragOver: (e) => handleDragOver(e, node.id), onDrop: (e) => handleDrop(e, node.id), children: [_jsx("span", { style: {
124
+ width: 12,
125
+ opacity: 0.6,
126
+ marginRight: 4,
127
+ cursor: 'pointer',
128
+ visibility: hasChildren ? 'visible' : 'hidden'
129
+ }, onClick: (e) => hasChildren && toggleCollapse(e, node.id), children: isCollapsed ? '▶' : '▼' }), !isRoot && _jsx("span", { style: { marginRight: 4, opacity: 0.4 }, children: "\u22EE\u22EE" }), _jsx("span", { style: { overflow: 'hidden', textOverflow: 'ellipsis' }, children: node.id })] }), !isCollapsed && node.children && node.children.map(child => renderNode(child, depth + 1))] }, node.id));
246
130
  };
247
- return (_jsxs(_Fragment, { children: [_jsxs("div", { style: Object.assign(Object.assign({}, styles.panel), { width: isTreeCollapsed ? 'auto' : '14rem' }), onClick: closeContextMenu, children: [_jsxs("div", { style: styles.panelHeader, onClick: (e) => { e.stopPropagation(); setIsTreeCollapsed(!isTreeCollapsed); }, onPointerEnter: (e) => {
248
- e.currentTarget.style.background = "rgba(255,255,255,0.08)";
249
- }, onPointerLeave: (e) => {
250
- e.currentTarget.style.background = "rgba(255,255,255,0.05)";
251
- }, children: [_jsx("span", { children: "Prefab Graph" }), _jsx("span", { style: { fontSize: 10, opacity: 0.8 }, children: isTreeCollapsed ? '▶' : '◀' })] }), !isTreeCollapsed && (_jsx("div", { style: Object.assign(Object.assign({}, styles.scroll), { padding: 2 }), children: renderNode(prefabData.root) }))] }), contextMenu && (_jsxs("div", { style: Object.assign(Object.assign({}, styles.contextMenu), { top: contextMenu.y, left: contextMenu.x }), onClick: (e) => e.stopPropagation(), onPointerLeave: closeContextMenu, children: [_jsx("button", { style: Object.assign(Object.assign({}, styles.menuItem), styles.menuDivider), onClick: () => handleAddChild(contextMenu.nodeId), onPointerEnter: (e) => {
252
- e.currentTarget.style.background = "rgba(255,255,255,0.08)";
253
- }, onPointerLeave: (e) => {
254
- e.currentTarget.style.background = "transparent";
255
- }, children: "Add Child" }), contextMenu.nodeId !== prefabData.root.id && (_jsxs(_Fragment, { children: [_jsx("button", { style: Object.assign(Object.assign({}, styles.menuItem), styles.menuDivider), onClick: () => handleDuplicate(contextMenu.nodeId), onPointerEnter: (e) => {
256
- e.currentTarget.style.background = "rgba(255,255,255,0.08)";
257
- }, onPointerLeave: (e) => {
258
- e.currentTarget.style.background = "transparent";
259
- }, children: "Duplicate" }), _jsx("button", { style: Object.assign(Object.assign({}, styles.menuItem), styles.menuItemDanger), onClick: () => handleDelete(contextMenu.nodeId), onPointerEnter: (e) => {
260
- e.currentTarget.style.background = "rgba(255,255,255,0.08)";
261
- }, onPointerLeave: (e) => {
262
- e.currentTarget.style.background = "transparent";
263
- }, children: "Delete" })] }))] }))] }));
264
- }
265
- // --- Helpers ---
266
- function findNode(root, id) {
267
- if (root.id === id)
268
- return root;
269
- if (root.children) {
270
- for (const child of root.children) {
271
- const found = findNode(child, id);
272
- if (found)
273
- return found;
274
- }
275
- }
276
- return null;
277
- }
278
- function findParent(root, id) {
279
- if (!root.children)
280
- return null;
281
- for (const child of root.children) {
282
- if (child.id === id)
283
- return root;
284
- const found = findParent(child, id);
285
- if (found)
286
- return found;
287
- }
288
- return null;
289
- }
290
- function deleteNodeFromTree(root, id) {
291
- if (root.id === id)
292
- return null;
293
- if (root.children) {
294
- root.children = root.children
295
- .map(child => deleteNodeFromTree(child, id))
296
- .filter((child) => child !== null);
297
- }
298
- return root;
299
- }
300
- function cloneNode(node) {
301
- const newNode = Object.assign(Object.assign({}, node), { id: crypto.randomUUID() });
302
- if (newNode.children) {
303
- newNode.children = newNode.children.map(child => cloneNode(child));
304
- }
305
- return newNode;
131
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { style: Object.assign(Object.assign({}, tree.panel), { width: collapsed ? 'auto' : 224 }), onClick: () => setContextMenu(null), children: [_jsxs("div", { style: base.header, onClick: () => setCollapsed(!collapsed), children: [_jsx("span", { children: "Scene" }), _jsx("span", { children: collapsed ? '▶' : '◀' })] }), !collapsed && _jsx("div", { style: tree.scroll, children: renderNode(prefabData.root) })] }), contextMenu && (_jsxs("div", { style: Object.assign(Object.assign({}, menu.container), { top: contextMenu.y, left: contextMenu.x }), onClick: (e) => e.stopPropagation(), onPointerLeave: () => setContextMenu(null), children: [_jsx("button", { style: menu.item, onClick: () => handleAddChild(contextMenu.nodeId), children: "Add Child" }), contextMenu.nodeId !== prefabData.root.id && (_jsxs(_Fragment, { children: [_jsx("button", { style: menu.item, onClick: () => handleDuplicate(contextMenu.nodeId), children: "Duplicate" }), _jsx("button", { style: Object.assign(Object.assign({}, menu.item), menu.danger), onClick: () => handleDelete(contextMenu.nodeId), children: "Delete" })] }))] }))] }));
306
132
  }
@@ -1,244 +1,63 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
1
12
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
13
  import { useState, useEffect } from 'react';
3
14
  import EditorTree from './EditorTree';
4
15
  import { getAllComponents } from './components/ComponentRegistry';
16
+ import { base, inspector } from './styles';
17
+ import { findNode, updateNode, deleteNode } from './utils';
5
18
  function EditorUI({ prefabData, setPrefabData, selectedId, setSelectedId, transformMode, setTransformMode, basePath }) {
6
- const [isInspectorCollapsed, setIsInspectorCollapsed] = useState(false);
7
- const ui = {
8
- panel: {
9
- position: 'absolute',
10
- top: 8,
11
- right: 8,
12
- zIndex: 20,
13
- width: 260,
14
- background: 'rgba(0,0,0,0.55)',
15
- color: 'rgba(255,255,255,0.9)',
16
- border: '1px solid rgba(255,255,255,0.12)',
17
- borderRadius: 6,
18
- overflow: 'hidden',
19
- backdropFilter: 'blur(6px)',
20
- WebkitBackdropFilter: 'blur(6px)',
21
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace",
22
- fontSize: 11,
23
- lineHeight: 1.2,
24
- },
25
- header: {
26
- padding: '4px 6px',
27
- display: 'flex',
28
- alignItems: 'center',
29
- justifyContent: 'space-between',
30
- cursor: 'pointer',
31
- background: 'rgba(255,255,255,0.05)',
32
- borderBottom: '1px solid rgba(255,255,255,0.10)',
33
- textTransform: 'uppercase',
34
- letterSpacing: '0.08em',
35
- fontSize: 10,
36
- color: 'rgba(255,255,255,0.7)',
37
- userSelect: 'none',
38
- WebkitUserSelect: 'none',
39
- },
40
- left: {
41
- position: 'absolute',
42
- top: 8,
43
- left: 8,
44
- zIndex: 20,
45
- },
46
- };
47
- const updateNode = (updater) => {
19
+ const [collapsed, setCollapsed] = useState(false);
20
+ const updateNodeHandler = (updater) => {
48
21
  if (!prefabData || !setPrefabData || !selectedId)
49
22
  return;
50
- setPrefabData(prev => (Object.assign(Object.assign({}, prev), { root: updatePrefabNode(prev.root, selectedId, updater) })));
23
+ setPrefabData(prev => (Object.assign(Object.assign({}, prev), { root: updateNode(prev.root, selectedId, updater) })));
51
24
  };
52
- const deleteNode = () => {
53
- if (!prefabData || !setPrefabData || !selectedId)
54
- return;
55
- if (selectedId === prefabData.root.id) {
56
- alert("Cannot delete root node");
25
+ const deleteNodeHandler = () => {
26
+ if (!prefabData || !setPrefabData || !selectedId || selectedId === prefabData.root.id)
57
27
  return;
58
- }
59
- setPrefabData(prev => {
60
- const newRoot = deletePrefabNode(prev.root, selectedId);
61
- return Object.assign(Object.assign({}, prev), { root: newRoot });
62
- });
28
+ setPrefabData(prev => (Object.assign(Object.assign({}, prev), { root: deleteNode(prev.root, selectedId) })));
63
29
  setSelectedId(null);
64
30
  };
65
31
  const selectedNode = selectedId && prefabData ? findNode(prefabData.root, selectedId) : null;
66
- // if (!selectedNode) return null;
67
- return _jsxs(_Fragment, { children: [_jsxs("div", { style: ui.panel, children: [_jsxs("div", { style: ui.header, onClick: () => setIsInspectorCollapsed(!isInspectorCollapsed), onPointerEnter: (e) => {
68
- e.currentTarget.style.background = 'rgba(255,255,255,0.08)';
69
- }, onPointerLeave: (e) => {
70
- e.currentTarget.style.background = 'rgba(255,255,255,0.05)';
71
- }, children: [_jsx("span", { children: "Inspector" }), _jsx("span", { style: { fontSize: 10, opacity: 0.8 }, children: isInspectorCollapsed ? '◀' : '▶' })] }), !isInspectorCollapsed && selectedNode && (_jsx(NodeInspector, { node: selectedNode, updateNode: updateNode, deleteNode: deleteNode, transformMode: transformMode, setTransformMode: setTransformMode, basePath: basePath }))] }), _jsx("div", { style: ui.left, children: _jsx(EditorTree, { prefabData: prefabData, setPrefabData: setPrefabData, selectedId: selectedId, setSelectedId: setSelectedId }) })] });
32
+ return _jsxs(_Fragment, { children: [_jsxs("div", { style: inspector.panel, children: [_jsxs("div", { style: base.header, onClick: () => setCollapsed(!collapsed), children: [_jsx("span", { children: "Inspector" }), _jsx("span", { children: collapsed ? '◀' : '▼' })] }), !collapsed && selectedNode && (_jsx(NodeInspector, { node: selectedNode, updateNode: updateNodeHandler, deleteNode: deleteNodeHandler, transformMode: transformMode, setTransformMode: setTransformMode, basePath: basePath }))] }), _jsx("div", { style: { position: 'absolute', top: 8, left: 8, zIndex: 20 }, children: _jsx(EditorTree, { prefabData: prefabData, setPrefabData: setPrefabData, selectedId: selectedId, setSelectedId: setSelectedId }) })] });
72
33
  }
73
34
  function NodeInspector({ node, updateNode, deleteNode, transformMode, setTransformMode, basePath }) {
74
35
  const ALL_COMPONENTS = getAllComponents();
75
- const allComponentKeys = Object.keys(ALL_COMPONENTS);
76
- const [addComponentType, setAddComponentType] = useState(allComponentKeys[0]);
77
- const s = {
78
- root: {
79
- display: 'flex',
80
- flexDirection: 'column',
81
- gap: 6,
82
- padding: 6,
83
- maxHeight: '80vh',
84
- overflowY: 'auto',
85
- },
86
- section: {
87
- paddingBottom: 6,
88
- borderBottom: '1px solid rgba(255,255,255,0.10)',
89
- },
90
- label: {
91
- display: 'block',
92
- fontSize: 10,
93
- opacity: 0.7,
94
- textTransform: 'uppercase',
95
- letterSpacing: '0.08em',
96
- marginBottom: 4,
97
- },
98
- input: {
99
- width: '100%',
100
- background: 'rgba(255,255,255,0.06)',
101
- border: '1px solid rgba(255,255,255,0.14)',
102
- borderRadius: 4,
103
- padding: '4px 6px',
104
- color: 'rgba(255,255,255,0.92)',
105
- font: 'inherit',
106
- outline: 'none',
107
- },
108
- row: {
109
- display: 'flex',
110
- alignItems: 'center',
111
- justifyContent: 'space-between',
112
- gap: 8,
113
- },
114
- button: {
115
- padding: '2px 6px',
116
- background: 'transparent',
117
- color: 'rgba(255,255,255,0.9)',
118
- border: '1px solid rgba(255,255,255,0.14)',
119
- borderRadius: 4,
120
- cursor: 'pointer',
121
- font: 'inherit',
122
- },
123
- buttonActive: {
124
- background: 'rgba(255,255,255,0.10)',
125
- },
126
- smallDanger: {
127
- background: 'transparent',
128
- border: 'none',
129
- cursor: 'pointer',
130
- color: 'rgba(255,120,120,0.95)',
131
- font: 'inherit',
132
- padding: '2px 4px',
133
- },
134
- componentHeader: {
135
- display: 'flex',
136
- alignItems: 'center',
137
- justifyContent: 'space-between',
138
- padding: '4px 0',
139
- borderBottom: '1px solid rgba(255,255,255,0.08)',
140
- marginBottom: 4,
141
- },
142
- componentTitle: {
143
- fontSize: 10,
144
- textTransform: 'uppercase',
145
- letterSpacing: '0.08em',
146
- opacity: 0.8,
147
- },
148
- select: {
149
- flex: 1,
150
- background: 'rgba(255,255,255,0.06)',
151
- border: '1px solid rgba(255,255,255,0.14)',
152
- borderRadius: 4,
153
- padding: '4px 6px',
154
- color: 'rgba(255,255,255,0.92)',
155
- font: 'inherit',
156
- outline: 'none',
157
- },
158
- addButton: {
159
- width: 28,
160
- padding: '4px 0',
161
- background: 'rgba(255,255,255,0.08)',
162
- color: 'rgba(255,255,255,0.92)',
163
- border: '1px solid rgba(255,255,255,0.14)',
164
- borderRadius: 4,
165
- cursor: 'pointer',
166
- font: 'inherit',
167
- },
168
- disabled: {
169
- opacity: 0.35,
170
- cursor: 'not-allowed',
171
- },
172
- };
173
- const componentKeys = Object.keys(node.components || {}).join(',');
36
+ const allKeys = Object.keys(ALL_COMPONENTS);
37
+ const available = allKeys.filter(k => { var _a; return !((_a = node.components) === null || _a === void 0 ? void 0 : _a[k.toLowerCase()]); });
38
+ const [addType, setAddType] = useState(available[0] || "");
174
39
  useEffect(() => {
175
- // Components stored on nodes use lowercase keys (e.g. 'geometry'),
176
- // while the registry keys are the component names (e.g. 'Geometry').
177
- const available = allComponentKeys.filter(k => { var _a; return !((_a = node.components) === null || _a === void 0 ? void 0 : _a[k.toLowerCase()]); });
178
- if (!available.includes(addComponentType)) {
179
- setAddComponentType(available[0] || "");
180
- }
181
- }, [componentKeys, addComponentType, node.components, allComponentKeys]);
182
- return _jsxs("div", { style: s.root, children: [_jsx("div", { style: s.section, children: _jsx("input", { style: s.input, value: node.id, onChange: e => updateNode(n => (Object.assign(Object.assign({}, n), { id: e.target.value }))) }) }), _jsxs("div", { style: Object.assign(Object.assign(Object.assign({}, s.row), s.section), { paddingBottom: 6 }), children: [_jsx("label", { style: Object.assign(Object.assign({}, s.label), { marginBottom: 0 }), children: "Components" }), _jsx("button", { onClick: deleteNode, style: s.smallDanger, title: "Delete node", children: "\u2715" })] }), node.components && Object.entries(node.components).map(([key, comp]) => {
183
- if (!comp)
184
- return null;
185
- const componentDef = ALL_COMPONENTS[comp.type];
186
- if (!componentDef)
187
- return _jsxs("div", { style: { padding: '4px 0', color: 'rgba(255,120,120,0.95)', fontSize: 11 }, children: ["Unknown component type: ", comp.type, _jsx("textarea", { defaultValue: JSON.stringify(comp) })] }, key);
188
- const EditorComp = componentDef.Editor;
189
- return (_jsxs("div", { style: { padding: '0 2px' }, children: [_jsxs("div", { style: s.componentHeader, children: [_jsx("span", { style: s.componentTitle, children: key }), _jsx("button", { onClick: () => updateNode(n => {
190
- const components = Object.assign({}, n.components);
191
- delete components[key];
192
- return Object.assign(Object.assign({}, n), { components });
193
- }), style: s.smallDanger, title: "Remove component", children: "\u2715" })] }), EditorComp ? (_jsx(EditorComp, { component: comp, onUpdate: (newProps) => updateNode(n => (Object.assign(Object.assign({}, n), { components: Object.assign(Object.assign({}, n.components), { [key]: Object.assign(Object.assign({}, comp), { properties: Object.assign(Object.assign({}, comp.properties), newProps) }) }) }))), basePath: basePath, transformMode: transformMode, setTransformMode: setTransformMode })) : null] }, key));
194
- }), _jsxs("div", { style: Object.assign(Object.assign({}, s.section), { borderBottom: 'none', paddingBottom: 0 }), children: [_jsx("label", { style: s.label, children: "Add Component" }), _jsxs("div", { style: { display: 'flex', gap: 6 }, children: [_jsx("select", { style: s.select, value: addComponentType, onChange: e => setAddComponentType(e.target.value), children: allComponentKeys.filter(k => { var _a; return !((_a = node.components) === null || _a === void 0 ? void 0 : _a[k.toLowerCase()]); }).map(k => (_jsx("option", { value: k, children: k }, k))) }), _jsx("button", { style: Object.assign(Object.assign({}, s.addButton), (!addComponentType ? s.disabled : null)), disabled: !addComponentType, onClick: () => {
195
- var _a;
196
- if (!addComponentType)
40
+ const newAvailable = allKeys.filter(k => { var _a; return !((_a = node.components) === null || _a === void 0 ? void 0 : _a[k.toLowerCase()]); });
41
+ if (!newAvailable.includes(addType))
42
+ setAddType(newAvailable[0] || "");
43
+ }, [Object.keys(node.components || {}).join(',')]);
44
+ return _jsxs("div", { style: inspector.content, children: [_jsxs("div", { style: base.section, children: [_jsx("div", { style: base.label, children: "Node ID" }), _jsx("input", { style: base.input, value: node.id, onChange: e => updateNode(n => (Object.assign(Object.assign({}, n), { id: e.target.value }))) })] }), _jsxs("div", { style: base.section, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }, children: [_jsx("div", { style: base.label, children: "Components" }), _jsx("button", { style: Object.assign(Object.assign({}, base.btn), base.btnDanger), onClick: deleteNode, children: "Delete Node" })] }), node.components && Object.entries(node.components).map(([key, comp]) => {
45
+ if (!comp)
46
+ return null;
47
+ const def = ALL_COMPONENTS[comp.type];
48
+ if (!def)
49
+ return _jsxs("div", { style: { color: '#ff8888', fontSize: 11 }, children: ["Unknown: ", comp.type] }, key);
50
+ return (_jsxs("div", { style: { marginBottom: 8 }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }, children: [_jsx("div", { style: { fontSize: 11, fontWeight: 500 }, children: key }), _jsx("button", { style: Object.assign(Object.assign({}, base.btn), { padding: '2px 6px' }), onClick: () => updateNode(n => {
51
+ const _a = n.components || {}, _b = key, _ = _a[_b], rest = __rest(_a, [typeof _b === "symbol" ? _b : _b + ""]);
52
+ return Object.assign(Object.assign({}, n), { components: rest });
53
+ }), children: "\u2715" })] }), def.Editor && (_jsx(def.Editor, { component: comp, onUpdate: (newProps) => updateNode(n => (Object.assign(Object.assign({}, n), { components: Object.assign(Object.assign({}, n.components), { [key]: Object.assign(Object.assign({}, comp), { properties: Object.assign(Object.assign({}, comp.properties), newProps) }) }) }))), basePath: basePath, transformMode: transformMode, setTransformMode: setTransformMode }))] }, key));
54
+ })] }), available.length > 0 && (_jsxs("div", { children: [_jsx("div", { style: base.label, children: "Add Component" }), _jsxs("div", { style: base.row, children: [_jsx("select", { style: Object.assign(Object.assign({}, base.input), { flex: 1 }), value: addType, onChange: e => setAddType(e.target.value), children: available.map(k => _jsx("option", { value: k, children: k }, k)) }), _jsx("button", { style: base.btn, disabled: !addType, onClick: () => {
55
+ if (!addType)
197
56
  return;
198
- const def = ALL_COMPONENTS[addComponentType];
199
- if (def && !((_a = node.components) === null || _a === void 0 ? void 0 : _a[addComponentType.toLowerCase()])) {
200
- const key = addComponentType.toLowerCase();
201
- updateNode(n => (Object.assign(Object.assign({}, n), { components: Object.assign(Object.assign({}, n.components), { [key]: { type: def.name, properties: def.defaultProperties } }) })));
57
+ const def = ALL_COMPONENTS[addType];
58
+ if (def) {
59
+ updateNode(n => (Object.assign(Object.assign({}, n), { components: Object.assign(Object.assign({}, n.components), { [addType.toLowerCase()]: { type: def.name, properties: def.defaultProperties } }) })));
202
60
  }
203
- }, onPointerEnter: (e) => {
204
- if (!addComponentType)
205
- return;
206
- e.currentTarget.style.background = 'rgba(255,255,255,0.12)';
207
- }, onPointerLeave: (e) => {
208
- if (!addComponentType)
209
- return;
210
- e.currentTarget.style.background = 'rgba(255,255,255,0.08)';
211
- }, children: "+" })] })] })] });
212
- }
213
- function findNode(root, id) {
214
- if (root.id === id)
215
- return root;
216
- if (root.children) {
217
- for (const child of root.children) {
218
- const found = findNode(child, id);
219
- if (found)
220
- return found;
221
- }
222
- }
223
- return null;
224
- }
225
- function updatePrefabNode(root, id, update) {
226
- if (root.id === id) {
227
- return update(root);
228
- }
229
- if (root.children) {
230
- return Object.assign(Object.assign({}, root), { children: root.children.map(child => updatePrefabNode(child, id, update)) });
231
- }
232
- return root;
233
- }
234
- function deletePrefabNode(root, id) {
235
- if (root.id === id)
236
- return null;
237
- if (root.children) {
238
- return Object.assign(Object.assign({}, root), { children: root.children
239
- .map(child => deletePrefabNode(child, id))
240
- .filter((child) => child !== null) });
241
- }
242
- return root;
61
+ }, children: "+" })] })] }))] });
243
62
  }
244
63
  export default EditorUI;