Skip to content

Commit

Permalink
Adds more scaffolding for experimental event API (#15112)
Browse files Browse the repository at this point in the history
* Adds more scaffolding for experimental event API
  • Loading branch information
trueadm authored Mar 20, 2019
1 parent daeda44 commit b83e01c
Show file tree
Hide file tree
Showing 19 changed files with 1,066 additions and 279 deletions.
6 changes: 5 additions & 1 deletion packages/react-art/src/ReactARTHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,10 @@ export function getChildHostContext() {
return NO_CONTEXT;
}

export function getChildHostContextForEvent() {
return NO_CONTEXT;
}

export const scheduleTimeout = setTimeout;
export const cancelTimeout = clearTimeout;
export const noTimeout = -1;
Expand Down Expand Up @@ -440,7 +444,7 @@ export function handleEventComponent(
}

export function handleEventTarget(
type: string,
type: Symbol | number,
props: Props,
internalInstanceHandle: Object,
) {
Expand Down
87 changes: 82 additions & 5 deletions packages/react-dom/src/client/ReactDOMHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ import dangerousStyleValue from '../shared/dangerousStyleValue';

import type {DOMContainer} from './ReactDOM';
import type {ReactEventResponder} from 'shared/ReactTypes';
import {
REACT_EVENT_COMPONENT_TYPE,
REACT_EVENT_TARGET_TYPE,
REACT_EVENT_TARGET_TOUCH_HIT,
} from 'shared/ReactSymbols';
import getElementFromTouchHitTarget from 'shared/getElementFromTouchHitTarget';

export type Type = string;
export type Props = {
Expand All @@ -65,6 +71,10 @@ 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;
Expand All @@ -73,7 +83,11 @@ export type ChildSet = void; // Unused
export type TimeoutHandle = TimeoutID;
export type NoTimeout = -1;

import {enableSuspenseServerRenderer} from 'shared/ReactFeatureFlags';
import {
enableSuspenseServerRenderer,
enableEventAPI,
} from 'shared/ReactFeatureFlags';
import warning from 'shared/warning';

// Intentionally not named imports because Rollup would
// use dynamic dispatch for CommonJS interop named imports.
Expand Down Expand Up @@ -142,7 +156,7 @@ export function getRootHostContext(
if (__DEV__) {
const validatedTag = type.toLowerCase();
const ancestorInfo = updatedAncestorInfo(null, validatedTag);
return {namespace, ancestorInfo};
return {namespace, ancestorInfo, eventData: null};
}
return namespace;
}
Expand All @@ -159,12 +173,42 @@ export function getChildHostContext(
parentHostContextDev.ancestorInfo,
type,
);
return {namespace, ancestorInfo};
return {namespace, ancestorInfo, eventData: null};
}
const parentNamespace = ((parentHostContext: any): HostContextProd);
return getChildNamespace(parentNamespace, type);
}

export function getChildHostContextForEvent(
parentHostContext: HostContext,
type: Symbol | number,
): HostContext {
if (__DEV__) {
const parentHostContextDev = ((parentHostContext: any): HostContextDev);
const {namespace, ancestorInfo} = parentHostContextDev;
let eventData = null;

if (type === REACT_EVENT_COMPONENT_TYPE) {
eventData = {
isEventComponent: true,
isEventTarget: false,
};
} else if (type === REACT_EVENT_TARGET_TYPE) {
warning(
parentHostContextDev.eventData !== null &&
parentHostContextDev.eventData.isEventComponent,
'validateDOMNesting: React event targets must be direct children of event components.',
);
eventData = {
isEventComponent: false,
isEventTarget: true,
};
}
return {namespace, ancestorInfo, eventData};
}
return parentHostContext;
}

export function getPublicInstance(instance: Instance): * {
return instance;
}
Expand Down Expand Up @@ -296,6 +340,23 @@ export function createTextInstance(
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,
);
warning(
!eventData.isEventTarget,
'validateDOMNesting: React event targets 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);
Expand Down Expand Up @@ -804,9 +865,25 @@ export function handleEventComponent(
}

export function handleEventTarget(
type: string,
type: Symbol | number,
props: Props,
internalInstanceHandle: Object,
) {
// TODO: add handleEventTarget implementation
// Touch target hit slop handling
if (type === REACT_EVENT_TARGET_TOUCH_HIT) {
// Validates that there is a single element
const element = getElementFromTouchHitTarget(internalInstanceHandle);
if (element !== null) {
// We update the event target state node to be that of the element.
// We can then diff this entry to determine if we need to add the
// hit slop element, or change the dimensions of the hit slop.
const lastElement = internalInstanceHandle.stateNode;
if (lastElement !== element) {
internalInstanceHandle.stateNode = element;
// TODO: Create the hit slop element and attach it to the element
} else {
// TODO: Diff the left, top, right, bottom props
}
}
}
}
10 changes: 9 additions & 1 deletion packages/react-native-renderer/src/ReactFabricHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,14 @@ export function getChildHostContext(
}
}

export function getChildHostContextForEvent(
parentHostContext: HostContext,
type: Symbol | number,
) {
// TODO: add getChildHostContextForEvent implementation
return parentHostContext;
}

export function getPublicInstance(instance: Instance): * {
return instance.canonical;
}
Expand Down Expand Up @@ -428,7 +436,7 @@ export function handleEventComponent(
}

export function handleEventTarget(
type: string,
type: Symbol | number,
props: Props,
internalInstanceHandle: Object,
) {
Expand Down
10 changes: 9 additions & 1 deletion packages/react-native-renderer/src/ReactNativeHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,14 @@ export function getChildHostContext(
}
}

export function getChildHostContextForEvent(
parentHostContext: HostContext,
type: Symbol | number,
) {
// TODO: add getChildHostContextForEvent implementation
return parentHostContext;
}

export function getPublicInstance(instance: Instance): * {
return instance;
}
Expand Down Expand Up @@ -487,7 +495,7 @@ export function handleEventComponent(
}

export function handleEventTarget(
type: string,
type: Symbol | number,
props: Props,
internalInstanceHandle: Object,
) {
Expand Down
61 changes: 60 additions & 1 deletion packages/react-noop-renderer/src/createReactNoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,18 @@ import type {ReactNodeList} from 'shared/ReactTypes';
import * as Scheduler from 'scheduler/unstable_mock';
import {createPortal} from 'shared/ReactPortal';
import expect from 'expect';
import {REACT_FRAGMENT_TYPE, REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
import {
REACT_FRAGMENT_TYPE,
REACT_ELEMENT_TYPE,
REACT_EVENT_COMPONENT_TYPE,
REACT_EVENT_TARGET_TYPE,
REACT_EVENT_TARGET_TOUCH_HIT,
} from 'shared/ReactSymbols';
import warningWithoutStack from 'shared/warningWithoutStack';
import warning from 'shared/warning';
import getElementFromTouchHitTarget from 'shared/getElementFromTouchHitTarget';

import {enableEventAPI} from 'shared/ReactFeatureFlags';

// for .act's return value
type Thenable = {
Expand Down Expand Up @@ -54,6 +64,8 @@ type HostContext = Object;

const NO_CONTEXT = {};
const UPPERCASE_CONTEXT = {};
const EVENT_COMPONENT_CONTEXT = {};
const EVENT_TARGET_CONTEXT = {};
const UPDATE_SIGNAL = {};
if (__DEV__) {
Object.freeze(NO_CONTEXT);
Expand Down Expand Up @@ -250,6 +262,24 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
return NO_CONTEXT;
},

getChildHostContextForEvent(
parentHostContext: HostContext,
type: Symbol | number,
) {
if (__DEV__ && enableEventAPI) {
if (type === REACT_EVENT_COMPONENT_TYPE) {
return EVENT_COMPONENT_CONTEXT;
} else if (type === REACT_EVENT_TARGET_TYPE) {
warning(
parentHostContext === EVENT_COMPONENT_CONTEXT,
'validateDOMNesting: React event targets must be direct children of event components.',
);
return EVENT_TARGET_CONTEXT;
}
}
return parentHostContext;
},

getPublicInstance(instance) {
return instance;
},
Expand Down Expand Up @@ -333,6 +363,20 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
hostContext: Object,
internalInstanceHandle: Object,
): TextInstance {
if (__DEV__ && enableEventAPI) {
warning(
hostContext !== EVENT_COMPONENT_CONTEXT,
'validateDOMNesting: React event components cannot have text DOM nodes as children. ' +
'Wrap the child text "%s" in an element.',
text,
);
warning(
hostContext !== EVENT_TARGET_CONTEXT,
'validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
'Wrap the child text "%s" in an element.',
text,
);
}
if (hostContext === UPPERCASE_CONTEXT) {
text = text.toUpperCase();
}
Expand Down Expand Up @@ -363,6 +407,21 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {

isPrimaryRenderer: true,
supportsHydration: false,

handleEventComponent() {
// NO-OP
},

handleEventTarget(
type: Symbol | number,
props: Props,
internalInstanceHandle: Object,
) {
if (type === REACT_EVENT_TARGET_TOUCH_HIT) {
// Validates that there is a single element
getElementFromTouchHitTarget(internalInstanceHandle);
}
},
};

const hostConfig = useMutation
Expand Down
Loading

0 comments on commit b83e01c

Please sign in to comment.