react-three-game 0.0.80 → 0.0.81

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.
@@ -373,17 +373,21 @@ function renderCompositionNode(gameObject, ctx, childNodes) {
373
373
  return applyNodeComposition(gameObject, _jsxs(_Fragment, { children: [primaryContent, childNodes] }));
374
374
  }
375
375
  function renderNodePrimaryContent(gameObject, ctx) {
376
- var _a;
376
+ var _a, _b;
377
377
  const geometry = findComponent(gameObject, "Geometry");
378
378
  const material = findComponent(gameObject, "Material");
379
379
  const model = findComponent(gameObject, "Model");
380
380
  const geometryDef = geometry && getComponentDef(geometry.type);
381
381
  const materialDef = material && getComponentDef(material.type);
382
382
  const modelDef = model && getComponentDef(model.type);
383
+ const geometryProperties = (_a = geometry === null || geometry === void 0 ? void 0 : geometry.properties) !== null && _a !== void 0 ? _a : {};
384
+ const meshVisible = geometryProperties.visible !== false;
385
+ const meshCastShadow = meshVisible && geometryProperties.castShadow !== false;
386
+ const meshReceiveShadow = meshVisible && geometryProperties.receiveShadow !== false;
383
387
  if ((geometry === null || geometry === void 0 ? void 0 : geometry.type) && (geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View)) {
384
- return (_jsxs("mesh", { castShadow: true, receiveShadow: true, children: [_jsx(geometryDef.View, { properties: geometry.properties }), material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, { properties: material.properties }, "material"))] }));
388
+ return (_jsxs("mesh", { visible: meshVisible, castShadow: meshCastShadow, receiveShadow: meshReceiveShadow, children: [_jsx(geometryDef.View, { properties: geometry.properties }), material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, { properties: material.properties }, "material"))] }));
385
389
  }
