continue flow integartion
This commit is contained in:
@@ -1,137 +0,0 @@
|
||||
import { View, Text, ScrollView, StyleSheet, Dimensions } from "react-native";
|
||||
import React, { useState } from "react";
|
||||
import Animated, {
|
||||
runOnJS,
|
||||
useAnimatedGestureHandler,
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
withSpring,
|
||||
} from "react-native-reanimated";
|
||||
import { PanGestureHandler } from "react-native-gesture-handler";
|
||||
|
||||
const { width } = Dimensions.get("window");
|
||||
|
||||
const DragDropTest = () => {
|
||||
const [leftItems, setLeftItems] = useState([
|
||||
"fortnite",
|
||||
"apex",
|
||||
"callofduty",
|
||||
]);
|
||||
const [rightItems, setRightItems] = useState([]);
|
||||
|
||||
const draggingItem = useSharedValue(null);
|
||||
const translateX = useSharedValue(0);
|
||||
const translateY = useSharedValue(0);
|
||||
|
||||
const moveItem = (item) => {
|
||||
setLeftItems((items) => items.filter((i) => i !== item));
|
||||
setRightItems((items) => [...items, item]);
|
||||
};
|
||||
|
||||
const gestureHandler = useAnimatedGestureHandler({
|
||||
onStart: (_, ctx) => {
|
||||
ctx.startX = translateX.value;
|
||||
ctx.startY = translateY.value;
|
||||
},
|
||||
onActive: (event, ctx) => {
|
||||
translateX.value = ctx.startX + event.translationX;
|
||||
translateY.value = ctx.startY + event.translationY;
|
||||
},
|
||||
onEnd: () => {
|
||||
if (translateX.value > width / 2) {
|
||||
runOnJS(moveItem)(draggingItem.value);
|
||||
}
|
||||
translateX.value = withSpring(0);
|
||||
translateY.value = withSpring(0);
|
||||
draggingItem.value = null;
|
||||
},
|
||||
});
|
||||
|
||||
const renderDraggable = (item) => {
|
||||
const style = useAnimatedStyle(() => ({
|
||||
transform: [
|
||||
{ translateX: draggingItem.value === item ? translateX.value : 0 },
|
||||
{ translateY: draggingItem.value === item ? translateY.value : 0 },
|
||||
],
|
||||
zIndex: draggingItem.value === item ? 10 : 0,
|
||||
}));
|
||||
|
||||
return (
|
||||
<PanGestureHandler
|
||||
key={item}
|
||||
onGestureEvent={gestureHandler}
|
||||
onHandlerStateChange={() => {
|
||||
draggingItem.value = item;
|
||||
}}
|
||||
>
|
||||
<Animated.View style={[styles.item, style]}>
|
||||
<Text style={styles.text}>{item}</Text>
|
||||
</Animated.View>
|
||||
</PanGestureHandler>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.scrollWrapper}>
|
||||
<ScrollView
|
||||
style={{
|
||||
...styles.scroll,
|
||||
zIndex: 2,
|
||||
}}
|
||||
>
|
||||
<Text style={styles.title}>Left</Text>
|
||||
{leftItems.map(renderDraggable)}
|
||||
</ScrollView>
|
||||
|
||||
<ScrollView
|
||||
style={{
|
||||
...styles.scroll,
|
||||
zIndex: -1,
|
||||
}}
|
||||
>
|
||||
<Text style={styles.title}>Right</Text>
|
||||
{rightItems.map((item) => (
|
||||
<View key={item} style={[styles.item, { backgroundColor: "#bdf" }]}>
|
||||
<Text style={styles.text}>{item}</Text>
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default DragDropTest;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
paddingTop: 50,
|
||||
},
|
||||
scrollWrapper: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-around",
|
||||
},
|
||||
scroll: {
|
||||
width: width / 2.2,
|
||||
height: "90%",
|
||||
backgroundColor: "#f0f0f0",
|
||||
borderRadius: 10,
|
||||
margin: 5,
|
||||
padding: 10,
|
||||
},
|
||||
title: {
|
||||
fontWeight: "bold",
|
||||
fontSize: 16,
|
||||
marginBottom: 10,
|
||||
},
|
||||
item: {
|
||||
backgroundColor: "#aaf",
|
||||
padding: 15,
|
||||
marginVertical: 5,
|
||||
borderRadius: 10,
|
||||
},
|
||||
text: {
|
||||
color: "#333",
|
||||
textAlign: "center",
|
||||
},
|
||||
});
|
||||
@@ -12,9 +12,10 @@ const GradientButton = ({
|
||||
props,
|
||||
containerStyle = {},
|
||||
icon,
|
||||
disabled = false,
|
||||
}) => {
|
||||
return (
|
||||
<Pressable onPress={onPress} style={{ ...containerStyle }}>
|
||||
<Pressable onPress={onPress} disabled={disabled} style={{ ...containerStyle, opacity: disabled ? 0.6 : 1 }}>
|
||||
<LinearGradient
|
||||
colors={colors}
|
||||
style={{
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
import { View, Text, ScrollView, StyleSheet, Dimensions } from "react-native";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import Animated, {
|
||||
runOnJS,
|
||||
useAnimatedGestureHandler,
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
withSpring,
|
||||
} from "react-native-reanimated";
|
||||
import { PanGestureHandler } from "react-native-gesture-handler";
|
||||
|
||||
const { width } = Dimensions.get("window");
|
||||
|
||||
// Child component to respect Rules of Hooks (no hooks in loops)
|
||||
const DraggableItem = ({ item, gestureHandler, draggingItem, translateX, translateY, onActivate }) => {
|
||||
const style = useAnimatedStyle(() => ({
|
||||
transform: [
|
||||
{ translateX: draggingItem.value === item.id ? translateX.value : 0 },
|
||||
{ translateY: draggingItem.value === item.id ? translateY.value : 0 },
|
||||
],
|
||||
zIndex: draggingItem.value === item.id ? 10 : 0,
|
||||
}));
|
||||
|
||||
return (
|
||||
<PanGestureHandler
|
||||
onGestureEvent={gestureHandler}
|
||||
onHandlerStateChange={() => onActivate(item.id)}
|
||||
>
|
||||
<Animated.View style={[styles.item, style]}>
|
||||
<Text style={styles.text}>{item.label ?? String(item)}</Text>
|
||||
</Animated.View>
|
||||
</PanGestureHandler>
|
||||
);
|
||||
};
|
||||
|
||||
// Props:
|
||||
// - sourceItems: array of { id, label, value } or strings
|
||||
// - initialSelected: array of ids to prefill right side (optional)
|
||||
// - onChange: callback with array of values (or strings) in right order
|
||||
const SongStructureDragDrop = ({ sourceItems, initialSelected, onChange }) => {
|
||||
const normalize = (arr = []) =>
|
||||
arr.map((item, idx) =>
|
||||
typeof item === "string"
|
||||
? { id: `${item}-${idx}`, label: item, value: item }
|
||||
: item,
|
||||
);
|
||||
|
||||
const normalizedSource = useMemo(
|
||||
() => normalize(sourceItems || []),
|
||||
[sourceItems],
|
||||
);
|
||||
|
||||
const [leftItems, setLeftItems] = useState(normalizedSource);
|
||||
const [rightItems, setRightItems] = useState([]);
|
||||
|
||||
// Compare arrays by item id and order to avoid unnecessary state churn
|
||||
const sameById = (a = [], b = []) => {
|
||||
if (a === b) return true;
|
||||
if (!a || !b) return false;
|
||||
if (a.length !== b.length) return false;
|
||||
for (let idx = 0; idx < a.length; idx++) {
|
||||
if (a[idx]?.id !== b[idx]?.id) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const selected = Array.isArray(initialSelected) ? initialSelected : [];
|
||||
const selectedSet = new Set(selected);
|
||||
const right = normalizedSource.filter((i) => selectedSet.has(i.id));
|
||||
const left = normalizedSource.filter((i) => !selectedSet.has(i.id));
|
||||
|
||||
setLeftItems((prev) => (sameById(prev, left) ? prev : left));
|
||||
setRightItems((prev) => (sameById(prev, right) ? prev : right));
|
||||
}, [normalizedSource, initialSelected]);
|
||||
|
||||
const draggingItem = useSharedValue(null);
|
||||
const translateX = useSharedValue(0);
|
||||
const translateY = useSharedValue(0);
|
||||
|
||||
const onActivate = (id) => {
|
||||
draggingItem.value = id;
|
||||
};
|
||||
|
||||
const moveItem = (itemId) => {
|
||||
setLeftItems((items) => {
|
||||
const found = items.find((i) => i.id === itemId);
|
||||
if (!found) return items;
|
||||
setRightItems((r) => {
|
||||
const next = [...r, found];
|
||||
onChange?.(next.map((i) => i.value ?? i.label));
|
||||
return next;
|
||||
});
|
||||
return items.filter((i) => i.id !== itemId);
|
||||
});
|
||||
};
|
||||
|
||||
const gestureHandler = useAnimatedGestureHandler({
|
||||
onStart: (_, ctx) => {
|
||||
ctx.startX = translateX.value;
|
||||
ctx.startY = translateY.value;
|
||||
},
|
||||
onActive: (event, ctx) => {
|
||||
translateX.value = ctx.startX + event.translationX;
|
||||
translateY.value = ctx.startY + event.translationY;
|
||||
},
|
||||
onEnd: () => {
|
||||
if (translateX.value > width / 2) {
|
||||
runOnJS(moveItem)(draggingItem.value);
|
||||
}
|
||||
translateX.value = withSpring(0);
|
||||
translateY.value = withSpring(0);
|
||||
draggingItem.value = null;
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={styles.scrollWrapper}>
|
||||
<ScrollView style={{ ...styles.scroll, zIndex: 2 }}>
|
||||
<Text style={styles.title}>Left</Text>
|
||||
{leftItems.map((item) => (
|
||||
<DraggableItem
|
||||
key={item.id}
|
||||
item={item}
|
||||
gestureHandler={gestureHandler}
|
||||
draggingItem={draggingItem}
|
||||
translateX={translateX}
|
||||
translateY={translateY}
|
||||
onActivate={onActivate}
|
||||
/>
|
||||
))}
|
||||
</ScrollView>
|
||||
|
||||
<ScrollView style={{ ...styles.scroll, zIndex: -1 }}>
|
||||
<Text style={styles.title}>Right</Text>
|
||||
{rightItems.map((item) => (
|
||||
<View key={item.id} style={[styles.item, { backgroundColor: "#bdf" }]}>
|
||||
<Text style={styles.text}>{item.label ?? String(item)}</Text>
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default SongStructureDragDrop;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
scrollWrapper: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-around",
|
||||
},
|
||||
scroll: {
|
||||
width: width / 2.2,
|
||||
height: "90%",
|
||||
backgroundColor: "#f0f0f0",
|
||||
borderRadius: 10,
|
||||
margin: 5,
|
||||
padding: 10,
|
||||
},
|
||||
title: {
|
||||
fontWeight: "bold",
|
||||
fontSize: 16,
|
||||
marginBottom: 10,
|
||||
},
|
||||
item: {
|
||||
backgroundColor: "#aaf",
|
||||
padding: 15,
|
||||
marginVertical: 5,
|
||||
borderRadius: 10,
|
||||
},
|
||||
text: {
|
||||
color: "#333",
|
||||
textAlign: "center",
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user