onejs-react 0.1.4 → 0.1.6
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 +2 -2
- package/src/host-config.ts +42 -5
- package/src/style-parser.ts +38 -0
- package/src/types.ts +72 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "onejs-react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "React 19 renderer for OneJS (Unity UI Toolkit)",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
],
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
20
|
-
"url": "https://github.com/Singtaa/onejs-react"
|
|
20
|
+
"url": "git+https://github.com/Singtaa/onejs-react.git"
|
|
21
21
|
},
|
|
22
22
|
"author": "Singtaa",
|
|
23
23
|
"scripts": {
|
package/src/host-config.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type {BaseProps, ViewStyle, VisualElement, GenerateVisualContentCallback}
|
|
|
3
3
|
import {parseStyleValue, parseColor} from './style-parser';
|
|
4
4
|
|
|
5
5
|
// CSObject is an alias for VisualElement - they represent the same C# objects
|
|
6
|
-
type CSObject = VisualElement;
|
|
6
|
+
type CSObject = VisualElement & { pickingMode?: number };
|
|
7
7
|
|
|
8
8
|
// Global declarations for QuickJS environment
|
|
9
9
|
declare function setTimeout(callback: () => void, ms?: number): number;
|
|
@@ -54,6 +54,7 @@ declare const CS: {
|
|
|
54
54
|
AlternatingRowBackground: CSEnum;
|
|
55
55
|
CollectionVirtualizationMethod: CSEnum;
|
|
56
56
|
DisplayStyle: CSEnum;
|
|
57
|
+
PickingMode: CSEnum;
|
|
57
58
|
};
|
|
58
59
|
};
|
|
59
60
|
OneJS: {
|
|
@@ -70,6 +71,8 @@ declare const __eventAPI: {
|
|
|
70
71
|
addEventListener: (element: CSObject, eventType: string, callback: Function) => void;
|
|
71
72
|
removeEventListener: (element: CSObject, eventType: string, callback: Function) => void;
|
|
72
73
|
removeAllEventListeners: (element: CSObject) => void;
|
|
74
|
+
setParent: (childHandle: number, parentHandle: number) => void;
|
|
75
|
+
removeParent: (childHandle: number) => void;
|
|
73
76
|
};
|
|
74
77
|
|
|
75
78
|
interface CSStyle {
|
|
@@ -416,6 +419,22 @@ function updateClassNames(element: CSObject, oldClassName: string | undefined, n
|
|
|
416
419
|
}
|
|
417
420
|
}
|
|
418
421
|
|
|
422
|
+
// Track parent-child relationships for event bubbling
|
|
423
|
+
function trackParent(child: CSObject, parent: CSObject) {
|
|
424
|
+
const childHandle = (child as unknown as { __csHandle: number }).__csHandle;
|
|
425
|
+
const parentHandle = (parent as unknown as { __csHandle: number }).__csHandle;
|
|
426
|
+
if (childHandle > 0 && parentHandle > 0) {
|
|
427
|
+
__eventAPI.setParent(childHandle, parentHandle);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function untrackParent(child: CSObject) {
|
|
432
|
+
const childHandle = (child as unknown as { __csHandle: number }).__csHandle;
|
|
433
|
+
if (childHandle > 0) {
|
|
434
|
+
__eventAPI.removeParent(childHandle);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
419
438
|
// Apply event handlers
|
|
420
439
|
function applyEvents(instance: Instance, props: BaseProps) {
|
|
421
440
|
for (const [propName, eventType] of Object.entries(EVENT_PROPS)) {
|
|
@@ -778,6 +797,11 @@ function createInstance(type: string, props: BaseProps): Instance {
|
|
|
778
797
|
applyVisualContentCallback(instance, props);
|
|
779
798
|
applyComponentProps(element, type, props as Record<string, unknown>);
|
|
780
799
|
|
|
800
|
+
// Apply pickingMode
|
|
801
|
+
if (props.pickingMode !== undefined) {
|
|
802
|
+
element.pickingMode = CS.UnityEngine.UIElements.PickingMode[props.pickingMode];
|
|
803
|
+
}
|
|
804
|
+
|
|
781
805
|
return instance;
|
|
782
806
|
}
|
|
783
807
|
|
|
@@ -806,6 +830,16 @@ function updateInstance(instance: Instance, oldProps: BaseProps, newProps: BaseP
|
|
|
806
830
|
// Update component-specific props
|
|
807
831
|
applyComponentProps(element, instance.type, newProps as Record<string, unknown>);
|
|
808
832
|
|
|
833
|
+
// Update pickingMode
|
|
834
|
+
if (oldProps.pickingMode !== newProps.pickingMode) {
|
|
835
|
+
if (newProps.pickingMode !== undefined) {
|
|
836
|
+
element.pickingMode = CS.UnityEngine.UIElements.PickingMode[newProps.pickingMode];
|
|
837
|
+
} else {
|
|
838
|
+
// Reset to default (Position)
|
|
839
|
+
element.pickingMode = CS.UnityEngine.UIElements.PickingMode.Position;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
809
843
|
instance.props = newProps;
|
|
810
844
|
}
|
|
811
845
|
|
|
@@ -862,31 +896,31 @@ export const hostConfig = {
|
|
|
862
896
|
if (shouldMergeText(parentInstance, child)) {
|
|
863
897
|
appendMergedTextChild(parentInstance, child);
|
|
864
898
|
} else {
|
|
865
|
-
// Non-text child - unmerge any previously merged text first
|
|
866
899
|
handleNonTextChild(parentInstance);
|
|
867
900
|
parentInstance.element.Add(child.element);
|
|
868
901
|
}
|
|
902
|
+
trackParent(child.element, parentInstance.element);
|
|
869
903
|
},
|
|
870
904
|
|
|
871
905
|
appendChild(parentInstance: Instance, child: Instance) {
|
|
872
906
|
if (shouldMergeText(parentInstance, child)) {
|
|
873
907
|
appendMergedTextChild(parentInstance, child);
|
|
874
908
|
} else {
|
|
875
|
-
// Non-text child - unmerge any previously merged text first
|
|
876
909
|
handleNonTextChild(parentInstance);
|
|
877
910
|
parentInstance.element.Add(child.element);
|
|
878
911
|
}
|
|
912
|
+
trackParent(child.element, parentInstance.element);
|
|
879
913
|
},
|
|
880
914
|
|
|
881
915
|
appendChildToContainer(container: Container, child: Instance) {
|
|
882
916
|
container.Add(child.element);
|
|
917
|
+
// Container is the root - no parent to track
|
|
883
918
|
},
|
|
884
919
|
|
|
885
920
|
insertBefore(parentInstance: Instance, child: Instance, beforeChild: Instance) {
|
|
886
921
|
if (shouldMergeText(parentInstance, child)) {
|
|
887
922
|
insertMergedTextChild(parentInstance, child, beforeChild);
|
|
888
923
|
} else {
|
|
889
|
-
// Non-text child - unmerge any previously merged text first
|
|
890
924
|
handleNonTextChild(parentInstance);
|
|
891
925
|
const index = parentInstance.element.IndexOf(beforeChild.element);
|
|
892
926
|
if (index >= 0) {
|
|
@@ -895,6 +929,7 @@ export const hostConfig = {
|
|
|
895
929
|
parentInstance.element.Add(child.element);
|
|
896
930
|
}
|
|
897
931
|
}
|
|
932
|
+
trackParent(child.element, parentInstance.element);
|
|
898
933
|
},
|
|
899
934
|
|
|
900
935
|
insertInContainerBefore(container: Container, child: Instance, beforeChild: Instance) {
|
|
@@ -904,21 +939,23 @@ export const hostConfig = {
|
|
|
904
939
|
} else {
|
|
905
940
|
container.Add(child.element);
|
|
906
941
|
}
|
|
942
|
+
// Container is the root - no parent to track
|
|
907
943
|
},
|
|
908
944
|
|
|
909
945
|
removeChild(parentInstance: Instance, child: Instance) {
|
|
910
946
|
if (child.mergedInto === parentInstance) {
|
|
911
|
-
// Child was merged into parent's text - remove from merged children
|
|
912
947
|
removeMergedTextChild(parentInstance, child);
|
|
913
948
|
} else {
|
|
914
949
|
__eventAPI.removeAllEventListeners(child.element);
|
|
915
950
|
parentInstance.element.Remove(child.element);
|
|
916
951
|
}
|
|
952
|
+
untrackParent(child.element);
|
|
917
953
|
},
|
|
918
954
|
|
|
919
955
|
removeChildFromContainer(container: Container, child: Instance) {
|
|
920
956
|
__eventAPI.removeAllEventListeners(child.element);
|
|
921
957
|
container.Remove(child.element);
|
|
958
|
+
untrackParent(child.element);
|
|
922
959
|
},
|
|
923
960
|
|
|
924
961
|
prepareUpdate(_instance: Instance, _type: string, oldProps: BaseProps, newProps: BaseProps) {
|
package/src/style-parser.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
declare const CS: {
|
|
10
10
|
UnityEngine: {
|
|
11
11
|
Color: new (r: number, g: number, b: number, a: number) => CSColor;
|
|
12
|
+
FontStyle: Record<string, number>;
|
|
12
13
|
UIElements: {
|
|
13
14
|
Length: new (value: number, unit?: number) => CSLength;
|
|
14
15
|
LengthUnit: { Pixel: number; Percent: number };
|
|
@@ -24,6 +25,9 @@ declare const CS: {
|
|
|
24
25
|
Visibility: Record<string, number>;
|
|
25
26
|
WhiteSpace: Record<string, number>;
|
|
26
27
|
TextAnchor: Record<string, number>;
|
|
28
|
+
TextOverflow: Record<string, number>;
|
|
29
|
+
TextOverflowPosition: Record<string, number>;
|
|
30
|
+
OverflowClipBox: Record<string, number>;
|
|
27
31
|
};
|
|
28
32
|
};
|
|
29
33
|
};
|
|
@@ -78,17 +82,20 @@ const LENGTH_PROPERTIES = new Set([
|
|
|
78
82
|
"borderWidth", "borderTopWidth", "borderRightWidth", "borderBottomWidth", "borderLeftWidth",
|
|
79
83
|
"borderRadius", "borderTopLeftRadius", "borderTopRightRadius", "borderBottomLeftRadius", "borderBottomRightRadius",
|
|
80
84
|
"fontSize",
|
|
85
|
+
"letterSpacing", "wordSpacing", "unityTextOutlineWidth", "unityParagraphSpacing",
|
|
81
86
|
])
|
|
82
87
|
|
|
83
88
|
// Style properties that expect color values
|
|
84
89
|
const COLOR_PROPERTIES = new Set([
|
|
85
90
|
"color", "backgroundColor",
|
|
86
91
|
"borderColor", "borderTopColor", "borderRightColor", "borderBottomColor", "borderLeftColor",
|
|
92
|
+
"unityTextOutlineColor", "unityBackgroundImageTintColor",
|
|
87
93
|
])
|
|
88
94
|
|
|
89
95
|
// Style properties that are plain numbers (no Length wrapper needed)
|
|
90
96
|
const NUMBER_PROPERTIES = new Set([
|
|
91
97
|
"flexGrow", "flexShrink", "opacity",
|
|
98
|
+
"unitySliceTop", "unitySliceRight", "unitySliceBottom", "unitySliceLeft", "unitySliceScale",
|
|
92
99
|
])
|
|
93
100
|
|
|
94
101
|
// Enum property mappings: React style value -> Unity enum value
|
|
@@ -186,6 +193,37 @@ const ENUM_MAPPINGS: Record<string, { enum: () => Record<string, number>, values
|
|
|
186
193
|
"nowrap": "NoWrap",
|
|
187
194
|
}
|
|
188
195
|
},
|
|
196
|
+
unityFontStyleAndWeight: {
|
|
197
|
+
enum: () => CS.UnityEngine.FontStyle,
|
|
198
|
+
values: {
|
|
199
|
+
"normal": "Normal",
|
|
200
|
+
"bold": "Bold",
|
|
201
|
+
"italic": "Italic",
|
|
202
|
+
"bold-and-italic": "BoldAndItalic",
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
textOverflow: {
|
|
206
|
+
enum: () => CS.UnityEngine.UIElements.TextOverflow,
|
|
207
|
+
values: {
|
|
208
|
+
"clip": "Clip",
|
|
209
|
+
"ellipsis": "Ellipsis",
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
unityTextOverflowPosition: {
|
|
213
|
+
enum: () => CS.UnityEngine.UIElements.TextOverflowPosition,
|
|
214
|
+
values: {
|
|
215
|
+
"end": "End",
|
|
216
|
+
"start": "Start",
|
|
217
|
+
"middle": "Middle",
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
unityOverflowClipBox: {
|
|
221
|
+
enum: () => CS.UnityEngine.UIElements.OverflowClipBox,
|
|
222
|
+
values: {
|
|
223
|
+
"padding-box": "PaddingBox",
|
|
224
|
+
"content-box": "ContentBox",
|
|
225
|
+
}
|
|
226
|
+
},
|
|
189
227
|
}
|
|
190
228
|
|
|
191
229
|
/**
|
package/src/types.ts
CHANGED
|
@@ -127,6 +127,70 @@ export interface ViewStyle {
|
|
|
127
127
|
/** Text alignment. Note: Use USS class or stylesheet for -unity-font-style (italic/bold) */
|
|
128
128
|
unityTextAlign?: 'upper-left' | 'upper-center' | 'upper-right' | 'middle-left' | 'middle-center' | 'middle-right' | 'lower-left' | 'lower-center' | 'lower-right';
|
|
129
129
|
whiteSpace?: 'normal' | 'nowrap';
|
|
130
|
+
|
|
131
|
+
// Typography
|
|
132
|
+
/** C# Font object */
|
|
133
|
+
unityFont?: any;
|
|
134
|
+
/** C# FontDefinition object */
|
|
135
|
+
unityFontDefinition?: any;
|
|
136
|
+
/** Font style and weight */
|
|
137
|
+
unityFontStyleAndWeight?: 'normal' | 'bold' | 'italic' | 'bold-and-italic';
|
|
138
|
+
/** Spacing between characters */
|
|
139
|
+
letterSpacing?: StyleLength;
|
|
140
|
+
/** Spacing between words */
|
|
141
|
+
wordSpacing?: StyleLength;
|
|
142
|
+
/** Spacing between paragraphs */
|
|
143
|
+
unityParagraphSpacing?: StyleLength;
|
|
144
|
+
/** Text outline color */
|
|
145
|
+
unityTextOutlineColor?: StyleColor;
|
|
146
|
+
/** Text outline width */
|
|
147
|
+
unityTextOutlineWidth?: StyleLength;
|
|
148
|
+
/** How overflowing text is handled */
|
|
149
|
+
textOverflow?: 'clip' | 'ellipsis';
|
|
150
|
+
/** Position of text overflow indicator */
|
|
151
|
+
unityTextOverflowPosition?: 'end' | 'start' | 'middle';
|
|
152
|
+
|
|
153
|
+
// Background (additional)
|
|
154
|
+
/** Tint color applied to the background image */
|
|
155
|
+
unityBackgroundImageTintColor?: StyleColor;
|
|
156
|
+
|
|
157
|
+
// Slicing
|
|
158
|
+
/** 9-slice top inset */
|
|
159
|
+
unitySliceTop?: number;
|
|
160
|
+
/** 9-slice right inset */
|
|
161
|
+
unitySliceRight?: number;
|
|
162
|
+
/** 9-slice bottom inset */
|
|
163
|
+
unitySliceBottom?: number;
|
|
164
|
+
/** 9-slice left inset */
|
|
165
|
+
unitySliceLeft?: number;
|
|
166
|
+
/** Scale applied to 9-slice borders */
|
|
167
|
+
unitySliceScale?: number;
|
|
168
|
+
|
|
169
|
+
// Transform
|
|
170
|
+
/** Rotation transform. Pass a C# Rotate struct. */
|
|
171
|
+
rotate?: any;
|
|
172
|
+
/** Scale transform. Pass a C# Scale struct. */
|
|
173
|
+
scale?: any;
|
|
174
|
+
/** Translation transform. Pass a C# Translate struct. */
|
|
175
|
+
translate?: any;
|
|
176
|
+
/** Transform origin point. Pass a C# TransformOrigin struct. */
|
|
177
|
+
transformOrigin?: any;
|
|
178
|
+
|
|
179
|
+
// Transition
|
|
180
|
+
/** Delay before transitions start. Pass a C# StyleList. */
|
|
181
|
+
transitionDelay?: any;
|
|
182
|
+
/** Duration of transitions. Pass a C# StyleList. */
|
|
183
|
+
transitionDuration?: any;
|
|
184
|
+
/** Properties to transition. Pass a C# StyleList. */
|
|
185
|
+
transitionProperty?: any;
|
|
186
|
+
/** Timing functions for transitions. Pass a C# StyleList. */
|
|
187
|
+
transitionTimingFunction?: any;
|
|
188
|
+
|
|
189
|
+
// Other
|
|
190
|
+
/** Overflow clipping box */
|
|
191
|
+
unityOverflowClipBox?: 'padding-box' | 'content-box';
|
|
192
|
+
/** Cursor style. Pass a C# Cursor struct. */
|
|
193
|
+
cursor?: any;
|
|
130
194
|
}
|
|
131
195
|
|
|
132
196
|
// Event types
|
|
@@ -331,6 +395,14 @@ export interface BaseProps {
|
|
|
331
395
|
onTransitionEnd?: TransitionEventHandler;
|
|
332
396
|
onTransitionCancel?: TransitionEventHandler;
|
|
333
397
|
|
|
398
|
+
// Picking mode - controls whether the element receives pointer events
|
|
399
|
+
/**
|
|
400
|
+
* Controls whether this element is pickable by pointer events.
|
|
401
|
+
* - "Position" (default): Element receives pointer events based on its rectangle
|
|
402
|
+
* - "Ignore": Element is transparent to pointer events (clicks pass through)
|
|
403
|
+
*/
|
|
404
|
+
pickingMode?: 'Position' | 'Ignore';
|
|
405
|
+
|
|
334
406
|
// Vector drawing
|
|
335
407
|
/**
|
|
336
408
|
* Callback for custom vector drawing via Unity's generateVisualContent.
|