386
- if ((model === null || model === void 0 ? void 0 : model.type) && (modelDef === null || modelDef === void 0 ? void 0 : modelDef.View) && !((_a = model.properties) === null || _a === void 0 ? void 0 : _a.instanced) && isNodeReady(gameObject, ctx.loadedModels)) {
390
+ if ((model === null || model === void 0 ? void 0 : model.type) && (modelDef === null || modelDef === void 0 ? void 0 : modelDef.View) && !((_b = model.properties) === null || _b === void 0 ? void 0 : _b.instanced) && isNodeReady(gameObject, ctx.loadedModels)) {
387
391
  return _jsx(modelDef.View, { properties: model.properties });
388
392
  }
389
393
  return null;
@@ -1,36 +1,66 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { PerspectiveCamera, useHelper } from '@react-three/drei';
2
+ import { OrthographicCamera, PerspectiveCamera, useHelper } from '@react-three/drei';
3
3
  import { useRef } from 'react';
4
4
  import { CameraHelper } from 'three';
5
- import { useFrame } from '@react-three/fiber';
5
+ import { useFrame, useThree } from '@react-three/fiber';
6
6
  import { useEntityRuntime } from '../runtimeContext';
7
- import { FieldGroup, NumberField } from './Input';
7
+ import { FieldGroup, NumberField, SelectField } from './Input';
8
+ const CAMERA_PROJECTION_OPTIONS = [
9
+ { value: 'perspective', label: 'Perspective' },
10
+ { value: 'orthographic', label: 'Orthographic' },
11
+ ];
8
12
  const cameraDefaults = {
13
+ projection: 'perspective',
9
14
  fov: 50,
10
15
  near: 0.1,
11
16
  zoom: 1,
12
17
  far: 1000,
18
+ orthographicSize: 10,
13
19
  };
14
20
  function CameraComponentEditor({ component, onUpdate }) {
21
+ var _a;
15
22
  const values = Object.assign(Object.assign({}, cameraDefaults), component.properties);
16
- return (_jsxs(FieldGroup, { children: [_jsx(NumberField, { name: "fov", label: "FOV", values: values, onChange: onUpdate, fallback: 50, min: 1, max: 179, step: 1 }), _jsx(NumberField, { name: "near", label: "Near", values: values, onChange: onUpdate, fallback: 0.1, min: 0.001, step: 0.1 }), _jsx(NumberField, { name: "zoom", label: "Zoom", values: values, onChange: onUpdate, fallback: 1, min: 0.01, step: 0.1 }), _jsx(NumberField, { name: "far", label: "Far", values: values, onChange: onUpdate, fallback: 1000, min: 0.1, step: 1 })] }));
23
+ const projection = (_a = values.projection) !== null && _a !== void 0 ? _a : cameraDefaults.projection;
24
+ return (_jsxs(FieldGroup, { children: [_jsx(SelectField, { name: "projection", label: "Projection", values: values, onChange: onUpdate, fallback: cameraDefaults.projection, options: [...CAMERA_PROJECTION_OPTIONS] }), projection === 'perspective' ? (_jsx(NumberField, { name: "fov", label: "FOV", values: values, onChange: onUpdate, fallback: 50, min: 1, max: 179, step: 1 })) : null, projection === 'orthographic' ? (_jsx(NumberField, { name: "orthographicSize", label: "Ortho Size", values: values, onChange: onUpdate, fallback: cameraDefaults.orthographicSize, min: 0.01, step: 0.1 })) : null, _jsx(NumberField, { name: "near", label: "Near", values: values, onChange: onUpdate, fallback: 0.1, min: 0.001, step: 0.1 }), _jsx(NumberField, { name: "zoom", label: "Zoom", values: values, onChange: onUpdate, fallback: 1, min: 0.01, step: 0.1 }), _jsx(NumberField, { name: "far", label: "Far", values: values, onChange: onUpdate, fallback: 1000, min: 0.1, step: 1 })] }));
17
25
  }
18
26
  function CameraComponentView({ properties, children }) {
27
+ var _a;
19
28
  const { editMode, isSelected } = useEntityRuntime();
29
+ const { size } = useThree();
20
30
  const merged = Object.assign(Object.assign({}, cameraDefaults), properties);
31
+ const projection = (_a = merged.projection) !== null && _a !== void 0 ? _a : cameraDefaults.projection;
21
32
  const fov = merged.fov;
22
33
  const near = merged.near;
23
34
  const zoom = merged.zoom;
24
35
  const far = merged.far;
25
- const cameraRef = useRef(null);
26
- useHelper(editMode && isSelected ? cameraRef : null, CameraHelper);
36
+ const orthographicSize = merged.orthographicSize;
37
+ const aspect = size.height > 0 ? size.width / size.height : 1;
38
+ const halfHeight = orthographicSize / 2;
39
+ const halfWidth = halfHeight * aspect;
40
+ const perspectiveCameraRef = useRef(null);
41
+ const orthographicCameraRef = useRef(null);
42
+ const activeCameraRef = projection === 'orthographic'
43
+ ? orthographicCameraRef
44
+ : perspectiveCameraRef;
45
+ useHelper(editMode && isSelected ? activeCameraRef : null, CameraHelper);
27
46
  useFrame(() => {
28
- if (cameraRef.current && editMode && isSelected) {
29
- cameraRef.current.updateProjectionMatrix();
30
- cameraRef.current.updateMatrixWorld();
47
+ if (!editMode || !isSelected)
48
+ return;
49
+ if (projection === 'orthographic' && orthographicCameraRef.current) {
50
+ orthographicCameraRef.current.updateProjectionMatrix();
51
+ orthographicCameraRef.current.updateMatrixWorld();
52
+ return;
53
+ }
54
+ if (perspectiveCameraRef.current) {
55
+ perspectiveCameraRef.current.updateProjectionMatrix();
56
+ perspectiveCameraRef.current.updateMatrixWorld();
31
57
  }
32
58
  });
33
- return (_jsxs(PerspectiveCamera, { ref: cameraRef, makeDefault: !editMode, fov: fov, near: near, zoom: zoom, far: far, children: [editMode ? (_jsxs("group", { children: [_jsxs("mesh", { children: [_jsx("boxGeometry", { args: [0.3, 0.3, 0.5] }), _jsx("meshBasicMaterial", { color: '#22d3ee', wireframe: true })] }), _jsxs("mesh", { position: [0, 0, -0.25], rotation: [Math.PI / 2, 0, 0], children: [_jsx("coneGeometry", { args: [0.08, 0.16, 16] }), _jsx("meshBasicMaterial", { color: '#22d3ee', wireframe: true })] })] })) : null, children] }));
59
+ const helperContent = editMode ? (_jsxs("group", { children: [_jsxs("mesh", { children: [_jsx("boxGeometry", { args: [0.3, 0.3, 0.5] }), _jsx("meshBasicMaterial", { color: '#22d3ee', wireframe: true })] }), _jsxs("mesh", { position: [0, 0, -0.25], rotation: [Math.PI / 2, 0, 0], children: [_jsx("coneGeometry", { args: [0.08, 0.16, 16] }), _jsx("meshBasicMaterial", { color: '#22d3ee', wireframe: true })] })] })) : null;
60
+ if (projection === 'orthographic') {
61
+ return (_jsxs(OrthographicCamera, { ref: orthographicCameraRef, makeDefault: !editMode, near: near, zoom: zoom, far: far, left: -halfWidth, right: halfWidth, top: halfHeight, bottom: -halfHeight, children: [helperContent, children] }));
62
+ }
63
+ return (_jsxs(PerspectiveCamera, { ref: perspectiveCameraRef, makeDefault: !editMode, fov: fov, near: near, zoom: zoom, far: far, children: [helperContent, children] }));
34
64
  }
35
65
  const CameraComponent = {
36
66
  name: 'Camera',
@@ -2,9 +2,9 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { useRef } from 'react';
3
3
  import { gameEvents } from '../GameEvents';
4
4
  import { useEntityRuntime } from '../runtimeContext';
5
- import { FieldGroup, StringField } from './Input';
5
+ import { EventField, FieldGroup } from './Input';
6
6
  function ClickComponentEditor({ component, onUpdate }) {
7
- 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
+ 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(EventField, { name: "eventName", label: "Emit Event", values: component.properties, onChange: onUpdate, placeholder: "click" })] }));
8
8
  }
