/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow */ import * as Scheduler from 'scheduler'; import {precacheFiberNode, updateFiberProps} from './ReactDOMComponentTree'; import { createElement, createTextNode, setInitialProperties, diffProperties, updateProperties, diffHydratedProperties, diffHydratedText, trapClickOnNonInteractiveElement, warnForUnmatchedText, warnForDeletedHydratableElement, warnForDeletedHydratableText, warnForInsertedHydratedElement, warnForInsertedHydratedText, listenToEventResponderEventTypes, } from './ReactDOMComponent'; import {getSelectionInformation, restoreSelection} from './ReactInputSelection'; import setTextContent from './setTextContent'; import {validateDOMNesting, updatedAncestorInfo} from './validateDOMNesting'; import { isEnabled as ReactBrowserEventEmitterIsEnabled, setEnabled as ReactBrowserEventEmitterSetEnabled, } from '../events/ReactBrowserEventEmitter'; import {Namespaces, getChildNamespace} from '../shared/DOMNamespaces'; import {addRootEventTypesForComponentInstance} from '../events/DOMEventResponderSystem'; import { ELEMENT_NODE, TEXT_NODE, COMMENT_NODE, DOCUMENT_NODE, DOCUMENT_FRAGMENT_NODE, } from '../shared/HTMLNodeType'; import dangerousStyleValue from '../shared/dangerousStyleValue'; import type {DOMContainer} from './ReactDOM'; import type { ReactDOMEventResponder, ReactDOMEventComponentInstance, } from 'shared/ReactDOMTypes'; import { mountEventResponder, unmountEventResponder, } from '../events/DOMEventResponderSystem'; import {REACT_EVENT_TARGET_TOUCH_HIT} from 'shared/ReactSymbols'; import {canUseDOM} from 'shared/ExecutionEnvironment'; export type Type = string; export type Props = { autoFocus?: boolean, children?: mixed, hidden?: boolean, suppressHydrationWarning?: boolean, dangerouslySetInnerHTML?: mixed, style?: { display?: string, }, bottom?: null | number, left?: null | number, right?: null | number, top?: null | number, }; export type EventTargetChildElement = { type: string, props: null | { style?: { position?: string, zIndex?: number, bottom?: string, left?: string, right?: string, top?: string, }, }, }; export type Container = Element | Document; export type Instance = Element; export type TextInstance = Text; export type SuspenseInstance = Comment & {_reactRetry?: () => void}; export type HydratableInstance = Instance | TextInstance | SuspenseInstance; export type PublicInstance = Element | Text; type HostContextDev = { namespace: string, ancestorInfo: mixed, eventData: null | {| isEventComponent?: boolean, isEventTarget?: boolean, |}, }; type HostContextProd = string; export type HostContext = HostContextDev | HostContextProd; export type UpdatePayload = Array; export type ChildSet = void; // Unused export type TimeoutHandle = TimeoutID; export type NoTimeout = -1; import { enableSuspenseServerRenderer, enableEventAPI, } from 'shared/ReactFeatureFlags'; import warning from 'shared/warning'; const {html: HTML_NAMESPACE} = Namespaces; // Intentionally not named imports because Rollup would // use dynamic dispatch for CommonJS interop named imports. const { unstable_now: now, unstable_scheduleCallback: scheduleDeferredCallback, unstable_shouldYield: shouldYield, unstable_cancelCallback: cancelDeferredCallback, } = Scheduler; export {now, scheduleDeferredCallback, shouldYield, cancelDeferredCallback}; let SUPPRESS_HYDRATION_WARNING; if (__DEV__) { SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning'; } const SUSPENSE_START_DATA = '$'; const SUSPENSE_END_DATA = '/$'; const SUSPENSE_PENDING_START_DATA = '$?'; const SUSPENSE_FALLBACK_START_DATA = '$!'; const STYLE = 'style'; let eventsEnabled: ?boolean = null; let selectionInformation: ?mixed = null; function shouldAutoFocusHostComponent(type: string, props: Props): boolean { switch (type) { case 'button': case 'input': case 'select': case 'textarea': return !!props.autoFocus; } return false; } export * from 'shared/HostConfigWithNoPersistence'; export function getRootHostContext( rootContainerInstance: Container, ): HostContext { let type; let namespace; const nodeType = rootContainerInstance.nodeType; switch (nodeType) { case DOCUMENT_NODE: case DOCUMENT_FRAGMENT_NODE: { type = nodeType === DOCUMENT_NODE ? '#document' : '#fragment'; let root = (rootContainerInstance: any).documentElement; namespace = root ? root.namespaceURI : getChildNamespace(null, ''); break; } default: { const container: any = nodeType === COMMENT_NODE ? rootContainerInstance.parentNode : rootContainerInstance; const ownNamespace = container.namespaceURI || null; type = container.tagName; namespace = getChildNamespace(ownNamespace, type); break; } } if (__DEV__) { const validatedTag = type.toLowerCase(); const ancestorInfo = updatedAncestorInfo(null, validatedTag); return {namespace, ancestorInfo, eventData: null}; } return namespace; } export function getChildHostContext( parentHostContext: HostContext, type: string, rootContainerInstance: Container, ): HostContext { if (__DEV__) { const parentHostContextDev = ((parentHostContext: any): HostContextDev); const namespace = getChildNamespace(parentHostContextDev.namespace, type); const ancestorInfo = updatedAncestorInfo( parentHostContextDev.ancestorInfo, type, ); return {namespace, ancestorInfo, eventData: null}; } const parentNamespace = ((parentHostContext: any): HostContextProd); return getChildNamespace(parentNamespace, type); } export function getChildHostContextForEventComponent( parentHostContext: HostContext, ): HostContext { if (__DEV__) { const parentHostContextDev = ((parentHostContext: any): HostContextDev); const {namespace, ancestorInfo} = parentHostContextDev; warning( parentHostContextDev.eventData === null || !parentHostContextDev.eventData.isEventTarget, 'validateDOMNesting: React event targets must not have event components as children.', ); const eventData = { isEventComponent: true, isEventTarget: false, }; return {namespace, ancestorInfo, eventData}; } return parentHostContext; } export function getChildHostContextForEventTarget( parentHostContext: HostContext, type: Symbol | number, ): HostContext { if (__DEV__) { const parentHostContextDev = ((parentHostContext: any): HostContextDev); const {namespace, ancestorInfo} = parentHostContextDev; if (type === REACT_EVENT_TARGET_TOUCH_HIT) { warning( parentHostContextDev.eventData === null || !parentHostContextDev.eventData.isEventComponent, 'validateDOMNesting: cannot not be a direct child of an event component. ' + 'Ensure is a direct child of a DOM element.', ); const parentNamespace = parentHostContextDev.namespace; if (parentNamespace !== HTML_NAMESPACE) { throw new Error( ' was used in an unsupported DOM namespace. ' + 'Ensure the is used in an HTML namespace.', ); } } const eventData = { isEventComponent: false, isEventTarget: true, }; return {namespace, ancestorInfo, eventData}; } return parentHostContext; } export function getPublicInstance(instance: Instance): * { return instance; } export function prepareForCommit(containerInfo: Container): void { eventsEnabled = ReactBrowserEventEmitterIsEnabled(); selectionInformation = getSelectionInformation(); ReactBrowserEventEmitterSetEnabled(false); } export function resetAfterCommit(containerInfo: Container): void { restoreSelection(selectionInformation); selectionInformation = null; ReactBrowserEventEmitterSetEnabled(eventsEnabled); eventsEnabled = null; } export function createInstance( type: string, props: Props, rootContainerInstance: Container, hostContext: HostContext, internalInstanceHandle: Object, ): Instance { let parentNamespace: string; if (__DEV__) { // TODO: take namespace into account when validating. const hostContextDev = ((hostContext: any): HostContextDev); validateDOMNesting(type, null, hostContextDev.ancestorInfo); if ( typeof props.children === 'string' || typeof props.children === 'number' ) { const string = '' + props.children; const ownAncestorInfo = updatedAncestorInfo( hostContextDev.ancestorInfo, type, ); validateDOMNesting(null, string, ownAncestorInfo); } parentNamespace = hostContextDev.namespace; } else { parentNamespace = ((hostContext: any): HostContextProd); } const domElement: Instance = createElement( type, props, rootContainerInstance, parentNamespace, ); precacheFiberNode(internalInstanceHandle, domElement); updateFiberProps(domElement, props); return domElement; } export function appendInitialChild( parentInstance: Instance, child: Instance | TextInstance, ): void { parentInstance.appendChild(child); } export function finalizeInitialChildren( domElement: Instance, type: string, props: Props, rootContainerInstance: Container, hostContext: HostContext, ): boolean { setInitialProperties(domElement, type, props, rootContainerInstance); return shouldAutoFocusHostComponent(type, props); } export function prepareUpdate( domElement: Instance, type: string, oldProps: Props, newProps: Props, rootContainerInstance: Container, hostContext: HostContext, ): null | Array { if (__DEV__) { const hostContextDev = ((hostContext: any): HostContextDev); if ( typeof newProps.children !== typeof oldProps.children && (typeof newProps.children === 'string' || typeof newProps.children === 'number') ) { const string = '' + newProps.children; const ownAncestorInfo = updatedAncestorInfo( hostContextDev.ancestorInfo, type, ); validateDOMNesting(null, string, ownAncestorInfo); } } return diffProperties( domElement, type, oldProps, newProps, rootContainerInstance, ); } export function shouldSetTextContent(type: string, props: Props): boolean { return ( type === 'textarea' || type === 'option' || type === 'noscript' || typeof props.children === 'string' || typeof props.children === 'number' || (typeof props.dangerouslySetInnerHTML === 'object' && props.dangerouslySetInnerHTML !== null && props.dangerouslySetInnerHTML.__html != null) ); } export function shouldDeprioritizeSubtree(type: string, props: Props): boolean { return !!props.hidden; } export function createTextInstance( text: string, rootContainerInstance: Container, hostContext: HostContext, internalInstanceHandle: Object, ): TextInstance { if (__DEV__) { const hostContextDev = ((hostContext: any): HostContextDev); validateDOMNesting(null, text, hostContextDev.ancestorInfo); if (enableEventAPI) { const eventData = hostContextDev.eventData; if (eventData !== null) { warning( !eventData.isEventComponent, 'validateDOMNesting: React event components cannot have text DOM nodes as children. ' + 'Wrap the child text "%s" in an element.', text, ); } } } const textNode: TextInstance = createTextNode(text, rootContainerInstance); precacheFiberNode(internalInstanceHandle, textNode); return textNode; } export const isPrimaryRenderer = true; // This initialization code may run even on server environments // if a component just imports ReactDOM (e.g. for findDOMNode). // Some environments might not have setTimeout or clearTimeout. export const scheduleTimeout = typeof setTimeout === 'function' ? setTimeout : (undefined: any); export const cancelTimeout = typeof clearTimeout === 'function' ? clearTimeout : (undefined: any); export const noTimeout = -1; // ------------------- // Mutation // ------------------- export const supportsMutation = true; export function commitMount( domElement: Instance, type: string, newProps: Props, internalInstanceHandle: Object, ): void { // Despite the naming that might imply otherwise, this method only // fires if there is an `Update` effect scheduled during mounting. // This happens if `finalizeInitialChildren` returns `true` (which it // does to implement the `autoFocus` attribute on the client). But // there are also other cases when this might happen (such as patching // up text content during hydration mismatch). So we'll check this again. if (shouldAutoFocusHostComponent(type, newProps)) { ((domElement: any): | HTMLButtonElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement).focus(); } } export function commitUpdate( domElement: Instance, updatePayload: Array, type: string, oldProps: Props, newProps: Props, internalInstanceHandle: Object, ): void { // Update the props handle so that we know which props are the ones with // with current event handlers. updateFiberProps(domElement, newProps); // Apply the diff to the DOM node. updateProperties(domElement, updatePayload, type, oldProps, newProps); } export function resetTextContent(domElement: Instance): void { setTextContent(domElement, ''); } export function commitTextUpdate( textInstance: TextInstance, oldText: string, newText: string, ): void { textInstance.nodeValue = newText; } export function appendChild( parentInstance: Instance, child: Instance | TextInstance, ): void { parentInstance.appendChild(child); } export function appendChildToContainer( container: DOMContainer, child: Instance | TextInstance, ): void { let parentNode; if (container.nodeType === COMMENT_NODE) { parentNode = (container.parentNode: any); parentNode.insertBefore(child, container); } else { parentNode = container; parentNode.appendChild(child); } // This container might be used for a portal. // If something inside a portal is clicked, that click should bubble // through the React tree. However, on Mobile Safari the click would // never bubble through the *DOM* tree unless an ancestor with onclick // event exists. So we wouldn't see it and dispatch it. // This is why we ensure that non React root containers have inline onclick // defined. // https://github.com/facebook/react/issues/11918 const reactRootContainer = container._reactRootContainer; if ( (reactRootContainer === null || reactRootContainer === undefined) && parentNode.onclick === null ) { // TODO: This cast may not be sound for SVG, MathML or custom elements. trapClickOnNonInteractiveElement(((parentNode: any): HTMLElement)); } } export function insertBefore( parentInstance: Instance, child: Instance | TextInstance, beforeChild: Instance | TextInstance | SuspenseInstance, ): void { parentInstance.insertBefore(child, beforeChild); } export function insertInContainerBefore( container: Container, child: Instance | TextInstance, beforeChild: Instance | TextInstance | SuspenseInstance, ): void { if (container.nodeType === COMMENT_NODE) { (container.parentNode: any).insertBefore(child, beforeChild); } else { container.insertBefore(child, beforeChild); } } export function removeChild( parentInstance: Instance, child: Instance | TextInstance | SuspenseInstance, ): void { parentInstance.removeChild(child); } export function removeChildFromContainer( container: Container, child: Instance | TextInstance | SuspenseInstance, ): void { if (container.nodeType === COMMENT_NODE) { (container.parentNode: any).removeChild(child); } else { container.removeChild(child); } } export function clearSuspenseBoundary( parentInstance: Instance, suspenseInstance: SuspenseInstance, ): void { let node = suspenseInstance; // Delete all nodes within this suspense boundary. // There might be nested nodes so we need to keep track of how // deep we are and only break out when we're back on top. let depth = 0; do { let nextNode = node.nextSibling; parentInstance.removeChild(node); if (nextNode && nextNode.nodeType === COMMENT_NODE) { let data = ((nextNode: any).data: string); if (data === SUSPENSE_END_DATA) { if (depth === 0) { parentInstance.removeChild(nextNode); return; } else { depth--; } } else if ( data === SUSPENSE_START_DATA || data === SUSPENSE_PENDING_START_DATA || data === SUSPENSE_FALLBACK_START_DATA ) { depth++; } } node = nextNode; } while (node); // TODO: Warn, we didn't find the end comment boundary. } export function clearSuspenseBoundaryFromContainer( container: Container, suspenseInstance: SuspenseInstance, ): void { if (container.nodeType === COMMENT_NODE) { clearSuspenseBoundary((container.parentNode: any), suspenseInstance); } else if (container.nodeType === ELEMENT_NODE) { clearSuspenseBoundary((container: any), suspenseInstance); } else { // Document nodes should never contain suspense boundaries. } } export function hideInstance(instance: Instance): void { // TODO: Does this work for all element types? What about MathML? Should we // pass host context to this method? instance = ((instance: any): HTMLElement); const style = instance.style; if (typeof style.setProperty === 'function') { style.setProperty('display', 'none', 'important'); } else { style.display = 'none'; } } export function hideTextInstance(textInstance: TextInstance): void { textInstance.nodeValue = ''; } export function unhideInstance(instance: Instance, props: Props): void { instance = ((instance: any): HTMLElement); const styleProp = props[STYLE]; const display = styleProp !== undefined && styleProp !== null && styleProp.hasOwnProperty('display') ? styleProp.display : null; instance.style.display = dangerousStyleValue('display', display); } export function unhideTextInstance( textInstance: TextInstance, text: string, ): void { textInstance.nodeValue = text; } // ------------------- // Hydration // ------------------- export const supportsHydration = true; export function canHydrateInstance( instance: HydratableInstance, type: string, props: Props, ): null | Instance { if ( instance.nodeType !== ELEMENT_NODE || type.toLowerCase() !== instance.nodeName.toLowerCase() ) { return null; } // This has now been refined to an element node. return ((instance: any): Instance); } export function canHydrateTextInstance( instance: HydratableInstance, text: string, ): null | TextInstance { if (text === '' || instance.nodeType !== TEXT_NODE) { // Empty strings are not parsed by HTML so there won't be a correct match here. return null; } // This has now been refined to a text node. return ((instance: any): TextInstance); } export function canHydrateSuspenseInstance( instance: HydratableInstance, ): null | SuspenseInstance { if (instance.nodeType !== COMMENT_NODE) { // Empty strings are not parsed by HTML so there won't be a correct match here. return null; } // This has now been refined to a suspense node. return ((instance: any): SuspenseInstance); } export function isSuspenseInstancePending(instance: SuspenseInstance) { return instance.data === SUSPENSE_PENDING_START_DATA; } export function isSuspenseInstanceFallback(instance: SuspenseInstance) { return instance.data === SUSPENSE_FALLBACK_START_DATA; } export function registerSuspenseInstanceRetry( instance: SuspenseInstance, callback: () => void, ) { instance._reactRetry = callback; } export function getNextHydratableSibling( instance: HydratableInstance, ): null | HydratableInstance { let node = instance.nextSibling; // Skip non-hydratable nodes. while ( node && node.nodeType !== ELEMENT_NODE && node.nodeType !== TEXT_NODE && (!enableSuspenseServerRenderer || node.nodeType !== COMMENT_NODE || ((node: any).data !== SUSPENSE_START_DATA && (node: any).data !== SUSPENSE_PENDING_START_DATA && (node: any).data !== SUSPENSE_FALLBACK_START_DATA)) ) { node = node.nextSibling; } return (node: any); } export function getFirstHydratableChild( parentInstance: Container | Instance, ): null | HydratableInstance { let next = parentInstance.firstChild; // Skip non-hydratable nodes. while ( next && next.nodeType !== ELEMENT_NODE && next.nodeType !== TEXT_NODE && (!enableSuspenseServerRenderer || next.nodeType !== COMMENT_NODE || ((next: any).data !== SUSPENSE_START_DATA && (next: any).data !== SUSPENSE_FALLBACK_START_DATA && (next: any).data !== SUSPENSE_PENDING_START_DATA)) ) { next = next.nextSibling; } return (next: any); } export function hydrateInstance( instance: Instance, type: string, props: Props, rootContainerInstance: Container, hostContext: HostContext, internalInstanceHandle: Object, ): null | Array { precacheFiberNode(internalInstanceHandle, instance); // TODO: Possibly defer this until the commit phase where all the events // get attached. updateFiberProps(instance, props); let parentNamespace: string; if (__DEV__) { const hostContextDev = ((hostContext: any): HostContextDev); parentNamespace = hostContextDev.namespace; } else { parentNamespace = ((hostContext: any): HostContextProd); } return diffHydratedProperties( instance, type, props, parentNamespace, rootContainerInstance, ); } export function hydrateTextInstance( textInstance: TextInstance, text: string, internalInstanceHandle: Object, ): boolean { precacheFiberNode(internalInstanceHandle, textInstance); return diffHydratedText(textInstance, text); } export function getNextHydratableInstanceAfterSuspenseInstance( suspenseInstance: SuspenseInstance, ): null | HydratableInstance { let node = suspenseInstance.nextSibling; // Skip past all nodes within this suspense boundary. // There might be nested nodes so we need to keep track of how // deep we are and only break out when we're back on top. let depth = 0; while (node) { if (node.nodeType === COMMENT_NODE) { let data = ((node: any).data: string); if (data === SUSPENSE_END_DATA) { if (depth === 0) { return getNextHydratableSibling((node: any)); } else { depth--; } } else if (data === SUSPENSE_START_DATA) { depth++; } } node = node.nextSibling; } // TODO: Warn, we didn't find the end comment boundary. return null; } export function didNotMatchHydratedContainerTextInstance( parentContainer: Container, textInstance: TextInstance, text: string, ) { if (__DEV__) { warnForUnmatchedText(textInstance, text); } } export function didNotMatchHydratedTextInstance( parentType: string, parentProps: Props, parentInstance: Instance, textInstance: TextInstance, text: string, ) { if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) { warnForUnmatchedText(textInstance, text); } } export function didNotHydrateContainerInstance( parentContainer: Container, instance: HydratableInstance, ) { if (__DEV__) { if (instance.nodeType === ELEMENT_NODE) { warnForDeletedHydratableElement(parentContainer, (instance: any)); } else if (instance.nodeType === COMMENT_NODE) { // TODO: warnForDeletedHydratableSuspenseBoundary } else { warnForDeletedHydratableText(parentContainer, (instance: any)); } } } export function didNotHydrateInstance( parentType: string, parentProps: Props, parentInstance: Instance, instance: HydratableInstance, ) { if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) { if (instance.nodeType === ELEMENT_NODE) { warnForDeletedHydratableElement(parentInstance, (instance: any)); } else if (instance.nodeType === COMMENT_NODE) { // TODO: warnForDeletedHydratableSuspenseBoundary } else { warnForDeletedHydratableText(parentInstance, (instance: any)); } } } export function didNotFindHydratableContainerInstance( parentContainer: Container, type: string, props: Props, ) { if (__DEV__) { warnForInsertedHydratedElement(parentContainer, type, props); } } export function didNotFindHydratableContainerTextInstance( parentContainer: Container, text: string, ) { if (__DEV__) { warnForInsertedHydratedText(parentContainer, text); } } export function didNotFindHydratableContainerSuspenseInstance( parentContainer: Container, ) { if (__DEV__) { // TODO: warnForInsertedHydratedSupsense(parentContainer); } } export function didNotFindHydratableInstance( parentType: string, parentProps: Props, parentInstance: Instance, type: string, props: Props, ) { if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) { warnForInsertedHydratedElement(parentInstance, type, props); } } export function didNotFindHydratableTextInstance( parentType: string, parentProps: Props, parentInstance: Instance, text: string, ) { if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) { warnForInsertedHydratedText(parentInstance, text); } } export function didNotFindHydratableSuspenseInstance( parentType: string, parentProps: Props, parentInstance: Instance, ) { if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) { // TODO: warnForInsertedHydratedSuspense(parentInstance); } } export function mountEventComponent( eventComponentInstance: ReactDOMEventComponentInstance, ): void { if (enableEventAPI) { const rootContainerInstance = ((eventComponentInstance.rootInstance: any): Container); const doc = rootContainerInstance.ownerDocument; const documentBody = doc.body || doc; const responder = eventComponentInstance.responder; const { rootEventTypes, targetEventTypes, } = ((responder: any): ReactDOMEventResponder); if (targetEventTypes !== undefined) { listenToEventResponderEventTypes(targetEventTypes, documentBody); } if (rootEventTypes !== undefined) { addRootEventTypesForComponentInstance( eventComponentInstance, rootEventTypes, ); listenToEventResponderEventTypes(rootEventTypes, documentBody); } mountEventResponder(eventComponentInstance); } } export function updateEventComponent( eventComponentInstance: ReactDOMEventComponentInstance, ): void { // NO-OP, why might use this in the future } export function unmountEventComponent( eventComponentInstance: ReactDOMEventComponentInstance, ): void { if (enableEventAPI) { // TODO stop listening to targetEventTypes unmountEventResponder(eventComponentInstance); } } export function getEventTargetChildElement( type: Symbol | number, props: Props, ): null | EventTargetChildElement { if (enableEventAPI) { if (type === REACT_EVENT_TARGET_TOUCH_HIT) { const {bottom, left, right, top} = props; if (!bottom && !left && !right && !top) { return null; } return { type: 'div', props: { style: { position: 'absolute', zIndex: -1, pointerEvents: null, bottom: bottom ? `-${bottom}px` : '0px', left: left ? `-${left}px` : '0px', right: right ? `-${right}px` : '0px', top: top ? `-${top}px` : '0px', }, hydrateTouchHitTarget: true, suppressHydrationWarning: true, }, }; } } return null; } export function handleEventTarget( type: Symbol | number, props: Props, rootContainerInstance: Container, internalInstanceHandle: Object, ): boolean { if ( __DEV__ && type === REACT_EVENT_TARGET_TOUCH_HIT && (props.left || props.right || props.top || props.bottom) ) { return true; } return false; } export function commitEventTarget( type: Symbol | number, props: Props, instance: Instance, parentInstance: Instance, ): void { if (enableEventAPI) { if (type === REACT_EVENT_TARGET_TOUCH_HIT) { if (__DEV__ && canUseDOM) { // This is done at DEV time because getComputedStyle will // typically force a style recalculation and force a layout, // reflow -– both of which are sync are expensive. const computedStyles = window.getComputedStyle(parentInstance); const position = computedStyles.getPropertyValue('position'); warning( position !== '' && position !== 'static', ' inserts an empty absolutely positioned
. ' + 'This requires its parent DOM node to be positioned too, but the ' + 'parent DOM node was found to have the style "position" set to ' + 'either no value, or a value of "static". Try using a "position" ' + 'value of "relative".', ); warning( computedStyles.getPropertyValue('z-index') !== '', ' inserts an empty
with "z-index" of "-1". ' + 'This requires its parent DOM node to have a "z-index" greater than "-1",' + 'but the parent DOM node was found to no "z-index" value set.' + ' Try using a "z-index" value of "0" or greater.', ); } } } }