@reckona/mreact-compat 0.0.139 → 0.0.141
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/dist/dom-props.d.ts.map +1 -1
- package/dist/dom-props.js +58 -44
- package/dist/dom-props.js.map +1 -1
- package/dist/element.d.ts +1 -0
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +21 -3
- package/dist/element.js.map +1 -1
- package/dist/event-listeners.d.ts +1 -4
- package/dist/event-listeners.d.ts.map +1 -1
- package/dist/event-listeners.js +2 -1
- package/dist/event-listeners.js.map +1 -1
- package/dist/events.d.ts +2 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +82 -3
- package/dist/events.js.map +1 -1
- package/dist/fiber-child.d.ts.map +1 -1
- package/dist/fiber-child.js +44 -0
- package/dist/fiber-child.js.map +1 -1
- package/dist/fiber-commit.d.ts.map +1 -1
- package/dist/fiber-commit.js +70 -0
- package/dist/fiber-commit.js.map +1 -1
- package/dist/host-event-binder.d.ts +1 -1
- package/dist/host-event-binder.d.ts.map +1 -1
- package/dist/host-event-binder.js +1 -1
- package/dist/host-event-binder.js.map +1 -1
- package/dist/host-reconciler.d.ts.map +1 -1
- package/dist/host-reconciler.js +28 -13
- package/dist/host-reconciler.js.map +1 -1
- package/package.json +3 -3
- package/src/dom-props.ts +119 -51
- package/src/element.ts +33 -4
- package/src/event-listeners.ts +3 -5
- package/src/events.ts +100 -3
- package/src/fiber-child.ts +67 -0
- package/src/fiber-commit.ts +92 -0
- package/src/host-event-binder.ts +2 -0
- package/src/host-reconciler.ts +37 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reckona/mreact-compat",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.141",
|
|
4
4
|
"description": "React-compatible runtime implementation for mreact.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"compatibility",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"access": "public"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@reckona/mreact-reactive-core": "0.0.
|
|
73
|
-
"@reckona/mreact-shared": "0.0.
|
|
72
|
+
"@reckona/mreact-reactive-core": "0.0.141",
|
|
73
|
+
"@reckona/mreact-shared": "0.0.141"
|
|
74
74
|
}
|
|
75
75
|
}
|
package/src/dom-props.ts
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getAppliedProps,
|
|
3
3
|
setAppliedProps,
|
|
4
|
-
|
|
5
|
-
type AppliedProps,
|
|
6
|
-
ensureDelegatedEventListener,
|
|
7
|
-
toEventNames,
|
|
4
|
+
ensureDelegatedEventListenersForProp,
|
|
8
5
|
} from "./host-event-binder.js";
|
|
9
6
|
import { HOST_OWN_PROPS_META } from "./element.js";
|
|
10
7
|
import { reportRecoverable, type RenderOptions } from "./hydration.js";
|
|
11
|
-
import type { SyntheticEvent } from "./event-types.js";
|
|
12
8
|
import {
|
|
13
9
|
isDangerousHtmlAttribute,
|
|
14
10
|
isDangerousHtmlOptIn,
|
|
@@ -36,23 +32,19 @@ export function applyProps(
|
|
|
36
32
|
if (previous === undefined && !preserveHydrationAttributes) {
|
|
37
33
|
if (applyInitialRowProps(element, nextProps)) {
|
|
38
34
|
setAppliedProps(element, {
|
|
39
|
-
attributeNames: collectAttributeNames(nextProps),
|
|
40
35
|
props: nextProps,
|
|
41
36
|
});
|
|
42
37
|
return;
|
|
43
38
|
}
|
|
44
39
|
|
|
45
|
-
|
|
40
|
+
applyInitialProps(element, nextProps, path, options);
|
|
46
41
|
setAppliedProps(element, {
|
|
47
|
-
attributeNames,
|
|
48
42
|
props: nextProps,
|
|
49
|
-
...applyInitialProps(element, nextProps, path, options),
|
|
50
43
|
});
|
|
51
44
|
return;
|
|
52
45
|
}
|
|
53
46
|
|
|
54
47
|
const previousProps = previous?.props ?? {};
|
|
55
|
-
let listeners = previous?.listeners;
|
|
56
48
|
const previousAttributeNames = previous?.attributeNames ?? collectAttributeNames(previousProps);
|
|
57
49
|
const nextAttributeNames = collectAttributeNames(nextProps);
|
|
58
50
|
|
|
@@ -77,16 +69,6 @@ export function applyProps(
|
|
|
77
69
|
}
|
|
78
70
|
}
|
|
79
71
|
|
|
80
|
-
if (listeners !== undefined) {
|
|
81
|
-
for (const [name, appliedListener] of listeners) {
|
|
82
|
-
const nextValue = nextProps[name];
|
|
83
|
-
|
|
84
|
-
if (nextValue !== appliedListener.handler) {
|
|
85
|
-
listeners.delete(name);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
72
|
for (const name in nextProps) {
|
|
91
73
|
if (!Object.prototype.hasOwnProperty.call(nextProps, name)) {
|
|
92
74
|
continue;
|
|
@@ -98,7 +80,7 @@ export function applyProps(
|
|
|
98
80
|
continue;
|
|
99
81
|
}
|
|
100
82
|
|
|
101
|
-
if (applyFormValueProp(element, name, value, path, options)) {
|
|
83
|
+
if (isFormValuePropName(name) && applyFormValueProp(element, name, value, path, options)) {
|
|
102
84
|
continue;
|
|
103
85
|
}
|
|
104
86
|
|
|
@@ -117,21 +99,16 @@ export function applyProps(
|
|
|
117
99
|
continue;
|
|
118
100
|
}
|
|
119
101
|
|
|
120
|
-
if (
|
|
121
|
-
if (
|
|
102
|
+
if (isReactEventHandlerPropName(name) && typeof value === "function") {
|
|
103
|
+
if (previousProps[name] === value) {
|
|
122
104
|
continue;
|
|
123
105
|
}
|
|
124
106
|
|
|
125
|
-
|
|
126
|
-
for (const eventName of toEventNames(name)) {
|
|
127
|
-
ensureDelegatedEventListener(options.eventRoot ?? element, eventName);
|
|
128
|
-
}
|
|
129
|
-
listeners ??= new Map<string, AppliedEventListener>();
|
|
130
|
-
listeners.set(name, { handler });
|
|
107
|
+
ensureDelegatedEventListenersForProp(options.eventRoot ?? element, name);
|
|
131
108
|
continue;
|
|
132
109
|
}
|
|
133
110
|
|
|
134
|
-
if (
|
|
111
|
+
if (isEventLikePropName(name)) {
|
|
135
112
|
continue;
|
|
136
113
|
}
|
|
137
114
|
|
|
@@ -183,7 +160,6 @@ export function applyProps(
|
|
|
183
160
|
setAppliedProps(element, {
|
|
184
161
|
attributeNames: nextAttributeNames,
|
|
185
162
|
props: nextProps,
|
|
186
|
-
...(listeners === undefined ? {} : { listeners }),
|
|
187
163
|
});
|
|
188
164
|
}
|
|
189
165
|
|
|
@@ -192,8 +168,8 @@ function applyInitialProps(
|
|
|
192
168
|
props: Record<string, unknown>,
|
|
193
169
|
path: string,
|
|
194
170
|
options: RenderOptions,
|
|
195
|
-
):
|
|
196
|
-
|
|
171
|
+
): void {
|
|
172
|
+
const useAttributeFastPath = options.hydration === undefined;
|
|
197
173
|
|
|
198
174
|
for (const name in props) {
|
|
199
175
|
if (!Object.prototype.hasOwnProperty.call(props, name)) {
|
|
@@ -206,7 +182,7 @@ function applyInitialProps(
|
|
|
206
182
|
continue;
|
|
207
183
|
}
|
|
208
184
|
|
|
209
|
-
if (applyFormValueProp(element, name, value, path, options)) {
|
|
185
|
+
if (isFormValuePropName(name) && applyFormValueProp(element, name, value, path, options)) {
|
|
210
186
|
continue;
|
|
211
187
|
}
|
|
212
188
|
|
|
@@ -215,12 +191,26 @@ function applyInitialProps(
|
|
|
215
191
|
}
|
|
216
192
|
|
|
217
193
|
if (name === "className") {
|
|
218
|
-
|
|
194
|
+
applyInitialOrHydrationAttribute(
|
|
195
|
+
element,
|
|
196
|
+
"class",
|
|
197
|
+
value,
|
|
198
|
+
path,
|
|
199
|
+
options,
|
|
200
|
+
useAttributeFastPath,
|
|
201
|
+
);
|
|
219
202
|
continue;
|
|
220
203
|
}
|
|
221
204
|
|
|
222
205
|
if (name === "htmlFor") {
|
|
223
|
-
|
|
206
|
+
applyInitialOrHydrationAttribute(
|
|
207
|
+
element,
|
|
208
|
+
"for",
|
|
209
|
+
value,
|
|
210
|
+
path,
|
|
211
|
+
options,
|
|
212
|
+
useAttributeFastPath,
|
|
213
|
+
);
|
|
224
214
|
continue;
|
|
225
215
|
}
|
|
226
216
|
|
|
@@ -229,29 +219,38 @@ function applyInitialProps(
|
|
|
229
219
|
continue;
|
|
230
220
|
}
|
|
231
221
|
|
|
232
|
-
if (
|
|
233
|
-
|
|
234
|
-
for (const eventName of toEventNames(name)) {
|
|
235
|
-
ensureDelegatedEventListener(options.eventRoot ?? element, eventName);
|
|
236
|
-
}
|
|
237
|
-
listeners ??= new Map<string, AppliedEventListener>();
|
|
238
|
-
listeners.set(name, { handler });
|
|
222
|
+
if (isReactEventHandlerPropName(name) && typeof value === "function") {
|
|
223
|
+
ensureDelegatedEventListenersForProp(options.eventRoot ?? element, name);
|
|
239
224
|
continue;
|
|
240
225
|
}
|
|
241
226
|
|
|
242
|
-
if (
|
|
227
|
+
if (isEventLikePropName(name)) {
|
|
243
228
|
continue;
|
|
244
229
|
}
|
|
245
230
|
|
|
246
231
|
const attributeName = toDomAttributeName(name);
|
|
247
232
|
|
|
248
233
|
if (typeof value === "boolean" && isBooleanishStringAttribute(attributeName)) {
|
|
249
|
-
|
|
234
|
+
applyInitialOrHydrationAttribute(
|
|
235
|
+
element,
|
|
236
|
+
attributeName,
|
|
237
|
+
value ? "true" : "false",
|
|
238
|
+
path,
|
|
239
|
+
options,
|
|
240
|
+
useAttributeFastPath,
|
|
241
|
+
);
|
|
250
242
|
continue;
|
|
251
243
|
}
|
|
252
244
|
|
|
253
245
|
if (typeof value === "boolean" && isDataAttribute(attributeName)) {
|
|
254
|
-
|
|
246
|
+
applyInitialOrHydrationAttribute(
|
|
247
|
+
element,
|
|
248
|
+
attributeName,
|
|
249
|
+
value ? "true" : "false",
|
|
250
|
+
path,
|
|
251
|
+
options,
|
|
252
|
+
useAttributeFastPath,
|
|
253
|
+
);
|
|
255
254
|
continue;
|
|
256
255
|
}
|
|
257
256
|
|
|
@@ -274,10 +273,16 @@ function applyInitialProps(
|
|
|
274
273
|
continue;
|
|
275
274
|
}
|
|
276
275
|
|
|
277
|
-
|
|
276
|
+
applyInitialOrHydrationAttribute(
|
|
277
|
+
element,
|
|
278
|
+
attributeName,
|
|
279
|
+
value,
|
|
280
|
+
path,
|
|
281
|
+
options,
|
|
282
|
+
useAttributeFastPath,
|
|
283
|
+
);
|
|
278
284
|
}
|
|
279
285
|
|
|
280
|
-
return listeners === undefined ? {} : { listeners };
|
|
281
286
|
}
|
|
282
287
|
|
|
283
288
|
function applyInitialRowProps(
|
|
@@ -361,7 +366,7 @@ function applyAttribute(
|
|
|
361
366
|
return;
|
|
362
367
|
}
|
|
363
368
|
|
|
364
|
-
if (
|
|
369
|
+
if (isEventLikePropName(name)) {
|
|
365
370
|
if (element.hasAttribute(name) && !preserveHydrationAttributes) {
|
|
366
371
|
element.removeAttribute(name);
|
|
367
372
|
}
|
|
@@ -433,6 +438,49 @@ function applyAttribute(
|
|
|
433
438
|
element.setAttribute(name, stringValue);
|
|
434
439
|
}
|
|
435
440
|
|
|
441
|
+
function applyInitialAttribute(
|
|
442
|
+
element: Element,
|
|
443
|
+
name: string,
|
|
444
|
+
value: unknown,
|
|
445
|
+
): void {
|
|
446
|
+
if (isDangerousHtmlAttribute(name) && !isDangerousHtmlOptIn(value)) {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (isEventLikePropName(name) || value === null || value === undefined || value === false) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const stringValue = isDangerousHtmlOptIn(value)
|
|
455
|
+
? (value as { __html: string }).__html
|
|
456
|
+
: String(value);
|
|
457
|
+
|
|
458
|
+
if (
|
|
459
|
+
(isUrlAttribute(name) || isSrcsetAttribute(name)) &&
|
|
460
|
+
isUnsafeUrlAttribute(name, stringValue)
|
|
461
|
+
) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
element.setAttribute(name, stringValue);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function applyInitialOrHydrationAttribute(
|
|
469
|
+
element: Element,
|
|
470
|
+
name: string,
|
|
471
|
+
value: unknown,
|
|
472
|
+
path: string,
|
|
473
|
+
options: RenderOptions,
|
|
474
|
+
useFastPath: boolean,
|
|
475
|
+
): void {
|
|
476
|
+
if (useFastPath) {
|
|
477
|
+
applyInitialAttribute(element, name, value);
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
applyAttribute(element, name, value, path, options);
|
|
482
|
+
}
|
|
483
|
+
|
|
436
484
|
function applyFormValueProp(
|
|
437
485
|
element: Element,
|
|
438
486
|
name: string,
|
|
@@ -507,6 +555,15 @@ function applyFormValueProp(
|
|
|
507
555
|
return false;
|
|
508
556
|
}
|
|
509
557
|
|
|
558
|
+
function isFormValuePropName(name: string): boolean {
|
|
559
|
+
return (
|
|
560
|
+
name === "value" ||
|
|
561
|
+
name === "defaultValue" ||
|
|
562
|
+
name === "checked" ||
|
|
563
|
+
name === "defaultChecked"
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
|
|
510
567
|
function applyStyle(
|
|
511
568
|
element: HostElement,
|
|
512
569
|
previousStyle: unknown,
|
|
@@ -577,7 +634,7 @@ function collectAttributeNames(props: Record<string, unknown>): string[] {
|
|
|
577
634
|
name === "children" ||
|
|
578
635
|
name === "ref" ||
|
|
579
636
|
name === "key" ||
|
|
580
|
-
|
|
637
|
+
isEventLikePropName(name) ||
|
|
581
638
|
value === null ||
|
|
582
639
|
value === undefined
|
|
583
640
|
) {
|
|
@@ -616,11 +673,22 @@ function pushUniqueAttributeName(names: string[], name: string): void {
|
|
|
616
673
|
}
|
|
617
674
|
}
|
|
618
675
|
|
|
676
|
+
function isReactEventHandlerPropName(name: string): boolean {
|
|
677
|
+
const third = name.charCodeAt(2);
|
|
678
|
+
return name.charCodeAt(0) === 111 && name.charCodeAt(1) === 110 && third >= 65 && third <= 90;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function isEventLikePropName(name: string): boolean {
|
|
682
|
+
const first = name.charCodeAt(0);
|
|
683
|
+
const second = name.charCodeAt(1);
|
|
684
|
+
return (first === 111 || first === 79) && (second === 110 || second === 78);
|
|
685
|
+
}
|
|
686
|
+
|
|
619
687
|
function sanitizeMetaRefreshElementProps(
|
|
620
688
|
element: Element,
|
|
621
689
|
props: Record<string, unknown>,
|
|
622
690
|
): Record<string, unknown> {
|
|
623
|
-
if (element.tagName.
|
|
691
|
+
if (element.tagName !== "META" && element.tagName !== "meta") {
|
|
624
692
|
return props;
|
|
625
693
|
}
|
|
626
694
|
|
package/src/element.ts
CHANGED
|
@@ -12,6 +12,9 @@ export const SuspenseList = Symbol.for("react.suspense_list");
|
|
|
12
12
|
export const Activity = Symbol.for("react.activity");
|
|
13
13
|
export const Profiler = Symbol.for("react.profiler");
|
|
14
14
|
export const HOST_OWN_PROPS_META = Symbol.for("modular.react.host_own_props_meta");
|
|
15
|
+
export const HOST_CHILDREN_ONLY_PROPS_META = Symbol.for(
|
|
16
|
+
"modular.react.host_children_only_props_meta",
|
|
17
|
+
);
|
|
15
18
|
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
16
19
|
|
|
17
20
|
export interface ReactCompatProviderType {
|
|
@@ -271,12 +274,25 @@ function copyElementProps(
|
|
|
271
274
|
base?: Record<string, unknown>,
|
|
272
275
|
omitChildren = false,
|
|
273
276
|
): Record<string, unknown> {
|
|
274
|
-
const props: Record<string, unknown> =
|
|
277
|
+
const props: Record<string, unknown> = {};
|
|
278
|
+
|
|
279
|
+
if (base !== undefined) {
|
|
280
|
+
copyOwnStringElementProps(base, props, omitChildren);
|
|
281
|
+
}
|
|
275
282
|
|
|
276
283
|
if (source === null || source === undefined) {
|
|
277
284
|
return props;
|
|
278
285
|
}
|
|
279
286
|
|
|
287
|
+
copyOwnStringElementProps(source, props, omitChildren);
|
|
288
|
+
return props;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function copyOwnStringElementProps(
|
|
292
|
+
source: Record<string, unknown>,
|
|
293
|
+
target: Record<string, unknown>,
|
|
294
|
+
omitChildren: boolean,
|
|
295
|
+
): void {
|
|
280
296
|
for (const name in source) {
|
|
281
297
|
if (!hasOwnProperty.call(source, name)) {
|
|
282
298
|
continue;
|
|
@@ -289,11 +305,9 @@ function copyElementProps(
|
|
|
289
305
|
name !== "__source" &&
|
|
290
306
|
(!omitChildren || name !== "children")
|
|
291
307
|
) {
|
|
292
|
-
|
|
308
|
+
target[name] = source[name];
|
|
293
309
|
}
|
|
294
310
|
}
|
|
295
|
-
|
|
296
|
-
return props;
|
|
297
311
|
}
|
|
298
312
|
|
|
299
313
|
function normalizeElementType<P>(type: ElementType<P>): ElementType<P> {
|
|
@@ -351,6 +365,11 @@ function setHostOwnPropsMeta(props: Record<string, unknown>): void {
|
|
|
351
365
|
const dataKey = props["data-key"];
|
|
352
366
|
|
|
353
367
|
if (typeof dataKey !== "number" || !Number.isSafeInteger(dataKey) || dataKey < 0) {
|
|
368
|
+
if (hostPropsAreChildrenOnly(props)) {
|
|
369
|
+
(props as { [HOST_CHILDREN_ONLY_PROPS_META]?: true })[
|
|
370
|
+
HOST_CHILDREN_ONLY_PROPS_META
|
|
371
|
+
] = true;
|
|
372
|
+
}
|
|
354
373
|
return;
|
|
355
374
|
}
|
|
356
375
|
|
|
@@ -402,6 +421,16 @@ function setHostOwnPropsMeta(props: Record<string, unknown>): void {
|
|
|
402
421
|
dataKey * 4 + selectedState;
|
|
403
422
|
}
|
|
404
423
|
|
|
424
|
+
function hostPropsAreChildrenOnly(props: Record<string, unknown>): boolean {
|
|
425
|
+
for (const name in props) {
|
|
426
|
+
if (hasOwnProperty.call(props, name) && name !== "children") {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
|
|
405
434
|
export const isValidElement = isReactCompatElement;
|
|
406
435
|
|
|
407
436
|
export const Children = {
|
package/src/event-listeners.ts
CHANGED
|
@@ -3,12 +3,9 @@ import type { SyntheticEvent } from "./event-types.js";
|
|
|
3
3
|
export interface AppliedProps {
|
|
4
4
|
attributeNames?: string[];
|
|
5
5
|
props: Record<string, unknown>;
|
|
6
|
-
listeners?: Map<string, AppliedEventListener>;
|
|
7
6
|
}
|
|
8
7
|
|
|
9
|
-
export
|
|
10
|
-
handler: (event: SyntheticEvent) => void;
|
|
11
|
-
}
|
|
8
|
+
export type AppliedEventListener = (event: SyntheticEvent) => void;
|
|
12
9
|
|
|
13
10
|
const appliedProps = new WeakMap<Element, AppliedProps>();
|
|
14
11
|
|
|
@@ -24,5 +21,6 @@ export function getAppliedEventHandler(
|
|
|
24
21
|
element: Element,
|
|
25
22
|
name: string,
|
|
26
23
|
): ((event: SyntheticEvent) => void) | undefined {
|
|
27
|
-
|
|
24
|
+
const handler = appliedProps.get(element)?.props[name];
|
|
25
|
+
return typeof handler === "function" ? (handler as (event: SyntheticEvent) => void) : undefined;
|
|
28
26
|
}
|
package/src/events.ts
CHANGED
|
@@ -90,12 +90,109 @@ const nativeEventToReactProps = new Map<string, string[]>([
|
|
|
90
90
|
]);
|
|
91
91
|
|
|
92
92
|
export function toEventNames(propName: string): string[] {
|
|
93
|
+
const eventNames: string[] = [];
|
|
94
|
+
forEachEventName(propName, (eventName) => {
|
|
95
|
+
eventNames.push(eventName);
|
|
96
|
+
});
|
|
97
|
+
return eventNames;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function forEachEventName(
|
|
101
|
+
propName: string,
|
|
102
|
+
callback: (eventName: string) => void,
|
|
103
|
+
): void {
|
|
104
|
+
const directEventName = directNativeEventName(propName);
|
|
105
|
+
|
|
106
|
+
if (directEventName !== undefined) {
|
|
107
|
+
callback(directEventName);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const basePropName = toBaseEventPropName(propName);
|
|
112
|
+
const mappedEventNames = reactPropToNativeEvent.get(basePropName);
|
|
113
|
+
|
|
114
|
+
if (mappedEventNames === undefined) {
|
|
115
|
+
callback(basePropName.slice(2).toLowerCase());
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
for (let index = 0; index < mappedEventNames.length; index += 1) {
|
|
120
|
+
callback(mappedEventNames[index]!);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function ensureDelegatedEventListenersForProp(
|
|
125
|
+
root: Element,
|
|
126
|
+
propName: string,
|
|
127
|
+
): void {
|
|
128
|
+
const directEventName = directNativeEventName(propName);
|
|
129
|
+
|
|
130
|
+
if (directEventName !== undefined) {
|
|
131
|
+
ensureDelegatedEventListener(root, directEventName);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const basePropName = toBaseEventPropName(propName);
|
|
136
|
+
const mappedEventNames = reactPropToNativeEvent.get(basePropName);
|
|
137
|
+
|
|
138
|
+
if (mappedEventNames === undefined) {
|
|
139
|
+
ensureDelegatedEventListener(root, basePropName.slice(2).toLowerCase());
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
for (let index = 0; index < mappedEventNames.length; index += 1) {
|
|
144
|
+
ensureDelegatedEventListener(root, mappedEventNames[index]!);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function directNativeEventName(propName: string): string | undefined {
|
|
149
|
+
switch (propName) {
|
|
150
|
+
case "onClick":
|
|
151
|
+
case "onClickCapture":
|
|
152
|
+
return "click";
|
|
153
|
+
case "onInput":
|
|
154
|
+
case "onInputCapture":
|
|
155
|
+
return "input";
|
|
156
|
+
case "onKeyDown":
|
|
157
|
+
case "onKeyDownCapture":
|
|
158
|
+
return "keydown";
|
|
159
|
+
case "onKeyUp":
|
|
160
|
+
case "onKeyUpCapture":
|
|
161
|
+
return "keyup";
|
|
162
|
+
case "onMouseDown":
|
|
163
|
+
case "onMouseDownCapture":
|
|
164
|
+
return "mousedown";
|
|
165
|
+
case "onMouseMove":
|
|
166
|
+
case "onMouseMoveCapture":
|
|
167
|
+
return "mousemove";
|
|
168
|
+
case "onMouseOut":
|
|
169
|
+
case "onMouseOutCapture":
|
|
170
|
+
return "mouseout";
|
|
171
|
+
case "onMouseOver":
|
|
172
|
+
case "onMouseOverCapture":
|
|
173
|
+
return "mouseover";
|
|
174
|
+
case "onMouseUp":
|
|
175
|
+
case "onMouseUpCapture":
|
|
176
|
+
return "mouseup";
|
|
177
|
+
case "onScroll":
|
|
178
|
+
case "onScrollCapture":
|
|
179
|
+
return "scroll";
|
|
180
|
+
case "onSubmit":
|
|
181
|
+
case "onSubmitCapture":
|
|
182
|
+
return "submit";
|
|
183
|
+
case "onWheel":
|
|
184
|
+
case "onWheelCapture":
|
|
185
|
+
return "wheel";
|
|
186
|
+
default:
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function toBaseEventPropName(propName: string): string {
|
|
93
192
|
const basePropName = propName.endsWith("Capture")
|
|
94
193
|
? propName.slice(0, -"Capture".length)
|
|
95
194
|
: propName;
|
|
96
|
-
return
|
|
97
|
-
basePropName.slice(2).toLowerCase(),
|
|
98
|
-
];
|
|
195
|
+
return basePropName;
|
|
99
196
|
}
|
|
100
197
|
|
|
101
198
|
export function toEventPropNames(eventName: string): string[] {
|
package/src/fiber-child.ts
CHANGED
|
@@ -27,6 +27,16 @@ export function reconcileChildFibers(
|
|
|
27
27
|
newChildren: ReactCompatNode,
|
|
28
28
|
): Fiber | undefined {
|
|
29
29
|
const children = normalizeChildren(newChildren);
|
|
30
|
+
const orderedKeyedChildren = reconcileSameKeyOrderChildren(
|
|
31
|
+
parent,
|
|
32
|
+
currentFirstChild,
|
|
33
|
+
children,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
if (orderedKeyedChildren !== undefined) {
|
|
37
|
+
return orderedKeyedChildren;
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
const keyed = collectKeyedChildren(currentFirstChild);
|
|
31
41
|
const oldIndexes = collectChildIndexes(currentFirstChild);
|
|
32
42
|
const used = new Set<Fiber>();
|
|
@@ -86,6 +96,63 @@ export function reconcileChildFibers(
|
|
|
86
96
|
return first;
|
|
87
97
|
}
|
|
88
98
|
|
|
99
|
+
function reconcileSameKeyOrderChildren(
|
|
100
|
+
parent: Fiber,
|
|
101
|
+
currentFirstChild: Fiber | undefined,
|
|
102
|
+
children: readonly ReactCompatNode[],
|
|
103
|
+
): Fiber | undefined {
|
|
104
|
+
if (currentFirstChild === undefined || children.length === 0) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let cursor: Fiber | undefined = currentFirstChild;
|
|
109
|
+
|
|
110
|
+
for (const child of children) {
|
|
111
|
+
const key = getNodeKey(child);
|
|
112
|
+
|
|
113
|
+
if (
|
|
114
|
+
key === undefined ||
|
|
115
|
+
cursor === undefined ||
|
|
116
|
+
cursor.key !== key ||
|
|
117
|
+
!isReactCompatElement(child) ||
|
|
118
|
+
!canReuseElementFiber(cursor, child)
|
|
119
|
+
) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
cursor = cursor.sibling;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (cursor !== undefined) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
cursor = currentFirstChild;
|
|
131
|
+
let first: Fiber | undefined;
|
|
132
|
+
let previous: Fiber | undefined;
|
|
133
|
+
|
|
134
|
+
for (const child of children) {
|
|
135
|
+
const key = getNodeKey(child) as string;
|
|
136
|
+
const fiber = reconcileSingleChild(parent, cursor, child, key) as Fiber;
|
|
137
|
+
|
|
138
|
+
fiber.lanes |= parent.lanes;
|
|
139
|
+
fiber.return = parent;
|
|
140
|
+
fiber.sibling = undefined;
|
|
141
|
+
|
|
142
|
+
if (first === undefined) {
|
|
143
|
+
first = fiber;
|
|
144
|
+
} else if (previous !== undefined) {
|
|
145
|
+
previous.sibling = fiber;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
previous = fiber;
|
|
149
|
+
cursor = cursor?.sibling;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
parent.child = first;
|
|
153
|
+
return first;
|
|
154
|
+
}
|
|
155
|
+
|
|
89
156
|
function reconcileSingleChild(
|
|
90
157
|
parent: Fiber,
|
|
91
158
|
current: Fiber | undefined,
|