feat: fix drag and drop

This commit is contained in:
2025-11-17 14:07:37 +01:00
parent 0dd9dbcfae
commit 97321cdbda
4 changed files with 320 additions and 68 deletions
+9 -10
View File
@@ -21,6 +21,7 @@ import { gutters, size } from "../../styles/Style";
import { FONT_FAMILY } from "../../styles/Fonts"; import { FONT_FAMILY } from "../../styles/Fonts";
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer"; import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader"; import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
import CreditAmount from "../../components/CreditAmount";
const MUSIC_GENERATION_COIN_COST = 8; const MUSIC_GENERATION_COIN_COST = 8;
const SONG_OPTIONS_PER_GENERATION = 2; const SONG_OPTIONS_PER_GENERATION = 2;
@@ -65,7 +66,7 @@ const SongReady = () => {
} catch (error) { } catch (error) {
console.log("SongReady pause error", error?.message); console.log("SongReady pause error", error?.message);
} }
}, }
); );
await Promise.all(tasks); await Promise.all(tasks);
}, []); }, []);
@@ -93,7 +94,7 @@ const SongReady = () => {
console.log("SongReady play error", error?.message); console.log("SongReady play error", error?.message);
} }
}, },
[pauseAllExcept], [pauseAllExcept]
); );
// Sync URLs from provider's selectedProject // Sync URLs from provider's selectedProject
@@ -110,8 +111,6 @@ const SongReady = () => {
}); });
}, [selectedProject?.musicUrls]); }, [selectedProject?.musicUrls]);
const validateSelection = async () => { const validateSelection = async () => {
try { try {
const url = musicUrls[selectedIndex]; const url = musicUrls[selectedIndex];
@@ -150,7 +149,7 @@ const SongReady = () => {
return () => { return () => {
pauseAllPlayers(); pauseAllPlayers();
}; };
}, [pauseAllPlayers]), }, [pauseAllPlayers])
); );
useEffect(() => { useEffect(() => {
@@ -177,7 +176,7 @@ const SongReady = () => {
}, },
}, },
], ],
{ cancelable: false }, { cancelable: false }
); );
return; return;
} }
@@ -208,7 +207,7 @@ const SongReady = () => {
onPress: () => handleConfirmRegenerate(), onPress: () => handleConfirmRegenerate(),
}, },
], ],
{ cancelable: true }, { cancelable: true }
); );
return; return;
} }
@@ -234,7 +233,7 @@ const SongReady = () => {
marginBottom: responsiveHeight(2), marginBottom: responsiveHeight(2),
}} }}
/> />
<View style={{ gap: 16 }}> <View style={{ gap: 16, marginTop: isWeb ? gutters * 3 : 0 }}>
{musicUrls.map((url, idx) => ( {musicUrls.map((url, idx) => (
<SongOptionCard <SongOptionCard
key={`${url || "song"}-${idx}`} key={`${url || "song"}-${idx}`}
@@ -341,11 +340,11 @@ const SongOptionCard = ({
const id = setInterval(() => { const id = setInterval(() => {
const durationMs = Math.max( const durationMs = Math.max(
0, 0,
Math.round((Number(player.duration) || 0) * 1000), Math.round((Number(player.duration) || 0) * 1000)
); );
const positionMs = Math.max( const positionMs = Math.max(
0, 0,
Math.round((Number(player.currentTime) || 0) * 1000), Math.round((Number(player.currentTime) || 0) * 1000)
); );
setProgressInfo((prev) => { setProgressInfo((prev) => {
if ( if (
+1 -4
View File
@@ -317,10 +317,7 @@ const CreateLyricsWithAi = () => {
}; };
const onPressBack = () => { const onPressBack = () => {
// If currently on SongStructure, go back to previous screen instead of previous step if (selectedIndex > 0) {
if (selectedIndex === 5) {
goBack();
} else if (selectedIndex > 0) {
setSelectedIndex((idx) => Math.max(0, idx - 1)); setSelectedIndex((idx) => Math.max(0, idx - 1));
} else { } else {
goBack(); goBack();
@@ -319,10 +319,7 @@ const CreateLyricsWithAi = () => {
}; };
const onPressBack = () => { const onPressBack = () => {
// If currently on SongStructure, go back to previous screen instead of previous step if (selectedIndex > 0) {
if (selectedIndex === 5) {
goBack();
} else if (selectedIndex > 0) {
setSelectedIndex((idx) => Math.max(0, idx - 1)); setSelectedIndex((idx) => Math.max(0, idx - 1));
} else { } else {
goBack(); goBack();
+309 -50
View File
@@ -65,6 +65,17 @@ const PALETTE_SEGMENT_OVERRIDES = [
}, },
]; ];
const getPaletteSegmentInfo = (type) => {
const meta = getSegmentMeta(type);
const override = PALETTE_SEGMENT_OVERRIDES.find(
(segment) => segment.type === type
);
return {
label: meta?.label || formatStructureLabel(type, 1),
description: override?.description || "",
};
};
const getScrollY = () => (typeof window !== "undefined" ? window.scrollY : 0); const getScrollY = () => (typeof window !== "undefined" ? window.scrollY : 0);
const randomSuffix = () => Math.random().toString(36).slice(2, 8); const randomSuffix = () => Math.random().toString(36).slice(2, 8);
@@ -163,6 +174,7 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
); );
const [activeItemId, setActiveItemId] = useState(null); const [activeItemId, setActiveItemId] = useState(null);
const [insertPreviewIndex, setInsertPreviewIndex] = useState(null); const [insertPreviewIndex, setInsertPreviewIndex] = useState(null);
const [dragOverlay, setDragOverlay] = useState(null);
const containerRef = useRef(null); const containerRef = useRef(null);
const sortableItemsRef = useRef(sortableItems); const sortableItemsRef = useRef(sortableItems);
const dragStateRef = useRef({ const dragStateRef = useRef({
@@ -175,8 +187,36 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
isOverContainer: false, isOverContainer: false,
startItems: null, startItems: null,
blockedReason: null, blockedReason: null,
startIndex: null,
slotIndex: null,
grabOffsetX: null,
grabOffsetY: null,
}); });
const globalListenersRef = useRef({ move: null, up: null }); const globalListenersRef = useRef({ move: null, up: null });
const clearDragOverlay = useCallback(() => {
setDragOverlay(null);
}, []);
const updateDragOverlayPosition = useCallback((clientX, clientY) => {
setDragOverlay((prev) => {
if (!prev) return prev;
const offsetX =
typeof dragStateRef.current?.grabOffsetX === "number"
? dragStateRef.current.grabOffsetX
: prev.width / 2;
const offsetY =
typeof dragStateRef.current?.grabOffsetY === "number"
? dragStateRef.current.grabOffsetY
: prev.height / 2;
const left =
typeof clientX === "number" ? clientX - offsetX : prev.left;
const top =
typeof clientY === "number" ? clientY - offsetY : prev.top;
if (left === prev.left && top === prev.top) {
return prev;
}
return { ...prev, left, top };
});
}, []);
const showTooltip = useCallback( const showTooltip = useCallback(
(text, type = "error") => { (text, type = "error") => {
if (!text) return; if (!text) return;
@@ -229,14 +269,11 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
const paletteSegments = useMemo( const paletteSegments = useMemo(
() => () =>
OPTIONAL_STRUCTURE_SEGMENTS.map((type) => { OPTIONAL_STRUCTURE_SEGMENTS.map((type) => {
const meta = getSegmentMeta(type); const info = getPaletteSegmentInfo(type);
const override = PALETTE_SEGMENT_OVERRIDES.find(
(segment) => segment.type === type
);
return { return {
type, type,
label: meta?.label || formatStructureLabel(type, 1), label: info.label,
description: override?.description || "", description: info.description,
}; };
}), }),
[] []
@@ -352,6 +389,10 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
isOverContainer: false, isOverContainer: false,
startItems: null, startItems: null,
blockedReason: null, blockedReason: null,
startIndex: null,
slotIndex: null,
grabOffsetX: null,
grabOffsetY: null,
}; };
}, []); }, []);
@@ -401,34 +442,77 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
: scrollY; : scrollY;
const pointerY = absoluteY - (dragState.containerTop || 0); const pointerY = absoluteY - (dragState.containerTop || 0);
updateDragOverlayPosition(event?.clientX, event?.clientY);
if (dragState.itemId) { if (dragState.itemId) {
dragStateRef.current.blockedReason = null; const rect = dragState.containerRect;
setSortableItems((prev) => { const listLength = sortableItemsRef.current.length;
const currentIndex = prev.findIndex( const isOver =
(it) => it.id === dragState.itemId rect == null
); ? true
if (currentIndex === -1) return prev; : event.clientX >= rect.left && event.clientX <= rect.right;
const maxIndex = Math.max(prev.length - 1, 0); dragStateRef.current.isOverContainer = isOver;
const targetIndex = clamp(
Math.floor(pointerY / ROW_HEIGHT), if (!isOver) {
0, dragStateRef.current.targetIndex = null;
maxIndex dragStateRef.current.slotIndex = null;
); if (insertPreviewIndex !== null) {
const movingItem = prev[currentIndex]; setInsertPreviewIndex(null);
const movingType = movingItem?.type || movingItem?.value;
const topType = prev[0]?.type || prev[0]?.value;
if (
targetIndex === 0 &&
INTRO_TYPES.has(topType) &&
!INTRO_TYPES.has(movingType)
) {
dragStateRef.current.blockedReason = "intro_must_be_first";
return prev;
} }
if (targetIndex === currentIndex) return prev; return;
const reordered = reorder(prev, currentIndex, targetIndex); }
return recalculateOccurrences(reordered);
}); const containerHeight = rect?.height ?? listLength * ROW_HEIGHT;
let slotIndex = Math.round(pointerY / ROW_HEIGHT);
if (pointerY < 0) slotIndex = 0;
if (pointerY > containerHeight) slotIndex = listLength;
slotIndex = clamp(slotIndex, 0, listLength);
const snapshot = dragState.startItems || sortableItemsRef.current;
const movingIndex =
typeof dragState.startIndex === "number"
? dragState.startIndex
: snapshot.findIndex((item) => item.id === dragState.itemId);
dragStateRef.current.startIndex = movingIndex;
if (movingIndex < 0) {
dragStateRef.current.slotIndex = null;
return;
}
const movingItem = snapshot[movingIndex];
const movingType = movingItem?.type || movingItem?.value;
const topType = snapshot[0]?.type || snapshot[0]?.value;
if (
slotIndex === 0 &&
INTRO_TYPES.has(topType) &&
!INTRO_TYPES.has(movingType)
) {
dragStateRef.current.blockedReason = "intro_must_be_first";
return;
}
dragStateRef.current.blockedReason = null;
const shouldPreview =
typeof movingIndex === "number" &&
slotIndex !== movingIndex &&
slotIndex !== movingIndex + 1;
if (shouldPreview) {
if (dragStateRef.current.slotIndex !== slotIndex) {
dragStateRef.current.slotIndex = slotIndex;
setInsertPreviewIndex(slotIndex);
}
} else if (insertPreviewIndex !== null) {
dragStateRef.current.slotIndex = null;
setInsertPreviewIndex(null);
}
let targetIndex = slotIndex;
if (typeof movingIndex === "number" && slotIndex > movingIndex) {
targetIndex -= 1;
}
targetIndex = clamp(targetIndex, 0, Math.max(listLength - 1, 0));
dragStateRef.current.targetIndex = targetIndex;
return; return;
} }
@@ -462,7 +546,7 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
setInsertPreviewIndex(targetIndex); setInsertPreviewIndex(targetIndex);
} }
}, },
[insertPreviewIndex, setSortableItems] [insertPreviewIndex, setInsertPreviewIndex, updateDragOverlayPosition]
); );
const handleGlobalPointerUp = useCallback( const handleGlobalPointerUp = useCallback(
@@ -480,17 +564,36 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
if (dragState.itemId) { if (dragState.itemId) {
if (dragState.blockedReason === "intro_must_be_first") { if (dragState.blockedReason === "intro_must_be_first") {
showTooltip(INTRO_ERROR_MESSAGE); showTooltip(INTRO_ERROR_MESSAGE);
} } else if (
const validation = validateStructureRules( dragState.isOverContainer &&
sortableItemsRef.current || [] typeof dragState.targetIndex === "number"
); ) {
if (!validation.valid) { const desiredIndex = dragState.targetIndex;
const fallbackItems = setSortableItems((prev) => {
dragState.startItems?.map((item) => item) || const currentIndex = prev.findIndex(
sortableItemsRef.current?.map((item) => item) || (it) => it.id === dragState.itemId
[]; );
setSortableItems(fallbackItems); if (currentIndex === -1) return prev;
showTooltip(validation.message); const targetIndex = clamp(
desiredIndex,
0,
Math.max(prev.length, 0)
);
if (targetIndex === currentIndex) {
return prev;
}
const reordered = reorder(prev, currentIndex, targetIndex);
const recalculated = recalculateOccurrences(reordered);
const validation = validateStructureRules(recalculated);
if (!validation.valid) {
const fallbackItems =
dragState.startItems?.map((item) => item) ||
prev;
showTooltip(validation.message);
return recalculateOccurrences(fallbackItems);
}
return recalculated;
});
} }
} else if (dragState.paletteType) { } else if (dragState.paletteType) {
let isOver = dragState.isOverContainer; let isOver = dragState.isOverContainer;
@@ -532,9 +635,16 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
setActiveItemId(null); setActiveItemId(null);
setInsertPreviewIndex(null); setInsertPreviewIndex(null);
clearDragOverlay();
cleanupPointerListeners(); cleanupPointerListeners();
}, },
[addOptionalSegment, cleanupPointerListeners, showTooltip] [
addOptionalSegment,
cleanupPointerListeners,
clearDragOverlay,
showTooltip,
setSortableItems,
]
); );
const attachGlobalListeners = useCallback(() => { const attachGlobalListeners = useCallback(() => {
@@ -555,7 +665,7 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
}, [handleGlobalPointerMove, handleGlobalPointerUp]); }, [handleGlobalPointerMove, handleGlobalPointerUp]);
const startItemDrag = useCallback( const startItemDrag = useCallback(
(itemId, event) => { (itemId, event, dragElement = null) => {
const nativeEvent = event?.nativeEvent || {}; const nativeEvent = event?.nativeEvent || {};
if (typeof nativeEvent.button === "number" && nativeEvent.button !== 0) if (typeof nativeEvent.button === "number" && nativeEvent.button !== 0)
return; return;
@@ -571,6 +681,32 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
: scrollY; : scrollY;
const containerTop = rect ? rect.top + scrollY : fallbackY; const containerTop = rect ? rect.top + scrollY : fallbackY;
const snapshot = sortableItemsRef.current?.map((item) => item) || []; const snapshot = sortableItemsRef.current?.map((item) => item) || [];
const startIndex = snapshot.findIndex((item) => item.id === itemId);
const movingItem = startIndex >= 0 ? snapshot[startIndex] : null;
const baseType = movingItem?.type || movingItem?.value;
const occurrence =
typeof movingItem?.occurrence === "number" ? movingItem.occurrence : 1;
const label =
baseType != null ? formatStructureLabel(baseType, occurrence) : "";
const pointerClientX =
typeof nativeEvent.clientX === "number" ? nativeEvent.clientX : 0;
const pointerClientY =
typeof nativeEvent.clientY === "number" ? nativeEvent.clientY : 0;
const getRect = (node) =>
typeof node?.getBoundingClientRect === "function"
? node.getBoundingClientRect()
: null;
const itemRect = getRect(dragElement) || getRect(event?.currentTarget);
const overlayWidth = itemRect?.width || rect?.width || 280;
const overlayHeight = itemRect?.height || ROW_HEIGHT;
const grabOffsetX = itemRect
? pointerClientX - itemRect.left
: overlayWidth / 2;
const grabOffsetY = itemRect
? pointerClientY - itemRect.top
: overlayHeight / 2;
const overlayLeft = pointerClientX - grabOffsetX;
const overlayTop = pointerClientY - grabOffsetY;
dragStateRef.current = { dragStateRef.current = {
itemId, itemId,
@@ -593,10 +729,23 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
isOverContainer: true, isOverContainer: true,
startItems: snapshot, startItems: snapshot,
blockedReason: null, blockedReason: null,
startIndex,
slotIndex: null,
grabOffsetX,
grabOffsetY,
}; };
setActiveItemId(itemId); setActiveItemId(itemId);
setInsertPreviewIndex(null); setInsertPreviewIndex(null);
setDragOverlay({
kind: "structure",
label,
description: "",
width: overlayWidth,
height: overlayHeight,
left: overlayLeft,
top: overlayTop,
});
if (typeof document !== "undefined") { if (typeof document !== "undefined") {
document.body.style.userSelect = "none"; document.body.style.userSelect = "none";
@@ -624,6 +773,25 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
? nativeEvent.clientY + scrollY ? nativeEvent.clientY + scrollY
: scrollY; : scrollY;
const containerTop = rect ? rect.top + scrollY : fallbackY; const containerTop = rect ? rect.top + scrollY : fallbackY;
const pointerClientX =
typeof nativeEvent.clientX === "number" ? nativeEvent.clientX : 0;
const pointerClientY =
typeof nativeEvent.clientY === "number" ? nativeEvent.clientY : 0;
const cardRect =
typeof event?.currentTarget?.getBoundingClientRect === "function"
? event.currentTarget.getBoundingClientRect()
: null;
const overlayWidth = cardRect?.width || 320;
const overlayHeight = cardRect?.height || ROW_HEIGHT;
const grabOffsetX = cardRect
? pointerClientX - cardRect.left
: overlayWidth / 2;
const grabOffsetY = cardRect
? pointerClientY - cardRect.top
: overlayHeight / 2;
const overlayLeft = pointerClientX - grabOffsetX;
const overlayTop = pointerClientY - grabOffsetY;
const segmentInfo = getPaletteSegmentInfo(type);
dragStateRef.current = { dragStateRef.current = {
itemId: null, itemId: null,
@@ -645,10 +813,23 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
targetIndex: null, targetIndex: null,
isOverContainer: false, isOverContainer: false,
blockedReason: null, blockedReason: null,
startIndex: null,
slotIndex: null,
grabOffsetX,
grabOffsetY,
}; };
setActiveItemId(`palette-${type}`); setActiveItemId(`palette-${type}`);
setInsertPreviewIndex(null); setInsertPreviewIndex(null);
setDragOverlay({
kind: "palette",
label: segmentInfo.label,
description: segmentInfo.description,
width: overlayWidth,
height: overlayHeight,
left: overlayLeft,
top: overlayTop,
});
if (typeof document !== "undefined") { if (typeof document !== "undefined") {
document.body.style.userSelect = "none"; document.body.style.userSelect = "none";
@@ -665,17 +846,22 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
const meta = getSegmentMeta(baseType); const meta = getSegmentMeta(baseType);
const canRemove = meta?.optional === true; const canRemove = meta?.optional === true;
const isIntroduction = INTRO_TYPES.has(baseType); const isIntroduction = INTRO_TYPES.has(baseType);
const rowRef = useRef(null);
const isActive = activeItemId === item.id;
const containerStyle = StyleSheet.flatten([ const containerStyle = StyleSheet.flatten([
styles.rowContainer, styles.rowContainer,
activeItemId === item.id ? styles.activeItem : null, isActive ? styles.activeItem : null,
isActive ? styles.draggingContainer : null,
]); ]);
const blurStyle = StyleSheet.flatten([ const blurStyle = StyleSheet.flatten([
styles.itemBlur, styles.itemBlur,
activeItemId === item.id ? styles.activeBlur : null, isActive ? styles.activeBlur : null,
isActive ? styles.draggingBlur : null,
]); ]);
const rowStyle = StyleSheet.flatten([ const rowStyle = StyleSheet.flatten([
styles.row, styles.row,
isIntroduction ? styles.rowDisabled : styles.rowDraggable, isIntroduction ? styles.rowDisabled : styles.rowDraggable,
isActive ? styles.rowHidden : null,
]); ]);
return ( return (
@@ -687,10 +873,11 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
blurViewStyle={blurStyle} blurViewStyle={blurStyle}
> >
<View <View
ref={rowRef}
style={rowStyle} style={rowStyle}
onPointerDown={(event) => { onPointerDown={(event) => {
if (isIntroduction) return; if (isIntroduction) return;
startItemDrag(item.id, event); startItemDrag(item.id, event, rowRef.current);
}} }}
> >
<Text style={styles.rowText}>{item.label}</Text> <Text style={styles.rowText}>{item.label}</Text>
@@ -714,7 +901,7 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
style={styles.handleWrapper} style={styles.handleWrapper}
onPointerDown={(event) => { onPointerDown={(event) => {
event?.stopPropagation?.(); event?.stopPropagation?.();
startItemDrag(item.id, event); startItemDrag(item.id, event, rowRef.current);
}} }}
> >
<Image source={icons.dragDots} style={styles.handleIcon} /> <Image source={icons.dragDots} style={styles.handleIcon} />
@@ -848,6 +1035,52 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
</View> </View>
</View> </View>
</ScrollView> </ScrollView>
{dragOverlay ? (
<View
pointerEvents="none"
style={StyleSheet.flatten([
styles.dragOverlay,
{
width: dragOverlay.width || 320,
height: dragOverlay.height || ROW_HEIGHT,
left: dragOverlay.left || 0,
top: dragOverlay.top || 0,
},
])}
>
<CreateLyricsHeader
tint="dark"
intensity={40}
showBorder={false}
containerStyle={StyleSheet.flatten([
styles.rowContainer,
styles.dragOverlayContainer,
])}
blurViewStyle={StyleSheet.flatten([
styles.itemBlur,
dragOverlay.kind === "palette" ? styles.paletteItemBlur : null,
styles.dragOverlayBlur,
])}
>
{dragOverlay.kind === "palette" ? (
<View style={styles.paletteRow}>
<View style={styles.paletteTextWrapper}>
<Text style={styles.rowText}>{dragOverlay.label}</Text>
{dragOverlay.description ? (
<Text style={styles.paletteDescription}>
{dragOverlay.description}
</Text>
) : null}
</View>
</View>
) : (
<View style={styles.row}>
<Text style={styles.rowText}>{dragOverlay.label}</Text>
</View>
)}
</CreateLyricsHeader>
</View>
) : null}
</View> </View>
); );
}; };
@@ -918,6 +1151,9 @@ const styles = StyleSheet.create({
borderWidth: 1, borderWidth: 1,
borderColor: Palette.white, borderColor: Palette.white,
}, },
draggingContainer: {
opacity: 0,
},
itemBlur: { itemBlur: {
minHeight: ROW_HEIGHT, minHeight: ROW_HEIGHT,
paddingHorizontal: 16, paddingHorizontal: 16,
@@ -934,6 +1170,9 @@ const styles = StyleSheet.create({
activeBlur: { activeBlur: {
backgroundColor: Palette.transparentWhite, backgroundColor: Palette.transparentWhite,
}, },
draggingBlur: {
opacity: 0,
},
row: { row: {
flex: 1, flex: 1,
flexDirection: "row", flexDirection: "row",
@@ -946,6 +1185,9 @@ const styles = StyleSheet.create({
rowDisabled: { rowDisabled: {
cursor: "default", cursor: "default",
}, },
rowHidden: {
opacity: 0,
},
rowText: { rowText: {
fontSize: 16, fontSize: 16,
color: Palette.white, color: Palette.white,
@@ -1033,4 +1275,21 @@ const styles = StyleSheet.create({
color: Palette.white, color: Palette.white,
fontFamily: FONT_FAMILY.InterMedium, fontFamily: FONT_FAMILY.InterMedium,
}, },
dragOverlay: {
position: "fixed",
zIndex: 999,
pointerEvents: "none",
},
dragOverlayContainer: {
width: "100%",
},
dragOverlayBlur: {
cursor: "grabbing",
opacity: 1,
shadowColor: "#000",
shadowOpacity: 0.35,
shadowRadius: 12,
shadowOffset: { width: 0, height: 8 },
elevation: 6,
},
}); });