9
9
  function ClickComponentView({ children, properties }) {
10
10
  const clickValid = useRef(false);
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { FieldGroup, NumberField, SelectField } from "./Input";
2
+ import { BooleanField, FieldGroup, NumberField, SelectField } from "./Input";
3
3
  const GEOMETRY_ARGS = {
4
4
  box: {
5
5
  fields: [
@@ -61,7 +61,7 @@ function GeometryComponentEditor({ component, onUpdate, }) {
61
61
  ] }), schema.fields.map((field, index) => {
62
62
  var _a;
63
63
  return (_jsx(NumberField, { name: field.name, label: field.label, values: { [field.name]: (_a = args[index]) !== null && _a !== void 0 ? _a : field.defaultValue }, onChange: (next) => updateArg(index, next[field.name]), fallback: field.defaultValue, min: field.min, step: field.step }, field.name));
64
- })] }));
64
+ }), _jsx(BooleanField, { name: "visible", label: "Visible", values: component.properties, onChange: handleChange, fallback: true }), _jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: component.properties, onChange: handleChange, fallback: true }), _jsx(BooleanField, { name: "receiveShadow", label: "Receive Shadow", values: component.properties, onChange: handleChange, fallback: true })] }));
65
65
  }
66
66
  // View for Geometry component
