onejs-react 0.1.0 → 0.1.1
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/README.md +84 -0
- package/package.json +2 -1
- package/src/__tests__/components.test.tsx +36 -0
- package/src/__tests__/host-config.test.ts +141 -99
- package/src/__tests__/mocks.ts +17 -1
- package/src/__tests__/setup.ts +23 -11
- package/src/components.tsx +77 -48
- package/src/error-boundary.tsx +175 -0
- package/src/host-config.ts +341 -158
- package/src/index.ts +35 -2
- package/src/renderer.ts +50 -23
- package/src/screen.tsx +1 -1
- package/src/style-parser.ts +42 -70
- package/src/types.ts +196 -5
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Components
|
|
2
2
|
export {
|
|
3
3
|
View,
|
|
4
|
+
Text,
|
|
4
5
|
Label,
|
|
5
6
|
Button,
|
|
6
7
|
TextField,
|
|
@@ -12,7 +13,11 @@ export {
|
|
|
12
13
|
} from './components';
|
|
13
14
|
|
|
14
15
|
// Renderer
|
|
15
|
-
export { render, unmount } from './renderer';
|
|
16
|
+
export { render, unmount, flushSync, batchedUpdates, getDebugInfo } from './renderer';
|
|
17
|
+
|
|
18
|
+
// Error Handling
|
|
19
|
+
export { ErrorBoundary, formatError } from './error-boundary';
|
|
20
|
+
export type { ErrorBoundaryProps } from './error-boundary';
|
|
16
21
|
|
|
17
22
|
// Responsive Design
|
|
18
23
|
export {
|
|
@@ -33,15 +38,32 @@ export type {
|
|
|
33
38
|
// Types
|
|
34
39
|
export type {
|
|
35
40
|
ViewStyle,
|
|
41
|
+
// Event data types
|
|
36
42
|
PointerEventData,
|
|
43
|
+
MouseEventData,
|
|
44
|
+
WheelEventData,
|
|
37
45
|
KeyEventData,
|
|
38
46
|
ChangeEventData,
|
|
47
|
+
FocusEventData,
|
|
48
|
+
DragEventData,
|
|
49
|
+
GeometryEventData,
|
|
50
|
+
NavigationEventData,
|
|
51
|
+
TransitionEventData,
|
|
52
|
+
// Event handler types
|
|
39
53
|
PointerEventHandler,
|
|
54
|
+
MouseEventHandler,
|
|
55
|
+
WheelEventHandler,
|
|
40
56
|
KeyEventHandler,
|
|
41
57
|
ChangeEventHandler,
|
|
42
58
|
FocusEventHandler,
|
|
59
|
+
DragEventHandler,
|
|
60
|
+
GeometryEventHandler,
|
|
61
|
+
NavigationEventHandler,
|
|
62
|
+
TransitionEventHandler,
|
|
63
|
+
// Component props
|
|
43
64
|
BaseProps,
|
|
44
65
|
ViewProps,
|
|
66
|
+
TextProps,
|
|
45
67
|
LabelProps,
|
|
46
68
|
ButtonProps,
|
|
47
69
|
TextFieldProps,
|
|
@@ -49,6 +71,17 @@ export type {
|
|
|
49
71
|
SliderProps,
|
|
50
72
|
ScrollViewProps,
|
|
51
73
|
ImageProps,
|
|
52
|
-
VisualElement,
|
|
53
74
|
ListViewProps,
|
|
75
|
+
// Container type for render()
|
|
76
|
+
RenderContainer,
|
|
77
|
+
// Element types for refs
|
|
78
|
+
VisualElement,
|
|
79
|
+
TextElement,
|
|
80
|
+
LabelElement,
|
|
81
|
+
ButtonElement,
|
|
82
|
+
TextFieldElement,
|
|
83
|
+
ToggleElement,
|
|
84
|
+
SliderElement,
|
|
85
|
+
ScrollViewElement,
|
|
86
|
+
ImageElement,
|
|
54
87
|
} from './types';
|
package/src/renderer.ts
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
import Reconciler from 'react-reconciler';
|
|
2
2
|
import type { ReactNode } from 'react';
|
|
3
3
|
import { hostConfig, type Container } from './host-config';
|
|
4
|
+
import type { RenderContainer, VisualElement } from './types';
|
|
4
5
|
|
|
5
6
|
declare const console: { log: (...args: unknown[]) => void; error: (...args: unknown[]) => void };
|
|
6
7
|
|
|
7
8
|
// Create the reconciler
|
|
8
9
|
const reconciler = Reconciler(hostConfig);
|
|
9
10
|
|
|
10
|
-
// Inject into dev tools
|
|
11
|
+
// Inject into dev tools with full configuration
|
|
12
|
+
// This enables React DevTools to inspect the component tree
|
|
11
13
|
reconciler.injectIntoDevTools({
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
bundleType: 1, // 0 for prod, 1 for dev
|
|
15
|
+
version: '19.0.0',
|
|
16
|
+
rendererPackageName: 'onejs-react',
|
|
15
17
|
});
|
|
16
18
|
|
|
17
19
|
// Track roots for hot reload / re-render
|
|
18
|
-
const roots = new Map<
|
|
20
|
+
const roots = new Map<RenderContainer, ReturnType<typeof reconciler.createContainer>>();
|
|
19
21
|
|
|
20
|
-
export function render(element: ReactNode, container:
|
|
21
|
-
console.log('[onejs-react] render() called');
|
|
22
|
+
export function render(element: ReactNode, container: RenderContainer): void {
|
|
22
23
|
let root = roots.get(container);
|
|
23
24
|
|
|
24
25
|
if (!root) {
|
|
25
|
-
console.log('[onejs-react] creating new container');
|
|
26
26
|
root = reconciler.createContainer(
|
|
27
|
-
container,
|
|
27
|
+
container as Container,
|
|
28
28
|
0, // LegacyRoot (0) vs ConcurrentRoot (1)
|
|
29
29
|
null, // hydrationCallbacks
|
|
30
30
|
false, // isStrictMode
|
|
@@ -36,30 +36,21 @@ export function render(element: ReactNode, container: Container): void {
|
|
|
36
36
|
roots.set(container, root);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
reconciler.updateContainer(element, root, null, () => {
|
|
41
|
-
console.log('[onejs-react] updateContainer callback fired');
|
|
42
|
-
});
|
|
39
|
+
reconciler.updateContainer(element, root, null, () => {});
|
|
43
40
|
|
|
44
41
|
// Try to flush synchronous work
|
|
45
|
-
console.log('[onejs-react] attempting to flush sync work');
|
|
46
42
|
try {
|
|
47
|
-
// flushSync may be exported differently - try flushSyncWork first
|
|
48
43
|
if (typeof (reconciler as any).flushSyncWork === 'function') {
|
|
49
44
|
(reconciler as any).flushSyncWork();
|
|
50
|
-
console.log('[onejs-react] flushSyncWork completed');
|
|
51
45
|
} else if (typeof (reconciler as any).flushSync === 'function') {
|
|
52
46
|
(reconciler as any).flushSync(() => {});
|
|
53
|
-
console.log('[onejs-react] flushSync completed');
|
|
54
|
-
} else {
|
|
55
|
-
console.log('[onejs-react] no sync flush method available, relying on microtasks');
|
|
56
47
|
}
|
|
57
48
|
} catch (e) {
|
|
58
|
-
|
|
49
|
+
// Sync flush failed, rely on microtasks
|
|
59
50
|
}
|
|
60
51
|
}
|
|
61
52
|
|
|
62
|
-
export function unmount(container:
|
|
53
|
+
export function unmount(container: RenderContainer): void {
|
|
63
54
|
const root = roots.get(container);
|
|
64
55
|
if (root) {
|
|
65
56
|
reconciler.updateContainer(null, root, null, () => {});
|
|
@@ -67,7 +58,43 @@ export function unmount(container: Container): void {
|
|
|
67
58
|
}
|
|
68
59
|
}
|
|
69
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Execute a callback synchronously, flushing all updates before returning.
|
|
63
|
+
* Useful for tests or when you need immediate UI updates.
|
|
64
|
+
*/
|
|
65
|
+
export function flushSync<T>(callback: () => T): T {
|
|
66
|
+
if (typeof (reconciler as any).flushSync === 'function') {
|
|
67
|
+
return (reconciler as any).flushSync(callback);
|
|
68
|
+
}
|
|
69
|
+
// Fallback: just call the callback
|
|
70
|
+
return callback();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Batch multiple updates together for better performance.
|
|
75
|
+
* All updates inside the callback are batched into a single render.
|
|
76
|
+
*/
|
|
77
|
+
export function batchedUpdates<T>(callback: () => T): T {
|
|
78
|
+
if (typeof (reconciler as any).batchedUpdates === 'function') {
|
|
79
|
+
return (reconciler as any).batchedUpdates(callback);
|
|
80
|
+
}
|
|
81
|
+
// Fallback: just call the callback
|
|
82
|
+
return callback();
|
|
83
|
+
}
|
|
84
|
+
|
|
70
85
|
// Export for testing/debugging
|
|
71
|
-
export function getRoot(container:
|
|
72
|
-
|
|
86
|
+
export function getRoot(container: RenderContainer) {
|
|
87
|
+
return roots.get(container);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get debug info about all active render roots.
|
|
92
|
+
* Useful for debugging and DevTools integration.
|
|
93
|
+
*/
|
|
94
|
+
export function getDebugInfo() {
|
|
95
|
+
return {
|
|
96
|
+
activeRoots: roots.size,
|
|
97
|
+
reconcilerVersion: '0.31.0',
|
|
98
|
+
reactVersion: '19.0.0',
|
|
99
|
+
};
|
|
73
100
|
}
|
package/src/screen.tsx
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Mobile-first: At 1400px width, sm/md/lg/xl are all active (not just xl).
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { createContext, useContext, useState, useEffect, ReactNode } from "react"
|
|
10
|
+
import { createContext, useContext, useState, useEffect, type ReactNode } from "react"
|
|
11
11
|
|
|
12
12
|
// Globals from QuickJS environment
|
|
13
13
|
declare const __root: {
|
package/src/style-parser.ts
CHANGED
|
@@ -130,13 +130,39 @@ export function parseLength(value: number | string): CSLength | number | null {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
/**
|
|
133
|
-
*
|
|
133
|
+
* Clamp a number to [0, 1] range
|
|
134
134
|
*/
|
|
135
|
-
function
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
function clamp01(n: number): number {
|
|
136
|
+
return n < 0 ? 0 : n > 1 ? 1 : n
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Create a Unity Color with clamped values
|
|
141
|
+
*/
|
|
142
|
+
function createColor(r: number, g: number, b: number, a: number): CSColor {
|
|
143
|
+
return new CS.UnityEngine.Color(clamp01(r), clamp01(g), clamp01(b), clamp01(a))
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Parse hex color string into RGBA components
|
|
148
|
+
* Supports: #rgb, #rgba, #rrggbb, #rrggbbaa
|
|
149
|
+
*/
|
|
150
|
+
function parseHexColor(hex: string): CSColor | null {
|
|
151
|
+
const len = hex.length
|
|
152
|
+
const isShort = len === 3 || len === 4
|
|
153
|
+
const isLong = len === 6 || len === 8
|
|
154
|
+
if (!isShort && !isLong) return null
|
|
155
|
+
|
|
156
|
+
const step = isShort ? 1 : 2
|
|
157
|
+
const parse = (i: number): number => {
|
|
158
|
+
const slice = hex.slice(i * step, i * step + step)
|
|
159
|
+
const expanded = isShort ? slice + slice : slice
|
|
160
|
+
return parseInt(expanded, 16) / 255
|
|
138
161
|
}
|
|
139
|
-
|
|
162
|
+
|
|
163
|
+
const r = parse(0), g = parse(1), b = parse(2)
|
|
164
|
+
const a = (len === 4 || len === 8) ? parse(3) : 1
|
|
165
|
+
return createColor(r, g, b, a)
|
|
140
166
|
}
|
|
141
167
|
|
|
142
168
|
/**
|
|
@@ -155,75 +181,21 @@ export function parseColor(value: string): CSColor | null {
|
|
|
155
181
|
return new CS.UnityEngine.Color(r, g, b, a)
|
|
156
182
|
}
|
|
157
183
|
|
|
158
|
-
// Hex colors
|
|
184
|
+
// Hex colors
|
|
159
185
|
if (trimmed.startsWith("#")) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (hex.length === 3) {
|
|
163
|
-
// #rgb
|
|
164
|
-
const r = parseHexComponent(hex[0])
|
|
165
|
-
const g = parseHexComponent(hex[1])
|
|
166
|
-
const b = parseHexComponent(hex[2])
|
|
167
|
-
return new CS.UnityEngine.Color(r, g, b, 1)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (hex.length === 4) {
|
|
171
|
-
// #rgba
|
|
172
|
-
const r = parseHexComponent(hex[0])
|
|
173
|
-
const g = parseHexComponent(hex[1])
|
|
174
|
-
const b = parseHexComponent(hex[2])
|
|
175
|
-
const a = parseHexComponent(hex[3])
|
|
176
|
-
return new CS.UnityEngine.Color(r, g, b, a)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (hex.length === 6) {
|
|
180
|
-
// #rrggbb
|
|
181
|
-
const r = parseHexComponent(hex.slice(0, 2))
|
|
182
|
-
const g = parseHexComponent(hex.slice(2, 4))
|
|
183
|
-
const b = parseHexComponent(hex.slice(4, 6))
|
|
184
|
-
return new CS.UnityEngine.Color(r, g, b, 1)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (hex.length === 8) {
|
|
188
|
-
// #rrggbbaa
|
|
189
|
-
const r = parseHexComponent(hex.slice(0, 2))
|
|
190
|
-
const g = parseHexComponent(hex.slice(2, 4))
|
|
191
|
-
const b = parseHexComponent(hex.slice(4, 6))
|
|
192
|
-
const a = parseHexComponent(hex.slice(6, 8))
|
|
193
|
-
return new CS.UnityEngine.Color(r, g, b, a)
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return null
|
|
186
|
+
return parseHexColor(trimmed.slice(1))
|
|
197
187
|
}
|
|
198
188
|
|
|
199
|
-
// rgb(r, g, b) or rgba(r, g, b, a)
|
|
200
|
-
const rgbMatch = trimmed.match(/^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)$/)
|
|
189
|
+
// rgb(r, g, b) or rgba(r, g, b, a) - supports both integer and percentage values
|
|
190
|
+
const rgbMatch = trimmed.match(/^rgba?\s*\(\s*([\d.]+)(%?)\s*,\s*([\d.]+)(%?)\s*,\s*([\d.]+)(%?)\s*(?:,\s*([\d.]+))?\s*\)$/)
|
|
201
191
|
if (rgbMatch) {
|
|
202
|
-
const
|
|
203
|
-
const
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
Math.min(1, Math.max(0, b)),
|
|
210
|
-
Math.min(1, Math.max(0, a))
|
|
211
|
-
)
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// rgb with percentages: rgb(100%, 0%, 0%)
|
|
215
|
-
const rgbPercentMatch = trimmed.match(/^rgba?\s*\(\s*([\d.]+)%\s*,\s*([\d.]+)%\s*,\s*([\d.]+)%\s*(?:,\s*([\d.]+))?\s*\)$/)
|
|
216
|
-
if (rgbPercentMatch) {
|
|
217
|
-
const r = parseFloat(rgbPercentMatch[1]) / 100
|
|
218
|
-
const g = parseFloat(rgbPercentMatch[2]) / 100
|
|
219
|
-
const b = parseFloat(rgbPercentMatch[3]) / 100
|
|
220
|
-
const a = rgbPercentMatch[4] !== undefined ? parseFloat(rgbPercentMatch[4]) : 1
|
|
221
|
-
return new CS.UnityEngine.Color(
|
|
222
|
-
Math.min(1, Math.max(0, r)),
|
|
223
|
-
Math.min(1, Math.max(0, g)),
|
|
224
|
-
Math.min(1, Math.max(0, b)),
|
|
225
|
-
Math.min(1, Math.max(0, a))
|
|
226
|
-
)
|
|
192
|
+
const isPercent = rgbMatch[2] === "%"
|
|
193
|
+
const divisor = isPercent ? 100 : 255
|
|
194
|
+
const r = parseFloat(rgbMatch[1]) / divisor
|
|
195
|
+
const g = parseFloat(rgbMatch[3]) / divisor
|
|
196
|
+
const b = parseFloat(rgbMatch[5]) / divisor
|
|
197
|
+
const a = rgbMatch[7] !== undefined ? parseFloat(rgbMatch[7]) : 1
|
|
198
|
+
return createColor(r, g, b, a)
|
|
227
199
|
}
|
|
228
200
|
|
|
229
201
|
return null
|
package/src/types.ts
CHANGED
|
@@ -81,6 +81,15 @@ export interface ViewStyle {
|
|
|
81
81
|
// Background
|
|
82
82
|
/** Background color. Examples: "#3498db", "rgba(0,0,0,0.5)", "red" */
|
|
83
83
|
backgroundColor?: StyleColor;
|
|
84
|
+
/**
|
|
85
|
+
* Background image - accepts a Texture2D, RenderTexture, or RenderTexture object from GPU compute.
|
|
86
|
+
*
|
|
87
|
+
* For GPU compute RenderTextures, you can pass the RenderTexture object directly:
|
|
88
|
+
* @example
|
|
89
|
+
* const rt = compute.renderTexture({ width: 100, height: 100 })
|
|
90
|
+
* <View style={{ backgroundImage: rt }} />
|
|
91
|
+
*/
|
|
92
|
+
backgroundImage?: object | null;
|
|
84
93
|
|
|
85
94
|
// Border
|
|
86
95
|
/** Border color for all sides. Examples: "#ccc", "rgba(0,0,0,0.1)" */
|
|
@@ -127,6 +136,23 @@ export interface PointerEventData {
|
|
|
127
136
|
y: number;
|
|
128
137
|
button: number;
|
|
129
138
|
pointerId: number;
|
|
139
|
+
modifiers?: number;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface MouseEventData {
|
|
143
|
+
type: string;
|
|
144
|
+
x: number;
|
|
145
|
+
y: number;
|
|
146
|
+
button: number;
|
|
147
|
+
modifiers?: number;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface WheelEventData {
|
|
151
|
+
type: string;
|
|
152
|
+
x: number;
|
|
153
|
+
y: number;
|
|
154
|
+
delta: { x: number; y: number };
|
|
155
|
+
modifiers?: number;
|
|
130
156
|
}
|
|
131
157
|
|
|
132
158
|
export interface KeyEventData {
|
|
@@ -145,10 +171,47 @@ export interface ChangeEventData<T = unknown> {
|
|
|
145
171
|
value: T;
|
|
146
172
|
}
|
|
147
173
|
|
|
174
|
+
export interface FocusEventData {
|
|
175
|
+
type: string;
|
|
176
|
+
relatedTarget?: unknown;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface DragEventData {
|
|
180
|
+
type: string;
|
|
181
|
+
x: number;
|
|
182
|
+
y: number;
|
|
183
|
+
// Drag-specific properties
|
|
184
|
+
getData?: (type: string) => unknown;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export interface GeometryEventData {
|
|
188
|
+
type: string;
|
|
189
|
+
oldRect: { x: number; y: number; width: number; height: number };
|
|
190
|
+
newRect: { x: number; y: number; width: number; height: number };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface NavigationEventData {
|
|
194
|
+
type: string;
|
|
195
|
+
direction?: string;
|
|
196
|
+
modifiers?: number;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export interface TransitionEventData {
|
|
200
|
+
type: string;
|
|
201
|
+
styleProperty: string;
|
|
202
|
+
elapsedTime: number;
|
|
203
|
+
}
|
|
204
|
+
|
|
148
205
|
export type PointerEventHandler = (event: PointerEventData) => void;
|
|
206
|
+
export type MouseEventHandler = (event: MouseEventData) => void;
|
|
207
|
+
export type WheelEventHandler = (event: WheelEventData) => void;
|
|
149
208
|
export type KeyEventHandler = (event: KeyEventData) => void;
|
|
150
209
|
export type ChangeEventHandler<T = unknown> = (event: ChangeEventData<T>) => void;
|
|
151
|
-
export type FocusEventHandler = () => void;
|
|
210
|
+
export type FocusEventHandler = (event?: FocusEventData) => void;
|
|
211
|
+
export type DragEventHandler = (event: DragEventData) => void;
|
|
212
|
+
export type GeometryEventHandler = (event: GeometryEventData) => void;
|
|
213
|
+
export type NavigationEventHandler = (event: NavigationEventData) => void;
|
|
214
|
+
export type TransitionEventHandler = (event: TransitionEventData) => void;
|
|
152
215
|
|
|
153
216
|
// Base props for all components
|
|
154
217
|
export interface BaseProps {
|
|
@@ -157,26 +220,76 @@ export interface BaseProps {
|
|
|
157
220
|
style?: ViewStyle;
|
|
158
221
|
className?: string;
|
|
159
222
|
|
|
160
|
-
//
|
|
223
|
+
// Click
|
|
161
224
|
onClick?: PointerEventHandler;
|
|
225
|
+
|
|
226
|
+
// Pointer events
|
|
162
227
|
onPointerDown?: PointerEventHandler;
|
|
163
228
|
onPointerUp?: PointerEventHandler;
|
|
164
229
|
onPointerMove?: PointerEventHandler;
|
|
165
230
|
onPointerEnter?: PointerEventHandler;
|
|
166
231
|
onPointerLeave?: PointerEventHandler;
|
|
232
|
+
onPointerCancel?: PointerEventHandler;
|
|
233
|
+
onPointerCapture?: PointerEventHandler;
|
|
234
|
+
onPointerCaptureOut?: PointerEventHandler;
|
|
235
|
+
onPointerStationary?: PointerEventHandler;
|
|
236
|
+
|
|
237
|
+
// Mouse events
|
|
238
|
+
onMouseDown?: MouseEventHandler;
|
|
239
|
+
onMouseUp?: MouseEventHandler;
|
|
240
|
+
onMouseMove?: MouseEventHandler;
|
|
241
|
+
onMouseEnter?: MouseEventHandler;
|
|
242
|
+
onMouseLeave?: MouseEventHandler;
|
|
243
|
+
onMouseOver?: MouseEventHandler;
|
|
244
|
+
onMouseOut?: MouseEventHandler;
|
|
245
|
+
onWheel?: WheelEventHandler;
|
|
246
|
+
onContextClick?: MouseEventHandler;
|
|
167
247
|
|
|
168
248
|
// Focus events
|
|
169
249
|
onFocus?: FocusEventHandler;
|
|
170
250
|
onBlur?: FocusEventHandler;
|
|
251
|
+
onFocusIn?: FocusEventHandler;
|
|
252
|
+
onFocusOut?: FocusEventHandler;
|
|
171
253
|
|
|
172
254
|
// Keyboard events
|
|
173
255
|
onKeyDown?: KeyEventHandler;
|
|
174
256
|
onKeyUp?: KeyEventHandler;
|
|
257
|
+
|
|
258
|
+
// Input events
|
|
259
|
+
onInput?: ChangeEventHandler;
|
|
260
|
+
|
|
261
|
+
// Drag events
|
|
262
|
+
onDragEnter?: DragEventHandler;
|
|
263
|
+
onDragLeave?: DragEventHandler;
|
|
264
|
+
onDragUpdated?: DragEventHandler;
|
|
265
|
+
onDragPerform?: DragEventHandler;
|
|
266
|
+
onDragExited?: DragEventHandler;
|
|
267
|
+
|
|
268
|
+
// Geometry events
|
|
269
|
+
onGeometryChanged?: GeometryEventHandler;
|
|
270
|
+
|
|
271
|
+
// Navigation events
|
|
272
|
+
onNavigationMove?: NavigationEventHandler;
|
|
273
|
+
onNavigationSubmit?: NavigationEventHandler;
|
|
274
|
+
onNavigationCancel?: NavigationEventHandler;
|
|
275
|
+
|
|
276
|
+
// Tooltip
|
|
277
|
+
onTooltip?: () => void;
|
|
278
|
+
|
|
279
|
+
// Transition events
|
|
280
|
+
onTransitionRun?: TransitionEventHandler;
|
|
281
|
+
onTransitionStart?: TransitionEventHandler;
|
|
282
|
+
onTransitionEnd?: TransitionEventHandler;
|
|
283
|
+
onTransitionCancel?: TransitionEventHandler;
|
|
175
284
|
}
|
|
176
285
|
|
|
177
286
|
// Component-specific props
|
|
178
287
|
export interface ViewProps extends BaseProps {}
|
|
179
288
|
|
|
289
|
+
export interface TextProps extends BaseProps {
|
|
290
|
+
text?: string;
|
|
291
|
+
}
|
|
292
|
+
|
|
180
293
|
export interface LabelProps extends BaseProps {
|
|
181
294
|
text?: string;
|
|
182
295
|
}
|
|
@@ -235,18 +348,96 @@ export interface ImageProps extends BaseProps {
|
|
|
235
348
|
scaleMode?: 'stretch-to-fill' | 'scale-and-crop' | 'scale-to-fit';
|
|
236
349
|
}
|
|
237
350
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
351
|
+
/**
|
|
352
|
+
* Minimal container type for render() function.
|
|
353
|
+
* Accepts any Unity VisualElement (CS.UnityEngine.UIElements.VisualElement)
|
|
354
|
+
* or the detailed VisualElement interface below.
|
|
355
|
+
*/
|
|
356
|
+
export interface RenderContainer {
|
|
241
357
|
__csHandle: number;
|
|
242
358
|
__csType: string;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// VisualElement - base type for all UI Toolkit elements
|
|
362
|
+
// This is the C# VisualElement exposed to JS via refs
|
|
363
|
+
// Note: This interface represents the JS-side view of Unity's VisualElement
|
|
364
|
+
export interface VisualElement extends RenderContainer {
|
|
243
365
|
style: Record<string, unknown>;
|
|
366
|
+
name: string;
|
|
367
|
+
visible: boolean;
|
|
368
|
+
enabledSelf: boolean;
|
|
369
|
+
enabledInHierarchy: boolean;
|
|
370
|
+
|
|
371
|
+
// Text content (for TextElement-derived types)
|
|
244
372
|
text?: string;
|
|
373
|
+
|
|
374
|
+
// Label (for labeled controls like Toggle)
|
|
375
|
+
label?: string;
|
|
376
|
+
|
|
377
|
+
// Value (for input controls)
|
|
245
378
|
value?: unknown;
|
|
379
|
+
|
|
380
|
+
// Hierarchy
|
|
246
381
|
Add: (child: VisualElement) => void;
|
|
382
|
+
Insert: (index: number, child: VisualElement) => void;
|
|
247
383
|
Remove: (child: VisualElement) => void;
|
|
384
|
+
RemoveAt: (index: number) => void;
|
|
385
|
+
Clear: () => void;
|
|
386
|
+
IndexOf: (child: VisualElement) => number;
|
|
387
|
+
childCount: number;
|
|
388
|
+
parent: VisualElement | null;
|
|
389
|
+
|
|
390
|
+
// Classes
|
|
248
391
|
AddToClassList: (className: string) => void;
|
|
249
392
|
RemoveFromClassList: (className: string) => void;
|
|
393
|
+
ClearClassList: () => void;
|
|
394
|
+
ClassListContains: (className: string) => boolean;
|
|
395
|
+
|
|
396
|
+
// Focus
|
|
397
|
+
Focus: () => void;
|
|
398
|
+
Blur: () => void;
|
|
399
|
+
focusable: boolean;
|
|
400
|
+
|
|
401
|
+
// Layout
|
|
402
|
+
MarkDirtyRepaint: () => void;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Specific element types for better ref typing
|
|
406
|
+
export interface TextElement extends VisualElement {
|
|
407
|
+
text: string;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export interface LabelElement extends TextElement {}
|
|
411
|
+
|
|
412
|
+
export interface ButtonElement extends TextElement {}
|
|
413
|
+
|
|
414
|
+
export interface TextFieldElement extends VisualElement {
|
|
415
|
+
value: string;
|
|
416
|
+
text: string;
|
|
417
|
+
isReadOnly: boolean;
|
|
418
|
+
isPasswordField: boolean;
|
|
419
|
+
maxLength: number;
|
|
420
|
+
SelectAll: () => void;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export interface ToggleElement extends VisualElement {
|
|
424
|
+
value: boolean;
|
|
425
|
+
text: string;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export interface SliderElement extends VisualElement {
|
|
429
|
+
value: number;
|
|
430
|
+
lowValue: number;
|
|
431
|
+
highValue: number;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export interface ScrollViewElement extends VisualElement {
|
|
435
|
+
scrollOffset: { x: number; y: number };
|
|
436
|
+
ScrollTo: (child: VisualElement) => void;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
export interface ImageElement extends VisualElement {
|
|
440
|
+
// Image-specific properties handled via style.backgroundImage
|
|
250
441
|
}
|
|
251
442
|
|
|
252
443
|
// ListView uses Unity's virtualization callbacks directly
|