Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 41 additions & 26 deletions packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -3056,13 +3056,13 @@ function indexOfEventListener(
listener: EventListener,
optionsOrUseCapture: void | EventListenerOptionsOrUseCapture,
): number {
const normalizedOptions = normalizeListenerOptions(optionsOrUseCapture);
for (let i = 0; i < eventListeners.length; i++) {
const item = eventListeners[i];
if (
item.type === type &&
item.listener === listener &&
normalizeListenerOptions(item.optionsOrUseCapture) ===
normalizeListenerOptions(optionsOrUseCapture)
normalizeListenerOptions(item.optionsOrUseCapture) === normalizedOptions
) {
return i;
}
Expand Down Expand Up @@ -3154,18 +3154,34 @@ function collectChildren(child: Fiber, collection: Array<Fiber>): boolean {
}
// $FlowFixMe[prop-missing]
FragmentInstance.prototype.blur = function (this: FragmentInstanceType): void {
// TODO: When we have a parent element reference, we can skip traversal if the fragment's parent
// does not contain document.activeElement
// Early exit if activeElement is not within the fragment's parent
const parentHostFiber = getFragmentParentHostFiber(this._fragmentFiber);
if (parentHostFiber === null) {
return;
}
const parentHostInstance =
getInstanceFromHostFiber<Instance>(parentHostFiber);
const activeElement = parentHostInstance.ownerDocument.activeElement;
if (activeElement === null || !parentHostInstance.contains(activeElement)) {
return;
}

traverseFragmentInstance(
this._fragmentFiber,
blurActiveElementWithinFragment,
activeElement,
);
};
function blurActiveElementWithinFragment(child: Fiber): boolean {
// TODO: We can get the activeElement from the parent outside of the loop when we have a reference.
function blurActiveElementWithinFragment(
child: Fiber,
activeElement: Element,
): boolean {
// Skip text nodes - they can't be focused
if (enableFragmentRefsTextNodes && child.tag === HostText) {
return false;
}
const instance = getInstanceFromHostFiber<Instance>(child);
const ownerDocument = instance.ownerDocument;
if (instance === ownerDocument.activeElement) {
if (instance === activeElement) {
// $FlowFixMe[prop-missing]
instance.blur();
return true;
Expand Down Expand Up @@ -3312,46 +3328,45 @@ FragmentInstance.prototype.compareDocumentPosition = function (
);
}

const firstElement = getInstanceFromHostFiber<Instance>(children[0]);
const lastElement = getInstanceFromHostFiber<Instance>(
const firstNode = getInstanceFromHostFiber<Instance>(children[0]);
const lastNode = getInstanceFromHostFiber<Instance>(
children[children.length - 1],
);

// If the fragment has been portaled into another host instance, we need to
// our best guess is to use the parent of the child instance, rather than
// the fiber tree host parent.
const firstInstance = getInstanceFromHostFiber<Instance>(children[0]);
const parentHostInstanceFromDOM = fiberIsPortaledIntoHost(this._fragmentFiber)
? (firstInstance.parentElement: ?Instance)
? (firstNode.parentElement: ?Instance)
: parentHostInstance;

if (parentHostInstanceFromDOM == null) {
return Node.DOCUMENT_POSITION_DISCONNECTED;
}

// Check if first and last element are actually in the expected document position
// before relying on them as source of truth for other contained elements
const firstElementIsContained =
parentHostInstanceFromDOM.compareDocumentPosition(firstElement) &
// Check if first and last node are actually in the expected document position
// before relying on them as source of truth for other contained nodes
const firstNodeIsContained =
parentHostInstanceFromDOM.compareDocumentPosition(firstNode) &
Node.DOCUMENT_POSITION_CONTAINED_BY;
const lastElementIsContained =
parentHostInstanceFromDOM.compareDocumentPosition(lastElement) &
const lastNodeIsContained =
parentHostInstanceFromDOM.compareDocumentPosition(lastNode) &
Node.DOCUMENT_POSITION_CONTAINED_BY;
const firstResult = firstElement.compareDocumentPosition(otherNode);
const lastResult = lastElement.compareDocumentPosition(otherNode);
const firstResult = firstNode.compareDocumentPosition(otherNode);
const lastResult = lastNode.compareDocumentPosition(otherNode);

const otherNodeIsFirstOrLastChild =
(firstElementIsContained && firstElement === otherNode) ||
(lastElementIsContained && lastElement === otherNode);
(firstNodeIsContained && firstNode === otherNode) ||
(lastNodeIsContained && lastNode === otherNode);
const otherNodeIsFirstOrLastChildDisconnected =
(!firstElementIsContained && firstElement === otherNode) ||
(!lastElementIsContained && lastElement === otherNode);
(!firstNodeIsContained && firstNode === otherNode) ||
(!lastNodeIsContained && lastNode === otherNode);
const otherNodeIsWithinFirstOrLastChild =
firstResult & Node.DOCUMENT_POSITION_CONTAINED_BY ||
lastResult & Node.DOCUMENT_POSITION_CONTAINED_BY;
const otherNodeIsBetweenFirstAndLastChildren =
firstElementIsContained &&
lastElementIsContained &&
firstNodeIsContained &&
lastNodeIsContained &&
firstResult & Node.DOCUMENT_POSITION_FOLLOWING &&
lastResult & Node.DOCUMENT_POSITION_PRECEDING;

Expand Down
Loading