@rpg-engine/long-bow 0.7.53 → 0.7.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,231 @@
1
+ import { IItem, ItemContainerType, ItemType } from '@rpg-engine/shared';
2
+ import { useCallback, useEffect, useRef, useState } from 'react';
3
+ import { DraggableEventHandler } from 'react-draggable';
4
+ import { useDragging } from '../context/DraggingContext';
5
+ import { ContextMenuState, DragState, TooltipState } from '../ItemSlot';
6
+
7
+ interface IUseItemSlotDragAndDrop {
8
+ isDepotSystem: boolean;
9
+ item: IItem;
10
+ onDrop: (item: IItem, dropPosition: { x: number; y: number }) => void;
11
+ onDragEnd?: (quantity?: number) => void;
12
+ checkIfItemCanBeMoved?: () => boolean;
13
+ checkIfItemShouldDragEnd?: () => boolean;
14
+ setItemShortcut?: (item: IItem, index: number) => void;
15
+ isSelectingShortcut?: boolean;
16
+ onDragStart?: (
17
+ item: IItem,
18
+ slotIndex: number,
19
+ containerType: ItemContainerType
20
+ ) => void;
21
+ onPointerDown: (
22
+ ItemType: ItemType,
23
+ itemContainerType: ItemContainerType | null,
24
+ item: IItem
25
+ ) => void;
26
+ containerType: ItemContainerType;
27
+ slotIndex: number;
28
+ openQuantitySelector: (
29
+ quantity: number,
30
+ onSuccess: (quantity?: number) => void
31
+ ) => void;
32
+ isContextMenuDisabled: boolean;
33
+ setTooltipState: React.Dispatch<React.SetStateAction<TooltipState>>;
34
+ setContextMenuState: React.Dispatch<React.SetStateAction<ContextMenuState>>;
35
+ contextMenuState: ContextMenuState;
36
+ }
37
+
38
+ export const useItemSlotDragAndDrop = ({
39
+ isDepotSystem,
40
+ item,
41
+ onDrop,
42
+ onDragEnd,
43
+ checkIfItemCanBeMoved,
44
+ checkIfItemShouldDragEnd,
45
+ setItemShortcut,
46
+ isSelectingShortcut,
47
+ onDragStart,
48
+ onPointerDown,
49
+ containerType,
50
+ slotIndex,
51
+ openQuantitySelector,
52
+ isContextMenuDisabled,
53
+ setTooltipState,
54
+ setContextMenuState,
55
+ }: IUseItemSlotDragAndDrop) => {
56
+ const dragContainer = useRef<HTMLDivElement>(null);
57
+ const { item: draggingItem, setDraggingItem } = useDragging();
58
+
59
+ const [dragState, setDragState] = useState<DragState>({
60
+ isFocused: false,
61
+ wasDragged: false,
62
+ position: { x: 0, y: 0 },
63
+ dropPosition: null,
64
+ });
65
+
66
+ useEffect(() => {
67
+ setDragState(prev => ({
68
+ ...prev,
69
+ position: { x: 0, y: 0 },
70
+ isFocused: false,
71
+ }));
72
+ }, [item, isDepotSystem]);
73
+
74
+ useEffect(() => {
75
+ if (onDrop && item && dragState.dropPosition) {
76
+ onDrop(item, dragState.dropPosition);
77
+ setDragState(prev => ({ ...prev, dropPosition: null }));
78
+ }
79
+ }, [dragState.dropPosition, item, onDrop]);
80
+
81
+ const getContainerBounds = useCallback(() => {
82
+ const container = dragContainer.current;
83
+ if (!container) return { left: 0, top: 0, right: 0, bottom: 0 };
84
+ const rect = container.getBoundingClientRect();
85
+ return {
86
+ left: rect.left,
87
+ top: rect.top,
88
+ right: window.innerWidth - rect.right,
89
+ bottom: window.innerHeight - rect.bottom,
90
+ };
91
+ }, []);
92
+
93
+ const resetDragState = useCallback(() => {
94
+ setTooltipState(prev => ({ ...prev, visible: false }));
95
+ setDragState(prev => ({
96
+ ...prev,
97
+ wasDragged: false,
98
+ isFocused: false,
99
+ position: { x: 0, y: 0 },
100
+ }));
101
+ }, [setTooltipState]);
102
+
103
+ const handleSuccessfulDrag = useCallback(
104
+ (quantity?: number) => {
105
+ resetDragState();
106
+ if (quantity !== -1 && item) {
107
+ onDragEnd?.(quantity);
108
+ }
109
+ },
110
+ [item, onDragEnd, resetDragState]
111
+ );
112
+
113
+ const onDraggableStart: DraggableEventHandler = useCallback(() => {
114
+ if (!item || isSelectingShortcut) return;
115
+ if (onDragStart && containerType) {
116
+ onDragStart(item, slotIndex, containerType);
117
+ }
118
+ }, [item, isSelectingShortcut, onDragStart, containerType, slotIndex]);
119
+
120
+ const onDraggableProgress: DraggableEventHandler = useCallback(
121
+ (_e, data) => {
122
+ const { x, y } = dragState.position;
123
+ if (Math.abs(data.x - x) > 5 || Math.abs(data.y - y) > 5) {
124
+ setDragState(prev => ({ ...prev, wasDragged: true, isFocused: true }));
125
+ }
126
+ if (!draggingItem) {
127
+ setDraggingItem(item);
128
+ }
129
+ },
130
+ [dragState.position, draggingItem, item, setDraggingItem]
131
+ );
132
+
133
+ const onDraggableStop: DraggableEventHandler = useCallback(
134
+ (e, data) => {
135
+ setTimeout(() => {
136
+ setDraggingItem(null);
137
+ }, 50);
138
+ const target = e.target as HTMLElement;
139
+ if (!target) return;
140
+
141
+ target.classList.remove('react-draggable-dragging');
142
+
143
+ if (target?.id.includes('shortcutSetter') && setItemShortcut && item) {
144
+ const index = parseInt(target.id.split('_')[1]);
145
+ if (!isNaN(index)) {
146
+ setItemShortcut(item, index);
147
+ }
148
+ }
149
+
150
+ if (dragState.wasDragged && item && !isSelectingShortcut) {
151
+ const classes: string[] = Array.from(target.classList);
152
+ const isOutsideDrop =
153
+ classes.some(elm => elm.includes('rpgui-content')) ||
154
+ classes.length === 0;
155
+
156
+ if (isOutsideDrop) {
157
+ setDragState(prev => ({
158
+ ...prev,
159
+ dropPosition: { x: data.x, y: data.y },
160
+ }));
161
+ }
162
+
163
+ setDragState(prev => ({ ...prev, wasDragged: false }));
164
+
165
+ setTimeout(() => {
166
+ if (
167
+ checkIfItemCanBeMoved?.() &&
168
+ (!checkIfItemShouldDragEnd || checkIfItemShouldDragEnd())
169
+ ) {
170
+ if (item.stackQty && item.stackQty !== 1 && openQuantitySelector) {
171
+ openQuantitySelector(item.stackQty, handleSuccessfulDrag);
172
+ } else {
173
+ handleSuccessfulDrag(item.stackQty);
174
+ }
175
+ } else {
176
+ resetDragState();
177
+ }
178
+ }, 50);
179
+ } else if (item) {
180
+ const isTouch = e.type === 'touchend';
181
+ if (
182
+ !isContextMenuDisabled &&
183
+ isTouch &&
184
+ !isSelectingShortcut &&
185
+ !draggingItem
186
+ ) {
187
+ setTooltipState(prev => ({ ...prev, mobileVisible: true }));
188
+ } else if (!isContextMenuDisabled && !isSelectingShortcut && !isTouch) {
189
+ const event = e as MouseEvent;
190
+ setContextMenuState(prev => ({
191
+ visible: !prev.visible,
192
+ position: {
193
+ x: event.clientX - 10,
194
+ y: event.clientY - 5,
195
+ },
196
+ }));
197
+ }
198
+
199
+ onPointerDown?.(item.type, containerType ?? null, item);
200
+ }
201
+ },
202
+ [
203
+ dragState.wasDragged,
204
+ item,
205
+ isSelectingShortcut,
206
+ checkIfItemCanBeMoved,
207
+ checkIfItemShouldDragEnd,
208
+ openQuantitySelector,
209
+ handleSuccessfulDrag,
210
+ resetDragState,
211
+ isContextMenuDisabled,
212
+ setTooltipState,
213
+ setContextMenuState,
214
+ onPointerDown,
215
+ containerType,
216
+ setItemShortcut,
217
+ ]
218
+ );
219
+
220
+ return {
221
+ dragContainer,
222
+ dragState,
223
+ draggingItem,
224
+ setDraggingItem,
225
+ getContainerBounds,
226
+ onDraggableStart,
227
+ onDraggableProgress,
228
+ onDraggableStop,
229
+ resetItem: resetDragState,
230
+ };
231
+ };