Skip to content

Commit

Permalink
feat(grid): add grid-related feature components
Browse files Browse the repository at this point in the history
  • Loading branch information
mgcrea committed Dec 4, 2023
1 parent d8cc5eb commit 3736d22
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 6 deletions.
13 changes: 13 additions & 0 deletions src/features/grid/DraggableGridContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { createContext, useContext } from "react";

export type DraggableGridContextValue = {
gridMode: number;
gridWidth: number;
gridHeight: number;
};

export const DraggableGridContext = createContext<DraggableGridContextValue>(null!);

export const useDraggableGridContext = () => {
return useContext(DraggableGridContext);
};
48 changes: 48 additions & 0 deletions src/features/grid/DraggableGridProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { FunctionComponent, PropsWithChildren, useEffect, useState } from "react";
import { StyleSheet, View, type StyleProp, type ViewStyle } from "react-native";
import { DraggableGridContext, type DraggableGridContextValue } from "./DraggableGridContext";
import { DraggableGrid } from "./components/DraggableGrid";

type DraggableGridProviderProps = {
mode: number;
width: number;
height: number;
containerStyle?: StyleProp<ViewStyle>;
style?: StyleProp<ViewStyle>;
};

export const DraggableGridProvider: FunctionComponent<PropsWithChildren<DraggableGridProviderProps>> = ({
children,
width,
height,
mode,
containerStyle,
style,
}) => {
const [value, setValue] = useState<DraggableGridContextValue>({
gridWidth: width,
gridHeight: height,
gridMode: mode,
});
useEffect(() => {
setValue((value) => ({ ...value, gridMode: mode }));
}, [mode]);
return (
<DraggableGridContext.Provider value={value}>
<View style={[styles.container, containerStyle]}>
<DraggableGrid style={style}>{children}</DraggableGrid>
</View>
</DraggableGridContext.Provider>
);
};

const styles = StyleSheet.create({
container: {
flexGrow: 1,
flexShrink: 1,
flexBasis: "auto",
justifyContent: "center",
aspectRatio: 1,
width: "100%",
},
});
25 changes: 25 additions & 0 deletions src/features/grid/components/DraggableGrid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { useMemo, type FunctionComponent, type PropsWithChildren } from "react";
import { StyleSheet, View, type ViewProps } from "react-native";
import { getGridModeStyle } from "src/hooks";
import { useDraggableGridContext } from "../DraggableGridContext";

export type DraggableGridProps = Pick<ViewProps, "style">;

export const DraggableGrid: FunctionComponent<PropsWithChildren<DraggableGridProps>> = ({
children,
style,
}) => {
const { gridMode } = useDraggableGridContext();
const gridStyle = useMemo(() => getGridModeStyle(gridMode), [gridMode]);
return <View style={[styles.container, gridStyle, style]}>{children}</View>;
};

const styles = StyleSheet.create({
container: {
flexGrow: 1,
flexShrink: 1,
flexBasis: "auto",
flexDirection: "column",
flexWrap: "wrap",
},
});
93 changes: 93 additions & 0 deletions src/features/grid/components/DraggableGridItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React, { FunctionComponent, PropsWithChildren } from "react";
import { Pressable, PressableProps, StyleSheet } from "react-native";
import Animated, {
Layout,
WithSpringConfig,
ZoomIn,
useAnimatedStyle,
useSharedValue,
withSpring,
} from "react-native-reanimated";
import { useActiveDragReaction, useDraggable } from "src/hooks";
import type { Data, UniqueIdentifier } from "src/types";
import { getRandomInt } from "src/utils";
import { useDraggableGridContext } from "../DraggableGridContext";

const AnimatedPressable = Animated.createAnimatedComponent(Pressable);

export type DraggableGridItemProps = Omit<PressableProps, "id"> & {
id: UniqueIdentifier;
value: string | number;
data?: Data;
springConfig?: WithSpringConfig;
};

export const DraggableGridItem: FunctionComponent<PropsWithChildren<DraggableGridItemProps>> = ({
children,
id,
style,
data,
...otherPressableProps
}) => {
const { setNodeRef, setNodeLayout, activeId, offset } = useDraggable({
id,
data,
});
const { gridWidth, gridHeight } = useDraggableGridContext();

const rotateZ = useSharedValue(0);
const pressCount = useSharedValue(1);
useActiveDragReaction(id, (isActive) => {
"worklet";
// pressCount.value++;
rotateZ.value = withSpring(isActive ? getRandomInt(-15 * pressCount.value, 15 * pressCount.value) : 0);
});

const animatedStyle = useAnimatedStyle(() => {
const isActive = activeId.value === id;
return {
opacity: isActive ? 0.9 : 1,
zIndex: isActive ? 999 : 1,
transform: [
{
translateX: isActive ? offset.x.value : withSpring(offset.x.value),
},
{
translateY: isActive ? offset.y.value : withSpring(offset.y.value),
},
{
rotateZ: `${rotateZ.value}deg`,
},
],
};
}, [id]);

return (
<AnimatedPressable
ref={setNodeRef}
onLayout={setNodeLayout}
entering={ZoomIn}
layout={Layout.springify()}
style={[
styles.item,
{
width: `${Math.floor(100 / gridWidth)}%`,
height: `${Math.floor(100 / gridHeight)}%`,
},
animatedStyle,
style,
]}
{...otherPressableProps}
>
{children}
</AnimatedPressable>
);
};

const styles = StyleSheet.create({
item: {
alignItems: "center",
justifyContent: "center",
flexWrap: "wrap",
},
});
2 changes: 2 additions & 0 deletions src/features/grid/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./DraggableGrid";
export * from "./DraggableGridItem";
1 change: 1 addition & 0 deletions src/features/grid/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./useDraggableGrid";
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { RefCallback, useMemo, useRef } from "react";
import { LayoutRectangle, ViewStyle } from "react-native";
import { runOnJS, useAnimatedReaction, useSharedValue } from "react-native-reanimated";
import { DndProviderHandle, DndProviderProps } from "../DndProvider";
import { UniqueIdentifier } from "../types";
import { applyOffset, centerPoint, includesPoint, moveArrayIndex } from "../utils";
import { useLatestSharedValue } from "./useLatestSharedValue";
import { SharedPoint } from "./useSharedPoint";
import { DndProviderHandle, DndProviderProps } from "src/DndProvider";
import { useLatestSharedValue } from "src/hooks/useLatestSharedValue";
import { SharedPoint } from "src/hooks/useSharedPoint";
import { UniqueIdentifier } from "src/types";
import { applyOffset, centerPoint, includesPoint, moveArrayIndex } from "src/utils";

export type GridItem = { id: UniqueIdentifier; [s: string]: unknown };

Expand Down
4 changes: 4 additions & 0 deletions src/features/grid/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./DraggableGridContext";
export * from "./DraggableGridProvider";
export * from "./components";
export * from "./hooks";
2 changes: 1 addition & 1 deletion src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export * from "../features/grid/hooks/useDraggableGrid";
export * from "./useActiveDragReaction";
export * from "./useActiveDropReaction";
export * from "./useDraggable";
export * from "./useDraggableActiveId";
export * from "./useDraggableGrid";
export * from "./useDraggableStyle";
export * from "./useDroppable";
export * from "./useDroppableStyle";
Expand Down

0 comments on commit 3736d22

Please sign in to comment.