onejs-react 0.1.15 → 0.1.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "onejs-react",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "React 19 renderer for OneJS (Unity UI Toolkit)",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -1,5 +1,6 @@
1
- import { forwardRef, useMemo, type ReactElement, type Ref } from 'react';
1
+ import { forwardRef, createElement, useMemo, type ReactElement, type Ref } from 'react';
2
2
  import type {
3
+ BaseProps,
3
4
  ViewProps,
4
5
  TextProps,
5
6
  LabelProps,
@@ -172,3 +173,39 @@ export const ListView = forwardRef<VisualElement, ListViewProps>((props, ref) =>
172
173
  return <ojs-listview ref={ref} {...props} />;
173
174
  });
174
175
  ListView.displayName = 'ListView';
176
+
177
+ /**
178
+ * Create a typed React component for a registered custom element.
179
+ * Use with `registerElement()` to add custom C# VisualElement types to React.
180
+ *
181
+ * @param name - Element name matching what was passed to registerElement()
182
+ * @param displayName - Optional display name for React DevTools
183
+ *
184
+ * @example
185
+ * import { registerElement, createComponent } from "onejs-react"
186
+ * import { BaseProps, VisualElement } from "onejs-react"
187
+ *
188
+ * // Define props for your custom element
189
+ * interface RadialProgressProps extends BaseProps {
190
+ * progress?: number
191
+ * trackColor?: string
192
+ * }
193
+ *
194
+ * // Register and create the component
195
+ * registerElement("radial-progress", CS.MyGame.UI.RadialProgress)
196
+ * export const RadialProgress = createComponent<RadialProgressProps>("radial-progress")
197
+ *
198
+ * // Use in JSX like any React component
199
+ * <RadialProgress progress={0.75} trackColor="#333" style={{ width: 100, height: 100 }} />
200
+ */
201
+ export function createComponent<P extends BaseProps = BaseProps>(
202
+ name: string,
203
+ displayName?: string
204
+ ) {
205
+ const type = name.startsWith('ojs-') ? name : `ojs-${name}`;
206
+ const Component = forwardRef<VisualElement, P>((props, ref) => {
207
+ return createElement(type, { ...props, ref });
208
+ });
209
+ Component.displayName = displayName || name;
210
+ return Component;
211
+ }
@@ -175,6 +175,35 @@ const TYPE_MAP: Record<string, () => CSObject> = {
175
175
  'ojs-listview': () => new CS.UnityEngine.UIElements.ListView(),
176
176
  };
177
177
 
178
+ // Built-in types with specific prop handling in applyComponentProps
179
+ const BUILT_IN_TYPES = new Set(Object.keys(TYPE_MAP));
180
+
181
+ /**
182
+ * Register a custom VisualElement type for use in React JSX.
183
+ *
184
+ * @param name - Element name (with or without 'ojs-' prefix)
185
+ * @param constructor - C# constructor reference (e.g., CS.MyNamespace.MyWidget)
186
+ *
187
+ * @example
188
+ * import { registerElement, createComponent } from "onejs-react"
189
+ *
190
+ * // Register the custom element
191
+ * registerElement("radial-progress", CS.MyGame.UI.RadialProgress)
192
+ *
193
+ * // Create a typed React component for it
194
+ * const RadialProgress = createComponent<RadialProgressProps>("radial-progress")
195
+ *
196
+ * // Use in JSX
197
+ * <RadialProgress progress={0.75} />
198
+ */
199
+ export function registerElement(name: string, constructor: new (...args: any[]) => any): void {
200
+ const key = name.startsWith('ojs-') ? name : `ojs-${name}`;
201
+ if (TYPE_MAP[key]) {
202
+ console.error(`registerElement: "${name}" is already registered. Overwriting.`);
203
+ }
204
+ TYPE_MAP[key] = () => new constructor();
205
+ }
206
+
178
207
  // Event prop to event type mapping
179
208
  const EVENT_PROPS: Record<string, string> = {
180
209
  // Click
@@ -728,8 +757,29 @@ function applyListViewProps(element: CSListView, props: Record<string, unknown>)
728
757
  setEnumProp(element, 'showAlternatingRowBackgrounds', props, 'showAlternatingRowBackgrounds', UIE.AlternatingRowBackground);
729
758
  }
730
759
 
760
+ // Props handled by the reconciler infrastructure - not forwarded to C# elements
761
+ const RESERVED_PROPS = new Set([
762
+ 'children', 'key', 'ref', 'style', 'className', 'pickingMode',
763
+ 'onGenerateVisualContent',
764
+ ...Object.keys(EVENT_PROPS),
765
+ ]);
766
+
767
+ // Forward non-reserved props directly to C# element (for custom elements)
768
+ function applyCustomProps(element: CSObject, props: Record<string, unknown>) {
769
+ for (const [key, value] of Object.entries(props)) {
770
+ if (value === undefined || RESERVED_PROPS.has(key)) continue;
771
+ (element as any)[key] = value;
772
+ }
773
+ }
774
+
731
775
  // Apply component-specific props based on element type
732
776
  function applyComponentProps(element: CSObject, type: string, props: Record<string, unknown>) {
777
+ // Custom elements: forward all non-reserved props directly to C# element
778
+ if (!BUILT_IN_TYPES.has(type)) {
779
+ applyCustomProps(element, props);
780
+ return;
781
+ }
782
+
733
783
  // For Slider, apply range props (lowValue/highValue) BEFORE value
734
784
  // Unity's Slider clamps value to [lowValue, highValue], so range must be set first
735
785
  if (type === 'ojs-slider') {
package/src/index.ts CHANGED
@@ -11,8 +11,12 @@ export {
11
11
  Image,
12
12
  ListView,
13
13
  clearImageCache,
14
+ createComponent,
14
15
  } from './components';
15
16
 
17
+ // Custom Element Registration
18
+ export { registerElement } from './host-config';
19
+
16
20
  // Renderer
17
21
  export { render, unmount, flushSync, batchedUpdates, getDebugInfo } from './renderer';
18
22