Skip to content

Commit

Permalink
fixed #3
Browse files Browse the repository at this point in the history
  • Loading branch information
jjjkkkjjj committed Jun 19, 2024
1 parent 549ea2c commit fe41da8
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 28 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ This component is needed above the `DnDable`. `DnDArea` manages all the child `D

`*` is required.

| props | type | description |
| :----------- | :---------------- | :----------------------------------- |
| \*`children` | `React.ReactNode` | Any components including `<DnDable>` |
| props | type | description |
| :------------ | :---------------- | :------------------------------------------------------------------------------------------ |
| \*`keyValues` | `Array<string>` | The DnDable's unique keys. These are managed inside the DnDArea to avoid too much rendering |
| \*`children` | `React.ReactNode` | Any components including `<DnDable>` |

Note: This component manages the global state by recoil package. DON'T use the below atom's key in your app.

Expand Down Expand Up @@ -81,7 +82,6 @@ When you enclose `DnDable` with the components, you can drag and drop them!

Note: The `styleParams` is managed inside the `DnDable`. That's why you can pass the functional component like this;


```typescript
<DnDable
keyValue={'1'}
Expand Down
9 changes: 7 additions & 2 deletions example/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ export default function App() {
return (
<View style={{ marginTop: 20 }}>
<View style={styles.container}>
<DnDArea>
<DnDArea
keyValues={Object.keys(parentItems).flatMap((keyValue) => [
keyValue,
`child-${keyValue}`,
])}
>
{Object.keys(parentItems).map((keyValue, index) => {
const parentItem = parentItems[keyValue];
const childItem = parentItem.child;
Expand Down Expand Up @@ -199,7 +204,7 @@ const Test = (_props: any) => {
return (
<View style={{ marginTop: 20 }}>
<View style={styles.container}>
<DnDArea>
<DnDArea keyValues={['1', 'child1', '2', 'child2']}>
<DnDable
keyValue={'1'}
x={0}
Expand Down
20 changes: 17 additions & 3 deletions src/DnDArea/container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import React from 'react';
import { RecoilRoot } from 'recoil';

import { DnDArea, DnDAreaContainerProps } from './presenter';
import { useDnDAreaAdmin, useDnDArea, useDragAndDrop } from './hooks';
import {
useDnDAreaAdmin,
useRegisterInformation,
useDnDRealtimeInformation,
useDragAndDrop,
} from './hooks';

const DnDAreaContainer = (props: DnDAreaContainerProps) => {
useDnDAreaAdmin();
Expand All @@ -14,10 +19,19 @@ const DnDAreaContainer = (props: DnDAreaContainerProps) => {
);
};

const Wrapper = (props) => {
useDnDArea();
const Wrapper = (props: DnDAreaContainerProps) => {
useDnDRealtimeInformation();
const { setRegisterInformation } = useRegisterInformation();
const { reloadLayout } = useDragAndDrop();

React.useEffect(() => {
const newValues = Object.fromEntries(
props.keyValues.map((value) => [value, false]),
);
setRegisterInformation((prev) => ({ ...prev, ...newValues }));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return <DnDArea {...props} onLayout={() => reloadLayout()} />;
};

Expand Down
137 changes: 130 additions & 7 deletions src/DnDArea/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,147 @@
import { useRecoilState, atom } from 'recoil';
import React from 'react';
import {
useRecoilState,
useSetRecoilState,
useRecoilValue,
atom,
} from 'recoil';

import {
DroppableInformationManager,
DraggableInformationManager,
DnDAreaManager,
} from '../types';

// Realtime atom
export const droppableInformationAtom = atom<DroppableInformationManager>({
key: 'DroppableInformationAtom',
default: {},
});
export const useDroppable = () => useRecoilState(droppableInformationAtom);
// Lazy atom
export const droppableInformationLazyAtom = atom<DroppableInformationManager>({
key: 'DroppableInformationLazyAtom',
default: {},
});
export const useDroppable = () => {
// Return the setter for realtime atom only to avoid too much rendering
const setDroppableInformation = useSetRecoilState(droppableInformationAtom);
return { setDroppableInformation };
};

// Realtime atom
export const draggableInformationAtom = atom<DraggableInformationManager>({
key: 'DraggableInformationAtom',
default: {},
});
export const useDraggable = () => useRecoilState(draggableInformationAtom);
// Lazy atom
export const draggableInformationLazyAtom = atom<DraggableInformationManager>({
key: 'DraggableInformationLazyAtom',
default: {},
});
export const useDraggable = () => {
// Return the setter for realtime atom only to avoid too much rendering
const setDraggableInformation = useSetRecoilState(draggableInformationAtom);
return { setDraggableInformation };
};

export const dnDareaAtom = atom<DnDAreaManager>({
key: 'DnDAreaAtom',
export const registerInformationAtom = atom<DnDAreaManager>({
key: 'RegisterInformationAtom',
default: {},
});
export const useDnDArea = () => useRecoilState(dnDareaAtom);
// The switch to update the lazy atoms
export const updateLazyStatesSwitchAtom = atom<boolean>({
key: 'LazyStatesUpdatingSwitchAtom',
default: false,
});
export const useRegisterInformation = () => {
const setRegisterInformation = useSetRecoilState(registerInformationAtom);
return { setRegisterInformation };
};

// Synchronize the realtime atom and the lazy atom here
export const useDnDRealtimeInformation = () => {
const [allRegisteredOnce, setAllRegisteredOnce] = React.useState(false);
// Lazy States to avoid too much rendering
const setDraggableInformation = useSetRecoilState(
draggableInformationLazyAtom,
);
const setDroppableInformation = useSetRecoilState(
droppableInformationLazyAtom,
);
// RealTime States
const currentDraggableInformation = useRecoilValue(draggableInformationAtom);
const currentDroppableInformation = useRecoilValue(droppableInformationAtom);
const [registerInformation, setRegisterInformation] = useRecoilState(
registerInformationAtom,
);
const updateLazyStatesSwitch = useRecoilValue(updateLazyStatesSwitchAtom);
const { reloadLayoutSwitch } = useReloadLayout();
const allRegistered = React.useMemo(
() =>
Object.keys(registerInformation).every(
(keyValue) => registerInformation[keyValue],
),
[registerInformation],
);
React.useEffect(() => {
if (allRegistered) {
setDraggableInformation({ ...currentDraggableInformation });
setDroppableInformation({ ...currentDroppableInformation });
const newValue = Object.fromEntries(
Object.keys(registerInformation).map((keyValue) => [keyValue, false]),
);
// console.log('called', currentDroppableInformation);
setRegisterInformation(newValue);
setAllRegisteredOnce(true);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [allRegistered, currentDraggableInformation, currentDroppableInformation]);

// Update the lazy states into the latest ones
React.useEffect(() => {
if (allRegisteredOnce) {
setDraggableInformation({ ...currentDraggableInformation });
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [allRegisteredOnce, updateLazyStatesSwitch, currentDraggableInformation]);

// Reset when reloaded
React.useEffect(() => {
const newValue = Object.fromEntries(
Object.keys(registerInformation).map((keyValue) => [keyValue, false]),
);
// console.log('called', currentDroppableInformation);
setRegisterInformation(newValue);
setAllRegisteredOnce(false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [reloadLayoutSwitch]);

return null;
};

// To handle the lazy atoms only to avoid too much rendering due to the realtime atoms
export const useDnDLazyInformation = () => {
const draggableInformation = useRecoilValue(draggableInformationLazyAtom);
const droppableInformation = useRecoilValue(droppableInformationLazyAtom);
const setRegisterInformation = useSetRecoilState(registerInformationAtom);
const setUpdateLazyStatesSwitch = useSetRecoilState(
updateLazyStatesSwitchAtom,
);

/**
* Update the lazy states into the latest ones
*/
const updateLazyStates = () => {
setUpdateLazyStatesSwitch((prev) => !prev);
};

return {
draggableInformation,
droppableInformation,
setRegisterInformation,
updateLazyStates,
};
};

export const reloadLayoutAtom = atom<boolean>({
key: 'ReloadLayoutAtom',
Expand All @@ -37,10 +156,14 @@ export const useReloadLayout = () => {
return { reloadLayoutSwitch, reloadLayout };
};

/**
* The hooks for user
* @returns reloadLayout: The function that reloads layout information manually
*/
export const useDragAndDrop = () => {
const [, setReloadLayoutSwitch] = useRecoilState(reloadLayoutAtom);
/**
* Reload layout information.
* Reload layout information manually.
* This function is useful when the DnDArea is responsible size such like drawer
*/
const reloadLayout = () => {
Expand Down
2 changes: 2 additions & 0 deletions src/DnDArea/presenter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export const DnDArea = (props: DnDAreaProps) => {
};

export interface DnDAreaContainerProps {
/** The DnDable's unique keys. These are managed inside the DnDArea to avoid too much rendering */
keyValues: Array<string>;
/** Child components */
children?: React.ReactNode;
}
Expand Down
37 changes: 26 additions & 11 deletions src/DnDable/container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import useDeepCompareEffect from 'use-deep-compare-effect';

import { DnDable, DnDableContainerProps } from './presenter';
import { useDnDable } from './hooks';
import { useDroppable, useDraggable, useReloadLayout } from '../DnDArea/hooks';
import {
useDroppable,
useDraggable,
useDnDLazyInformation,
useReloadLayout,
} from '../DnDArea/hooks';
import { DnDEventHandlers, DroppableInformation } from '../types';
import { checkDroppable, checkDragStartable } from '../utils';

Expand All @@ -26,8 +31,14 @@ const DnDableContainer = <T, U extends object>(
dragRelativeOffset,
setDragRelativeOffset,
} = useDnDable(props.styleParams);
const [droppableInformation, setDroppableInformation] = useDroppable();
const [draggableInformation, setDraggableInformation] = useDraggable();
const { setDroppableInformation } = useDroppable();
const { setDraggableInformation } = useDraggable();
const {
droppableInformation,
draggableInformation,
setRegisterInformation,
updateLazyStates,
} = useDnDLazyInformation();
const { reloadLayoutSwitch } = useReloadLayout();
const startShouldSetHandler = React.useCallback(
(e: GestureResponderEvent, _gesture: PanResponderGestureState): boolean => {
Expand All @@ -40,13 +51,11 @@ const DnDableContainer = <T, U extends object>(
// if droppable component exists, then it is allowed to drag
// if it is parent
// if droppable component doesn't exist, then it is allowed to drag
return (
checkDragStartable(
props.keyValue,
props.parentkeyValue,
{ x, y },
droppableInformation,
)
return checkDragStartable(
props.keyValue,
props.parentkeyValue,
{ x, y },
droppableInformation,
);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down Expand Up @@ -79,6 +88,7 @@ const DnDableContainer = <T, U extends object>(
[props.keyValue]: { ...draggedInformation, dragging: true },
};
});
updateLazyStates();
// Event Handling
props.eventHandlers?.onDragStart?.();
// Event Handling for Style Parameters
Expand Down Expand Up @@ -123,6 +133,7 @@ const DnDableContainer = <T, U extends object>(
[props.keyValue]: { ...draggedInformation, dragging: false },
};
});
updateLazyStates();
// Event Handling
props.eventHandlers?.onDragEnd?.();
// Event Handling for Style Parameters
Expand Down Expand Up @@ -358,6 +369,7 @@ const DnDableContainer = <T, U extends object>(
},
};
});
setRegisterInformation((prev) => ({ ...prev, [props.keyValue]: true }));
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down Expand Up @@ -388,9 +400,12 @@ const DnDableContainer = <T, U extends object>(
},
};
});
setRegisterInformation((prev) => ({ ...prev, [props.keyValue]: true }));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.item]);

// if (props.keyValue === 'player-0') {
// console.log('rendered');
// }
// console.log('dragging', props.keyValue, dragging);
// console.log('rendered', props.keyValue, droppableInformation);
// console.log('offset', dragRelativeOffset, offset);
Expand Down
4 changes: 3 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ export interface DraggableInformationManager {
[key: string]: DraggableInformation;
}

export interface DnDAreaManager {}
export interface DnDAreaManager {
[key: string]: boolean;
}

//=========== For user ===========//
export interface DnDItemType<T> {
Expand Down

0 comments on commit fe41da8

Please sign in to comment.