import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import type {ReactScopeInstance} from 'shared/ReactTypes';
import type {
ReactDOMEventHandle,
ReactDOMEventHandleListener,
} from './ReactDOMEventHandleTypes';
import type {
Container,
TextInstance,
Instance,
ActivityInstance,
SuspenseInstance,
Props,
HoistableRoot,
RootResources,
} from './ReactFiberConfigDOM';
import {
HostComponent,
HostHoistable,
HostSingleton,
HostText,
HostRoot,
SuspenseComponent,
ActivityComponent,
} from 'react-reconciler/src/ReactWorkTags';
import {getParentHydrationBoundary} from './ReactFiberConfigDOM';
import {enableScopeAPI} from 'shared/ReactFeatureFlags';
const randomKey = Math.random().toString(36).slice(2);
const internalInstanceKey = '__reactFiber$' + randomKey;
const internalPropsKey = '__reactProps$' + randomKey;
const internalContainerInstanceKey = '__reactContainer$' + randomKey;
const internalEventHandlersKey = '__reactEvents$' + randomKey;
const internalEventHandlerListenersKey = '__reactListeners$' + randomKey;
const internalEventHandlesSetKey = '__reactHandles$' + randomKey;
const internalRootNodeResourcesKey = '__reactResources$' + randomKey;
const internalHoistableMarker = '__reactMarker$' + randomKey;
const internalScrollTimer = '__reactScroll$' + randomKey;
export function detachDeletedInstance(node: Instance): void {
delete (node: any)[internalInstanceKey];
delete (node: any)[internalPropsKey];
delete (node: any)[internalEventHandlersKey];
delete (node: any)[internalEventHandlerListenersKey];
delete (node: any)[internalEventHandlesSetKey];
}
export function precacheFiberNode(
hostInst: Fiber,
node:
| Instance
| TextInstance
| SuspenseInstance
| ActivityInstance
| ReactScopeInstance,
): void {
(node: any)[internalInstanceKey] = hostInst;
}
export function markContainerAsRoot(hostRoot: Fiber, node: Container): void {
node[internalContainerInstanceKey] = hostRoot;
}
export function unmarkContainerAsRoot(node: Container): void {
node[internalContainerInstanceKey] = null;
}
export function isContainerMarkedAsRoot(node: Container): boolean {
return !!node[internalContainerInstanceKey];
}
export function getClosestInstanceFromNode(targetNode: Node): null | Fiber {
let targetInst = (targetNode: any)[internalInstanceKey];
if (targetInst) {
return targetInst;
}
let parentNode = targetNode.parentNode;
while (parentNode) {
targetInst =
(parentNode: any)[internalContainerInstanceKey] ||
(parentNode: any)[internalInstanceKey];
if (targetInst) {
const alternate = targetInst.alternate;
if (
targetInst.child !== null ||
(alternate !== null && alternate.child !== null)
) {
let hydrationInstance = getParentHydrationBoundary(targetNode);
while (hydrationInstance !== null) {
const targetFiber = hydrationInstance[internalInstanceKey];
if (targetFiber) {
return targetFiber;
}
hydrationInstance = getParentHydrationBoundary(hydrationInstance);
}
}
return targetInst;
}
targetNode = parentNode;
parentNode = targetNode.parentNode;
}
return null;
}
export function getInstanceFromNode(node: Node): Fiber | null {
const inst =
(node: any)[internalInstanceKey] ||
(node: any)[internalContainerInstanceKey];
if (inst) {
const tag = inst.tag;
if (
tag === HostComponent ||
tag === HostText ||
tag === SuspenseComponent ||
tag === ActivityComponent ||
tag === HostHoistable ||
tag === HostSingleton ||
tag === HostRoot
) {
return inst;
} else {
return null;
}
}
return null;
}
export function getNodeFromInstance(inst: Fiber): Instance | TextInstance {
const tag = inst.tag;
if (
tag === HostComponent ||
tag === HostHoistable ||
tag === HostSingleton ||
tag === HostText
) {
return inst.stateNode;
}
throw new Error('getNodeFromInstance: Invalid argument.');
}
export function getFiberCurrentPropsFromNode(
node:
| Container
| Instance
| TextInstance
| SuspenseInstance
| ActivityInstance,
): Props {
return (node: any)[internalPropsKey] || null;
}
export function updateFiberProps(node: Instance, props: Props): void {
(node: any)[internalPropsKey] = props;
}
export function getEventListenerSet(node: EventTarget): Set<string> {
let elementListenerSet = (node: any)[internalEventHandlersKey];
if (elementListenerSet === undefined) {
elementListenerSet = (node: any)[internalEventHandlersKey] = new Set();
}
return elementListenerSet;
}
export function getFiberFromScopeInstance(
scope: ReactScopeInstance,
): null | Fiber {
if (enableScopeAPI) {
return (scope: any)[internalInstanceKey] || null;
}
return null;
}
export function setEventHandlerListeners(
scope: EventTarget | ReactScopeInstance,
listeners: Set<ReactDOMEventHandleListener>,
): void {
(scope: any)[internalEventHandlerListenersKey] = listeners;
}
export function getEventHandlerListeners(
scope: EventTarget | ReactScopeInstance,
): null | Set<ReactDOMEventHandleListener> {
return (scope: any)[internalEventHandlerListenersKey] || null;
}
export function addEventHandleToTarget(
target: EventTarget | ReactScopeInstance,
eventHandle: ReactDOMEventHandle,
): void {
let eventHandles = (target: any)[internalEventHandlesSetKey];
if (eventHandles === undefined) {
eventHandles = (target: any)[internalEventHandlesSetKey] = new Set();
}
eventHandles.add(eventHandle);
}
export function doesTargetHaveEventHandle(
target: EventTarget | ReactScopeInstance,
eventHandle: ReactDOMEventHandle,
): boolean {
const eventHandles = (target: any)[internalEventHandlesSetKey];
if (eventHandles === undefined) {
return false;
}
return eventHandles.has(eventHandle);
}
export function getResourcesFromRoot(root: HoistableRoot): RootResources {
let resources = (root: any)[internalRootNodeResourcesKey];
if (!resources) {
resources = (root: any)[internalRootNodeResourcesKey] = {
hoistableStyles: new Map(),
hoistableScripts: new Map(),
};
}
return resources;
}
export function isMarkedHoistable(node: Node): boolean {
return !!(node: any)[internalHoistableMarker];
}
export function markNodeAsHoistable(node: Node) {
(node: any)[internalHoistableMarker] = true;
}
export function getScrollEndTimer(node: EventTarget): ?TimeoutID {
return (node: any)[internalScrollTimer];
}
export function setScrollEndTimer(node: EventTarget, timer: TimeoutID): void {
(node: any)[internalScrollTimer] = timer;
}
export function clearScrollEndTimer(node: EventTarget): void {
(node: any)[internalScrollTimer] = undefined;
}
export function isOwnedInstance(node: Node): boolean {
return !!(
(node: any)[internalHoistableMarker] || (node: any)[internalInstanceKey]
);
}