onejs-react 0.1.0

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.
@@ -0,0 +1,749 @@
1
+ import type {HostConfig} from 'react-reconciler';
2
+ import type {BaseProps, ViewStyle} from './types';
3
+ import {parseStyleValue} from './style-parser';
4
+
5
+ // Global declarations for QuickJS environment
6
+ declare function setTimeout(callback: () => void, ms?: number): number;
7
+
8
+ declare function clearTimeout(id: number): void;
9
+
10
+ declare const console: { log: (...args: unknown[]) => void; error: (...args: unknown[]) => void };
11
+
12
+ // Priority constants from react-reconciler/constants
13
+ // These match React's internal lane priorities
14
+ const DiscreteEventPriority = 2;
15
+ const ContinuousEventPriority = 8;
16
+ const DefaultEventPriority = 32;
17
+ const IdleEventPriority = 536870912;
18
+
19
+ // Current update priority - used by React's scheduler
20
+ let currentUpdatePriority = DefaultEventPriority;
21
+
22
+ // Microtask scheduling
23
+ declare function queueMicrotask(callback: () => void): void;
24
+
25
+ // Unity enum types (accessed as CS.UnityEngine.UIElements.EnumName.Value)
26
+ interface CSEnum {
27
+ [key: string]: number;
28
+ }
29
+
30
+ // CS interop - these are provided by QuickJSBootstrap.js
31
+ declare const CS: {
32
+ UnityEngine: {
33
+ UIElements: {
34
+ VisualElement: new () => CSObject;
35
+ Label: new () => CSObject;
36
+ Button: new () => CSObject;
37
+ TextField: new () => CSObject;
38
+ Toggle: new () => CSObject;
39
+ Slider: new () => CSObject;
40
+ ScrollView: new () => CSObject;
41
+ Image: new () => CSObject;
42
+ ListView: new () => CSListView;
43
+ // Enums
44
+ ScrollViewMode: CSEnum;
45
+ ScrollerVisibility: CSEnum;
46
+ TouchScrollBehavior: CSEnum;
47
+ NestedInteractionKind: CSEnum;
48
+ SelectionType: CSEnum;
49
+ ListViewReorderMode: CSEnum;
50
+ AlternatingRowBackground: CSEnum;
51
+ CollectionVirtualizationMethod: CSEnum;
52
+ };
53
+ };
54
+ };
55
+
56
+ declare const __eventAPI: {
57
+ addEventListener: (element: CSObject, eventType: string, callback: Function) => void;
58
+ removeEventListener: (element: CSObject, eventType: string, callback: Function) => void;
59
+ removeAllEventListeners: (element: CSObject) => void;
60
+ };
61
+
62
+ interface CSObject {
63
+ __csHandle: number;
64
+ __csType: string;
65
+ Add: (child: CSObject) => void;
66
+ Insert: (index: number, child: CSObject) => void;
67
+ Remove: (child: CSObject) => void;
68
+ RemoveAt: (index: number) => void;
69
+ IndexOf: (child: CSObject) => number;
70
+ Clear: () => void;
71
+ style: CSStyle;
72
+ text?: string;
73
+ value?: unknown;
74
+ label?: string;
75
+ AddToClassList: (className: string) => void;
76
+ RemoveFromClassList: (className: string) => void;
77
+ ClearClassList: () => void;
78
+ }
79
+
80
+ interface CSStyle {
81
+ [key: string]: unknown;
82
+ }
83
+
84
+ // ScrollView-specific interface
85
+ interface CSScrollView extends CSObject {
86
+ mode: number;
87
+ horizontalScrollerVisibility: number;
88
+ verticalScrollerVisibility: number;
89
+ elasticity: number;
90
+ elasticAnimationIntervalMs: number;
91
+ scrollDecelerationRate: number;
92
+ mouseWheelScrollSize: number;
93
+ horizontalPageSize: number;
94
+ verticalPageSize: number;
95
+ touchScrollBehavior: number;
96
+ nestedInteractionKind: number;
97
+ }
98
+
99
+ // ListView-specific interface
100
+ interface CSListView extends CSObject {
101
+ // Data binding callbacks
102
+ itemsSource: unknown[];
103
+ makeItem: () => CSObject;
104
+ bindItem: (element: CSObject, index: number) => void;
105
+ unbindItem: (element: CSObject, index: number) => void;
106
+ destroyItem: (element: CSObject) => void;
107
+
108
+ // Virtualization
109
+ fixedItemHeight: number;
110
+ virtualizationMethod: number;
111
+
112
+ // Selection
113
+ selectionType: number;
114
+ selectedIndex: number;
115
+ selectedIndices: number[];
116
+
117
+ // Reordering
118
+ reorderable: boolean;
119
+ reorderMode: number;
120
+
121
+ // Header/Footer
122
+ showFoldoutHeader: boolean;
123
+ headerTitle: string;
124
+ showAddRemoveFooter: boolean;
125
+
126
+ // Appearance
127
+ showBorder: boolean;
128
+ showAlternatingRowBackgrounds: number;
129
+
130
+ // Methods
131
+ RefreshItems: () => void;
132
+ Rebuild: () => void;
133
+ }
134
+
135
+ // Instance type used by the reconciler
136
+ export interface Instance {
137
+ element: CSObject;
138
+ type: string;
139
+ props: BaseProps;
140
+ eventHandlers: Map<string, Function>;
141
+ appliedStyleKeys: Set<string>; // Track which style properties are currently applied
142
+ }
143
+
144
+ export type TextInstance = Instance; // For Label elements with text content
145
+ export type Container = CSObject;
146
+ export type ChildSet = never; // Not using persistent mode
147
+
148
+ // Map React element types to UI Toolkit classes
149
+ // Element types use 'ojs-' prefix to avoid conflicts with HTML/SVG in @types/react
150
+ const TYPE_MAP: Record<string, () => CSObject> = {
151
+ 'ojs-view': () => new CS.UnityEngine.UIElements.VisualElement(),
152
+ 'ojs-label': () => new CS.UnityEngine.UIElements.Label(),
153
+ 'ojs-button': () => new CS.UnityEngine.UIElements.Button(),
154
+ 'ojs-textfield': () => new CS.UnityEngine.UIElements.TextField(),
155
+ 'ojs-toggle': () => new CS.UnityEngine.UIElements.Toggle(),
156
+ 'ojs-slider': () => new CS.UnityEngine.UIElements.Slider(),
157
+ 'ojs-scrollview': () => new CS.UnityEngine.UIElements.ScrollView(),
158
+ 'ojs-image': () => new CS.UnityEngine.UIElements.Image(),
159
+ 'ojs-listview': () => new CS.UnityEngine.UIElements.ListView(),
160
+ };
161
+
162
+ // Event prop to event type mapping
163
+ const EVENT_PROPS: Record<string, string> = {
164
+ onClick: 'click',
165
+ onPointerDown: 'pointerdown',
166
+ onPointerUp: 'pointerup',
167
+ onPointerMove: 'pointermove',
168
+ onPointerEnter: 'pointerenter',
169
+ onPointerLeave: 'pointerleave',
170
+ onFocus: 'focus',
171
+ onBlur: 'blur',
172
+ onKeyDown: 'keydown',
173
+ onKeyUp: 'keyup',
174
+ onChange: 'change',
175
+ };
176
+
177
+ // Shorthand style properties that expand to multiple properties
178
+ const STYLE_SHORTHANDS: Record<string, string[]> = {
179
+ padding: ['paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft'],
180
+ margin: ['marginTop', 'marginRight', 'marginBottom', 'marginLeft'],
181
+ borderWidth: ['borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth'],
182
+ borderColor: ['borderTopColor', 'borderRightColor', 'borderBottomColor', 'borderLeftColor'],
183
+ borderRadius: ['borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius'],
184
+ };
185
+
186
+ // Character escape mapping for Tailwind/USS compatibility
187
+ // USS class names only support [\w-] so special characters must be escaped
188
+ const CLASS_ESCAPE_MAP: [string, string][] = [
189
+ [':', '_c_'], // Pseudo-classes and breakpoints (hover:, sm:)
190
+ ['/', '_s_'], // Fractions (w-1/2)
191
+ ['.', '_d_'], // Decimals (p-2.5)
192
+ ['[', '_lb_'], // Arbitrary values ([100px])
193
+ [']', '_rb_'],
194
+ ['(', '_lp_'], // Functions (calc())
195
+ [')', '_rp_'],
196
+ ['#', '_n_'], // Hex colors
197
+ ['%', '_p_'], // Percentages
198
+ [',', '_cm_'], // Multiple values
199
+ ['&', '_amp_'],
200
+ ['>', '_gt_'],
201
+ ['<', '_lt_'],
202
+ ['*', '_ast_'],
203
+ ["'", '_sq_'],
204
+ ];
205
+
206
+ /**
207
+ * Escape special characters in a class name for USS compatibility
208
+ * Tailwind class names like "hover:bg-red-500" become "hover_c_bg-red-500"
209
+ */
210
+ function escapeClassName(name: string): string {
211
+ let escaped = name;
212
+ for (const [char, replacement] of CLASS_ESCAPE_MAP) {
213
+ if (escaped.includes(char)) {
214
+ escaped = escaped.split(char).join(replacement);
215
+ }
216
+ }
217
+ return escaped;
218
+ }
219
+
220
+ // Get all expanded property keys for a style object
221
+ function getExpandedStyleKeys(style: ViewStyle | undefined): Set<string> {
222
+ const keys = new Set<string>();
223
+ if (!style) return keys;
224
+
225
+ for (const [key, value] of Object.entries(style)) {
226
+ if (value === undefined) continue;
227
+
228
+ const expanded = STYLE_SHORTHANDS[key];
229
+ if (expanded) {
230
+ for (const prop of expanded) {
231
+ keys.add(prop);
232
+ }
233
+ } else {
234
+ keys.add(key);
235
+ }
236
+ }
237
+ return keys;
238
+ }
239
+
240
+ // Apply style properties to element, returns the set of applied keys
241
+ function applyStyle(element: CSObject, style: ViewStyle | undefined): Set<string> {
242
+ const appliedKeys = new Set<string>();
243
+ if (!style) return appliedKeys;
244
+
245
+ const s = element.style;
246
+ for (const [key, value] of Object.entries(style)) {
247
+ if (value === undefined) continue;
248
+
249
+ // Handle shorthand properties
250
+ const expanded = STYLE_SHORTHANDS[key];
251
+ if (expanded) {
252
+ // Parse the value once, apply to all expanded properties
253
+ const parsed = parseStyleValue(expanded[0], value);
254
+ for (const prop of expanded) {
255
+ s[prop] = parsed;
256
+ appliedKeys.add(prop);
257
+ }
258
+ } else {
259
+ // Parse and apply individual property
260
+ s[key] = parseStyleValue(key, value);
261
+ appliedKeys.add(key);
262
+ }
263
+ }
264
+ return appliedKeys;
265
+ }
266
+
267
+ // Clear style properties that are no longer in the new style
268
+ function clearRemovedStyles(element: CSObject, oldKeys: Set<string>, newKeys: Set<string>) {
269
+ const s = element.style;
270
+ for (const key of oldKeys) {
271
+ if (!newKeys.has(key)) {
272
+ // Setting to undefined clears the inline style, falling back to USS
273
+ s[key] = undefined;
274
+ }
275
+ }
276
+ }
277
+
278
+ // Parse className string into a Set of escaped class names
279
+ function parseClassNames(className: string | undefined): Set<string> {
280
+ if (!className) return new Set();
281
+ return new Set(
282
+ className.split(/\s+/)
283
+ .filter(Boolean)
284
+ .map(escapeClassName)
285
+ );
286
+ }
287
+
288
+ // Apply className(s) to element (with escaping for Tailwind/USS compatibility)
289
+ function applyClassName(element: CSObject, className: string | undefined) {
290
+ if (!className) return;
291
+
292
+ const classes = className.split(/\s+/).filter(Boolean);
293
+ for (const cls of classes) {
294
+ element.AddToClassList(escapeClassName(cls));
295
+ }
296
+ }
297
+
298
+ // Update className selectively - only add/remove what changed
299
+ function updateClassNames(element: CSObject, oldClassName: string | undefined, newClassName: string | undefined) {
300
+ const oldClasses = parseClassNames(oldClassName);
301
+ const newClasses = parseClassNames(newClassName);
302
+
303
+ // Remove classes that are no longer present
304
+ for (const cls of oldClasses) {
305
+ if (!newClasses.has(cls)) {
306
+ element.RemoveFromClassList(cls);
307
+ }
308
+ }
309
+
310
+ // Add classes that are new
311
+ for (const cls of newClasses) {
312
+ if (!oldClasses.has(cls)) {
313
+ element.AddToClassList(cls);
314
+ }
315
+ }
316
+ }
317
+
318
+ // Apply event handlers
319
+ function applyEvents(instance: Instance, props: BaseProps) {
320
+ for (const [propName, eventType] of Object.entries(EVENT_PROPS)) {
321
+ const handler = (props as Record<string, unknown>)[propName] as Function | undefined;
322
+ const existingHandler = instance.eventHandlers.get(eventType);
323
+
324
+ if (handler !== existingHandler) {
325
+ if (existingHandler) {
326
+ __eventAPI.removeEventListener(instance.element, eventType, existingHandler);
327
+ instance.eventHandlers.delete(eventType);
328
+ }
329
+ if (handler) {
330
+ __eventAPI.addEventListener(instance.element, eventType, handler);
331
+ instance.eventHandlers.set(eventType, handler);
332
+ }
333
+ }
334
+ }
335
+ }
336
+
337
+ // Apply component-specific props
338
+ function applyComponentProps(element: CSObject, type: string, props: Record<string, unknown>) {
339
+ // For Label, Button - set text property directly
340
+ if (props.text !== undefined) {
341
+ (element as { text: string }).text = props.text as string;
342
+ }
343
+ // For TextField, Toggle, Slider - set value property
344
+ if (props.value !== undefined) {
345
+ (element as { value: unknown }).value = props.value;
346
+ }
347
+ // For input elements that have a label
348
+ if (props.label !== undefined) {
349
+ (element as { label: string }).label = props.label as string;
350
+ }
351
+
352
+ // ScrollView-specific properties
353
+ if (type === 'ojs-scrollview') {
354
+ const sv = element as CSScrollView;
355
+ if (props.mode !== undefined) {
356
+ sv.mode = CS.UnityEngine.UIElements.ScrollViewMode[props.mode as string];
357
+ }
358
+ if (props.horizontalScrollerVisibility !== undefined) {
359
+ sv.horizontalScrollerVisibility = CS.UnityEngine.UIElements.ScrollerVisibility[props.horizontalScrollerVisibility as string];
360
+ }
361
+ if (props.verticalScrollerVisibility !== undefined) {
362
+ sv.verticalScrollerVisibility = CS.UnityEngine.UIElements.ScrollerVisibility[props.verticalScrollerVisibility as string];
363
+ }
364
+ if (props.elasticity !== undefined) {
365
+ sv.elasticity = props.elasticity as number;
366
+ }
367
+ if (props.elasticAnimationIntervalMs !== undefined) {
368
+ sv.elasticAnimationIntervalMs = props.elasticAnimationIntervalMs as number;
369
+ }
370
+ if (props.scrollDecelerationRate !== undefined) {
371
+ sv.scrollDecelerationRate = props.scrollDecelerationRate as number;
372
+ }
373
+ if (props.mouseWheelScrollSize !== undefined) {
374
+ sv.mouseWheelScrollSize = props.mouseWheelScrollSize as number;
375
+ }
376
+ if (props.horizontalPageSize !== undefined) {
377
+ sv.horizontalPageSize = props.horizontalPageSize as number;
378
+ }
379
+ if (props.verticalPageSize !== undefined) {
380
+ sv.verticalPageSize = props.verticalPageSize as number;
381
+ }
382
+ if (props.touchScrollBehavior !== undefined) {
383
+ sv.touchScrollBehavior = CS.UnityEngine.UIElements.TouchScrollBehavior[props.touchScrollBehavior as string];
384
+ }
385
+ if (props.nestedInteractionKind !== undefined) {
386
+ sv.nestedInteractionKind = CS.UnityEngine.UIElements.NestedInteractionKind[props.nestedInteractionKind as string];
387
+ }
388
+ }
389
+
390
+ // ListView-specific properties
391
+ if (type === 'ojs-listview') {
392
+ const lv = element as CSListView;
393
+
394
+ // Data binding - these are the core callbacks
395
+ if (props.itemsSource !== undefined) {
396
+ lv.itemsSource = props.itemsSource as unknown[];
397
+ }
398
+ if (props.makeItem !== undefined) {
399
+ lv.makeItem = props.makeItem as () => CSObject;
400
+ }
401
+ if (props.bindItem !== undefined) {
402
+ lv.bindItem = props.bindItem as (element: CSObject, index: number) => void;
403
+ }
404
+ if (props.unbindItem !== undefined) {
405
+ lv.unbindItem = props.unbindItem as (element: CSObject, index: number) => void;
406
+ }
407
+ if (props.destroyItem !== undefined) {
408
+ lv.destroyItem = props.destroyItem as (element: CSObject) => void;
409
+ }
410
+
411
+ // Virtualization
412
+ if (props.fixedItemHeight !== undefined) {
413
+ lv.fixedItemHeight = props.fixedItemHeight as number;
414
+ }
415
+ if (props.virtualizationMethod !== undefined) {
416
+ lv.virtualizationMethod = CS.UnityEngine.UIElements.CollectionVirtualizationMethod[props.virtualizationMethod as string];
417
+ }
418
+
419
+ // Selection
420
+ if (props.selectionType !== undefined) {
421
+ lv.selectionType = CS.UnityEngine.UIElements.SelectionType[props.selectionType as string];
422
+ }
423
+ if (props.selectedIndex !== undefined) {
424
+ lv.selectedIndex = props.selectedIndex as number;
425
+ }
426
+ if (props.selectedIndices !== undefined) {
427
+ lv.selectedIndices = props.selectedIndices as number[];
428
+ }
429
+
430
+ // Reordering
431
+ if (props.reorderable !== undefined) {
432
+ lv.reorderable = props.reorderable as boolean;
433
+ }
434
+ if (props.reorderMode !== undefined) {
435
+ lv.reorderMode = CS.UnityEngine.UIElements.ListViewReorderMode[props.reorderMode as string];
436
+ }
437
+
438
+ // Header/Footer
439
+ if (props.showFoldoutHeader !== undefined) {
440
+ lv.showFoldoutHeader = props.showFoldoutHeader as boolean;
441
+ }
442
+ if (props.headerTitle !== undefined) {
443
+ lv.headerTitle = props.headerTitle as string;
444
+ }
445
+ if (props.showAddRemoveFooter !== undefined) {
446
+ lv.showAddRemoveFooter = props.showAddRemoveFooter as boolean;
447
+ }
448
+
449
+ // Appearance
450
+ if (props.showBorder !== undefined) {
451
+ lv.showBorder = props.showBorder as boolean;
452
+ }
453
+ if (props.showAlternatingRowBackgrounds !== undefined) {
454
+ lv.showAlternatingRowBackgrounds = CS.UnityEngine.UIElements.AlternatingRowBackground[props.showAlternatingRowBackgrounds as string];
455
+ }
456
+ }
457
+ }
458
+
459
+ // Create an instance
460
+ function createInstance(type: string, props: BaseProps): Instance {
461
+ const factory = TYPE_MAP[type];
462
+ if (!factory) {
463
+ throw new Error(`Unknown element type: ${type}`);
464
+ }
465
+
466
+ const element = factory();
467
+ const appliedStyleKeys = applyStyle(element, props.style);
468
+ const instance: Instance = {
469
+ element,
470
+ type,
471
+ props,
472
+ eventHandlers: new Map(),
473
+ appliedStyleKeys,
474
+ };
475
+
476
+ applyClassName(element, props.className);
477
+ applyEvents(instance, props);
478
+ applyComponentProps(element, type, props as Record<string, unknown>);
479
+
480
+ return instance;
481
+ }
482
+
483
+ // Update an instance with new props
484
+ function updateInstance(instance: Instance, oldProps: BaseProps, newProps: BaseProps) {
485
+ const element = instance.element;
486
+
487
+ // Update style - clear removed properties, then apply new ones
488
+ if (oldProps.style !== newProps.style) {
489
+ const newStyleKeys = getExpandedStyleKeys(newProps.style);
490
+ clearRemovedStyles(element, instance.appliedStyleKeys, newStyleKeys);
491
+ instance.appliedStyleKeys = applyStyle(element, newProps.style);
492
+ }
493
+
494
+ // Update className - selectively add/remove classes
495
+ if (oldProps.className !== newProps.className) {
496
+ updateClassNames(element, oldProps.className, newProps.className);
497
+ }
498
+
499
+ // Update events
500
+ applyEvents(instance, newProps);
501
+
502
+ // Update component-specific props
503
+ applyComponentProps(element, instance.type, newProps as Record<string, unknown>);
504
+
505
+ instance.props = newProps;
506
+ }
507
+
508
+ // The host config for react-reconciler
509
+ export const hostConfig: HostConfig<
510
+ string, // Type
511
+ BaseProps, // Props
512
+ Container, // Container
513
+ Instance, // Instance
514
+ TextInstance, // TextInstance
515
+ never, // SuspenseInstance
516
+ never, // HydratableInstance
517
+ Instance, // PublicInstance
518
+ {}, // HostContext
519
+ true, // UpdatePayload (true = needs update)
520
+ ChildSet, // ChildSet
521
+ number, // TimeoutHandle
522
+ number // NoTimeout
523
+ > = {
524
+ supportsMutation: true,
525
+ supportsPersistence: false,
526
+ supportsHydration: false,
527
+
528
+ isPrimaryRenderer: true,
529
+ noTimeout: -1,
530
+
531
+ createInstance(type, props) {
532
+ return createInstance(type, props);
533
+ },
534
+
535
+ createTextInstance(text) {
536
+ // Create a Label for text content
537
+ const element = new CS.UnityEngine.UIElements.Label();
538
+ element.text = text;
539
+ return {
540
+ element,
541
+ type: 'text',
542
+ props: {},
543
+ eventHandlers: new Map(),
544
+ appliedStyleKeys: new Set(),
545
+ };
546
+ },
547
+
548
+ appendInitialChild(parentInstance, child) {
549
+ parentInstance.element.Add(child.element);
550
+ },
551
+
552
+ appendChild(parentInstance, child) {
553
+ parentInstance.element.Add(child.element);
554
+ },
555
+
556
+ appendChildToContainer(container, child) {
557
+ container.Add(child.element);
558
+ },
559
+
560
+ insertBefore(parentInstance, child, beforeChild) {
561
+ const index = parentInstance.element.IndexOf(beforeChild.element);
562
+ if (index >= 0) {
563
+ parentInstance.element.Insert(index, child.element);
564
+ } else {
565
+ parentInstance.element.Add(child.element);
566
+ }
567
+ },
568
+
569
+ insertInContainerBefore(container, child, beforeChild) {
570
+ const index = container.IndexOf(beforeChild.element);
571
+ if (index >= 0) {
572
+ container.Insert(index, child.element);
573
+ } else {
574
+ container.Add(child.element);
575
+ }
576
+ },
577
+
578
+ removeChild(parentInstance, child) {
579
+ __eventAPI.removeAllEventListeners(child.element);
580
+ parentInstance.element.Remove(child.element);
581
+ },
582
+
583
+ removeChildFromContainer(container, child) {
584
+ __eventAPI.removeAllEventListeners(child.element);
585
+ container.Remove(child.element);
586
+ },
587
+
588
+ prepareUpdate(_instance, _type, oldProps, newProps) {
589
+ // Return true if we need to update, null if no update needed
590
+ return oldProps !== newProps ? true : null;
591
+ },
592
+
593
+ // React 19 changed the signature: (instance, type, oldProps, newProps, fiber)
594
+ // The updatePayload parameter was removed!
595
+ commitUpdate(instance, _type, oldProps, newProps, _fiber) {
596
+ updateInstance(instance, oldProps, newProps);
597
+ },
598
+
599
+ commitTextUpdate(textInstance, _oldText, newText) {
600
+ textInstance.element.text = newText;
601
+ },
602
+
603
+ finalizeInitialChildren() {
604
+ return false;
605
+ },
606
+
607
+ getPublicInstance(instance) {
608
+ return instance;
609
+ },
610
+
611
+ prepareForCommit() {
612
+ return null;
613
+ },
614
+
615
+ resetAfterCommit() {
616
+ // Nothing to do
617
+ },
618
+
619
+ preparePortalMount() {
620
+ // Nothing to do
621
+ },
622
+
623
+ getRootHostContext() {
624
+ return {};
625
+ },
626
+
627
+ getChildHostContext(parentHostContext) {
628
+ return parentHostContext;
629
+ },
630
+
631
+ shouldSetTextContent() {
632
+ return false;
633
+ },
634
+
635
+ clearContainer(container) {
636
+ container.Clear();
637
+ },
638
+
639
+ scheduleTimeout: setTimeout,
640
+ cancelTimeout: clearTimeout,
641
+
642
+ // Priority management - required by React 19's reconciler
643
+ setCurrentUpdatePriority(priority: number) {
644
+ currentUpdatePriority = priority;
645
+ },
646
+
647
+ getCurrentUpdatePriority() {
648
+ return currentUpdatePriority;
649
+ },
650
+
651
+ resolveUpdatePriority() {
652
+ // When no specific priority is set, use default
653
+ return currentUpdatePriority || DefaultEventPriority;
654
+ },
655
+
656
+ getCurrentEventPriority() {
657
+ return DefaultEventPriority;
658
+ },
659
+
660
+ // Microtask support
661
+ supportsMicrotasks: true,
662
+ scheduleMicrotask: queueMicrotask,
663
+
664
+ // Transition support
665
+ shouldAttemptEagerTransition() {
666
+ return false;
667
+ },
668
+
669
+ // Form support (React 19)
670
+ NotPendingTransition: null as unknown,
671
+ resetFormInstance() {},
672
+
673
+ getInstanceFromNode() {
674
+ return null;
675
+ },
676
+
677
+ beforeActiveInstanceBlur() {
678
+ },
679
+ afterActiveInstanceBlur() {
680
+ },
681
+ prepareScopeUpdate() {
682
+ },
683
+ getInstanceFromScope() {
684
+ return null;
685
+ },
686
+
687
+ detachDeletedInstance() {
688
+ },
689
+
690
+ // Suspense commit support (React 19)
691
+ maySuspendCommit() {
692
+ return false;
693
+ },
694
+ preloadInstance() {
695
+ return true; // Already loaded
696
+ },
697
+ startSuspendingCommit() {
698
+ },
699
+ suspendInstance() {
700
+ },
701
+ waitForCommitToBeReady() {
702
+ return null;
703
+ },
704
+
705
+ // Visibility support
706
+ hideInstance(instance: Instance) {
707
+ instance.element.style.display = 'none';
708
+ },
709
+ hideTextInstance(textInstance: TextInstance) {
710
+ textInstance.element.style.display = 'none';
711
+ },
712
+ unhideInstance(instance: Instance, _props: BaseProps) {
713
+ instance.element.style.display = '';
714
+ },
715
+ unhideTextInstance(textInstance: TextInstance, _text: string) {
716
+ textInstance.element.style.display = '';
717
+ },
718
+
719
+ // Text content
720
+ resetTextContent(_instance: Instance) {
721
+ // Nothing to do for UI Toolkit
722
+ },
723
+
724
+ // Resources (not used)
725
+ supportsResources: false,
726
+
727
+ // Singletons (not used)
728
+ supportsSingletons: false,
729
+
730
+ // Test selectors (not used)
731
+ supportsTestSelectors: false,
732
+
733
+ // Post paint callback (not used)
734
+ requestPostPaintCallback() {
735
+ },
736
+
737
+ // Event resolution (not used)
738
+ resolveEventType() {
739
+ return null;
740
+ },
741
+ resolveEventTimeStamp() {
742
+ return 0;
743
+ },
744
+
745
+ // Console binding (dev tools)
746
+ bindToConsole(methodName: string, args: unknown[], _badgeName: string) {
747
+ return (console as Record<string, Function>)[methodName]?.bind(console, ...args);
748
+ },
749
+ };