-
Notifications
You must be signed in to change notification settings - Fork 47.5k
/
Copy pathReactNativeViewConfigRegistry.js
122 lines (110 loc) · 3.47 KB
/
ReactNativeViewConfigRegistry.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @noformat
* @nolint
* @flow strict-local
*/
'use strict';
import {type ViewConfig} from './ReactNativeTypes';
import invariant from 'invariant';
// Event configs
export const customBubblingEventTypes: {
[eventName: string]: $ReadOnly<{
phasedRegistrationNames: $ReadOnly<{
captured: string,
bubbled: string,
skipBubbling?: ?boolean,
}>,
}>,
} = {};
export const customDirectEventTypes: {
[eventName: string]: $ReadOnly<{
registrationName: string,
}>,
} = {};
const viewConfigCallbacks = new Map<string, ?() => ViewConfig>();
const viewConfigs = new Map<string, ViewConfig>();
function processEventTypes(viewConfig: ViewConfig): void {
const {bubblingEventTypes, directEventTypes} = viewConfig;
if (__DEV__) {
if (bubblingEventTypes != null && directEventTypes != null) {
for (const topLevelType in directEventTypes) {
invariant(
bubblingEventTypes[topLevelType] == null,
'Event cannot be both direct and bubbling: %s',
topLevelType,
);
}
}
}
if (bubblingEventTypes != null) {
for (const topLevelType in bubblingEventTypes) {
if (customBubblingEventTypes[topLevelType] == null) {
customBubblingEventTypes[topLevelType] =
bubblingEventTypes[topLevelType];
}
}
}
if (directEventTypes != null) {
for (const topLevelType in directEventTypes) {
if (customDirectEventTypes[topLevelType] == null) {
customDirectEventTypes[topLevelType] = directEventTypes[topLevelType];
}
}
}
}
/**
* Registers a native view/component by name.
* A callback is provided to load the view config from UIManager.
* The callback is deferred until the view is actually rendered.
*/
export function register(name: string, callback: () => ViewConfig): string {
invariant(
!viewConfigCallbacks.has(name),
'Tried to register two views with the same name %s',
name,
);
invariant(
typeof callback === 'function',
'View config getter callback for component `%s` must be a function (received `%s`)',
name,
callback === null ? 'null' : typeof callback,
);
viewConfigCallbacks.set(name, callback);
return name;
}
/**
* Retrieves a config for the specified view.
* If this is the first time the view has been used,
* This configuration will be lazy-loaded from UIManager.
*/
export function get(name: string): ViewConfig {
let viewConfig = viewConfigs.get(name);
if (viewConfig == null) {
const callback = viewConfigCallbacks.get(name);
if (typeof callback !== 'function') {
invariant(
false,
'View config getter callback for component `%s` must be a function (received `%s`).%s',
name,
callback === null ? 'null' : typeof callback,
// $FlowFixMe[recursive-definition]
typeof name[0] === 'string' && /[a-z]/.test(name[0])
? ' Make sure to start component names with a capital letter.'
: '',
);
}
viewConfig = callback();
invariant(viewConfig, 'View config not found for component `%s`', name);
processEventTypes(viewConfig);
viewConfigs.set(name, viewConfig);
// Clear the callback after the config is set so that
// we don't mask any errors during registration.
viewConfigCallbacks.set(name, null);
}
return viewConfig;
}