Skip to content
Closed
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
111 changes: 78 additions & 33 deletions src/SideEffect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,45 +104,52 @@ export function RemoveScrollSideCar(props: IRemoveScrollEffectProps) {
return handleScroll(cancelingAxis, parent, event, cancelingAxis === 'h' ? deltaX : deltaY, true);
}, []);

const shouldPrevent = React.useCallback((_event: Event) => {
const event: WheelEvent | TouchEvent = _event as any;
const shouldPrevent = React.useCallback(
(subscriberElement: Document | ShadowRoot) => (_event: Event) => {
const event: WheelEvent | TouchEvent = _event as any;

if (!lockStack.length || lockStack[lockStack.length - 1] !== Style) {
// not the last active
return;
}
if (!lockStack.length || lockStack[lockStack.length - 1] !== Style) {
// not the last active
return;
}

const delta = 'deltaY' in event ? getDeltaXY(event) : getTouchXY(event);
const sourceEvent = shouldPreventQueue.current.filter(
(e) => e.name === event.type && e.target === event.target && deltaCompare(e.delta, delta)
)[0];
const delta = 'deltaY' in event ? getDeltaXY(event) : getTouchXY(event);
const sourceEvent = shouldPreventQueue.current.filter(
(e) => e.name === event.type && e.target === event.target && deltaCompare(e.delta, delta)
)[0];

// self event, and should be canceled
if (sourceEvent && sourceEvent.should) {
if (event.cancelable) {
event.preventDefault();
// self event, and should be canceled
if (sourceEvent && sourceEvent.should) {
if (event.cancelable) {
event.preventDefault();
}

return;
}

return;
}
const shouldPreventQueueContainsEventButSubscriberDoesntContainTarget = shouldPreventQueue.current.some(
(e) => e.name === event.type && deltaCompare(e.delta, delta) && !subscriberElement.contains(e.target)
);

// outside or shard event
if (!sourceEvent) {
const shardNodes = (lastProps.current.shards || [])
.map(extractRef)
.filter(Boolean)
.filter((node) => node.contains(event.target as any));
// outside or shard event
if (!sourceEvent && !shouldPreventQueueContainsEventButSubscriberDoesntContainTarget) {
const shardNodes = (lastProps.current.shards || [])
.map(extractRef)
.filter(Boolean)
.filter((node) => node.contains(event.target as any));

const shouldStop =
shardNodes.length > 0 ? shouldCancelEvent(event, shardNodes[0]) : !lastProps.current.noIsolation;
const shouldStop =
shardNodes.length > 0 ? shouldCancelEvent(event, shardNodes[0]) : !lastProps.current.noIsolation;

if (shouldStop) {
if (event.cancelable) {
event.preventDefault();
if (shouldStop) {
if (event.cancelable) {
event.preventDefault();
}
}
}
}
}, []);
},
[]
);

const shouldCancel = React.useCallback((name: string, delta: number[], target: any, should: boolean) => {
const event = { name, delta, target, should };
Expand Down Expand Up @@ -175,15 +182,28 @@ export function RemoveScrollSideCar(props: IRemoveScrollEffectProps) {
onTouchMoveCapture: scrollTouchMove,
});

document.addEventListener('wheel', shouldPrevent, nonPassive);
document.addEventListener('touchmove', shouldPrevent, nonPassive);
const documentAndShadowRootsSubscribers = [document, ...getShadowRootsInNode(document)].map(
(documentOrShadowRoot) => ({
shouldPrevent: shouldPrevent(documentOrShadowRoot),
elementToSubscribeTo: documentOrShadowRoot,
})
);

documentAndShadowRootsSubscribers.forEach(({ shouldPrevent, elementToSubscribeTo }) => {
elementToSubscribeTo.addEventListener('wheel', shouldPrevent, nonPassive);
elementToSubscribeTo.addEventListener('touchmove', shouldPrevent, nonPassive);
});

document.addEventListener('touchstart', scrollTouchStart, nonPassive);

return () => {
lockStack = lockStack.filter((inst) => inst !== Style);

document.removeEventListener('wheel', shouldPrevent, nonPassive as any);
document.removeEventListener('touchmove', shouldPrevent, nonPassive as any);
documentAndShadowRootsSubscribers.forEach(({ shouldPrevent, elementToSubscribeTo }) => {
elementToSubscribeTo.removeEventListener('wheel', shouldPrevent, nonPassive as any);
elementToSubscribeTo.removeEventListener('touchmove', shouldPrevent, nonPassive as any);
});

document.removeEventListener('touchstart', scrollTouchStart, nonPassive as any);
};
}, []);
Expand All @@ -197,3 +217,28 @@ export function RemoveScrollSideCar(props: IRemoveScrollEffectProps) {
</React.Fragment>
);
}

function getShadowRootsInNode(node: Node): ShadowRoot[] {
const shadowRoots: ShadowRoot[] = [];

walkNodeTree(node, (currentNode) => {
const maybeShadowRoot = (currentNode as HTMLElement).shadowRoot;

if (maybeShadowRoot) {
shadowRoots.push(maybeShadowRoot);
}
});

return shadowRoots;
}

function walkNodeTree(node: Node, cb: (node: Node) => void) {
cb(node);
node.childNodes.forEach((childNode) => walkNodeTree(childNode, cb));

const maybeShadowRoot = (node as HTMLElement).shadowRoot;

if (maybeShadowRoot) {
maybeShadowRoot.childNodes.forEach((childNode) => walkNodeTree(childNode, cb));
}
}