react-three-game 0.0.68 → 0.0.70
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.
- package/dist/helpers/SoundManager.d.ts +2 -0
- package/dist/helpers/SoundManager.js +6 -0
- package/dist/index.d.ts +10 -7
- package/dist/index.js +8 -4
- package/dist/shared/GameCanvas.js +0 -2
- package/dist/tools/assetviewer/page.d.ts +5 -0
- package/dist/tools/assetviewer/page.js +3 -0
- package/dist/tools/dragdrop/DragDropLoader.d.ts +3 -2
- package/dist/tools/dragdrop/DragDropLoader.js +18 -3
- package/dist/tools/dragdrop/index.d.ts +2 -2
- package/dist/tools/dragdrop/index.js +1 -1
- package/dist/tools/dragdrop/modelLoader.d.ts +10 -0
- package/dist/tools/dragdrop/modelLoader.js +60 -0
- package/dist/tools/prefabeditor/EditorTree.js +6 -30
- package/dist/tools/prefabeditor/EditorTreeMenus.js +3 -3
- package/dist/tools/prefabeditor/EditorUI.js +6 -4
- package/dist/tools/prefabeditor/InstanceProvider.d.ts +2 -0
- package/dist/tools/prefabeditor/InstanceProvider.js +54 -52
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +22 -0
- package/dist/tools/prefabeditor/PrefabEditor.js +68 -27
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +5 -1
- package/dist/tools/prefabeditor/PrefabRoot.js +148 -145
- package/dist/tools/prefabeditor/components/ClickComponent.js +10 -7
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +10 -4
- package/dist/tools/prefabeditor/components/ComponentRegistry.js +6 -6
- package/dist/tools/prefabeditor/components/GeometryComponent.js +1 -1
- package/dist/tools/prefabeditor/components/Input.d.ts +16 -0
- package/dist/tools/prefabeditor/components/Input.js +33 -0
- package/dist/tools/prefabeditor/components/MaterialComponent.js +10 -2
- package/dist/tools/prefabeditor/components/ModelComponent.js +35 -43
- package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +10 -1
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +122 -28
- package/dist/tools/prefabeditor/components/SoundComponent.d.ts +3 -0
- package/dist/tools/prefabeditor/components/SoundComponent.js +240 -0
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +6 -1
- package/dist/tools/prefabeditor/components/TransformComponent.js +2 -2
- package/dist/tools/prefabeditor/components/index.js +2 -0
- package/dist/tools/prefabeditor/prefabStore.d.ts +1 -0
- package/dist/tools/prefabeditor/prefabStore.js +11 -13
- package/dist/tools/prefabeditor/sceneApi.d.ts +15 -1
- package/dist/tools/prefabeditor/sceneApi.js +77 -32
- package/dist/tools/prefabeditor/styles.d.ts +1 -0
- package/dist/tools/prefabeditor/styles.js +9 -0
- package/dist/tools/prefabeditor/types.d.ts +13 -0
- package/dist/tools/prefabeditor/types.js +28 -1
- package/dist/tools/prefabeditor/useClickValid.d.ts +13 -0
- package/dist/tools/prefabeditor/useClickValid.js +21 -0
- package/dist/tools/prefabeditor/utils.d.ts +2 -0
- package/dist/tools/prefabeditor/utils.js +34 -35
- package/package.json +1 -1
- package/dist/tools/prefabeditor/EditorContext.d.ts +0 -16
- package/dist/tools/prefabeditor/EditorContext.js +0 -9
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
var __rest = (this && this.__rest) || function (s, e) {
|
|
11
2
|
var t = {};
|
|
12
3
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
@@ -21,35 +12,63 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
21
12
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
22
13
|
import { useHelper } from "@react-three/drei";
|
|
23
14
|
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
24
|
-
import { BoxHelper, Euler, Matrix4,
|
|
15
|
+
import { BoxHelper, Euler, Matrix4, } from "three";
|
|
25
16
|
import { useStore } from "zustand";
|
|
26
|
-
import {
|
|
17
|
+
import { useClickValid } from "./useClickValid";
|
|
18
|
+
import { findComponent } from "./types";
|
|
19
|
+
import { getComponentDef, getComponentAssetRefs, registerComponent } from "./components/ComponentRegistry";
|
|
27
20
|
import components from "./components";
|
|
28
|
-
import { loadModel, loadTexture } from "../dragdrop";
|
|
21
|
+
import { loadModel, loadSound, loadTexture } from "../dragdrop";
|
|
29
22
|
import { GameInstance, GameInstanceProvider, getRepeatAxesFromModelProperties, useInstanceCheck } from "./InstanceProvider";
|
|
30
|
-
import { decompose } from "./utils";
|
|
31
|
-
import {
|
|
23
|
+
import { composeTransform, decompose } from "./utils";
|
|
24
|
+
import { isPhysicsProps } from "./components/PhysicsComponent";
|
|
25
|
+
import { createPrefabStore, PrefabStoreProvider, prefabStoreToPrefab, useOptionalPrefabStoreApi, usePrefabChildIds, usePrefabNode, usePrefabRootId } from "./prefabStore";
|
|
26
|
+
import { sound as soundManager } from "../../helpers/SoundManager";
|
|
32
27
|
components.forEach(registerComponent);
|
|
33
28
|
const IDENTITY = new Matrix4();
|
|
34
29
|
const EMPTY_MODELS = {};
|
|
35
30
|
const EMPTY_TEXTURES = {};
|
|
36
|
-
|
|
31
|
+
const EMPTY_SOUNDS = {};
|
|
32
|
+
/** Resolve a relative or absolute asset file path against a base path. */
|
|
33
|
+
function resolveAssetPath(basePath, file) {
|
|
34
|
+
if (file.startsWith("http://") || file.startsWith("https://"))
|
|
35
|
+
return file;
|
|
36
|
+
return file.startsWith("/") ? `${basePath}${file}` : `${basePath}/${file}`;
|
|
37
|
+
}
|
|
38
|
+
/** Check if all model assets required by a node are loaded. */
|
|
39
|
+
function isNodeReady(node, loadedModels) {
|
|
40
|
+
var _a;
|
|
41
|
+
const model = findComponent(node, "Model");
|
|
42
|
+
if (!((_a = model === null || model === void 0 ? void 0 : model.properties) === null || _a === void 0 ? void 0 : _a.filename))
|
|
43
|
+
return true;
|
|
44
|
+
return Boolean(loadedModels[model.properties.filename]);
|
|
45
|
+
}
|
|
46
|
+
export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSelect, onClick, onSelectedObjectChange, onFocusNode, basePath = "", injectedModels = EMPTY_MODELS, injectedTextures = EMPTY_TEXTURES, injectedSounds = EMPTY_SOUNDS }, ref) => {
|
|
47
|
+
var _a;
|
|
37
48
|
const [models, setModels] = useState({});
|
|
38
49
|
const [textures, setTextures] = useState({});
|
|
50
|
+
const [sounds, setSounds] = useState({});
|
|
39
51
|
const loading = useRef(new Set());
|
|
52
|
+
const failedModels = useRef(new Set());
|
|
40
53
|
const failedTextures = useRef(new Set());
|
|
54
|
+
const failedSounds = useRef(new Set());
|
|
41
55
|
const objectRefs = useRef({});
|
|
42
56
|
const rigidBodyRefs = useRef(new Map());
|
|
43
57
|
const rootRef = useRef(null);
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
const
|
|
58
|
+
const parentStore = useOptionalPrefabStoreApi();
|
|
59
|
+
const [ownedStore] = useState(() => { var _a, _b; return createPrefabStore(data !== null && data !== void 0 ? data : prefabStoreToPrefab((_b = (_a = store === null || store === void 0 ? void 0 : store.getState()) !== null && _a !== void 0 ? _a : parentStore === null || parentStore === void 0 ? void 0 : parentStore.getState()) !== null && _b !== void 0 ? _b : missingStoreState())); });
|
|
60
|
+
const resolvedStore = (_a = store !== null && store !== void 0 ? store : parentStore) !== null && _a !== void 0 ? _a : ownedStore;
|
|
61
|
+
const usesOwnedStore = resolvedStore === ownedStore;
|
|
62
|
+
const shouldProvideStoreContext = !parentStore || parentStore !== resolvedStore;
|
|
63
|
+
const assetManifestKey = useStore(resolvedStore, state => state.assetManifestKey);
|
|
47
64
|
const availableModels = useMemo(() => (Object.assign(Object.assign({}, models), injectedModels)), [models, injectedModels]);
|
|
48
65
|
const availableTextures = useMemo(() => (Object.assign(Object.assign({}, textures), injectedTextures)), [textures, injectedTextures]);
|
|
66
|
+
const availableSounds = useMemo(() => (Object.assign(Object.assign({}, sounds), injectedSounds)), [sounds, injectedSounds]);
|
|
49
67
|
useImperativeHandle(ref, () => ({
|
|
50
68
|
root: rootRef.current,
|
|
51
69
|
rigidBodyRefs: rigidBodyRefs.current,
|
|
52
70
|
getObject: (nodeId) => { var _a; return (_a = objectRefs.current[nodeId]) !== null && _a !== void 0 ? _a : null; },
|
|
71
|
+
getRigidBody: (nodeId) => { var _a; return (_a = rigidBodyRefs.current.get(nodeId)) !== null && _a !== void 0 ? _a : null; },
|
|
53
72
|
focusNode: (nodeId) => onFocusNode === null || onFocusNode === void 0 ? void 0 : onFocusNode(nodeId),
|
|
54
73
|
}), [onFocusNode]);
|
|
55
74
|
const registerRef = useCallback((id, obj) => {
|
|
@@ -61,6 +80,10 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
61
80
|
const registerRigidBodyRef = useCallback((id, rb) => {
|
|
62
81
|
rigidBodyRefs.current.set(id, rb);
|
|
63
82
|
}, []);
|
|
83
|
+
const getRigidBody = useCallback((id) => {
|
|
84
|
+
var _a;
|
|
85
|
+
return (_a = rigidBodyRefs.current.get(id)) !== null && _a !== void 0 ? _a : null;
|
|
86
|
+
}, []);
|
|
64
87
|
useEffect(() => {
|
|
65
88
|
const originalError = console.error;
|
|
66
89
|
console.error = (...args) => {
|
|
@@ -71,78 +94,81 @@ export const PrefabRoot = forwardRef(({ editMode, data, store, selectedId, onSel
|
|
|
71
94
|
return () => { console.error = originalError; };
|
|
72
95
|
}, []);
|
|
73
96
|
useEffect(() => {
|
|
74
|
-
if (
|
|
75
|
-
|
|
97
|
+
if (usesOwnedStore && data) {
|
|
98
|
+
resolvedStore.getState().replacePrefab(data);
|
|
76
99
|
}
|
|
77
|
-
}, [data,
|
|
100
|
+
}, [data, resolvedStore, usesOwnedStore]);
|
|
78
101
|
useEffect(() => {
|
|
79
|
-
const syncAssets = (snapshot =
|
|
102
|
+
const syncAssets = (snapshot = resolvedStore.getState()) => {
|
|
80
103
|
const modelsToLoad = new Set();
|
|
81
104
|
const texturesToLoad = new Set();
|
|
105
|
+
const soundsToLoad = new Set();
|
|
82
106
|
Object.values(snapshot.nodesById).forEach(node => {
|
|
83
107
|
var _a;
|
|
84
108
|
Object.values((_a = node.components) !== null && _a !== void 0 ? _a : {}).forEach(component => {
|
|
85
|
-
var _a
|
|
109
|
+
var _a;
|
|
86
110
|
if (!(component === null || component === void 0 ? void 0 : component.type))
|
|
87
111
|
return;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (component.type === 'SpotLight' && ((_d = component.properties) === null || _d === void 0 ? void 0 : _d.map)) {
|
|
96
|
-
texturesToLoad.add(component.properties.map);
|
|
112
|
+
for (const ref of getComponentAssetRefs(component.type, (_a = component.properties) !== null && _a !== void 0 ? _a : {})) {
|
|
113
|
+
if (ref.type === 'model')
|
|
114
|
+
modelsToLoad.add(ref.path);
|
|
115
|
+
else if (ref.type === 'texture')
|
|
116
|
+
texturesToLoad.add(ref.path);
|
|
117
|
+
else if (ref.type === 'sound')
|
|
118
|
+
soundsToLoad.add(ref.path);
|
|
97
119
|
}
|
|
98
120
|
});
|
|
99
121
|
});
|
|
100
|
-
|
|
101
|
-
if (
|
|
122
|
+
const loadAsset = (file, loaded, injected, failed, loader) => {
|
|
123
|
+
if (loaded[file] || injected[file] || loading.current.has(file) || failed.has(file))
|
|
102
124
|
return;
|
|
103
125
|
loading.current.add(file);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
126
|
+
void loader(resolveAssetPath(basePath, file)).then(result => {
|
|
127
|
+
if (!result.success) {
|
|
128
|
+
console.warn(`Failed to load asset: ${file}`, result.error);
|
|
129
|
+
loading.current.delete(file);
|
|
130
|
+
failed.add(file);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
modelsToLoad.forEach(file => loadAsset(file, models, injectedModels, failedModels.current, (path) => loadModel(path).then(result => {
|
|
135
|
+
const model = result.model;
|
|
136
|
+
if (result.success && model) {
|
|
110
137
|
setModels(m => (Object.assign(Object.assign({}, m), { [file]: model })));
|
|
111
138
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (result.success && result.texture) {
|
|
125
|
-
setTextures(t => (Object.assign(Object.assign({}, t), { [file]: result.texture })));
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
console.warn(`Failed to load texture: ${path}`, result.error);
|
|
139
|
+
return result;
|
|
140
|
+
})));
|
|
141
|
+
texturesToLoad.forEach(file => loadAsset(file, textures, injectedTextures, failedTextures.current, (path) => loadTexture(path).then(result => {
|
|
142
|
+
if (result.success && result.texture) {
|
|
143
|
+
setTextures(t => (Object.assign(Object.assign({}, t), { [file]: result.texture })));
|
|
144
|
+
}
|
|
145
|
+
return result;
|
|
146
|
+
})));
|
|
147
|
+
soundsToLoad.forEach(file => loadAsset(file, sounds, injectedSounds, failedSounds.current, (path) => loadSound(path).then(result => {
|
|
148
|
+
if (result.success && result.sound) {
|
|
149
|
+
soundManager.setBuffer(file, result.sound);
|
|
150
|
+
setSounds(current => (Object.assign(Object.assign({}, current), { [file]: result.sound })));
|
|
129
151
|
loading.current.delete(file);
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
});
|
|
152
|
+
}
|
|
153
|
+
return result;
|
|
154
|
+
})));
|
|
133
155
|
};
|
|
134
156
|
syncAssets();
|
|
135
|
-
}, [
|
|
136
|
-
|
|
157
|
+
}, [resolvedStore, assetManifestKey, basePath, injectedModels, injectedSounds, injectedTextures, models, sounds, textures]);
|
|
158
|
+
const content = (_jsx("group", { ref: rootRef, children: _jsx(GameInstanceProvider, { models: availableModels, selectedId: selectedId, editMode: editMode, onSelect: editMode ? onSelect : undefined, registerRef: registerRef, children: _jsx(StoreRootNode, { selectedId: selectedId, onSelect: editMode ? onSelect : undefined, onClick: onClick, registerRef: registerRef, registerRigidBodyRef: registerRigidBodyRef, getRigidBody: getRigidBody, loadedModels: availableModels, loadedSounds: availableSounds, loadedTextures: availableTextures, editMode: editMode, parentMatrix: IDENTITY }) }) }));
|
|
159
|
+
if (!shouldProvideStoreContext) {
|
|
160
|
+
return content;
|
|
161
|
+
}
|
|
162
|
+
return _jsx(PrefabStoreProvider, { store: resolvedStore, children: content });
|
|
137
163
|
});
|
|
138
164
|
function StoreRootNode(props) {
|
|
139
165
|
const rootId = usePrefabRootId();
|
|
140
166
|
return _jsx(GameObjectRenderer, Object.assign({}, props, { nodeId: rootId }));
|
|
141
167
|
}
|
|
142
168
|
export function GameObjectRenderer(props) {
|
|
143
|
-
var _a, _b
|
|
169
|
+
var _a, _b;
|
|
144
170
|
const node = usePrefabNode(props.nodeId);
|
|
145
|
-
const isInstanced = (
|
|
171
|
+
const isInstanced = (_b = (_a = findComponent(node, "Model")) === null || _a === void 0 ? void 0 : _a.properties) === null || _b === void 0 ? void 0 : _b.instanced;
|
|
146
172
|
const prevInstancedRef = useRef(undefined);
|
|
147
173
|
const [isTransitioning, setIsTransitioning] = useState(false);
|
|
148
174
|
useEffect(() => {
|
|
@@ -160,24 +186,27 @@ export function GameObjectRenderer(props) {
|
|
|
160
186
|
? _jsx(InstancedNode, Object.assign({}, props), key)
|
|
161
187
|
: _jsx(StandardNode, Object.assign({}, props), key);
|
|
162
188
|
}
|
|
163
|
-
function isPhysicsProps(v) {
|
|
164
|
-
return (v === null || v === void 0 ? void 0 : v.type) === "fixed" || (v === null || v === void 0 ? void 0 : v.type) === "dynamic" || (v === null || v === void 0 ? void 0 : v.type) === "kinematicPosition" || (v === null || v === void 0 ? void 0 : v.type) === "kinematicVelocity";
|
|
165
|
-
}
|
|
166
189
|
function InstancedNode({ nodeId, parentMatrix = IDENTITY, editMode, registerRef, onSelect, onClick }) {
|
|
167
|
-
var _a, _b, _c
|
|
190
|
+
var _a, _b, _c;
|
|
168
191
|
const gameObject = usePrefabNode(nodeId);
|
|
169
192
|
if (!gameObject)
|
|
170
193
|
return null;
|
|
171
194
|
const localTransform = getNodeTransformProps(gameObject);
|
|
172
195
|
const isLocked = Boolean(gameObject.locked);
|
|
173
|
-
const
|
|
174
|
-
const
|
|
175
|
-
|
|
196
|
+
const clickComponent = findComponent(gameObject, "Click");
|
|
197
|
+
const clickable = Boolean(clickComponent);
|
|
198
|
+
const clickEventName = (_a = clickComponent === null || clickComponent === void 0 ? void 0 : clickComponent.properties) === null || _a === void 0 ? void 0 : _a.eventName;
|
|
199
|
+
const physicsData = findComponent(gameObject, "Physics");
|
|
200
|
+
const physicsProps = isPhysicsProps(physicsData === null || physicsData === void 0 ? void 0 : physicsData.properties)
|
|
201
|
+
? physicsData === null || physicsData === void 0 ? void 0 : physicsData.properties
|
|
176
202
|
: undefined;
|
|
177
|
-
const modelUrl = (
|
|
203
|
+
const modelUrl = (_c = (_b = findComponent(gameObject, "Model")) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c.filename;
|
|
178
204
|
const instances = useMemo(() => buildRepeatedInstances(gameObject, parentMatrix, modelUrl, physicsProps), [gameObject, modelUrl, parentMatrix, physicsProps]);
|
|
179
205
|
const groupRef = useRef(null);
|
|
180
|
-
const
|
|
206
|
+
const editClickHandlers = useClickValid(!!editMode && !isLocked, (e) => {
|
|
207
|
+
onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
|
|
208
|
+
onClick === null || onClick === void 0 ? void 0 : onClick(e, gameObject);
|
|
209
|
+
});
|
|
181
210
|
useEffect(() => {
|
|
182
211
|
if (editMode) {
|
|
183
212
|
registerRef(nodeId, groupRef.current);
|
|
@@ -185,19 +214,12 @@ function InstancedNode({ nodeId, parentMatrix = IDENTITY, editMode, registerRef,
|
|
|
185
214
|
}
|
|
186
215
|
}, [nodeId, registerRef, editMode]);
|
|
187
216
|
if (editMode) {
|
|
188
|
-
return (_jsxs(_Fragment, { children: [_jsx("group", { ref: groupRef, position: localTransform.position, rotation: localTransform.rotation, scale: localTransform.scale,
|
|
189
|
-
if (clickValid.current) {
|
|
190
|
-
e.stopPropagation();
|
|
191
|
-
onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
|
|
192
|
-
onClick === null || onClick === void 0 ? void 0 : onClick(e, gameObject);
|
|
193
|
-
}
|
|
194
|
-
clickValid.current = false;
|
|
195
|
-
}, children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) }), instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, clickable: clickable, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, locked: isLocked, physics: instance.physics }, instance.id)))] }));
|
|
217
|
+
return (_jsxs(_Fragment, { children: [_jsx("group", Object.assign({ ref: groupRef, position: localTransform.position, rotation: localTransform.rotation, scale: localTransform.scale }, editClickHandlers, { children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) })), instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, clickable: clickable, clickEventName: clickEventName, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, locked: isLocked, physics: instance.physics }, instance.id)))] }));
|
|
196
218
|
}
|
|
197
|
-
return (_jsx(_Fragment, { children: instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, clickable: clickable, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, locked: isLocked, physics: instance.physics }, instance.id))) }));
|
|
219
|
+
return (_jsx(_Fragment, { children: instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, clickable: clickable, clickEventName: clickEventName, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, locked: isLocked, physics: instance.physics }, instance.id))) }));
|
|
198
220
|
}
|
|
199
|
-
function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, registerRigidBodyRef, loadedModels, loadedTextures, editMode, parentMatrix = IDENTITY, }) {
|
|
200
|
-
var _a, _b
|
|
221
|
+
function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, registerRigidBodyRef, getRigidBody, loadedModels, loadedSounds, loadedTextures, editMode, parentMatrix = IDENTITY, }) {
|
|
222
|
+
var _a, _b;
|
|
201
223
|
const gameObject = usePrefabNode(nodeId);
|
|
202
224
|
const childIds = usePrefabChildIds(nodeId);
|
|
203
225
|
const isSelected = selectedId === nodeId;
|
|
@@ -205,40 +227,30 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, regi
|
|
|
205
227
|
return null;
|
|
206
228
|
const groupRef = useRef(null);
|
|
207
229
|
const helperRef = useRef(null);
|
|
208
|
-
const clickValid = useRef(false);
|
|
209
230
|
const isLocked = Boolean(gameObject.locked);
|
|
210
231
|
const stillInstanced = useInstanceCheck(nodeId);
|
|
232
|
+
const clickHandlers = useClickValid(!!editMode && !isLocked, (e) => {
|
|
233
|
+
onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
|
|
234
|
+
onClick === null || onClick === void 0 ? void 0 : onClick(e, gameObject);
|
|
235
|
+
});
|
|
211
236
|
useHelper(editMode && isSelected ? helperRef : null, BoxHelper, "cyan");
|
|
212
237
|
useEffect(() => {
|
|
213
238
|
registerRef(nodeId, groupRef.current);
|
|
214
239
|
return () => registerRef(nodeId, null);
|
|
215
240
|
}, [nodeId, registerRef]);
|
|
216
241
|
const world = parentMatrix.clone().multiply(compose(gameObject));
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
clickValid.current = true;
|
|
220
|
-
};
|
|
221
|
-
const onUp = (e) => {
|
|
222
|
-
if (clickValid.current) {
|
|
223
|
-
e.stopPropagation();
|
|
224
|
-
onSelect === null || onSelect === void 0 ? void 0 : onSelect(nodeId);
|
|
225
|
-
onClick === null || onClick === void 0 ? void 0 : onClick(e, gameObject);
|
|
226
|
-
}
|
|
227
|
-
clickValid.current = false;
|
|
228
|
-
};
|
|
229
|
-
const physics = (_a = gameObject.components) === null || _a === void 0 ? void 0 : _a.physics;
|
|
230
|
-
const ready = !((_b = gameObject.components) === null || _b === void 0 ? void 0 : _b.model) ||
|
|
231
|
-
loadedModels[gameObject.components.model.properties.filename];
|
|
242
|
+
const physics = findComponent(gameObject, "Physics");
|
|
243
|
+
const ready = isNodeReady(gameObject, loadedModels);
|
|
232
244
|
const hasPhysics = physics && ready && !stillInstanced;
|
|
233
245
|
const transform = getNodeTransformProps(gameObject);
|
|
234
|
-
const physicsDef = hasPhysics ?
|
|
235
|
-
const isInstanced = (
|
|
246
|
+
const physicsDef = hasPhysics ? getComponentDef(physics.type) : null;
|
|
247
|
+
const isInstanced = (_b = (_a = findComponent(gameObject, "Model")) === null || _a === void 0 ? void 0 : _a.properties) === null || _b === void 0 ? void 0 : _b.instanced;
|
|
236
248
|
const physicsKey = `physics_${nodeId}_${isInstanced ? 'instanced' : 'standard'}`;
|
|
237
|
-
const renderCtx = { loadedModels, loadedTextures, editMode, registerRef };
|
|
249
|
+
const renderCtx = { loadedModels, loadedSounds, loadedTextures, editMode, registerRef, getRigidBody };
|
|
238
250
|
const childNodes = getChildHostComponents(gameObject).length > 0
|
|
239
251
|
? _jsx(CompositionChildren, { childIds: childIds, selectedId: selectedId, ctx: renderCtx, parentMatrix: world })
|
|
240
|
-
: _jsx(ChildNodes, { childIds: childIds, parentMatrix: world, selectedId: selectedId, onSelect: onSelect, onClick: onClick, registerRef: registerRef, registerRigidBodyRef: registerRigidBodyRef, loadedModels: loadedModels, loadedTextures: loadedTextures, editMode: editMode });
|
|
241
|
-
const inner = (_jsx("group", {
|
|
252
|
+
: _jsx(ChildNodes, { childIds: childIds, parentMatrix: world, selectedId: selectedId, onSelect: onSelect, onClick: onClick, registerRef: registerRef, registerRigidBodyRef: registerRigidBodyRef, getRigidBody: getRigidBody, loadedModels: loadedModels, loadedSounds: loadedSounds, loadedTextures: loadedTextures, editMode: editMode });
|
|
253
|
+
const inner = (_jsx("group", Object.assign({}, clickHandlers, { children: renderCompositionNode(gameObject, renderCtx, isSelected, parentMatrix, childNodes) })));
|
|
242
254
|
if (editMode) {
|
|
243
255
|
return (_jsxs(_Fragment, { children: [_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) }), _jsx("group", { ref: helperRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }), hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) ? (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, editMode: editMode, nodeId: nodeId, registerRigidBodyRef: registerRigidBodyRef, children: inner }, physicsKey)) : null] }));
|
|
244
256
|
}
|
|
@@ -247,17 +259,17 @@ function StandardNode({ nodeId, selectedId, onSelect, onClick, registerRef, regi
|
|
|
247
259
|
}
|
|
248
260
|
return (_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }));
|
|
249
261
|
}
|
|
250
|
-
const LEAF_COMPONENT_TYPES = new Set(["Geometry", "Material", "Physics"]);
|
|
251
262
|
function getChildHostComponents(gameObject) {
|
|
252
263
|
var _a;
|
|
253
|
-
return Object.entries((_a = gameObject.components) !== null && _a !== void 0 ? _a : {}).
|
|
264
|
+
return Object.entries((_a = gameObject.components) !== null && _a !== void 0 ? _a : {}).reduce((result, [key, comp]) => {
|
|
254
265
|
if (!(comp === null || comp === void 0 ? void 0 : comp.type))
|
|
255
|
-
return
|
|
256
|
-
const def =
|
|
257
|
-
if (!(def === null || def === void 0 ? void 0 : def.View) ||
|
|
258
|
-
return
|
|
259
|
-
|
|
260
|
-
|
|
266
|
+
return result;
|
|
267
|
+
const def = getComponentDef(comp.type);
|
|
268
|
+
if (!(def === null || def === void 0 ? void 0 : def.View) || def.isWrapper)
|
|
269
|
+
return result;
|
|
270
|
+
result.push({ key, View: def.View, properties: comp.properties });
|
|
271
|
+
return result;
|
|
272
|
+
}, []);
|
|
261
273
|
}
|
|
262
274
|
function ChildNodes(_a) {
|
|
263
275
|
var { childIds, parentMatrix } = _a, props = __rest(_a, ["childIds", "parentMatrix"]);
|
|
@@ -265,11 +277,11 @@ function ChildNodes(_a) {
|
|
|
265
277
|
}
|
|
266
278
|
function compose(node) {
|
|
267
279
|
const { position, rotation, scale } = getNodeTransformProps(node);
|
|
268
|
-
return
|
|
280
|
+
return composeTransform(position, rotation, scale);
|
|
269
281
|
}
|
|
270
282
|
function getModelRepeatSettings(node) {
|
|
271
|
-
var _a, _b
|
|
272
|
-
const properties = (
|
|
283
|
+
var _a, _b;
|
|
284
|
+
const properties = (_b = (_a = findComponent(node, "Model")) === null || _a === void 0 ? void 0 : _a.properties) !== null && _b !== void 0 ? _b : {};
|
|
273
285
|
return {
|
|
274
286
|
repeat: Boolean(properties.repeat),
|
|
275
287
|
repeatAxes: getRepeatAxesFromModelProperties(properties),
|
|
@@ -320,12 +332,12 @@ function buildRepeatedInstances(gameObject, parentMatrix, modelUrl, physics) {
|
|
|
320
332
|
return instances;
|
|
321
333
|
}
|
|
322
334
|
function getNodeTransformProps(node) {
|
|
323
|
-
var _a, _b, _c, _d
|
|
324
|
-
const t = (
|
|
335
|
+
var _a, _b, _c, _d;
|
|
336
|
+
const t = (_a = findComponent(node, "Transform")) === null || _a === void 0 ? void 0 : _a.properties;
|
|
325
337
|
return {
|
|
326
|
-
position: (
|
|
327
|
-
rotation: (
|
|
328
|
-
scale: (
|
|
338
|
+
position: (_b = t === null || t === void 0 ? void 0 : t.position) !== null && _b !== void 0 ? _b : [0, 0, 0],
|
|
339
|
+
rotation: (_c = t === null || t === void 0 ? void 0 : t.rotation) !== null && _c !== void 0 ? _c : [0, 0, 0],
|
|
340
|
+
scale: (_d = t === null || t === void 0 ? void 0 : t.scale) !== null && _d !== void 0 ? _d : [1, 1, 1],
|
|
329
341
|
};
|
|
330
342
|
}
|
|
331
343
|
function renderCompositionSubtree(gameObject, ctx, isSelected, childIds, parentMatrix = IDENTITY) {
|
|
@@ -351,40 +363,31 @@ function renderCompositionNode(gameObject, ctx, isSelected, parentMatrix, childN
|
|
|
351
363
|
const ownContent = renderNodeOwnContent(gameObject, ctx, isSelected, parentMatrix);
|
|
352
364
|
return wrapWithChildHosts(gameObject, ctx, isSelected, parentMatrix, _jsxs(_Fragment, { children: [ownContent, childNodes] }));
|
|
353
365
|
}
|
|
354
|
-
function
|
|
355
|
-
|
|
356
|
-
const geometry = (_a = gameObject.components) === null || _a === void 0 ? void 0 : _a.geometry;
|
|
357
|
-
const material = (_b = gameObject.components) === null || _b === void 0 ? void 0 : _b.material;
|
|
358
|
-
const geometryDef = geometry && getComponent("Geometry");
|
|
359
|
-
const materialDef = material && getComponent("Material");
|
|
360
|
-
const contextProps = {
|
|
366
|
+
function buildContextProps(gameObject, ctx, isSelected, parentMatrix) {
|
|
367
|
+
return {
|
|
361
368
|
loadedModels: ctx.loadedModels,
|
|
369
|
+
loadedSounds: ctx.loadedSounds,
|
|
362
370
|
loadedTextures: ctx.loadedTextures,
|
|
363
371
|
editMode: ctx.editMode,
|
|
364
372
|
isSelected,
|
|
365
373
|
nodeId: gameObject.id,
|
|
366
374
|
parentMatrix,
|
|
367
375
|
registerRef: ctx.registerRef,
|
|
376
|
+
getRigidBody: ctx.getRigidBody,
|
|
368
377
|
};
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
378
|
+
}
|
|
379
|
+
function renderNodeOwnContent(gameObject, ctx, isSelected, parentMatrix) {
|
|
380
|
+
const geometry = findComponent(gameObject, "Geometry");
|
|
381
|
+
const material = findComponent(gameObject, "Material");
|
|
382
|
+
const geometryDef = geometry && getComponentDef(geometry.type);
|
|
383
|
+
const materialDef = material && getComponentDef(material.type);
|
|
384
|
+
if (!geometry || !(geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View))
|
|
385
|
+
return null;
|
|
386
|
+
const contextProps = buildContextProps(gameObject, ctx, isSelected, parentMatrix);
|
|
387
|
+
return (_jsxs("mesh", { castShadow: true, receiveShadow: true, children: [_jsx(geometryDef.View, Object.assign({ properties: geometry.properties }, contextProps)), material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material"))] }));
|
|
377
388
|
}
|
|
378
389
|
function wrapWithChildHosts(gameObject, ctx, isSelected, parentMatrix, subtree) {
|
|
379
|
-
const contextProps =
|
|
380
|
-
loadedModels: ctx.loadedModels,
|
|
381
|
-
loadedTextures: ctx.loadedTextures,
|
|
382
|
-
editMode: ctx.editMode,
|
|
383
|
-
isSelected,
|
|
384
|
-
nodeId: gameObject.id,
|
|
385
|
-
parentMatrix,
|
|
386
|
-
registerRef: ctx.registerRef,
|
|
387
|
-
};
|
|
390
|
+
const contextProps = buildContextProps(gameObject, ctx, isSelected, parentMatrix);
|
|
388
391
|
const childHosts = getChildHostComponents(gameObject);
|
|
389
392
|
return childHosts.reduce((acc, { key, View, properties }) => (_jsx(View, Object.assign({ properties: properties }, contextProps, { children: acc }), key)), subtree);
|
|
390
393
|
}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useRef } from 'react';
|
|
3
3
|
import { gameEvents } from '../GameEvents';
|
|
4
|
-
import { FieldGroup } from './Input';
|
|
5
|
-
function ClickComponentEditor() {
|
|
6
|
-
return (
|
|
4
|
+
import { FieldGroup, StringField } from './Input';
|
|
5
|
+
function ClickComponentEditor({ component, onUpdate }) {
|
|
6
|
+
return (_jsxs(FieldGroup, { children: [_jsx("div", { style: { fontSize: 12, opacity: 0.8 }, children: "Emits a game event in play mode when this entity is clicked." }), _jsx(StringField, { name: "eventName", label: "Emit Event", values: component.properties, onChange: onUpdate, placeholder: "click" })] }));
|
|
7
7
|
}
|
|
8
|
-
function ClickComponentView({ children, editMode, nodeId }) {
|
|
8
|
+
function ClickComponentView({ children, editMode, nodeId, properties }) {
|
|
9
9
|
const clickValid = useRef(false);
|
|
10
|
+
const eventName = (properties === null || properties === void 0 ? void 0 : properties.eventName) || 'click';
|
|
10
11
|
const emitClick = (event) => {
|
|
11
12
|
if (!nodeId)
|
|
12
13
|
return;
|
|
13
|
-
gameEvents.emit(
|
|
14
|
+
gameEvents.emit(eventName, {
|
|
14
15
|
sourceEntityId: nodeId,
|
|
15
16
|
point: [event.point.x, event.point.y, event.point.z],
|
|
16
17
|
button: event.button,
|
|
@@ -40,6 +41,8 @@ const ClickComponent = {
|
|
|
40
41
|
name: 'Click',
|
|
41
42
|
Editor: ClickComponentEditor,
|
|
42
43
|
View: ClickComponentView,
|
|
43
|
-
defaultProperties: {
|
|
44
|
+
defaultProperties: {
|
|
45
|
+
eventName: 'click',
|
|
46
|
+
},
|
|
44
47
|
};
|
|
45
48
|
export default ClickComponent;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { FC } from "react";
|
|
2
2
|
import { ComponentData, GameObject } from "../types";
|
|
3
|
+
export type AssetRef = {
|
|
4
|
+
type: "model" | "texture" | "sound";
|
|
5
|
+
path: string;
|
|
6
|
+
};
|
|
3
7
|
export interface Component {
|
|
4
8
|
name: string;
|
|
5
9
|
Editor: FC<{
|
|
@@ -10,9 +14,11 @@ export interface Component {
|
|
|
10
14
|
}>;
|
|
11
15
|
defaultProperties: any;
|
|
12
16
|
View?: FC<any>;
|
|
13
|
-
|
|
17
|
+
/** When true, this component wraps child entities (e.g. Physics wraps children in RigidBody). */
|
|
18
|
+
isWrapper?: boolean;
|
|
19
|
+
getAssetRefs?: (properties: Record<string, any>) => AssetRef[];
|
|
14
20
|
}
|
|
15
21
|
export declare function registerComponent(component: Component): void;
|
|
16
|
-
export declare function
|
|
17
|
-
export declare function
|
|
18
|
-
export declare function
|
|
22
|
+
export declare function getComponentDef(name: string): Component | undefined;
|
|
23
|
+
export declare function getAllComponentDefs(): Record<string, Component>;
|
|
24
|
+
export declare function getComponentAssetRefs(componentType: string, properties: Record<string, any>): AssetRef[];
|
|
@@ -2,14 +2,14 @@ const REGISTRY = {};
|
|
|
2
2
|
export function registerComponent(component) {
|
|
3
3
|
REGISTRY[component.name] = component;
|
|
4
4
|
}
|
|
5
|
-
export function
|
|
5
|
+
export function getComponentDef(name) {
|
|
6
6
|
return REGISTRY[name];
|
|
7
7
|
}
|
|
8
|
-
export function
|
|
8
|
+
export function getAllComponentDefs() {
|
|
9
9
|
return Object.assign({}, REGISTRY);
|
|
10
10
|
}
|
|
11
|
-
export function
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
export function getComponentAssetRefs(componentType, properties) {
|
|
12
|
+
var _a, _b;
|
|
13
|
+
const component = REGISTRY[componentType];
|
|
14
|
+
return (_b = (_a = component === null || component === void 0 ? void 0 : component.getAssetRefs) === null || _a === void 0 ? void 0 : _a.call(component, properties)) !== null && _b !== void 0 ? _b : [];
|
|
15
15
|
}
|
|
@@ -136,6 +136,22 @@ interface BoundVector3FieldProps extends BoundFieldProps {
|
|
|
136
136
|
export declare function FieldGroup({ children }: {
|
|
137
137
|
children: React.ReactNode;
|
|
138
138
|
}): import("react/jsx-runtime").JSX.Element;
|
|
139
|
+
interface ListEditorOption {
|
|
140
|
+
value: string;
|
|
141
|
+
label: string;
|
|
142
|
+
}
|
|
143
|
+
interface ListEditorProps<T> {
|
|
144
|
+
label: string;
|
|
145
|
+
items: T[];
|
|
146
|
+
renderItem: (item: T, index: number) => React.ReactNode;
|
|
147
|
+
onAdd: (value: string) => void;
|
|
148
|
+
addOptions?: ListEditorOption[];
|
|
149
|
+
emptyMessage?: string;
|
|
150
|
+
canAdd?: boolean;
|
|
151
|
+
addButtonTitle?: string;
|
|
152
|
+
addDisabledTitle?: string;
|
|
153
|
+
}
|
|
154
|
+
export declare function ListEditor<T>({ label, items, renderItem, onAdd, addOptions, emptyMessage, canAdd, addButtonTitle, addDisabledTitle, }: ListEditorProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
139
155
|
export declare function NumberField({ name, label, values, onChange, fallback, step, min, max, style, }: BoundNumberFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
140
156
|
export declare function StringField({ name, label, values, onChange, fallback, placeholder, }: BoundStringFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
141
157
|
export declare function ColorField({ name, label, values, onChange, fallback, }: BoundColorFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -294,6 +294,39 @@ function bindFieldChange(name, onChange) {
|
|
|
294
294
|
export function FieldGroup({ children }) {
|
|
295
295
|
return _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: children });
|
|
296
296
|
}
|
|
297
|
+
export function ListEditor({ label, items, renderItem, onAdd, addOptions = [], emptyMessage = 'No items added.', canAdd = true, addButtonTitle = 'Add item', addDisabledTitle = 'No more items available', }) {
|
|
298
|
+
var _a;
|
|
299
|
+
const [selectedAddValue, setSelectedAddValue] = useState('');
|
|
300
|
+
const hasAddSelector = addOptions.length > 0;
|
|
301
|
+
const resolvedAddValue = hasAddSelector ? (selectedAddValue || ((_a = addOptions[0]) === null || _a === void 0 ? void 0 : _a.value) || '') : '';
|
|
302
|
+
const canAddItem = canAdd && (!hasAddSelector || resolvedAddValue !== '');
|
|
303
|
+
useEffect(() => {
|
|
304
|
+
var _a, _b;
|
|
305
|
+
if (!hasAddSelector) {
|
|
306
|
+
if (selectedAddValue !== '') {
|
|
307
|
+
setSelectedAddValue('');
|
|
308
|
+
}
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const stillAvailable = addOptions.some(option => option.value === selectedAddValue);
|
|
312
|
+
if (!stillAvailable) {
|
|
313
|
+
setSelectedAddValue((_b = (_a = addOptions[0]) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : '');
|
|
314
|
+
}
|
|
315
|
+
}, [addOptions, hasAddSelector, selectedAddValue]);
|
|
316
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsx(Label, { children: label }), _jsxs("div", { style: { display: 'flex', gap: 6, alignItems: 'center' }, children: [hasAddSelector ? (_jsx("div", { style: { minWidth: 140 }, children: _jsx(SelectInput, { value: resolvedAddValue, onChange: setSelectedAddValue, options: canAdd ? addOptions : [{ value: '', label: 'All items added' }] }) })) : null, _jsx("button", { type: "button", onClick: () => onAdd(resolvedAddValue), disabled: !canAddItem, style: {
|
|
317
|
+
width: 22,
|
|
318
|
+
height: 22,
|
|
319
|
+
borderRadius: 3,
|
|
320
|
+
border: `1px solid ${canAddItem ? colors.accentBorder : colors.border}`,
|
|
321
|
+
background: canAddItem ? colors.accentBg : colors.bgSurface,
|
|
322
|
+
color: canAddItem ? colors.accent : colors.textMuted,
|
|
323
|
+
cursor: canAddItem ? 'pointer' : 'not-allowed',
|
|
324
|
+
fontSize: 14,
|
|
325
|
+
lineHeight: 1,
|
|
326
|
+
padding: 0,
|
|
327
|
+
flexShrink: 0,
|
|
328
|
+
}, title: canAddItem ? addButtonTitle : addDisabledTitle, children: "+" })] })] }), items.length === 0 ? (_jsx("div", { style: { fontSize: 11, color: colors.textMuted }, children: emptyMessage })) : null, items.map(renderItem)] }));
|
|
329
|
+
}
|
|
297
330
|
export function NumberField({ name, label, values, onChange, fallback = 0, step, min, max, style, }) {
|
|
298
331
|
var _a;
|
|
299
332
|
return (_jsx(FieldRow, { label: label, children: _jsx(NumberInput, { value: (_a = values[name]) !== null && _a !== void 0 ? _a : fallback, onChange: bindFieldChange(name, onChange), step: step, min: min, max: max, style: style }) }));
|