@rpg-engine/long-bow 0.7.71 → 0.7.73

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.
@@ -2,6 +2,7 @@ import {
2
2
  IEquipmentSet,
3
3
  IItem,
4
4
  IItemContainer,
5
+ isMobile,
5
6
  ItemContainerType,
6
7
  ItemSlotType,
7
8
  ItemSubType,
@@ -9,15 +10,14 @@ import {
9
10
  } from '@rpg-engine/shared';
10
11
 
11
12
  import { observer } from 'mobx-react-lite';
12
- import React, { useEffect, useRef, useState } from 'react';
13
+ import React, { useEffect, useRef } from 'react';
13
14
  import Draggable, { DraggableEventHandler } from 'react-draggable';
14
15
  import styled from 'styled-components';
15
16
  import { IPosition } from '../../../types/eventTypes';
16
17
  import { rarityColor } from './ItemSlotRarity';
17
18
  import { ItemSlotRenderer } from './ItemSlotRenderer';
18
- import { ItemSlotToolTips } from './ItemSlotTooltips';
19
- import { useDragging } from './context/DraggingContext';
20
- import { IContextMenuItem, generateContextMenu } from './itemContainerHelper';
19
+ import { useItemSlotDetails } from './context/ItemSlotDetailsContext';
20
+ import { useItemSlotDragging } from './context/ItemSlotDraggingContext';
21
21
 
22
22
  export const EquipmentSlotSpriteByType: any = {
23
23
  Neck: 'accessories/corruption-necklace.png',
@@ -38,7 +38,6 @@ interface IProps {
38
38
  itemContainer?: IItemContainer | null;
39
39
  itemContainerType?: ItemContainerType | null;
40
40
  slotSpriteMask?: ItemSlotType | null;
41
- onSelected?: (selectedOption: string, item: IItem) => void;
42
41
  onMouseOver?: (
43
42
  event: any,
44
43
  slotIndex: number,
@@ -86,7 +85,6 @@ export const ItemSlot: React.FC<IProps> = observer(
86
85
  onMouseOver,
87
86
  onMouseOut,
88
87
  onPointerDown,
89
- onSelected,
90
88
  atlasJSON,
91
89
  atlasIMG,
92
90
  isContextMenuDisabled = false,
@@ -99,64 +97,62 @@ export const ItemSlot: React.FC<IProps> = observer(
99
97
  checkIfItemShouldDragEnd,
100
98
  dragScale,
101
99
  isSelectingShortcut,
102
- equipmentSet,
100
+
103
101
  setItemShortcut,
104
102
  isDepotSystem,
105
103
  }) => {
106
- // Centralized state using a single useState hook
107
- const [state, setState] = useState({
108
- isTooltipVisible: false,
109
- isTooltipMobileVisible: false,
110
- isContextMenuVisible: false,
111
- contextMenuPosition: { x: 0, y: 0 },
112
- isFocused: false,
113
- wasDragged: false,
114
- dragPosition: { x: 0, y: 0 } as IPosition,
115
- dropPosition: null as IPosition | null,
116
- contextActions: [] as IContextMenuItem[],
117
- draggingDistance: 0,
118
- });
104
+ const {
105
+ detailsState,
106
+ updateDetailsState,
107
+ setContextActions,
108
+ } = useItemSlotDetails();
119
109
 
120
110
  const {
121
- isTooltipVisible,
122
- isTooltipMobileVisible,
123
111
  isContextMenuVisible,
124
- contextMenuPosition,
125
- isFocused,
126
- wasDragged,
127
- dragPosition,
128
- dropPosition,
129
- contextActions,
130
- } = state;
112
+
113
+ clearContextActions,
114
+ } = detailsState;
131
115
 
132
116
  const dragContainer = useRef<HTMLDivElement>(null);
133
- const { item: draggingItem, setDraggingItem } = useDragging();
117
+
118
+ const {
119
+ draggingState,
120
+ updateDraggingState,
121
+ clearDraggingState,
122
+ } = useItemSlotDragging();
123
+
124
+ const {
125
+ isFocused,
126
+ dropPosition,
127
+ isDragging,
128
+ draggingDistance,
129
+ } = draggingState;
134
130
 
135
131
  useEffect(() => {
136
132
  // Reset drag position and focus when item changes
137
- setState(prevState => ({
138
- ...prevState,
139
- dragPosition: { x: 0, y: 0 },
133
+
134
+ updateDraggingState({
135
+ position: { x: 0, y: 0 },
140
136
  isFocused: false,
141
- }));
137
+ });
142
138
 
143
139
  // Update context actions when item or depot system changes
144
- if (item && containerType) {
145
- setState(prevState => ({
146
- ...prevState,
147
- contextActions: generateContextMenu(
148
- item,
149
- containerType,
150
- isDepotSystem
151
- ),
152
- }));
140
+ if (item && containerType && !isContextMenuDisabled) {
141
+ setContextActions(
142
+ item,
143
+ containerType as ItemContainerType,
144
+ isDepotSystem ?? false
145
+ );
153
146
  } else {
154
- setState(prevState => ({
155
- ...prevState,
156
- contextActions: [],
157
- }));
147
+ clearContextActions();
158
148
  }
159
- }, [item, isDepotSystem]);
149
+ }, [
150
+ item,
151
+ containerType,
152
+ isDepotSystem,
153
+ setContextActions,
154
+ clearContextActions,
155
+ ]);
160
156
 
161
157
  useEffect(() => {
162
158
  // Handle outside drop
@@ -166,50 +162,44 @@ export const ItemSlot: React.FC<IProps> = observer(
166
162
  }, [dropPosition]);
167
163
 
168
164
  const resetItem = () => {
169
- setState(prevState => ({
170
- ...prevState,
165
+ clearDraggingState();
166
+ updateDetailsState({
167
+ item,
171
168
  isTooltipVisible: false,
172
- wasDragged: false,
173
- }));
169
+ });
174
170
  };
175
171
 
176
172
  const onSuccessfulDrag = (quantity?: number) => {
177
173
  resetItem();
178
174
 
179
175
  if (quantity === -1) {
180
- setState(prevState => ({
181
- ...prevState,
182
- dragPosition: { x: 0, y: 0 },
176
+ updateDraggingState({
177
+ position: { x: 0, y: 0 },
183
178
  isFocused: false,
184
- }));
179
+ });
185
180
  } else if (item) {
186
181
  onDragEnd?.(quantity);
187
182
  }
188
183
  };
189
184
 
190
185
  const onDraggableStop: DraggableEventHandler = (e, data) => {
191
- console.log('>>> ON_DRAGGABLE_STOP');
192
- // Stop the dragging state
193
-
194
- console.log('setDraggingItem(null)');
195
- setDraggingItem(null);
186
+ requestAnimationFrame(() => {
187
+ updateDraggingState({
188
+ item: null,
189
+ });
196
190
 
197
- const target = e.target as HTMLElement;
191
+ const target = e.target as HTMLElement;
192
+ handleShortcutSetter(target);
193
+ removeDraggingClass(target);
198
194
 
199
- console.log('handleShortcutSetter(target)');
200
- handleShortcutSetter(target);
195
+ const shouldHandleDraggedItemResult = shouldHandleDraggedItem();
201
196
 
202
- console.log('removeDraggingClass(target)');
203
- removeDraggingClass(target);
204
-
205
- console.log('shouldHandleDraggedItem()');
206
- if (shouldHandleDraggedItem()) {
207
- console.log('handleDraggedItem(e, data)');
208
- handleDraggedItem(e, data);
209
- } else if (item && !wasDragged) {
210
- console.log('handleContextMenuOrTooltip(e)');
211
- handleContextMenuOrTooltip(e);
212
- }
197
+ if (shouldHandleDraggedItemResult) {
198
+ handleDraggedItem(e, data);
199
+ } else if (item && !isDragging) {
200
+ handleContextMenuOrTooltip(e as React.MouseEvent);
201
+ }
202
+ });
213
203
  };
214
204
 
215
205
  /**
@@ -235,52 +225,43 @@ export const ItemSlot: React.FC<IProps> = observer(
235
225
  * Determines whether the dragged item should be processed.
236
226
  */
237
227
  const shouldHandleDraggedItem = (): boolean => {
238
- console.log(
239
- `Debug: shouldHandleDraggedItem()`,
240
- `wasDragged: ${wasDragged}`,
241
- `item: ${item}`,
242
- `isSelectingShortcut: ${isSelectingShortcut}`
243
- );
244
- return !!(wasDragged && item && !isSelectingShortcut);
228
+ return !!(isDragging && draggingState.item && !isSelectingShortcut);
245
229
  };
246
230
 
247
231
  /**
248
232
  * Handles the logic when an item has been dragged.
249
233
  */
250
234
  const handleDraggedItem = (e: any, data: any) => {
251
- const targetClasses = Array.from((e.target as HTMLElement).classList);
252
- const isOutsideDrop =
253
- targetClasses.some(elm => elm.includes('rpgui-content')) ||
254
- targetClasses.length === 0;
255
-
256
- if (isOutsideDrop) {
257
- setState(prevState => ({
258
- ...prevState,
259
- dropPosition: {
260
- x: data.x,
261
- y: data.y,
262
- },
263
- }));
264
- }
235
+ requestAnimationFrame(() => {
236
+ const targetClasses = Array.from((e.target as HTMLElement).classList);
237
+ const isOutsideDrop =
238
+ targetClasses.some(elm => elm.includes('rpgui-content')) ||
239
+ targetClasses.length === 0;
240
+
241
+ if (isOutsideDrop) {
242
+ updateDraggingState({
243
+ dropPosition: {
244
+ x: data.x,
245
+ y: data.y,
246
+ },
247
+ });
248
+ }
265
249
 
266
- setState(prevState => ({
267
- ...prevState,
268
- wasDragged: false,
269
- }));
250
+ updateDraggingState({
251
+ isDragging: false,
252
+ });
270
253
 
271
- const targetElement = dragContainer.current;
272
- if (!targetElement) return;
254
+ const targetElement = dragContainer.current;
255
+ if (!targetElement) return;
273
256
 
274
- const { x, y } = getElementTransform(targetElement);
275
- setState(prevState => ({
276
- ...prevState,
277
- dragPosition: { x, y },
278
- }));
257
+ const { x, y } = getElementTransform(targetElement);
258
+
259
+ updateDraggingState({
260
+ position: { x, y },
261
+ });
279
262
 
280
- // Delay to ensure state updates before proceeding
281
- setTimeout(() => {
282
263
  processDragEnd(item!);
283
- }, 50);
264
+ });
284
265
  };
285
266
 
286
267
  /**
@@ -298,67 +279,53 @@ export const ItemSlot: React.FC<IProps> = observer(
298
279
  * Processes the end of a drag event, handling quantity selection or resetting state.
299
280
  */
300
281
  const processDragEnd = (item: IItem) => {
301
- console.log(`Debug: processDragEnd(item)`, `item: ${item}`);
302
282
  if (checkIfItemCanBeMoved?.()) {
303
- console.log(
304
- `Debug: checkIfItemCanBeMoved()`,
305
- `result: ${checkIfItemCanBeMoved()}`
306
- );
307
-
308
283
  if (checkIfItemShouldDragEnd && !checkIfItemShouldDragEnd()) return;
309
284
 
310
- console.log(
311
- `Debug: checkIfItemShouldDragEnd()`,
312
- `result: ${checkIfItemShouldDragEnd?.()}`
313
- );
314
-
315
285
  if (item.stackQty && item.stackQty !== 1 && openQuantitySelector) {
316
- console.log(
317
- `Debug: openQuantitySelector(item.stackQty, onSuccessfulDrag)`
318
- );
319
286
  openQuantitySelector(item.stackQty, onSuccessfulDrag);
320
287
  } else {
321
- console.log(`Debug: onSuccessfulDrag(item.stackQty)`);
322
288
  onSuccessfulDrag(item.stackQty);
323
289
  }
324
290
  } else {
325
- console.log(`Debug: resetItem()`);
326
291
  resetItem();
327
- setState(prevState => ({
328
- ...prevState,
292
+
293
+ updateDraggingState({
329
294
  isFocused: false,
330
- dragPosition: { x: 0, y: 0 },
331
- }));
295
+ position: { x: 0, y: 0 },
296
+ });
332
297
  }
333
298
  };
334
299
 
335
300
  /**
336
301
  * Handles the context menu or tooltip display after dragging stops without a drop.
337
302
  */
338
- const handleContextMenuOrTooltip = (e: any) => {
339
- let isTouchEvent = false;
340
-
341
- if (
342
- !isContextMenuDisabled &&
343
- e.type === 'touchend' &&
344
- !isSelectingShortcut
345
- ) {
346
- isTouchEvent = true;
347
- setState(prevState => ({
348
- ...prevState,
303
+ const handleContextMenuOrTooltip = (
304
+ e: React.MouseEvent | React.TouchEvent
305
+ ) => {
306
+ const isTouchEvent = e.type === 'touchend';
307
+ const shouldShowMobileTooltip =
308
+ !isContextMenuDisabled && isTouchEvent && !isSelectingShortcut;
309
+ const shouldToggleContextMenu =
310
+ !isContextMenuDisabled && !isSelectingShortcut && !isTouchEvent;
311
+
312
+ if (shouldShowMobileTooltip) {
313
+ updateDetailsState({
314
+ item,
349
315
  isTooltipMobileVisible: true,
350
- }));
351
- }
352
-
353
- if (!isContextMenuDisabled && !isSelectingShortcut && !isTouchEvent) {
354
- setState(prevState => ({
355
- ...prevState,
356
- isContextMenuVisible: !prevState.isContextMenuVisible,
316
+ isContextMenuVisible: false, // Ensure context menu is hidden
317
+ });
318
+ } else if (shouldToggleContextMenu) {
319
+ const mouseEvent = e as React.MouseEvent;
320
+ updateDetailsState({
321
+ item,
322
+ isContextMenuVisible: !isContextMenuVisible && !isMobile(),
323
+ isTooltipMobileVisible: false, // Ensure mobile tooltip is hidden
357
324
  contextMenuPosition: {
358
- x: (e as MouseEvent).clientX - 10,
359
- y: (e as MouseEvent).clientY - 5,
325
+ x: mouseEvent.clientX - 10,
326
+ y: mouseEvent.clientY - 5,
360
327
  },
361
- }));
328
+ });
362
329
  }
363
330
 
364
331
  if (item) {
@@ -378,28 +345,27 @@ export const ItemSlot: React.FC<IProps> = observer(
378
345
 
379
346
  const onDraggableProgress: DraggableEventHandler = (_e, _data) => {
380
347
  // increment draggingDistance by 1
348
+ updateDraggingState({
349
+ draggingDistance: draggingState.draggingDistance + 1,
350
+ });
381
351
 
382
- setState(prevState => ({
383
- ...prevState,
384
- draggingDistance: prevState.draggingDistance + 1,
385
- }));
386
-
387
- if (state.draggingDistance > 10) {
388
- setState(prevState => ({
389
- ...prevState,
390
- wasDragged: true,
352
+ if (draggingDistance > 10) {
353
+ updateDraggingState({
354
+ isDragging: true,
391
355
  isFocused: true,
392
- }));
356
+ });
393
357
  }
394
358
 
395
- if (!draggingItem) {
396
- setDraggingItem(item);
359
+ if (!draggingState.item) {
360
+ updateDraggingState({
361
+ item: item,
362
+ });
397
363
  }
398
364
  };
399
365
 
400
366
  return (
401
367
  <Container
402
- isDraggingItem={!!draggingItem}
368
+ isDraggingItem={!!draggingState.item}
403
369
  item={item}
404
370
  className="rpgui-icon empty-slot"
405
371
  onMouseUp={() => {
@@ -441,7 +407,7 @@ export const ItemSlot: React.FC<IProps> = observer(
441
407
  onStop={onDraggableStop}
442
408
  onStart={onDraggableStart}
443
409
  onDrag={onDraggableProgress}
444
- position={dragPosition}
410
+ position={draggingState.position}
445
411
  cancel=".empty-slot"
446
412
  bounds=".item-container-body, .equipment-container-body"
447
413
  >
@@ -461,16 +427,16 @@ export const ItemSlot: React.FC<IProps> = observer(
461
427
  if (onMouseOut) onMouseOut();
462
428
  }}
463
429
  onMouseEnter={() => {
464
- setState(prevState => ({
465
- ...prevState,
430
+ updateDetailsState({
431
+ item,
466
432
  isTooltipVisible: true,
467
- }));
433
+ });
468
434
  }}
469
435
  onMouseLeave={() => {
470
- setState(prevState => ({
471
- ...prevState,
436
+ updateDetailsState({
437
+ item,
472
438
  isTooltipVisible: false,
473
- }));
439
+ });
474
440
  }}
475
441
  >
476
442
  <ItemSlotRenderer
@@ -482,46 +448,6 @@ export const ItemSlot: React.FC<IProps> = observer(
482
448
  />
483
449
  </ItemContainer>
484
450
  </Draggable>
485
-
486
- <ItemSlotToolTips
487
- isTooltipVisible={isTooltipVisible}
488
- isTooltipMobileVisible={isTooltipMobileVisible}
489
- setIsTooltipMobileVisible={value =>
490
- setState(prevState => ({
491
- ...prevState,
492
- isTooltipMobileVisible: value,
493
- }))
494
- }
495
- isFocused={isFocused}
496
- isContextMenuVisible={isContextMenuVisible}
497
- isContextMenuDisabled={isContextMenuDisabled}
498
- item={item}
499
- contextActions={contextActions}
500
- contextMenuPosition={contextMenuPosition}
501
- dragScale={dragScale}
502
- setIsContextMenuVisible={value =>
503
- setState(prevState => ({
504
- ...prevState,
505
- isContextMenuVisible: value,
506
- }))
507
- }
508
- onSelected={(optionId: string, item: IItem) => {
509
- setState(prevState => ({
510
- ...prevState,
511
- isContextMenuVisible: false,
512
- }));
513
- if (onSelected) onSelected(optionId, item);
514
- }}
515
- atlasIMG={atlasIMG}
516
- atlasJSON={atlasJSON}
517
- equipmentSet={equipmentSet}
518
- setIsTooltipVisible={value =>
519
- setState(prevState => ({
520
- ...prevState,
521
- isTooltipVisible: value,
522
- }))
523
- }
524
- />
525
451
  </Container>
526
452
  );
527
453
  }
@@ -537,6 +463,10 @@ interface ContainerTypes {
537
463
  const Container = styled.div<ContainerTypes>`
538
464
  margin: 0.1rem;
539
465
 
466
+ * {
467
+ border: 1px solid yellow;
468
+ }
469
+
540
470
  .react-draggable-dragging {
541
471
  // If dragging item, set opacity to 0
542
472
  opacity: ${({ isDraggingItem }) => (isDraggingItem ? 0 : 1)};
@@ -1,47 +1,43 @@
1
1
  import { IEquipmentSet, IItem } from '@rpg-engine/shared';
2
2
  import React from 'react';
3
- import { IPosition } from '../../../types/eventTypes';
4
3
  import { RelativeListMenu } from '../../RelativeListMenu';
5
4
  import { ItemTooltip } from '../Cards/ItemTooltip';
6
5
  import { MobileItemTooltip } from '../Cards/MobileItemTooltip';
7
- import { IContextMenuItem } from './itemContainerHelper';
6
+ import { useItemSlotDetails } from './context/ItemSlotDetailsContext';
7
+ import { useItemSlotDragging } from './context/ItemSlotDraggingContext';
8
8
 
9
9
  interface IProps {
10
- isTooltipVisible: boolean;
11
- isFocused: boolean;
12
- isContextMenuVisible: boolean;
13
- isContextMenuDisabled: boolean;
14
- item: IItem | null;
15
- isTooltipMobileVisible: boolean;
16
- contextActions: IContextMenuItem[];
17
- contextMenuPosition: IPosition;
18
10
  dragScale: number | undefined;
19
- setIsContextMenuVisible: (visible: boolean) => void;
20
- setIsTooltipMobileVisible: (visible: boolean) => void;
21
- setIsTooltipVisible: (visible: boolean) => void;
22
11
  onSelected?: (optionId: string, item: IItem) => void;
23
12
  atlasIMG: any;
24
13
  atlasJSON: any;
25
14
  equipmentSet?: IEquipmentSet | null;
15
+ isContextMenuDisabled?: boolean;
26
16
  }
27
17
 
28
18
  export const ItemSlotToolTips = ({
29
- isTooltipVisible,
30
- isFocused,
31
- isContextMenuVisible,
32
- isContextMenuDisabled,
33
- item,
34
- contextActions,
35
- contextMenuPosition,
36
19
  dragScale,
37
- setIsContextMenuVisible,
38
- setIsTooltipMobileVisible,
39
- isTooltipMobileVisible,
40
20
  onSelected,
41
21
  atlasIMG,
42
22
  atlasJSON,
43
23
  equipmentSet,
24
+ isContextMenuDisabled,
44
25
  }: IProps): JSX.Element => {
26
+ const { detailsState, updateDetailsState } = useItemSlotDetails();
27
+
28
+ const {
29
+ isTooltipVisible,
30
+ isTooltipMobileVisible,
31
+ isContextMenuVisible,
32
+ contextMenuPosition,
33
+ contextActions,
34
+ item,
35
+ } = detailsState;
36
+
37
+ const { draggingState } = useItemSlotDragging();
38
+
39
+ const { isFocused } = draggingState;
40
+
45
41
  return (
46
42
  <>
47
43
  {isTooltipVisible && item && !isFocused && (
@@ -60,12 +56,14 @@ export const ItemSlotToolTips = ({
60
56
  atlasJSON={atlasJSON}
61
57
  equipmentSet={equipmentSet}
62
58
  closeTooltip={() => {
63
- setIsTooltipMobileVisible(false);
59
+ updateDetailsState({ isTooltipMobileVisible: false });
64
60
  }}
65
61
  scale={dragScale}
66
62
  options={contextActions}
67
63
  onSelected={(optionId: string) => {
68
- setIsContextMenuVisible(false);
64
+ updateDetailsState({
65
+ isContextMenuVisible: false,
66
+ });
69
67
  if (item) {
70
68
  onSelected?.(optionId, item);
71
69
  }
@@ -73,21 +71,24 @@ export const ItemSlotToolTips = ({
73
71
  />
74
72
  )}
75
73
 
76
- {!isContextMenuDisabled && isContextMenuVisible && contextActions && (
77
- <RelativeListMenu
78
- options={contextActions}
79
- onSelected={(optionId: string) => {
80
- setIsContextMenuVisible(false);
81
- if (item) {
82
- onSelected?.(optionId, item);
83
- }
84
- }}
85
- onOutsideClick={() => {
86
- setIsContextMenuVisible(false);
87
- }}
88
- pos={contextMenuPosition}
89
- />
90
- )}
74
+ {!isContextMenuDisabled &&
75
+ isContextMenuVisible &&
76
+ contextActions &&
77
+ contextActions.length > 0 && (
78
+ <RelativeListMenu
79
+ options={contextActions}
80
+ onSelected={(optionId: string) => {
81
+ updateDetailsState({ isContextMenuVisible: false });
82
+ if (item) {
83
+ onSelected?.(optionId, item);
84
+ }
85
+ }}
86
+ onOutsideClick={() => {
87
+ updateDetailsState({ isContextMenuVisible: false });
88
+ }}
89
+ pos={contextMenuPosition}
90
+ />
91
+ )}
91
92
  </>
92
93
  );
93
94
  };