67
67
  function GeometryComponentView({ properties, children }) {
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- export type FieldType = 'vector3' | 'number' | 'string' | 'color' | 'boolean' | 'select';
2
+ export type FieldType = 'vector3' | 'number' | 'string' | 'color' | 'boolean' | 'select' | 'node' | 'event';
3
3
  interface BaseFieldDefinition {
4
4
  name: string;
5
5
  label: string;
@@ -31,6 +31,15 @@ interface SelectFieldDefinition extends BaseFieldDefinition {
31
31
  label: string;
32
32
  }[];
33
33
  }
34
+ interface NodeFieldDefinition extends BaseFieldDefinition {
35
+ type: 'node';
36
+ placeholder?: string;
37
+ includeRoot?: boolean;
38
+ }
39
+ interface EventFieldDefinition extends BaseFieldDefinition {
40
+ type: 'event';
41
+ placeholder?: string;
42
+ }
34
43
  interface CustomFieldDefinition extends BaseFieldDefinition {
35
44
  type: 'custom';
36
45
  render: (props: {
@@ -40,7 +49,7 @@ interface CustomFieldDefinition extends BaseFieldDefinition {
40
49
  onChangeMultiple: (values: Record<string, any>) => void;
41
50
  }) => React.ReactNode;
42
51
  }
43
- export type FieldDefinition = Vector3FieldDefinition | NumberFieldDefinition | StringFieldDefinition | ColorFieldDefinition | BooleanFieldDefinition | SelectFieldDefinition | CustomFieldDefinition;
52
+ export type FieldDefinition = Vector3FieldDefinition | NumberFieldDefinition | StringFieldDefinition | ColorFieldDefinition | BooleanFieldDefinition | SelectFieldDefinition | NodeFieldDefinition | EventFieldDefinition | CustomFieldDefinition;
44
53
  declare const styles: {
45
54
  input: React.CSSProperties;
46
55
  label: React.CSSProperties;
@@ -79,6 +88,19 @@ export declare function StringInput({ label, value, onChange, placeholder }: {
79
88
  onChange: (value: string) => void;
80
89
  placeholder?: string;
81
90
  }): import("react/jsx-runtime").JSX.Element;
91
+ export declare function NodeInput({ label, value, onChange, placeholder, includeRoot, }: {
92
+ label: string;
93
+ value: string;
94
+ onChange: (value: string) => void;
95
+ placeholder?: string;
96
+ includeRoot?: boolean;
97
+ }): import("react/jsx-runtime").JSX.Element;
98
+ export declare function EventInput({ label, value, onChange, placeholder, }: {
99
+ label: string;
100
+ value: string;
101
+ onChange: (value: string) => void;
102
+ placeholder?: string;
103
+ }): import("react/jsx-runtime").JSX.Element;
82
104
  export declare function BooleanInput({ label, value, onChange }: {
83
105
  label?: string;
84
106
  value: boolean;
@@ -157,6 +179,10 @@ export declare function StringField({ name, label, values, onChange, fallback, p
157
179
  export declare function ColorField({ name, label, values, onChange, fallback, }: BoundColorFieldProps): import("react/jsx-runtime").JSX.Element;
158
180
  export declare function BooleanField({ name, label, values, onChange, fallback, }: BoundBooleanFieldProps): import("react/jsx-runtime").JSX.Element;
159
181
  export declare function SelectField({ name, label, values, onChange, fallback, options, }: BoundSelectFieldProps): import("react/jsx-runtime").JSX.Element;
182
+ export declare function NodeField({ name, label, values, onChange, fallback, }: BoundStringFieldProps & {
183
+ fallback?: string;
184
+ }): import("react/jsx-runtime").JSX.Element;
185
+ export declare function EventField({ name, label, values, onChange, fallback, placeholder, }: BoundStringFieldProps): import("react/jsx-runtime").JSX.Element;
160
186
  export declare function Vector3Field({ name, label, values, onChange, fallback, snap, labelExtra, }: BoundVector3FieldProps): import("react/jsx-runtime").JSX.Element;
161
187
  interface FieldRendererProps {
162
188
  fields: FieldDefinition[];
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useRef, useState } from 'react';
2
+ import { useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { colors } from '../styles';
4
+ import { useOptionalPrefabStoreApi } from '../prefabStore';
4
5
  // ============================================================================
5
6
  // Shared Styles (derived from shared color tokens)
6
7
  // ============================================================================
@@ -277,6 +278,124 @@ export function ColorInput({ label, value, onChange }) {
277
278
  export function StringInput({ label, value, onChange, placeholder }) {
278
279
  return (_jsxs("div", { children: [label && _jsx(Label, { children: label }), _jsx("input", { type: "text", style: styles.input, value: value, onChange: e => onChange(e.target.value), placeholder: placeholder })] }));
279
280
  }
281
+ function useOptionalPrefabSnapshot() {
282
+ const store = useOptionalPrefabStoreApi();
283
+ const [state, setState] = useState(() => { var _a; return (_a = store === null || store === void 0 ? void 0 : store.getState()) !== null && _a !== void 0 ? _a : null; });
284
+ useEffect(() => {
285
+ if (!store) {
286
+ setState(null);
287
+ return;
288
+ }
289
+ setState(store.getState());
290
+ return store.subscribe(nextState => setState(nextState));
291
+ }, [store]);
292
+ return state;
293
+ }
294
+ function SearchSuggestionList({ query, options, onSelect, emptyMessage, }) {
295
+ const normalizedQuery = query.trim().toLowerCase();
296
+ const filtered = useMemo(() => {
297
+ if (!normalizedQuery)
298
+ return options.slice(0, 8);
299
+ return options.filter(option => option.searchText.includes(normalizedQuery)).slice(0, 8);
300
+ }, [normalizedQuery, options]);
301
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [_jsx("input", { type: "text", style: Object.assign(Object.assign({}, styles.input), { width: '100%', textAlign: 'left' }), value: query, onChange: () => undefined, readOnly: true, "aria-hidden": true, tabIndex: -1, hidden: true }), _jsx("div", { style: {
302
+ display: 'flex',
303
+ flexDirection: 'column',
304
+ gap: 4,
305
+ maxHeight: 160,
306
+ overflowY: 'auto',
307
+ border: `1px solid ${colors.border}`,
308
+ borderRadius: 3,
309
+ background: colors.bgSurface,
310
+ padding: 4,
311
+ }, children: filtered.length === 0 ? (_jsx("div", { style: { fontSize: 11, color: colors.textMuted, padding: '4px 6px' }, children: emptyMessage })) : filtered.map(option => (_jsxs("button", { type: "button", onClick: () => onSelect(option.value), style: {
312
+ display: 'flex',
313
+ flexDirection: 'column',
314
+ alignItems: 'flex-start',
315
+ gap: 2,
316
+ border: `1px solid ${colors.border}`,
317
+ borderRadius: 3,
318
+ background: colors.bgInput,
319
+ color: colors.text,
320
+ padding: '6px 8px',
321
+ cursor: 'pointer',
322
+ textAlign: 'left',
323
+ }, children: [_jsx("span", { style: { fontSize: 11, fontWeight: 500 }, children: option.label }), option.description ? (_jsx("span", { style: { fontSize: 10, color: colors.textMuted, fontFamily: 'monospace' }, children: option.description })) : null] }, option.value))) })] }));
324
+ }
325
+ export function NodeInput({ label, value, onChange, placeholder, includeRoot = true, }) {
326
+ const prefabState = useOptionalPrefabSnapshot();
327
+ const [query, setQuery] = useState('');
328
+ const options = useMemo(() => {
329
+ var _a;
330
+ const nodesById = (_a = prefabState === null || prefabState === void 0 ? void 0 : prefabState.nodesById) !== null && _a !== void 0 ? _a : {};
331
+ const rootId = prefabState === null || prefabState === void 0 ? void 0 : prefabState.rootId;
332
+ return Object.values(nodesById)
333
+ .filter(node => includeRoot || node.id !== rootId)
334
+ .map(node => {
335
+ const nodeName = typeof node.name === 'string' && node.name.trim().length > 0 ? node.name.trim() : '(unnamed)';
336
+ return {
337
+ value: node.id,
338
+ label: nodeName,
339
+ description: node.id,
340
+ searchText: `${nodeName} ${node.id}`.toLowerCase(),
341
+ };
342
+ })
343
+ .sort((left, right) => left.label.localeCompare(right.label) || left.value.localeCompare(right.value));
344
+ }, [includeRoot, prefabState === null || prefabState === void 0 ? void 0 : prefabState.nodesById, prefabState === null || prefabState === void 0 ? void 0 : prefabState.rootId]);
345
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsx(StringInput, { label: label, value: value, onChange: onChange, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : 'Node id' }), options.length > 0 ? (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [_jsx("input", { type: "text", style: Object.assign(Object.assign({}, styles.input), { width: '100%', textAlign: 'left' }), value: query, onChange: e => setQuery(e.target.value), placeholder: "Search nodes by name or id" }), _jsx(SearchSuggestionList, { query: query, options: options, onSelect: (nextValue) => {
346
+ onChange(nextValue);
347
+ setQuery('');
348
+ }, emptyMessage: "No matching nodes." })] })) : null] }));
349
+ }
350
+ const BUILT_IN_EVENT_OPTIONS = [
351
+ 'sensor:enter',
352
+ 'sensor:exit',
353
+ 'collision:enter',
354
+ 'collision:exit',
355
+ 'click',
356
+ ].map(eventName => ({
357
+ value: eventName,
358
+ label: eventName,
359
+ searchText: eventName.toLowerCase(),
360
+ }));
361
+ export function EventInput({ label, value, onChange, placeholder, }) {
362
+ const prefabState = useOptionalPrefabSnapshot();
363
+ const [query, setQuery] = useState('');
364
+ const options = useMemo(() => {
365
+ var _a;
366
+ const authoredEvents = new Map();
367
+ Object.values((_a = prefabState === null || prefabState === void 0 ? void 0 : prefabState.nodesById) !== null && _a !== void 0 ? _a : {}).forEach(node => {
368
+ var _a;
369
+ Object.values((_a = node.components) !== null && _a !== void 0 ? _a : {}).forEach(component => {
370
+ var _a;
371
+ Object.entries((_a = component === null || component === void 0 ? void 0 : component.properties) !== null && _a !== void 0 ? _a : {}).forEach(([key, entry]) => {
372
+ var _a, _b;
373
+ if (typeof entry !== 'string')
374
+ return;
375
+ if (!(key === 'eventName' || key.endsWith('EventName')))
376
+ return;
377
+ const eventName = entry.trim();
378
+ if (!eventName)
379
+ return;
380
+ authoredEvents.set(eventName, {
381
+ value: eventName,
382
+ label: eventName,
383
+ description: `${(_a = component === null || component === void 0 ? void 0 : component.type) !== null && _a !== void 0 ? _a : 'Component'} -> ${key}`,
384
+ searchText: `${eventName} ${(_b = component === null || component === void 0 ? void 0 : component.type) !== null && _b !== void 0 ? _b : ''} ${key}`.toLowerCase(),
385
+ });
386
+ });
387
+ });
388
+ });
389
+ const merged = new Map();
390
+ BUILT_IN_EVENT_OPTIONS.forEach(option => merged.set(option.value, option));
391
+ authoredEvents.forEach((option, key) => merged.set(key, option));
392
+ return [...merged.values()].sort((left, right) => left.value.localeCompare(right.value));
393
+ }, [prefabState === null || prefabState === void 0 ? void 0 : prefabState.nodesById]);
394
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsx(StringInput, { label: label, value: value, onChange: onChange, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : 'Event name' }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [_jsx("input", { type: "text", style: Object.assign(Object.assign({}, styles.input), { width: '100%', textAlign: 'left' }), value: query, onChange: e => setQuery(e.target.value), placeholder: "Search built-in and authored events" }), _jsx(SearchSuggestionList, { query: query, options: options, onSelect: (nextValue) => {
395
+ onChange(nextValue);
396
+ setQuery('');
397
+ }, emptyMessage: "No matching events." })] })] }));
398
+ }
280
399
  export function BooleanInput({ label, value, onChange }) {
281
400
  return (_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [label && _jsx(Label, { children: label }), _jsx("input", { type: "checkbox", style: {
282
401
  height: 16,
@@ -347,6 +466,14 @@ export function SelectField({ name, label, values, onChange, fallback, options,
347
466
  var _a, _b, _c, _d;
348
467
  return (_jsx(SelectInput, { label: label, value: (_d = (_b = (_a = values[name]) !== null && _a !== void 0 ? _a : fallback) !== null && _b !== void 0 ? _b : (_c = options[0]) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : '', onChange: bindFieldChange(name, onChange), options: options }));
349
468
  }
469
+ export function NodeField({ name, label, values, onChange, fallback = '', }) {
470
+ var _a;
471
+ return (_jsx(NodeInput, { label: label, value: (_a = values[name]) !== null && _a !== void 0 ? _a : fallback, onChange: bindFieldChange(name, onChange) }));
472
+ }
473
+ export function EventField({ name, label, values, onChange, fallback = '', placeholder, }) {
474
+ var _a;
475
+ return (_jsx(EventInput, { label: label, value: (_a = values[name]) !== null && _a !== void 0 ? _a : fallback, onChange: bindFieldChange(name, onChange), placeholder: placeholder }));
476
+ }
350
477
  export function Vector3Field({ name, label, values, onChange, fallback = [0, 0, 0], snap, labelExtra, }) {
351
478
  var _a;
352
479
  return (_jsx(Vector3Input, { label: label, value: (_a = values[name]) !== null && _a !== void 0 ? _a : fallback, onChange: bindFieldChange(name, onChange), snap: snap, labelExtra: labelExtra }));
@@ -371,6 +498,10 @@ export function FieldRenderer({ fields, values, onChange }) {
371
498
  return (_jsx(BooleanInput, { label: field.label, value: value !== null && value !== void 0 ? value : false, onChange: v => updateField(field.name, v) }, field.name));
372
499
  case 'select':
373
500
  return (_jsx(SelectInput, { label: field.label, value: (_b = value !== null && value !== void 0 ? value : (_a = field.options[0]) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : '', onChange: v => updateField(field.name, v), options: field.options }, field.name));
501
+ case 'node':
502
+ return (_jsx(NodeInput, { label: field.label, value: value !== null && value !== void 0 ? value : '', onChange: v => updateField(field.name, v), placeholder: field.placeholder, includeRoot: field.includeRoot }, field.name));
503
+ case 'event':
504
+ return (_jsx(EventInput, { label: field.label, value: value !== null && value !== void 0 ? value : '', onChange: v => updateField(field.name, v), placeholder: field.placeholder }, field.name));
374
505
  case 'custom':
375
506
  return (_jsxs("div", { children: [field.label && _jsx(Label, { children: field.label }), field.render({
376
507
  value,
@@ -4,7 +4,7 @@ import { useContext, useMemo } from 'react';
4
4
  import { BooleanField, FieldGroup, Label, ListEditor, NumberInput, SelectInput } from './Input';
5
5
  import { useAssetRuntime } from '../runtimeContext';
6
6
  import { EditorContext } from '../PrefabEditor';
7
- import { DEFAULT_REPEAT_AXES, getRepeatAxesFromModelProperties, normalizeRepeatAxes } from '../InstanceProvider';
7
+ import { getRepeatAxesFromModelProperties, normalizeRepeatAxes } from '../InstanceProvider';
8
8
  import { colors } from '../styles';
9
9
  const AXIS_OPTIONS = [
10
10
  { value: 'x', label: 'X' },
@@ -91,12 +91,7 @@ const ModelComponent = {
91
91
  name: 'Model',
92
92
  Editor: ModelComponentEditor,
93
93
  View: ModelComponentView,
94
- defaultProperties: {
95
- filename: '',
96
- instanced: false,
97
- repeat: false,
98
- repeatAxes: DEFAULT_REPEAT_AXES
99
- },
94
+ defaultProperties: {},
100
95
  getAssetRefs: (properties) => {
101
96
  if (properties.filename)
102
97
  return [{ type: 'model', path: properties.filename }];
@@ -13,7 +13,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
13
13
  import { CapsuleCollider, RigidBody, useRapier } from "@react-three/rapier";
14
14
  import { useRef, useEffect, useCallback } from 'react';
15
15
  import { useAssetRuntime, useEntityRuntime } from "../runtimeContext";
16
- import { BooleanField, FieldGroup, ListEditor, NumberField, SelectField, SelectInput, StringInput, Vector3Field } from "./Input";
16
+ import { BooleanField, EventInput, FieldGroup, ListEditor, NumberField, SelectField, SelectInput, Vector3Field } from "./Input";
17
17
  import { gameEvents, getEntityIdFromRigidBody } from "../GameEvents";
18
18
  import { colors } from "../styles";
19
19
  export function isPhysicsProps(v) {
@@ -103,7 +103,7 @@ function PhysicsEventBindingsEditor({ values, onChange, }) {
103
103
  cursor: 'pointer',
104
104
  padding: 0,
105
105
  flexShrink: 0,
106
- }, title: "Remove physics event", children: "\u00D7" })] }), _jsx(StringInput, { label: "Event Name", value: (_a = values[option.key]) !== null && _a !== void 0 ? _a : option.defaultName, onChange: (eventName) => updateEventName(option.key, eventName), placeholder: option.defaultName })] }, option.key));
106
+ }, title: "Remove physics event", children: "\u00D7" })] }), _jsx(EventInput, { label: "Event Name", value: (_a = values[option.key]) !== null && _a !== void 0 ? _a : option.defaultName, onChange: (eventName) => updateEventName(option.key, eventName), placeholder: option.defaultName })] }, option.key));
107
107
  } }));
