mirror of
https://github.com/facebook/react.git
synced 2026-02-27 03:07:57 +00:00
* shared/src -> shared It's not a real package and doesn't even have package.json. This will also make importing less weird if we drop Haste. * Get rid of shared/utils Moved event-specific into shared/event. Moved rest to the root since distinction has always been pretty arbitrary. * Fix references to old shared/src paths
159 lines
3.6 KiB
JavaScript
159 lines
3.6 KiB
JavaScript
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @providesModule ReactTreeTraversal
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var {HostComponent} = require('ReactTypeOfWork');
|
|
|
|
function getParent(inst) {
|
|
do {
|
|
inst = inst.return;
|
|
// TODO: If this is a HostRoot we might want to bail out.
|
|
// That is depending on if we want nested subtrees (layers) to bubble
|
|
// events to their parent. We could also go through parentNode on the
|
|
// host node but that wouldn't work for React Native and doesn't let us
|
|
// do the portal feature.
|
|
} while (inst && inst.tag !== HostComponent);
|
|
if (inst) {
|
|
return inst;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Return the lowest common ancestor of A and B, or null if they are in
|
|
* different trees.
|
|
*/
|
|
function getLowestCommonAncestor(instA, instB) {
|
|
var depthA = 0;
|
|
for (var tempA = instA; tempA; tempA = getParent(tempA)) {
|
|
depthA++;
|
|
}
|
|
var depthB = 0;
|
|
for (var tempB = instB; tempB; tempB = getParent(tempB)) {
|
|
depthB++;
|
|
}
|
|
|
|
// If A is deeper, crawl up.
|
|
while (depthA - depthB > 0) {
|
|
instA = getParent(instA);
|
|
depthA--;
|
|
}
|
|
|
|
// If B is deeper, crawl up.
|
|
while (depthB - depthA > 0) {
|
|
instB = getParent(instB);
|
|
depthB--;
|
|
}
|
|
|
|
// Walk in lockstep until we find a match.
|
|
var depth = depthA;
|
|
while (depth--) {
|
|
if (instA === instB || instA === instB.alternate) {
|
|
return instA;
|
|
}
|
|
instA = getParent(instA);
|
|
instB = getParent(instB);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Return if A is an ancestor of B.
|
|
*/
|
|
function isAncestor(instA, instB) {
|
|
while (instB) {
|
|
if (instA === instB || instA === instB.alternate) {
|
|
return true;
|
|
}
|
|
instB = getParent(instB);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Return the parent instance of the passed-in instance.
|
|
*/
|
|
function getParentInstance(inst) {
|
|
return getParent(inst);
|
|
}
|
|
|
|
/**
|
|
* Simulates the traversal of a two-phase, capture/bubble event dispatch.
|
|
*/
|
|
function traverseTwoPhase(inst, fn, arg) {
|
|
var path = [];
|
|
while (inst) {
|
|
path.push(inst);
|
|
inst = getParent(inst);
|
|
}
|
|
var i;
|
|
for (i = path.length; i-- > 0; ) {
|
|
fn(path[i], 'captured', arg);
|
|
}
|
|
for (i = 0; i < path.length; i++) {
|
|
fn(path[i], 'bubbled', arg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that
|
|
* should would receive a `mouseEnter` or `mouseLeave` event.
|
|
*
|
|
* Does not invoke the callback on the nearest common ancestor because nothing
|
|
* "entered" or "left" that element.
|
|
*/
|
|
function traverseEnterLeave(from, to, fn, argFrom, argTo) {
|
|
const common = from && to ? getLowestCommonAncestor(from, to) : null;
|
|
const pathFrom = [];
|
|
while (true) {
|
|
if (!from) {
|
|
break;
|
|
}
|
|
if (from === common) {
|
|
break;
|
|
}
|
|
const alternate = from.alternate;
|
|
if (alternate !== null && alternate === common) {
|
|
break;
|
|
}
|
|
pathFrom.push(from);
|
|
from = getParent(from);
|
|
}
|
|
const pathTo = [];
|
|
while (true) {
|
|
if (!to) {
|
|
break;
|
|
}
|
|
if (to === common) {
|
|
break;
|
|
}
|
|
const alternate = to.alternate;
|
|
if (alternate !== null && alternate === common) {
|
|
break;
|
|
}
|
|
pathTo.push(to);
|
|
to = getParent(to);
|
|
}
|
|
for (let i = 0; i < pathFrom.length; i++) {
|
|
fn(pathFrom[i], 'bubbled', argFrom);
|
|
}
|
|
for (let i = pathTo.length; i-- > 0; ) {
|
|
fn(pathTo[i], 'captured', argTo);
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
isAncestor: isAncestor,
|
|
getLowestCommonAncestor: getLowestCommonAncestor,
|
|
getParentInstance: getParentInstance,
|
|
traverseTwoPhase: traverseTwoPhase,
|
|
traverseEnterLeave: traverseEnterLeave,
|
|
};
|