Skip to content
Open
Show file tree
Hide file tree
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
16 changes: 15 additions & 1 deletion packages/react-native-web/src/exports/FlatList/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,19 @@
* @flow
*/

import FlatList from '../../vendor/react-native/FlatList';
import * as React from 'react';
import useDraggableScroll from '../../modules/useDraggableScroll';
import OriginFlatList from '../../vendor/react-native/FlatList';

const FlatList: React.AbstractComponent<
React.ElementConfig<typeof OriginFlatList>,
React.ElementRef<typeof OriginFlatList>
> = React.forwardRef((props, forwardedRef) => {
const { setRef } = useDraggableScroll(props, forwardedRef);

return <OriginFlatList ref={setRef} {...props} />;
});

FlatList.displayName = 'FlatList';

export default FlatList;
5 changes: 4 additions & 1 deletion packages/react-native-web/src/exports/ScrollView/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import dismissKeyboard from '../../modules/dismissKeyboard';
import invariant from 'fbjs/lib/invariant';
import mergeRefs from '../../modules/mergeRefs';
import ScrollResponder from '../../modules/ScrollResponder';
import useDraggableScroll from '../../modules/useDraggableScroll';
import ScrollViewBase from './ScrollViewBase';
import StyleSheet from '../StyleSheet';
import View from '../View';
Expand Down Expand Up @@ -356,7 +357,9 @@ const ForwardedScrollView: React.AbstractComponent<
React.ElementConfig<typeof ScrollView>,
React.ElementRef<typeof ScrollView>
> = React.forwardRef((props, forwardedRef) => {
return <ScrollView {...props} forwardedRef={forwardedRef} />;
const { setRef } = useDraggableScroll(props, forwardedRef);

return <ScrollView forwardedRef={setRef} {...props} />;
});

ForwardedScrollView.displayName = 'ScrollView';
Expand Down
16 changes: 15 additions & 1 deletion packages/react-native-web/src/exports/VirtualizedList/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,19 @@
* @flow
*/

import VirtualizedList from '../../vendor/react-native/VirtualizedList';
import * as React from 'react';
import useDraggableScroll from '../../modules/useDraggableScroll';
import OriginVirtualizedList from '../../vendor/react-native/VirtualizedList';

const VirtualizedList: React.AbstractComponent<
React.ElementConfig<typeof OriginVirtualizedList>,
React.ElementRef<typeof OriginVirtualizedList>
> = React.forwardRef((props, forwardedRef) => {
const { setRef } = useDraggableScroll(props, forwardedRef);

return <OriginVirtualizedList ref={setRef} {...props} />;
});

VirtualizedList.displayName = 'VirtualizedList';

export default VirtualizedList;
87 changes: 87 additions & 0 deletions packages/react-native-web/src/modules/useDraggableScroll/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { useEffect, useRef } from 'react';

import findNodeHandle from '../../exports/findNodeHandle';
import useMergeRefs from '../useMergeRefs';

export default function useDraggableScroll(props = {}, forwardedRef = null) {
const disabled = !props.horizontal || props.WORKAROUND_disableDrag;

const hostRef = useRef(null);

useEffect(() => {
if (disabled) {
return;
}

const slider = findNodeHandle(hostRef.current);

if (!slider) {
return;
}

const state = {
isDragging: false,
shouldPreventClick: false,
startX: 0,
scrollLeft: 0
};

const handleMouseDown = (e) => {
state.isDragging = true;
state.shouldPreventClick = false;
state.startX = e.pageX - slider.offsetLeft;
state.scrollLeft = slider.scrollLeft;
};

const handleMouseMove = (e) => {
if (!state.isDragging) {
return;
}

e.preventDefault();

const x = e.pageX - slider.offsetLeft;
const dX = x - state.startX;

state.shouldPreventClick = Math.abs(dX) > 1;
slider.scrollLeft = state.scrollLeft - dX;
};

function preventClick(e) {
e.preventDefault();
e.stopPropagation();
slider.removeEventListener('click', preventClick, true);
}

const handleMouseUp = () => {
if (state.shouldPreventClick) {
slider.addEventListener('click', preventClick, true);
}

state.isDragging = false;
state.shouldPreventClick = false;
};

const handleMouseLeave = () => {
state.isDragging = false;
state.shouldPreventClick = false;
};

slider.addEventListener('mousedown', handleMouseDown);
slider.addEventListener('mouseup', handleMouseUp);
slider.addEventListener('mouseleave', handleMouseLeave);
slider.addEventListener('mousemove', handleMouseMove);

return () => {
slider.removeEventListener('mousedown', handleMouseDown);
slider.removeEventListener('mouseup', handleMouseUp);
slider.removeEventListener('mouseleave', handleMouseLeave);
slider.removeEventListener('mousemove', handleMouseMove);
slider.removeEventListener('click', preventClick, true);
};
}, [disabled]);

const setRef = useMergeRefs(hostRef, forwardedRef);

return { setRef };
}