Skip to content

Commit

Permalink
Merge pull request #31267 from VickyStash/ts-migration/dragAndDrop-co…
Browse files Browse the repository at this point in the history
…mponent

[TS migration] Migrate 'DragAndDrop' component to TypeScript
  • Loading branch information
arosiclair authored Nov 14, 2023
2 parents 62e437b + 0a5f897 commit cc5a272
Show file tree
Hide file tree
Showing 14 changed files with 88 additions and 74 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import dragAndDropConsumerPropTypes from './dragAndDropConsumerPropTypes';

function DragAndDropConsumer() {
return null;
}

DragAndDropConsumer.propTypes = dragAndDropConsumerPropTypes;
DragAndDropConsumer.displayName = 'DragAndDropConsumer';

export default DragAndDropConsumer;
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import {Portal} from '@gorhom/portal';
import React, {useContext, useEffect} from 'react';
import {DragAndDropContext} from '@components/DragAndDrop/Provider';
import dragAndDropConsumerPropTypes from './dragAndDropConsumerPropTypes';
import type DragAndDropConsumerProps from './types';

function DragAndDropConsumer({children, onDrop}) {
function DragAndDropConsumer({children, onDrop}: DragAndDropConsumerProps) {
const {isDraggingOver, setOnDropHandler, dropZoneID} = useContext(DragAndDropContext);

useEffect(() => {
setOnDropHandler(onDrop);
setOnDropHandler?.(onDrop);
}, [onDrop, setOnDropHandler]);

if (!isDraggingOver) {
Expand All @@ -17,7 +17,6 @@ function DragAndDropConsumer({children, onDrop}) {
return <Portal hostName={dropZoneID}>{children}</Portal>;
}

DragAndDropConsumer.propTypes = dragAndDropConsumerPropTypes;
DragAndDropConsumer.displayName = 'DragAndDropConsumer';

export default DragAndDropConsumer;
11 changes: 11 additions & 0 deletions src/components/DragAndDrop/Consumer/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {ReactNode} from 'react';

type DragAndDropConsumerProps = {
/** Children to render inside this component. */
children: ReactNode;

/** Function to execute when an item is dropped in the drop zone. */
onDrop: (event: DragEvent) => void;
};

export default DragAndDropConsumerProps;
5 changes: 0 additions & 5 deletions src/components/DragAndDrop/NoDropZone/index.native.js

This file was deleted.

7 changes: 7 additions & 0 deletions src/components/DragAndDrop/NoDropZone/index.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type NoDropZoneProps from './types';

const NoDropZone = ({children}: NoDropZoneProps) => children;

NoDropZone.displayName = 'NoDropZone';

export default NoDropZone;
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
import PropTypes from 'prop-types';
import React, {useRef} from 'react';
import {View} from 'react-native';
import useDragAndDrop from '@hooks/useDragAndDrop';
import styles from '@styles/styles';
import type NoDropZoneProps from './types';

const propTypes = {
/** Content */
children: PropTypes.node.isRequired,
};
function NoDropZone({children}: NoDropZoneProps) {
const noDropZone = useRef<View>(null);

function NoDropZone({children}) {
const noDropZone = useRef(null);
useDragAndDrop({
dropZone: noDropZone,
shouldAllowDrop: false,
});

return (
<View
ref={(e) => (noDropZone.current = e)}
ref={noDropZone}
style={[styles.fullScreen]}
>
{children}
Expand All @@ -26,6 +23,5 @@ function NoDropZone({children}) {
}

NoDropZone.displayName = 'NoDropZone';
NoDropZone.propTypes = propTypes;

export default NoDropZone;
8 changes: 8 additions & 0 deletions src/components/DragAndDrop/NoDropZone/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {ReactNode} from 'react';

type NoDropZoneProps = {
/** Content */
children: ReactNode;
};

export default NoDropZoneProps;

This file was deleted.

13 changes: 0 additions & 13 deletions src/components/DragAndDrop/Provider/index.native.js

This file was deleted.

12 changes: 12 additions & 0 deletions src/components/DragAndDrop/Provider/index.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type {DragAndDropContextParams, DragAndDropProviderProps} from './types';

const DragAndDropContext: DragAndDropContextParams = {};

function DragAndDropProvider({children}: DragAndDropProviderProps) {
return children;
}

DragAndDropProvider.displayName = 'DragAndDropProvider';

export default DragAndDropProvider;
export {DragAndDropContext};
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,22 @@ import {PortalHost} from '@gorhom/portal';
import Str from 'expensify-common/lib/str';
import React, {useCallback, useEffect, useMemo, useRef} from 'react';
import {View} from 'react-native';
import _ from 'underscore';
import useDragAndDrop from '@hooks/useDragAndDrop';
import styles from '@styles/styles';
import dragAndDropProviderPropTypes from './dragAndDropProviderPropTypes';
import type {DragAndDropContextParams, DragAndDropProviderProps, SetOnDropHandlerCallback} from './types';

const DragAndDropContext = React.createContext({});
const DragAndDropContext = React.createContext<DragAndDropContextParams>({});

/**
* @param {Event} event – drag event
* @returns {Boolean}
*/
function shouldAcceptDrop(event) {
return _.some(event.dataTransfer.types, (type) => type === 'Files');
function shouldAcceptDrop(event: DragEvent): boolean {
return !!event.dataTransfer?.types.some((type) => type === 'Files');
}

function DragAndDropProvider({children, isDisabled = false, setIsDraggingOver = () => {}}) {
const dropZone = useRef(null);
function DragAndDropProvider({children, isDisabled = false, setIsDraggingOver = () => {}}: DragAndDropProviderProps) {
const dropZone = useRef<View>(null);
const dropZoneID = useRef(Str.guid('drag-n-drop'));

const onDropHandler = useRef(() => {});
const setOnDropHandler = useCallback((callback) => {
const onDropHandler = useRef<SetOnDropHandlerCallback>(() => {});
const setOnDropHandler = useCallback((callback: SetOnDropHandlerCallback) => {
onDropHandler.current = callback;
}, []);

Expand All @@ -38,10 +33,11 @@ function DragAndDropProvider({children, isDisabled = false, setIsDraggingOver =
}, [isDraggingOver, setIsDraggingOver]);

const contextValue = useMemo(() => ({isDraggingOver, setOnDropHandler, dropZoneID: dropZoneID.current}), [isDraggingOver, setOnDropHandler]);

return (
<DragAndDropContext.Provider value={contextValue}>
<View
ref={(e) => (dropZone.current = e)}
ref={dropZone}
style={[styles.flex1, styles.w100, styles.h100]}
>
{isDraggingOver && (
Expand All @@ -55,7 +51,6 @@ function DragAndDropProvider({children, isDisabled = false, setIsDraggingOver =
);
}

DragAndDropProvider.propTypes = dragAndDropProviderPropTypes;
DragAndDropProvider.displayName = 'DragAndDropProvider';

export default DragAndDropProvider;
Expand Down
27 changes: 27 additions & 0 deletions src/components/DragAndDrop/Provider/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {ReactNode} from 'react';

type DragAndDropProviderProps = {
/** Children to render inside this component. */
children: ReactNode;

/** Should this dropZone be disabled? */
isDisabled?: boolean;

/** Indicate that users are dragging file or not */
setIsDraggingOver: (value: boolean) => void;
};

type SetOnDropHandlerCallback = (event: DragEvent) => void;

type DragAndDropContextParams = {
/** Whether something is dragging over a drop zone. */
isDraggingOver?: boolean;

/** Execute callback when an item is dropped in the drop zone. */
setOnDropHandler?: (callback: SetOnDropHandlerCallback) => void;

/** Drop zone id. */
dropZoneID?: string;
};

export type {DragAndDropProviderProps, DragAndDropContextParams, SetOnDropHandlerCallback};
9 changes: 5 additions & 4 deletions src/hooks/useDragAndDrop.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {useIsFocused} from '@react-navigation/native';
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {View} from 'react-native';

const COPY_DROP_EFFECT = 'copy';
const NONE_DROP_EFFECT = 'none';
Expand All @@ -9,11 +10,11 @@ const DRAG_LEAVE_EVENT = 'dragleave';
const DROP_EVENT = 'drop';

type DragAndDropParams = {
dropZone: React.MutableRefObject<HTMLDivElement | null>;
onDrop?: (event?: DragEvent) => void;
dropZone: React.MutableRefObject<HTMLDivElement | View | null>;
onDrop?: (event: DragEvent) => void;
shouldAllowDrop?: boolean;
isDisabled?: boolean;
shouldAcceptDrop?: (event?: DragEvent) => boolean;
shouldAcceptDrop?: (event: DragEvent) => boolean;
};

type DragAndDropOptions = {
Expand Down Expand Up @@ -105,7 +106,7 @@ export default function useDragAndDrop({dropZone, onDrop = () => {}, shouldAllow
return;
}

const dropZoneRef = dropZone.current;
const dropZoneRef = dropZone.current as HTMLDivElement;

// Note that the dragover event needs to be called with `event.preventDefault` in order for the drop event to be fired:
// https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome
Expand Down

0 comments on commit cc5a272

Please sign in to comment.