@rpg-engine/long-bow 0.7.52 → 0.7.60
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.
- package/dist/components/Item/Inventory/ItemSlot.d.ts +1 -2
- package/dist/components/Item/Inventory/hooks/useItemSlotDragAndDrop.d.ts +43 -0
- package/dist/long-bow.cjs.development.js +281 -282
- package/dist/long-bow.cjs.development.js.map +1 -1
- package/dist/long-bow.cjs.production.min.js +1 -1
- package/dist/long-bow.cjs.production.min.js.map +1 -1
- package/dist/long-bow.esm.js +281 -282
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Item/Inventory/ItemContainer.tsx +0 -2
- package/src/components/Item/Inventory/ItemSlot.tsx +106 -286
- package/src/components/Item/Inventory/hooks/useItemSlotDragAndDrop.ts +226 -0
package/package.json
CHANGED
|
@@ -87,7 +87,6 @@ export const ItemContainer: React.FC<IItemContainerProps> = ({
|
|
|
87
87
|
onOutsideDrop,
|
|
88
88
|
checkIfItemCanBeMoved,
|
|
89
89
|
initialPosition,
|
|
90
|
-
checkIfItemShouldDragEnd,
|
|
91
90
|
scale,
|
|
92
91
|
shortcuts,
|
|
93
92
|
setItemShortcut,
|
|
@@ -191,7 +190,6 @@ export const ItemContainer: React.FC<IItemContainerProps> = ({
|
|
|
191
190
|
onDragEnd={onDragEnd}
|
|
192
191
|
dragScale={scale}
|
|
193
192
|
checkIfItemCanBeMoved={checkIfItemCanBeMoved}
|
|
194
|
-
checkIfItemShouldDragEnd={checkIfItemShouldDragEnd}
|
|
195
193
|
openQuantitySelector={(maxQuantity, callback) => {
|
|
196
194
|
setQuantitySelect({
|
|
197
195
|
isOpen: true,
|
|
@@ -9,14 +9,14 @@ import {
|
|
|
9
9
|
} from '@rpg-engine/shared';
|
|
10
10
|
|
|
11
11
|
import { observer } from 'mobx-react-lite';
|
|
12
|
-
import React, { useCallback, useEffect,
|
|
13
|
-
import Draggable
|
|
12
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
13
|
+
import Draggable from 'react-draggable';
|
|
14
14
|
import styled from 'styled-components';
|
|
15
15
|
import { IPosition } from '../../../types/eventTypes';
|
|
16
16
|
import { rarityColor } from './ItemSlotRarity';
|
|
17
17
|
import { ItemSlotRenderer } from './ItemSlotRenderer';
|
|
18
18
|
import { ItemSlotToolTips } from './ItemSlotTooltips';
|
|
19
|
-
import {
|
|
19
|
+
import { useItemSlotDragAndDrop } from './hooks/useItemSlotDragAndDrop';
|
|
20
20
|
import { IContextMenuItem, generateContextMenu } from './itemContainerHelper';
|
|
21
21
|
|
|
22
22
|
export const EquipmentSlotSpriteByType: any = {
|
|
@@ -61,7 +61,6 @@ interface IProps {
|
|
|
61
61
|
onOutsideDrop?: (item: IItem, position: IPosition) => void;
|
|
62
62
|
dragScale?: number;
|
|
63
63
|
checkIfItemCanBeMoved?: () => boolean;
|
|
64
|
-
checkIfItemShouldDragEnd?: () => boolean;
|
|
65
64
|
openQuantitySelector?: (maxQuantity: number, callback: () => void) => void;
|
|
66
65
|
onPlaceDrop?: (
|
|
67
66
|
item: IItem | null,
|
|
@@ -114,7 +113,6 @@ export const ItemSlot = React.memo(
|
|
|
114
113
|
onOutsideDrop: onDrop,
|
|
115
114
|
checkIfItemCanBeMoved,
|
|
116
115
|
openQuantitySelector,
|
|
117
|
-
checkIfItemShouldDragEnd,
|
|
118
116
|
dragScale,
|
|
119
117
|
isSelectingShortcut,
|
|
120
118
|
equipmentSet,
|
|
@@ -131,30 +129,39 @@ export const ItemSlot = React.memo(
|
|
|
131
129
|
visible: false,
|
|
132
130
|
position: { x: 0, y: 0 },
|
|
133
131
|
});
|
|
134
|
-
|
|
135
|
-
isFocused: false,
|
|
136
|
-
wasDragged: false,
|
|
137
|
-
position: { x: 0, y: 0 },
|
|
138
|
-
dropPosition: null,
|
|
139
|
-
});
|
|
140
|
-
const dragContainer = useRef<HTMLDivElement>(null);
|
|
141
|
-
const { item: draggingItem, setDraggingItem } = useDragging();
|
|
132
|
+
|
|
142
133
|
const [contextActions, setContextActions] = useState<IContextMenuItem[]>(
|
|
143
134
|
[]
|
|
144
135
|
);
|
|
145
|
-
const [touchStartTime, setTouchStartTime] = useState<number | null>(null);
|
|
146
|
-
const [
|
|
147
|
-
touchStartPosition,
|
|
148
|
-
setTouchStartPosition,
|
|
149
|
-
] = useState<IPosition | null>(null);
|
|
150
136
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
137
|
+
const {
|
|
138
|
+
dragContainer,
|
|
139
|
+
dragState,
|
|
140
|
+
draggingItem,
|
|
141
|
+
getContainerBounds,
|
|
142
|
+
onDraggableStart,
|
|
143
|
+
onDraggableProgress,
|
|
144
|
+
onDraggableStop,
|
|
145
|
+
} = useItemSlotDragAndDrop({
|
|
146
|
+
isDepotSystem: !!isDepotSystem,
|
|
147
|
+
item: item!,
|
|
148
|
+
onDrop: onDrop ?? (() => {}),
|
|
149
|
+
onDragEnd,
|
|
150
|
+
checkIfItemCanBeMoved,
|
|
151
|
+
setItemShortcut,
|
|
152
|
+
isSelectingShortcut,
|
|
153
|
+
onDragStart,
|
|
154
|
+
onPointerDown,
|
|
155
|
+
containerType: containerType!,
|
|
156
|
+
slotIndex,
|
|
157
|
+
openQuantitySelector: openQuantitySelector ?? (() => {}),
|
|
158
|
+
isContextMenuDisabled,
|
|
159
|
+
setTooltipState,
|
|
160
|
+
contextMenuState,
|
|
161
|
+
setContextMenuState,
|
|
162
|
+
});
|
|
157
163
|
|
|
164
|
+
useEffect(() => {
|
|
158
165
|
if (item && containerType) {
|
|
159
166
|
setContextActions(
|
|
160
167
|
generateContextMenu(item, containerType, isDepotSystem)
|
|
@@ -162,276 +169,98 @@ export const ItemSlot = React.memo(
|
|
|
162
169
|
}
|
|
163
170
|
}, [item, isDepotSystem]);
|
|
164
171
|
|
|
165
|
-
|
|
166
|
-
if (onDrop && item && dragState.dropPosition) {
|
|
167
|
-
onDrop(item, dragState.dropPosition);
|
|
168
|
-
setDragState(prev => ({
|
|
169
|
-
...prev,
|
|
170
|
-
dropPosition: null,
|
|
171
|
-
}));
|
|
172
|
-
}
|
|
173
|
-
}, [dragState.dropPosition]);
|
|
174
|
-
|
|
175
|
-
const getContainerBounds = useCallback(() => {
|
|
176
|
-
const container = dragContainer.current;
|
|
177
|
-
if (!container) return { left: 0, top: 0, right: 0, bottom: 0 };
|
|
178
|
-
const rect = container.getBoundingClientRect();
|
|
179
|
-
return {
|
|
180
|
-
left: rect.left,
|
|
181
|
-
top: rect.top,
|
|
182
|
-
right: window.innerWidth - rect.right,
|
|
183
|
-
bottom: window.innerHeight - rect.bottom,
|
|
184
|
-
};
|
|
185
|
-
}, [dragContainer]);
|
|
186
|
-
|
|
187
|
-
const resetItem = () => {
|
|
188
|
-
setTooltipState(prev => ({ ...prev, visible: false }));
|
|
189
|
-
setDragState(prev => ({ ...prev, wasDragged: false }));
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const onSuccessfulDrag = (quantity?: number) => {
|
|
193
|
-
resetItem();
|
|
194
|
-
|
|
195
|
-
if (quantity === -1) {
|
|
196
|
-
setDragState(prev => ({
|
|
197
|
-
...prev,
|
|
198
|
-
position: { x: 0, y: 0 },
|
|
199
|
-
isFocused: false,
|
|
200
|
-
}));
|
|
201
|
-
} else if (item) {
|
|
202
|
-
onDragEnd?.(quantity);
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
const onDraggableStop: DraggableEventHandler = (e, data) => {
|
|
207
|
-
setDraggingItem(null);
|
|
208
|
-
|
|
209
|
-
const target = e.target as HTMLElement;
|
|
210
|
-
if (!target) return;
|
|
211
|
-
|
|
212
|
-
if (target?.id.includes('shortcutSetter') && setItemShortcut && item) {
|
|
213
|
-
const index = parseInt(target.id.split('_')[1]);
|
|
214
|
-
if (!isNaN(index)) {
|
|
215
|
-
setItemShortcut(item, index);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// remove the class react-draggable-dragging from the element
|
|
220
|
-
// to prevent the item from being dragged again
|
|
221
|
-
target.classList.remove('react-draggable-dragging');
|
|
222
|
-
|
|
223
|
-
const isTouch = e.type.startsWith('touch');
|
|
224
|
-
|
|
225
|
-
if (isTouch) {
|
|
226
|
-
const touchEvent = e as TouchEvent;
|
|
227
|
-
const touch = touchEvent.changedTouches[0];
|
|
228
|
-
const touchEndTime = new Date().getTime();
|
|
229
|
-
const touchDuration = touchStartTime
|
|
230
|
-
? touchEndTime - touchStartTime
|
|
231
|
-
: 0;
|
|
232
|
-
|
|
233
|
-
// Check if it's a short tap (less than 200ms) and hasn't moved much
|
|
234
|
-
const isShortTap = touchDuration < 200;
|
|
235
|
-
const hasMovedSignificantly =
|
|
236
|
-
touchStartPosition &&
|
|
237
|
-
(Math.abs(touch.clientX - touchStartPosition.x) > 10 ||
|
|
238
|
-
Math.abs(touch.clientY - touchStartPosition.y) > 10);
|
|
239
|
-
|
|
240
|
-
if (isShortTap && !hasMovedSignificantly) {
|
|
241
|
-
// Handle as a tap/click
|
|
242
|
-
if (item) {
|
|
243
|
-
setTooltipState(prev => ({
|
|
244
|
-
...prev,
|
|
245
|
-
mobileVisible: true,
|
|
246
|
-
}));
|
|
247
|
-
onPointerDown(item.type, containerType ?? null, item);
|
|
248
|
-
}
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Threshold for considering a tap/click as a drag
|
|
254
|
-
const dragThreshold = 5; // pixels
|
|
255
|
-
const isDrag =
|
|
256
|
-
Math.abs(data.x) > dragThreshold || Math.abs(data.y) > dragThreshold;
|
|
257
|
-
|
|
258
|
-
if (dragState.wasDragged && item && !isSelectingShortcut) {
|
|
259
|
-
//@ts-ignore
|
|
260
|
-
const classes: string[] = Array.from(e.target?.classList);
|
|
261
|
-
|
|
262
|
-
const isOutsideDrop =
|
|
263
|
-
classes.some(elm => {
|
|
264
|
-
return elm.includes('rpgui-content');
|
|
265
|
-
}) || classes.length === 0;
|
|
266
|
-
|
|
267
|
-
if (isOutsideDrop) {
|
|
268
|
-
setDragState(prev => ({
|
|
269
|
-
...prev,
|
|
270
|
-
dropPosition: { x: data.x, y: data.y },
|
|
271
|
-
}));
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
setDragState(prev => ({ ...prev, wasDragged: false }));
|
|
275
|
-
|
|
276
|
-
const target = dragContainer.current;
|
|
277
|
-
if (!target || !dragState.wasDragged) return;
|
|
172
|
+
const bounds = getContainerBounds();
|
|
278
173
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const y = matrix.m42;
|
|
174
|
+
const handleInteraction = useCallback(
|
|
175
|
+
(event: React.MouseEvent | React.TouchEvent) => {
|
|
176
|
+
event.stopPropagation();
|
|
283
177
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
position: { x, y },
|
|
287
|
-
}));
|
|
178
|
+
const { clientX, clientY } =
|
|
179
|
+
'touches' in event ? event.touches[0] : event;
|
|
288
180
|
|
|
289
|
-
|
|
290
|
-
if (
|
|
291
|
-
|
|
292
|
-
return;
|
|
293
|
-
if (
|
|
294
|
-
item.stackQty &&
|
|
295
|
-
item.stackQty !== 1 &&
|
|
296
|
-
openQuantitySelector
|
|
297
|
-
) {
|
|
298
|
-
openQuantitySelector(item.stackQty, onSuccessfulDrag);
|
|
299
|
-
} else onSuccessfulDrag(item.stackQty);
|
|
300
|
-
} else {
|
|
301
|
-
resetItem();
|
|
302
|
-
setDragState(prev => ({
|
|
303
|
-
...prev,
|
|
304
|
-
isFocused: false,
|
|
305
|
-
position: { x: 0, y: 0 },
|
|
306
|
-
}));
|
|
181
|
+
if (item && containerType) {
|
|
182
|
+
if (onPlaceDrop && draggingItem) {
|
|
183
|
+
onPlaceDrop(item, slotIndex, containerType);
|
|
307
184
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}));
|
|
315
|
-
} else if (
|
|
316
|
-
!isTouch &&
|
|
317
|
-
!isContextMenuDisabled &&
|
|
318
|
-
!isSelectingShortcut
|
|
319
|
-
) {
|
|
320
|
-
setContextMenuState(prev => ({
|
|
321
|
-
...prev,
|
|
322
|
-
visible: !contextMenuState.visible,
|
|
323
|
-
}));
|
|
324
|
-
|
|
325
|
-
const event = e as MouseEvent;
|
|
326
|
-
|
|
327
|
-
if (event.clientX && event.clientY) {
|
|
328
|
-
setContextMenuState(prev => ({
|
|
329
|
-
...prev,
|
|
330
|
-
position: {
|
|
331
|
-
x: event.clientX - 10,
|
|
332
|
-
y: event.clientY - 5,
|
|
333
|
-
},
|
|
334
|
-
}));
|
|
185
|
+
if (
|
|
186
|
+
onPointerDown &&
|
|
187
|
+
onDragStart === undefined &&
|
|
188
|
+
onDragEnd === undefined
|
|
189
|
+
) {
|
|
190
|
+
onPointerDown(item.type, containerType, item);
|
|
335
191
|
}
|
|
336
192
|
}
|
|
337
193
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
if (onDragStart && containerType) {
|
|
354
|
-
onDragStart(item, slotIndex, containerType);
|
|
355
|
-
}
|
|
356
|
-
};
|
|
357
|
-
|
|
358
|
-
const onDraggableProgress: DraggableEventHandler = (_e, data) => {
|
|
359
|
-
if (
|
|
360
|
-
Math.abs(data.x - dragState.position.x) > 5 ||
|
|
361
|
-
Math.abs(data.y - dragState.position.y) > 5
|
|
362
|
-
) {
|
|
363
|
-
setDragState(prev => ({
|
|
364
|
-
...prev,
|
|
365
|
-
wasDragged: true,
|
|
366
|
-
isFocused: true,
|
|
367
|
-
}));
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
if (!draggingItem) {
|
|
371
|
-
setDraggingItem(item);
|
|
372
|
-
}
|
|
373
|
-
};
|
|
374
|
-
|
|
375
|
-
const onTouchStart = (e: React.TouchEvent) => {
|
|
376
|
-
setTouchStartTime(new Date().getTime());
|
|
377
|
-
setTouchStartPosition({
|
|
378
|
-
x: e.touches[0].clientX,
|
|
379
|
-
y: e.touches[0].clientY,
|
|
380
|
-
});
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
const onTouchEnd = (e: React.TouchEvent) => {
|
|
384
|
-
// Prevent default to stop potential unwanted behaviors
|
|
385
|
-
e.preventDefault();
|
|
386
|
-
|
|
387
|
-
const touch = e.changedTouches[0];
|
|
388
|
-
const touchEndTime = new Date().getTime();
|
|
389
|
-
const touchDuration = touchStartTime
|
|
390
|
-
? touchEndTime - touchStartTime
|
|
391
|
-
: 0;
|
|
392
|
-
|
|
393
|
-
// Check if it's a short tap (less than 200ms) and hasn't moved much
|
|
394
|
-
const isShortTap = touchDuration < 200;
|
|
395
|
-
const hasMovedSignificantly =
|
|
396
|
-
touchStartPosition &&
|
|
397
|
-
(Math.abs(touch.clientX - touchStartPosition.x) > 10 ||
|
|
398
|
-
Math.abs(touch.clientY - touchStartPosition.y) > 10);
|
|
194
|
+
setTooltipState(prev => ({ ...prev, visible: true }));
|
|
195
|
+
onMouseOver?.(event, slotIndex, item, clientX, clientY);
|
|
196
|
+
},
|
|
197
|
+
[
|
|
198
|
+
item,
|
|
199
|
+
containerType,
|
|
200
|
+
slotIndex,
|
|
201
|
+
onPlaceDrop,
|
|
202
|
+
onPointerDown,
|
|
203
|
+
onMouseOver,
|
|
204
|
+
onDragStart,
|
|
205
|
+
onDragEnd,
|
|
206
|
+
]
|
|
207
|
+
);
|
|
399
208
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
209
|
+
const handleInteractionEnd = useCallback(
|
|
210
|
+
(event: React.MouseEvent | React.TouchEvent) => {
|
|
211
|
+
event.preventDefault();
|
|
212
|
+
event.stopPropagation();
|
|
213
|
+
|
|
214
|
+
setTooltipState(prev => ({ ...prev, visible: false }));
|
|
215
|
+
onMouseOut?.();
|
|
216
|
+
|
|
217
|
+
if (event.type === 'touchend' && 'changedTouches' in event) {
|
|
218
|
+
const { clientX, clientY } = event.changedTouches[0];
|
|
219
|
+
const simulatedEvent = new MouseEvent('mouseup', {
|
|
220
|
+
clientX,
|
|
221
|
+
clientY,
|
|
222
|
+
bubbles: true,
|
|
223
|
+
});
|
|
224
|
+
document
|
|
225
|
+
.elementFromPoint(clientX, clientY)
|
|
226
|
+
?.dispatchEvent(simulatedEvent);
|
|
408
227
|
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
clientX: touch.clientX,
|
|
413
|
-
clientY: touch.clientY,
|
|
414
|
-
bubbles: true,
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
document
|
|
418
|
-
.elementFromPoint(touch.clientX, touch.clientY)
|
|
419
|
-
?.dispatchEvent(simulatedEvent);
|
|
420
|
-
}
|
|
421
|
-
};
|
|
228
|
+
},
|
|
229
|
+
[onMouseOut]
|
|
230
|
+
);
|
|
422
231
|
|
|
423
|
-
const bounds = getContainerBounds();
|
|
424
232
|
return (
|
|
425
233
|
<Container
|
|
426
234
|
isDraggingItem={!!draggingItem}
|
|
427
235
|
item={item}
|
|
428
236
|
className="rpgui-icon empty-slot"
|
|
429
|
-
|
|
237
|
+
isSelectingShortcut={
|
|
238
|
+
isSelectingShortcut &&
|
|
239
|
+
(item?.type === ItemType.Consumable ||
|
|
240
|
+
item?.type === ItemType.Tool ||
|
|
241
|
+
item?.subType === ItemSubType.Seed)
|
|
242
|
+
}
|
|
243
|
+
onMouseDown={handleInteraction}
|
|
244
|
+
onTouchStart={handleInteraction}
|
|
245
|
+
onMouseUp={e => {
|
|
246
|
+
handleInteractionEnd(e);
|
|
430
247
|
const data = item ? item : null;
|
|
431
|
-
if (onPlaceDrop && containerType) {
|
|
248
|
+
if (onPlaceDrop && containerType && draggingItem) {
|
|
432
249
|
onPlaceDrop(data, slotIndex, containerType);
|
|
433
250
|
}
|
|
434
251
|
}}
|
|
252
|
+
onTouchEnd={e => {
|
|
253
|
+
handleInteractionEnd(e);
|
|
254
|
+
const { clientX, clientY } = e.changedTouches[0];
|
|
255
|
+
const simulatedEvent = new MouseEvent('mouseup', {
|
|
256
|
+
clientX,
|
|
257
|
+
clientY,
|
|
258
|
+
bubbles: true,
|
|
259
|
+
});
|
|
260
|
+
document
|
|
261
|
+
.elementFromPoint(clientX, clientY)
|
|
262
|
+
?.dispatchEvent(simulatedEvent);
|
|
263
|
+
}}
|
|
435
264
|
onPointerDown={
|
|
436
265
|
onDragStart !== undefined && onDragEnd !== undefined
|
|
437
266
|
? undefined
|
|
@@ -440,14 +269,7 @@ export const ItemSlot = React.memo(
|
|
|
440
269
|
onPointerDown(item.type, containerType ?? null, item);
|
|
441
270
|
}
|
|
442
271
|
}
|
|
443
|
-
|
|
444
|
-
isSelectingShortcut &&
|
|
445
|
-
(item?.type === ItemType.Consumable ||
|
|
446
|
-
item?.type === ItemType.Tool ||
|
|
447
|
-
item?.subType === ItemSubType.Seed)
|
|
448
|
-
}
|
|
449
|
-
onTouchStart={onTouchStart}
|
|
450
|
-
onTouchEnd={onTouchEnd}
|
|
272
|
+
onMouseLeave={handleInteractionEnd}
|
|
451
273
|
>
|
|
452
274
|
<Draggable
|
|
453
275
|
axis={isSelectingShortcut ? 'none' : 'both'}
|
|
@@ -473,9 +295,7 @@ export const ItemSlot = React.memo(
|
|
|
473
295
|
event.clientY
|
|
474
296
|
);
|
|
475
297
|
}}
|
|
476
|
-
onMouseOut={
|
|
477
|
-
if (onMouseOut) onMouseOut();
|
|
478
|
-
}}
|
|
298
|
+
onMouseOut={onMouseOut}
|
|
479
299
|
onMouseEnter={() => {
|
|
480
300
|
setTooltipState(prev => ({ ...prev, visible: true }));
|
|
481
301
|
}}
|