1
- import { UIManager , DevSettings } from 'react-native'
1
+ import { UIManager } from 'react-native'
2
+ import getNativeComponentAttributes from 'react-native/Libraries/ReactNative/getNativeComponentAttributes'
2
3
3
4
/**
4
5
* @todo : figure out a way to handle refresh based renders
@@ -12,11 +13,12 @@ const CURRENT_STYLE = Symbol.for('current')
12
13
const OWNER_NODE = Symbol . for ( 'owner' )
13
14
14
15
const BINDINGS = new Map ( )
15
- const INSTANCES = new WeakMap ( )
16
- const NODES = new WeakMap ( )
16
+ let INSTANCES = new WeakMap ( )
17
+ let NODES = new WeakMap ( )
17
18
18
19
let VIEWS_RENDERED = false
19
20
let pChain = Promise . resolve ( )
21
+ let processing = false
20
22
let renderQ = [ ]
21
23
22
24
export const REACT_ELEMENT_TYPE =
@@ -41,6 +43,13 @@ const bridge = {
41
43
const node = NODES . get ( binding )
42
44
43
45
switch ( method ) {
46
+ case 'clear' : {
47
+ const childIndices = ( node . childNodes || [ ] ) . map ( ( _ , i ) => i )
48
+ try {
49
+ UIManager . manageChildren ( ROOT_TAG , [ ] , [ ] , [ ] , [ ] , childIndices )
50
+ } catch ( err ) { }
51
+ break
52
+ }
44
53
case 'create' : {
45
54
const type = params [ 1 ]
46
55
if ( type === '#document' ) {
@@ -65,18 +74,39 @@ const bridge = {
65
74
updateNodeProps ( id )
66
75
break
67
76
}
68
- case 'updateChildren' : {
77
+ case 'moveChild' : {
78
+ const fromIndex = params [ 1 ]
79
+ const toIndex = params [ 2 ]
80
+ UIManager . manageChildren (
81
+ id , // containerID
82
+ [ fromIndex ] , // moveFromIndices
83
+ [ toIndex ] , // moveToIndices
84
+ [ ] , // addChildReactTags
85
+ [ ] , // addAtIndices
86
+ [ ] // removeAtIndices
87
+ )
88
+ break
89
+ }
90
+ case 'appendChild' : {
91
+ let parentTag = id
92
+ const toAdd = params [ 1 ]
69
93
if ( binding . type === '#document' ) {
70
- UIManager . setChildren (
71
- ROOT_TAG ,
72
- node . children . map ( x => x [ BINDING ] . id )
73
- )
74
- } else {
75
- UIManager . setChildren (
76
- binding . id ,
77
- node . children . map ( x => x [ BINDING ] . id )
78
- )
94
+ parentTag = ROOT_TAG
95
+ console . log ( 'appending to root' , ROOT_TAG )
79
96
}
97
+ UIManager . setChildren ( parentTag , [ toAdd ] )
98
+ break
99
+ }
100
+ case 'removeChild' : {
101
+ const removeAt = params [ 1 ]
102
+ UIManager . manageChildren (
103
+ id , // containerID
104
+ [ ] , // moveFromIndices
105
+ [ ] , // moveToIndices
106
+ [ ] , // addChildReactTags
107
+ [ ] , // addAtIndices
108
+ [ removeAt ] // removeAtIndices
109
+ )
80
110
break
81
111
}
82
112
}
@@ -88,7 +118,10 @@ const bridge = {
88
118
params,
89
119
} ) === 1
90
120
) {
91
- pChain . then ( process )
121
+ if ( typeof ROOT_TAG != null && ! processing ) {
122
+ processing = true
123
+ pChain = pChain . then ( process )
124
+ }
92
125
}
93
126
} ,
94
127
}
@@ -130,13 +163,30 @@ class Node {
130
163
131
164
appendChild ( node ) {
132
165
node . parent = this
133
- this . children . push ( node )
134
- this [ BINDING ] . updateChildren ( )
166
+ const existingChild = this . children . findIndex (
167
+ x => x [ BINDING ] . id === node [ BINDING ] . id
168
+ )
169
+ if ( existingChild > - 1 ) {
170
+ this . children . splice ( existingChild , 1 )
171
+ this . children . push ( node )
172
+ this [ BINDING ] . moveChild ( existingChild , this . children . length - 1 )
173
+ } else {
174
+ this . children . push ( node )
175
+ this [ BINDING ] . appendChild ( node [ BINDING ] . id )
176
+ }
135
177
}
136
178
137
179
removeChild ( node ) {
138
- this . children . filter ( x => x [ BINDING ] . id === node [ BINDING ] . id )
139
- this [ BINDING ] . updateChildren ( )
180
+ let index = - 1
181
+ this . children . filter ( ( x , i ) => {
182
+ if ( x [ BINDING ] . id === node [ BINDING ] . id ) {
183
+ index = i
184
+ }
185
+ return x [ BINDING ] . id !== node [ BINDING ] . id
186
+ } )
187
+ if ( index > - 1 ) {
188
+ this [ BINDING ] . removeChild ( index )
189
+ }
140
190
}
141
191
142
192
get ref ( ) {
@@ -148,9 +198,12 @@ class Node {
148
198
}
149
199
150
200
class Element extends Node {
151
- constructor ( type ) {
201
+ constructor ( type , reset ) {
152
202
super ( type )
153
203
this . style = createStyleBinding ( this [ BINDING ] . id )
204
+ if ( reset ) {
205
+ this [ BINDING ] . clear ( )
206
+ }
154
207
this [ BINDING ] . create ( )
155
208
}
156
209
@@ -240,7 +293,6 @@ class Element extends Node {
240
293
ref : x => {
241
294
if ( ! VIEWS_RENDERED ) {
242
295
VIEWS_RENDERED = true
243
- pChain . then ( process )
244
296
}
245
297
_self . ref = x
246
298
INSTANCES . set ( this [ BINDING ] , x )
@@ -280,7 +332,7 @@ class Text extends Node {
280
332
export class Document extends Element {
281
333
constructor ( rootTag ) {
282
334
ROOT_TAG = rootTag
283
- super ( '#document' )
335
+ super ( '#document' , true )
284
336
}
285
337
286
338
createElement ( type ) {
@@ -310,12 +362,20 @@ export function render(node) {
310
362
}
311
363
312
364
function createBinding ( node ) {
313
- const id = ++ bridge . currentId
365
+ let nextId = ++ bridge . currentId
366
+ if ( nextId === ROOT_TAG ) {
367
+ nextId = ++ bridge . currentId
368
+ }
369
+ const id = nextId
314
370
const props = new Map ( )
315
371
return {
316
372
id,
317
373
props,
318
374
type : node . localName ,
375
+ clear ( ) {
376
+ renderQ = [ ]
377
+ bridge . enqueue ( 'clear' , [ id ] )
378
+ } ,
319
379
create ( ) {
320
380
bridge . enqueue ( 'create' , [ id , node . localName ] )
321
381
} ,
@@ -333,26 +393,98 @@ function createBinding(node) {
333
393
}
334
394
return res
335
395
} ,
336
- updateChildren ( ) {
337
- bridge . enqueue ( 'updateChildren' , [ id ] )
396
+ moveChild ( x , y ) {
397
+ bridge . enqueue ( 'moveChild' , [ id , x , y ] )
398
+ } ,
399
+ appendChild ( nodeId ) {
400
+ bridge . enqueue ( 'appendChild' , [ id , nodeId ] )
401
+ } ,
402
+ removeChild ( atIndex ) {
403
+ bridge . enqueue ( 'removeChild' , [ id , atIndex ] )
338
404
} ,
339
405
}
340
406
}
341
407
342
408
function process ( ) {
343
- let toProcess
344
- while ( ( toProcess = renderQ . shift ( ) ) ) {
345
- bridge . call ( toProcess . method , toProcess . params )
409
+ let methodDef = renderQ . shift ( )
410
+ if ( methodDef ) {
411
+ dispatch ( methodDef )
412
+ setTimeout ( ( ) => {
413
+ process ( )
414
+ } , 10 )
415
+ } else {
416
+ processing = false
346
417
}
347
418
}
348
419
420
+ function dispatch ( methodDef ) {
421
+ bridge . call ( methodDef . method , methodDef . params )
422
+ }
423
+
349
424
function updateNodeProps ( id ) {
350
425
const binding = BINDINGS . get ( id )
351
426
const instance = INSTANCES . get ( binding )
352
427
const props = Object . fromEntries ( binding . props )
428
+
353
429
if ( instance ) {
354
430
instance . setNativeProps ( props )
355
431
}
432
+
433
+ if ( TYPES [ binding . type ] ) {
434
+ const managerName = TYPES [ binding . type ] . type
435
+ const viewConfig = getNativeComponentAttributes ( managerName )
436
+ const validProps = processProps ( props , viewConfig . validAttributes )
437
+ UIManager . updateView ( id , viewConfig . uiViewClassName , validProps )
438
+ }
439
+ }
440
+
441
+ function processProps ( props , validAttributes ) {
442
+ const result = { }
443
+
444
+ for ( var key in props ) {
445
+ if ( ! validAttributes [ key ] ) {
446
+ continue
447
+ }
448
+
449
+ if ( key == 'style' ) {
450
+ normalizeStyle ( props [ key ] )
451
+ }
452
+
453
+ const propItem = props [ key ]
454
+ const config = validAttributes [ key ]
455
+
456
+ if ( typeof propItem == 'undefined' ) {
457
+ continue
458
+ }
459
+ result [ key ] = propItem
460
+
461
+ if ( typeof propItem == 'object' && typeof config == 'object' ) {
462
+ result [ key ] = processProps ( propItem , config )
463
+ }
464
+
465
+ if ( typeof config . process == 'function' ) {
466
+ result [ key ] = config . process ( propItem )
467
+ }
468
+ }
469
+
470
+ // flatten styles to the top level props
471
+ Object . assign ( result , result . style )
472
+
473
+ return result
474
+ }
475
+
476
+ function normalizeStyle ( styleProps ) {
477
+ for ( let key in styleProps ) {
478
+ const old = styleProps [ key ]
479
+ try {
480
+ styleProps [ key ] = styleProps [ key ] . replace ( / ( p x ) / g, '' )
481
+ if ( ! isNaN ( styleProps [ key ] ) ) {
482
+ styleProps [ key ] = Number ( styleProps [ key ] )
483
+ }
484
+ } catch ( err ) {
485
+ styleProps [ key ] = old
486
+ }
487
+ }
356
488
}
357
489
358
490
function updateTextNode ( id ) {
@@ -387,19 +519,9 @@ function createStyleBinding(id) {
387
519
return new Proxy ( style , STYLE_PROXY )
388
520
}
389
521
390
- function onRefresh ( cb ) {
391
- if ( __DEV__ ) {
392
- let oldRefresh = DevSettings . onFastRefresh
393
- DevSettings . onFastRefresh = ( ) => {
394
- cb && cb ( )
395
- oldRefresh && oldRefresh ( )
396
- }
397
- }
398
- }
399
-
400
- function noop ( ) { }
401
-
402
522
export function createDOM ( rootTag ) {
403
- onRefresh ( noop )
404
- return new Document ( rootTag )
523
+ bridge . currentId = 0
524
+ BINDINGS . clear ( )
525
+ const rootNode = new Document ( rootTag )
526
+ return rootNode
405
527
}
0 commit comments