108
108
  }
109
109
  function LockedAxisField({ label, name, values, onChange, }) {
@@ -5,7 +5,7 @@ import { SoundPicker } from '../../assetviewer/page';
5
5
  import { sound as soundManager } from '../../../helpers/SoundManager';
6
6
  import { gameEvents } from '../GameEvents';
7
7
  import { useAssetRuntime, useEntityRuntime } from '../runtimeContext';
8
- import { BooleanField, FieldGroup, FieldRenderer, ListEditor, NumberField, SelectField, StringField } from './Input';
8
+ import { BooleanField, EventField, FieldGroup, FieldRenderer, ListEditor, NumberField, SelectField } from './Input';
9
9
  import { colors } from '../styles';
10
10
  import { AudioListener } from 'three';
11
11
  const CLIP_MODE_OPTIONS = [
@@ -84,7 +84,7 @@ function SoundComponentEditor({ component, onUpdate, basePath = '' }) {
84
84
  const removeClip = (index) => {
85
85
  setClips(clips.filter((_, clipIndex) => clipIndex !== index));
86
86
  };
87
- return (_jsxs(FieldGroup, { children: [_jsx(StringField, { name: "eventName", label: "Listen Event", values: component.properties, onChange: onUpdate, placeholder: "click" }), _jsx(FieldRenderer, { fields: [
87
+ return (_jsxs(FieldGroup, { children: [_jsx(EventField, { name: "eventName", label: "Listen Event", values: component.properties, onChange: onUpdate, placeholder: "click" }), _jsx(FieldRenderer, { fields: [
88
88
  {
89
89
  name: 'clipMode',
90
90
  label: 'Clip Mode',
@@ -48,10 +48,6 @@ function TransformComponentEditor({ component, onUpdate }) {
48
48
  const TransformComponent = {
49
49
  name: 'Transform',
50
50
  Editor: TransformComponentEditor,
51
- defaultProperties: {
52
- position: [0, 0, 0],
53
- rotation: [0, 0, 0],
54
- scale: [1, 1, 1]
55
- }
51
+ defaultProperties: {}
56
52
  };
57
53
  export default TransformComponent;
@@ -12,11 +12,11 @@ export interface AssetRuntime {
12
12
  getTexture: (path: string) => Texture | null;
13
13
  getSound: (path: string) => AudioBuffer | null;
14
14
  getAssetRevision: () => string;
15
- }
16
- export interface AssetRuntimeContextValue extends AssetRuntime {
17
15
  getObject: (id: string) => Object3D | null;
18
16
  getRigidBody: (id: string) => any;
19
17
  }
18
+ export interface AssetRuntimeContextValue extends AssetRuntime {
19
+ }
20
20
  export interface EntityRuntime {
21
21
  nodeId: string;
22
22
  editMode?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-three-game",
3
- "version": "0.0.80",
3
+ "version": "0.0.81",
4
4
  "description": "high performance 3D game engine built in React",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",