@reckona/mreact-compat 0.0.66 → 0.0.67
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/package.json +4 -3
- package/src/class-component.ts +386 -0
- package/src/context.ts +140 -0
- package/src/devtools.ts +1275 -0
- package/src/dom-children.ts +34 -0
- package/src/dom-props.ts +408 -0
- package/src/element.ts +317 -0
- package/src/event-listeners.ts +27 -0
- package/src/event-priority.ts +1 -0
- package/src/event-replay.ts +154 -0
- package/src/event-types.ts +18 -0
- package/src/events.ts +384 -0
- package/src/fiber-child.ts +364 -0
- package/src/fiber-commit.ts +83 -0
- package/src/fiber-flags.ts +21 -0
- package/src/fiber-host.ts +1564 -0
- package/src/fiber-lanes.ts +99 -0
- package/src/fiber-reconciler.ts +639 -0
- package/src/fiber-scheduler.ts +435 -0
- package/src/fiber-work-loop.ts +224 -0
- package/src/fiber.ts +148 -0
- package/src/flight-decoder.ts +205 -0
- package/src/flight-element-builder.ts +110 -0
- package/src/flight-parser.ts +698 -0
- package/src/flight-protocol.ts +71 -0
- package/src/flight-types.ts +148 -0
- package/src/flight.ts +162 -0
- package/src/hooks.ts +1940 -0
- package/src/hydration.ts +314 -0
- package/src/index.ts +95 -0
- package/src/internal.ts +7 -0
- package/src/jsx-dev-runtime.ts +40 -0
- package/src/jsx-runtime.ts +119 -0
- package/src/prop-comparison.ts +50 -0
- package/src/reconcile-types.ts +26 -0
- package/src/reconciler.ts +692 -0
- package/src/render.ts +29 -0
- package/src/root.ts +493 -0
- package/src/scheduler.ts +157 -0
- package/src/suspense.ts +317 -0
- package/src/thenable.ts +7 -0
- package/src/url-safety.ts +7 -0
package/src/element.ts
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
export const REACT_COMPAT_ELEMENT_TYPE = Symbol.for("modular.react.element");
|
|
2
|
+
export const ERROR_BOUNDARY_TYPE = Symbol.for("modular.react.error_boundary");
|
|
3
|
+
export const FORWARD_REF_TYPE = Symbol.for("modular.react.forward_ref");
|
|
4
|
+
export const MEMO_TYPE = Symbol.for("modular.react.memo");
|
|
5
|
+
export const LAZY_TYPE = Symbol.for("modular.react.lazy");
|
|
6
|
+
export const STRICT_MODE_TYPE = Symbol.for("modular.react.strict_mode");
|
|
7
|
+
export const PORTAL_TYPE = Symbol.for("modular.react.portal");
|
|
8
|
+
const REACT_COMPAT_PROVIDER_TYPE = Symbol.for("modular.react.provider");
|
|
9
|
+
export const Fragment = Symbol.for("modular.react.fragment");
|
|
10
|
+
export const Suspense = Symbol.for("modular.react.suspense");
|
|
11
|
+
export const SuspenseList = Symbol.for("modular.react.suspense_list");
|
|
12
|
+
export const Activity = Symbol.for("modular.react.activity");
|
|
13
|
+
export const Profiler = Symbol.for("modular.react.profiler");
|
|
14
|
+
|
|
15
|
+
export interface ReactCompatProviderType {
|
|
16
|
+
$$typeof: symbol;
|
|
17
|
+
context: unknown;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ReactCompatContextProviderShorthand {
|
|
21
|
+
Provider: ReactCompatProviderType;
|
|
22
|
+
Consumer: unknown;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type ElementType<P = Record<string, unknown>> =
|
|
26
|
+
| string
|
|
27
|
+
| typeof Fragment
|
|
28
|
+
| typeof Suspense
|
|
29
|
+
| typeof SuspenseList
|
|
30
|
+
| typeof Activity
|
|
31
|
+
| typeof Profiler
|
|
32
|
+
| typeof ERROR_BOUNDARY_TYPE
|
|
33
|
+
| typeof STRICT_MODE_TYPE
|
|
34
|
+
| ReactCompatContextProviderShorthand
|
|
35
|
+
| ReactCompatProviderType
|
|
36
|
+
| ForwardRefType<P>
|
|
37
|
+
| MemoType<P>
|
|
38
|
+
| LazyType<P>
|
|
39
|
+
| ((props: P) => ReactCompatNode | PromiseLike<ReactCompatNode>)
|
|
40
|
+
| (new (props: P) => { render(): ReactCompatNode });
|
|
41
|
+
|
|
42
|
+
export type ReactCompatNode =
|
|
43
|
+
| ReactCompatElement
|
|
44
|
+
| ReactCompatPortal
|
|
45
|
+
| string
|
|
46
|
+
| number
|
|
47
|
+
| boolean
|
|
48
|
+
| null
|
|
49
|
+
| undefined
|
|
50
|
+
| ReactCompatNode[];
|
|
51
|
+
|
|
52
|
+
export interface ReactCompatElement<P = Record<string, unknown>> {
|
|
53
|
+
$$typeof: typeof REACT_COMPAT_ELEMENT_TYPE;
|
|
54
|
+
type: ElementType<P>;
|
|
55
|
+
key: string | null;
|
|
56
|
+
ref: unknown;
|
|
57
|
+
props: P & { children?: ReactCompatNode };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface ReactCompatPortal {
|
|
61
|
+
$$typeof: typeof PORTAL_TYPE;
|
|
62
|
+
container: Element;
|
|
63
|
+
children: ReactCompatNode;
|
|
64
|
+
key: string | null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function createElement<P extends Record<string, unknown>>(
|
|
68
|
+
type: ElementType<P>,
|
|
69
|
+
config: (P & { key?: unknown; ref?: unknown }) | null,
|
|
70
|
+
...children: ReactCompatNode[]
|
|
71
|
+
): ReactCompatElement<P> {
|
|
72
|
+
const normalizedType = normalizeElementType(type);
|
|
73
|
+
const props = applyDefaultProps(normalizedType, { ...config }) as P & {
|
|
74
|
+
children?: ReactCompatNode;
|
|
75
|
+
key?: unknown;
|
|
76
|
+
ref?: unknown;
|
|
77
|
+
};
|
|
78
|
+
const key = props.key === undefined ? null : String(props.key);
|
|
79
|
+
const ref = props.ref ?? null;
|
|
80
|
+
|
|
81
|
+
delete props.key;
|
|
82
|
+
delete props.ref;
|
|
83
|
+
|
|
84
|
+
if (children.length === 1) {
|
|
85
|
+
props.children = children[0];
|
|
86
|
+
} else if (children.length > 1) {
|
|
87
|
+
props.children = children;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
$$typeof: REACT_COMPAT_ELEMENT_TYPE,
|
|
92
|
+
type: normalizedType as ElementType<P>,
|
|
93
|
+
key,
|
|
94
|
+
ref,
|
|
95
|
+
props: props as P & { children?: ReactCompatNode },
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function isReactCompatElement(
|
|
100
|
+
value: unknown,
|
|
101
|
+
): value is ReactCompatElement {
|
|
102
|
+
return (
|
|
103
|
+
typeof value === "object" &&
|
|
104
|
+
value !== null &&
|
|
105
|
+
(value as { $$typeof?: unknown }).$$typeof === REACT_COMPAT_ELEMENT_TYPE
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function createPortal(
|
|
110
|
+
children: ReactCompatNode,
|
|
111
|
+
container: Element,
|
|
112
|
+
key?: unknown,
|
|
113
|
+
): ReactCompatPortal {
|
|
114
|
+
return {
|
|
115
|
+
$$typeof: PORTAL_TYPE,
|
|
116
|
+
container,
|
|
117
|
+
children,
|
|
118
|
+
key: key === undefined ? null : String(key),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function isReactCompatPortal(value: unknown): value is ReactCompatPortal {
|
|
123
|
+
return (
|
|
124
|
+
typeof value === "object" &&
|
|
125
|
+
value !== null &&
|
|
126
|
+
(value as { $$typeof?: unknown }).$$typeof === PORTAL_TYPE
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function createRef<T>(): { current: T | null } {
|
|
131
|
+
return { current: null };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface ForwardRefType<P = Record<string, unknown>> {
|
|
135
|
+
$$typeof: typeof FORWARD_REF_TYPE;
|
|
136
|
+
render: (props: P, ref: unknown) => ReactCompatNode;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface MemoType<P = Record<string, unknown>> {
|
|
140
|
+
$$typeof: typeof MEMO_TYPE;
|
|
141
|
+
type: ElementType<P>;
|
|
142
|
+
compare?: (previous: P, next: P) => boolean;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface LazyType<P = Record<string, unknown>> {
|
|
146
|
+
$$typeof: typeof LAZY_TYPE;
|
|
147
|
+
load: () => Promise<{ default: ElementType<P> }>;
|
|
148
|
+
status: "uninitialized" | "pending" | "resolved" | "rejected";
|
|
149
|
+
promise?: Promise<void>;
|
|
150
|
+
resolved?: ElementType<P>;
|
|
151
|
+
error?: unknown;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function forwardRef<P, T>(
|
|
155
|
+
render: (props: P, ref: { current: T | null } | ((value: T | null) => void) | null) => ReactCompatNode,
|
|
156
|
+
): ForwardRefType<P & { ref?: unknown }> {
|
|
157
|
+
return { $$typeof: FORWARD_REF_TYPE, render: render as ForwardRefType<P>["render"] };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function memo<P>(
|
|
161
|
+
type: ElementType<P>,
|
|
162
|
+
compare?: (previous: P, next: P) => boolean,
|
|
163
|
+
): MemoType<P> {
|
|
164
|
+
return compare === undefined
|
|
165
|
+
? { $$typeof: MEMO_TYPE, type }
|
|
166
|
+
: { $$typeof: MEMO_TYPE, type, compare };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function lazy<P>(
|
|
170
|
+
load: () => Promise<{ default: ElementType<P> }>,
|
|
171
|
+
): LazyType<P> {
|
|
172
|
+
return {
|
|
173
|
+
$$typeof: LAZY_TYPE,
|
|
174
|
+
load,
|
|
175
|
+
status: "uninitialized",
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export const StrictMode = STRICT_MODE_TYPE;
|
|
180
|
+
|
|
181
|
+
export function cloneElement<P extends Record<string, unknown>>(
|
|
182
|
+
element: ReactCompatElement<P>,
|
|
183
|
+
props: Partial<P> | null,
|
|
184
|
+
...children: ReactCompatNode[]
|
|
185
|
+
): ReactCompatElement<P> {
|
|
186
|
+
const nextProps = applyDefaultProps(element.type, {
|
|
187
|
+
...element.props,
|
|
188
|
+
...props,
|
|
189
|
+
}) as P & { key?: unknown; ref?: unknown };
|
|
190
|
+
const key = nextProps.key === undefined ? element.key : String(nextProps.key);
|
|
191
|
+
const ref = nextProps.ref === undefined ? element.ref : nextProps.ref;
|
|
192
|
+
|
|
193
|
+
delete nextProps.key;
|
|
194
|
+
delete nextProps.ref;
|
|
195
|
+
|
|
196
|
+
if (children.length === 1) {
|
|
197
|
+
(nextProps as P & { children?: ReactCompatNode }).children = children[0];
|
|
198
|
+
} else if (children.length > 1) {
|
|
199
|
+
(nextProps as P & { children?: ReactCompatNode }).children = children;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
$$typeof: REACT_COMPAT_ELEMENT_TYPE,
|
|
204
|
+
type: element.type,
|
|
205
|
+
key,
|
|
206
|
+
ref,
|
|
207
|
+
props: nextProps as P & { children?: ReactCompatNode },
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function normalizeElementType<P>(type: ElementType<P>): ElementType<P> {
|
|
212
|
+
return isReactCompatContextProviderShorthand(type) ? (type.Provider as ElementType<P>) : type;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function applyDefaultProps(
|
|
216
|
+
type: unknown,
|
|
217
|
+
props: Record<string, unknown>,
|
|
218
|
+
): Record<string, unknown> {
|
|
219
|
+
const defaultProps = (type as { defaultProps?: Record<string, unknown> } | undefined)
|
|
220
|
+
?.defaultProps;
|
|
221
|
+
|
|
222
|
+
if (defaultProps === undefined) {
|
|
223
|
+
return props;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
for (const [name, value] of Object.entries(defaultProps)) {
|
|
227
|
+
if (props[name] === undefined) {
|
|
228
|
+
props[name] = value;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return props;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function isReactCompatContextProviderShorthand(
|
|
236
|
+
value: unknown,
|
|
237
|
+
): value is ReactCompatContextProviderShorthand {
|
|
238
|
+
if (typeof value !== "object" || value === null) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const provider = (value as { Provider?: unknown }).Provider;
|
|
243
|
+
|
|
244
|
+
return (
|
|
245
|
+
typeof provider === "object" &&
|
|
246
|
+
provider !== null &&
|
|
247
|
+
(provider as { $$typeof?: unknown }).$$typeof === REACT_COMPAT_PROVIDER_TYPE
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export const isValidElement = isReactCompatElement;
|
|
252
|
+
|
|
253
|
+
export const Children = {
|
|
254
|
+
map<T>(
|
|
255
|
+
children: ReactCompatNode,
|
|
256
|
+
fn: (child: Exclude<ReactCompatNode, null | undefined | boolean>, index: number) => T,
|
|
257
|
+
): T[] | null {
|
|
258
|
+
if (children === null || children === undefined) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return flattenChildren(children).map((child, index) =>
|
|
263
|
+
fn(child as Exclude<ReactCompatNode, null | undefined | boolean>, index),
|
|
264
|
+
);
|
|
265
|
+
},
|
|
266
|
+
count(children: ReactCompatNode): number {
|
|
267
|
+
if (children === null || children === undefined) {
|
|
268
|
+
return 0;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return flattenChildren(children).length;
|
|
272
|
+
},
|
|
273
|
+
toArray(children: ReactCompatNode): Exclude<ReactCompatNode, null | undefined | boolean>[] {
|
|
274
|
+
return toChildArray(children);
|
|
275
|
+
},
|
|
276
|
+
only(children: ReactCompatNode): Exclude<ReactCompatNode, null | undefined | boolean> {
|
|
277
|
+
const array = toChildArray(children);
|
|
278
|
+
|
|
279
|
+
if (array.length !== 1) {
|
|
280
|
+
throw new Error("Expected exactly one child.");
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return array[0] as Exclude<ReactCompatNode, null | undefined | boolean>;
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
function toChildArray(
|
|
288
|
+
children: ReactCompatNode,
|
|
289
|
+
): Exclude<ReactCompatNode, null | undefined | boolean>[] {
|
|
290
|
+
return flattenChildren(children).filter(
|
|
291
|
+
(child): child is Exclude<ReactCompatNode, null | undefined | boolean> =>
|
|
292
|
+
child !== null && child !== undefined && typeof child !== "boolean",
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function flattenChildren(children: ReactCompatNode): ReactCompatNode[] {
|
|
297
|
+
if (Array.isArray(children)) {
|
|
298
|
+
return children.flatMap((child) => flattenChildren(child));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return [children];
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export interface ErrorBoundaryOptions {
|
|
305
|
+
fallback: (error: Error) => ReactCompatNode;
|
|
306
|
+
onError?: (error: Error) => void;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function createErrorBoundary(
|
|
310
|
+
options: ErrorBoundaryOptions,
|
|
311
|
+
children: ReactCompatNode,
|
|
312
|
+
): ReactCompatElement<ErrorBoundaryOptions & { children: ReactCompatNode }> {
|
|
313
|
+
return createElement(ERROR_BOUNDARY_TYPE, {
|
|
314
|
+
...options,
|
|
315
|
+
children,
|
|
316
|
+
});
|
|
317
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SyntheticEvent } from "./event-types.js";
|
|
2
|
+
|
|
3
|
+
export interface AppliedProps {
|
|
4
|
+
props: Record<string, unknown>;
|
|
5
|
+
listeners: Map<string, AppliedEventListener>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface AppliedEventListener {
|
|
9
|
+
handler: (event: SyntheticEvent) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const appliedProps = new WeakMap<HTMLElement, AppliedProps>();
|
|
13
|
+
|
|
14
|
+
export function getAppliedProps(element: HTMLElement): AppliedProps | undefined {
|
|
15
|
+
return appliedProps.get(element);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function setAppliedProps(element: HTMLElement, props: AppliedProps): void {
|
|
19
|
+
appliedProps.set(element, props);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getAppliedEventHandler(
|
|
23
|
+
element: HTMLElement,
|
|
24
|
+
name: string,
|
|
25
|
+
): ((event: SyntheticEvent) => void) | undefined {
|
|
26
|
+
return appliedProps.get(element)?.listeners.get(name)?.handler;
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { runWithEventPriority, type EventPriority } from "./hooks.js";
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
const queuedHydrationEvents = new WeakMap<Element, QueuedHydrationEvent[]>();
|
|
2
|
+
const replayedEvents = new WeakSet<Event>();
|
|
3
|
+
const allowedReplayEventTypes = new Set(["click", "input", "change", "submit"]);
|
|
4
|
+
|
|
5
|
+
export interface EventHydrationManifest {
|
|
6
|
+
version: 1;
|
|
7
|
+
events: EventHydrationManifestEntry[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface EventHydrationManifestEntry {
|
|
11
|
+
id: string;
|
|
12
|
+
event: string;
|
|
13
|
+
handler: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface QueuedHydrationEvent {
|
|
17
|
+
target: EventTarget;
|
|
18
|
+
event: Event;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface HydrationEventReplayOptions {
|
|
22
|
+
onCapturedEvent?: (event: Event, target: EventTarget) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function queueHydrationEvent(
|
|
26
|
+
container: Element,
|
|
27
|
+
event: Event,
|
|
28
|
+
target: EventTarget,
|
|
29
|
+
): void {
|
|
30
|
+
if (
|
|
31
|
+
!allowedReplayEventTypes.has(event.type) ||
|
|
32
|
+
!(target instanceof Node) ||
|
|
33
|
+
!container.contains(target)
|
|
34
|
+
) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const events = queuedHydrationEvents.get(container) ?? [];
|
|
39
|
+
events.push({ event, target });
|
|
40
|
+
queuedHydrationEvents.set(container, events);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function enableHydrationEventReplay(container: Element): () => void {
|
|
44
|
+
return enableHydrationEventReplayForTypes(container, allowedReplayEventTypes);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function readEventHydrationManifest(
|
|
48
|
+
root: ParentNode = document,
|
|
49
|
+
): EventHydrationManifest | undefined {
|
|
50
|
+
const script = root.querySelector<HTMLScriptElement>(
|
|
51
|
+
"script[data-mreact-event-manifest]",
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
if (script === null) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const value = JSON.parse(script.textContent ?? "") as EventHydrationManifest;
|
|
59
|
+
|
|
60
|
+
if (value.version !== 1 || !Array.isArray(value.events)) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function enableEventHydrationManifestReplay(
|
|
68
|
+
container: Element,
|
|
69
|
+
manifest: EventHydrationManifest | undefined,
|
|
70
|
+
options: HydrationEventReplayOptions = {},
|
|
71
|
+
): () => void {
|
|
72
|
+
if (manifest === undefined) {
|
|
73
|
+
return () => undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const eventTypes = new Set(
|
|
77
|
+
manifest.events
|
|
78
|
+
.map((event) => event.event)
|
|
79
|
+
.filter((event) => allowedReplayEventTypes.has(event)),
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return enableHydrationEventReplayForTypes(container, eventTypes, options);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function replayQueuedHydrationEvents(container: Element): void {
|
|
86
|
+
const events = queuedHydrationEvents.get(container) ?? [];
|
|
87
|
+
queuedHydrationEvents.delete(container);
|
|
88
|
+
|
|
89
|
+
for (const { event, target } of events) {
|
|
90
|
+
replayedEvents.add(event);
|
|
91
|
+
target.dispatchEvent(event);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function enableHydrationEventReplayForTypes(
|
|
96
|
+
container: Element,
|
|
97
|
+
eventTypes: Iterable<string>,
|
|
98
|
+
options: HydrationEventReplayOptions = {},
|
|
99
|
+
): () => void {
|
|
100
|
+
const listeners = Array.from(eventTypes, (type) => {
|
|
101
|
+
const listener = (event: Event): void => {
|
|
102
|
+
if (replayedEvents.has(event) || !(event.target instanceof Node)) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const replayEvent = cloneReplayableEvent(event);
|
|
107
|
+
queueHydrationEvent(container, replayEvent, event.target);
|
|
108
|
+
options.onCapturedEvent?.(replayEvent, event.target);
|
|
109
|
+
event.stopImmediatePropagation();
|
|
110
|
+
event.preventDefault();
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
container.addEventListener(type, listener, true);
|
|
114
|
+
return { type, listener };
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return () => {
|
|
118
|
+
for (const { type, listener } of listeners) {
|
|
119
|
+
container.removeEventListener(type, listener, true);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function cloneReplayableEvent(event: Event): Event {
|
|
125
|
+
const init = {
|
|
126
|
+
bubbles: event.bubbles,
|
|
127
|
+
cancelable: event.cancelable,
|
|
128
|
+
composed: event.composed,
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
if (typeof MouseEvent !== "undefined" && event instanceof MouseEvent) {
|
|
132
|
+
return new MouseEvent(event.type, {
|
|
133
|
+
...init,
|
|
134
|
+
button: event.button,
|
|
135
|
+
buttons: event.buttons,
|
|
136
|
+
clientX: event.clientX,
|
|
137
|
+
clientY: event.clientY,
|
|
138
|
+
ctrlKey: event.ctrlKey,
|
|
139
|
+
metaKey: event.metaKey,
|
|
140
|
+
shiftKey: event.shiftKey,
|
|
141
|
+
altKey: event.altKey,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (typeof InputEvent !== "undefined" && event instanceof InputEvent) {
|
|
146
|
+
return new InputEvent(event.type, {
|
|
147
|
+
...init,
|
|
148
|
+
data: event.data,
|
|
149
|
+
inputType: event.inputType,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return new Event(event.type, init);
|
|
154
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface SyntheticEvent {
|
|
2
|
+
bubbles: boolean;
|
|
3
|
+
cancelable: boolean;
|
|
4
|
+
defaultPrevented: boolean;
|
|
5
|
+
eventPhase: number;
|
|
6
|
+
isTrusted: boolean;
|
|
7
|
+
nativeEvent: Event;
|
|
8
|
+
timeStamp: number;
|
|
9
|
+
type: string;
|
|
10
|
+
target: EventTarget | null;
|
|
11
|
+
currentTarget: EventTarget | null;
|
|
12
|
+
persist(): void;
|
|
13
|
+
preventDefault(): void;
|
|
14
|
+
stopPropagation(): void;
|
|
15
|
+
isDefaultPrevented(): boolean;
|
|
16
|
+
isPersistent(): boolean;
|
|
17
|
+
isPropagationStopped(): boolean;
|
|
18
|
+
}
|