@rpg-engine/long-bow 0.7.72 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpg-engine/long-bow",
3
- "version": "0.7.72",
3
+ "version": "0.7.73",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -2,6 +2,7 @@ import {
2
2
  IEquipmentSet,
3
3
  IItem,
4
4
  IItemContainer,
5
+ isMobile,
5
6
  ItemContainerType,
6
7
  ItemSlotType,
7
8
  ItemType,
@@ -10,9 +11,11 @@ import React from 'react';
10
11
  import styled from 'styled-components';
11
12
  import { IPosition } from '../../types/eventTypes';
12
13
  import { DraggableContainer } from '../DraggableContainer';
14
+ import { ItemSlotDetailsProvider } from '../Item/Inventory/context/ItemSlotDetailsContext';
15
+ import { ItemSlotDraggingProvider } from '../Item/Inventory/context/ItemSlotDraggingContext';
13
16
  import DraggedItem from '../Item/Inventory/DraggedItem';
14
17
  import { ItemSlot } from '../Item/Inventory/ItemSlot';
15
- import { DraggingProvider } from '../Item/Inventory/context/DraggingContext';
18
+ import { ItemSlotToolTips } from '../Item/Inventory/ItemSlotTooltips';
16
19
  import { RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
17
20
 
18
21
  export interface IEquipmentSetProps {
@@ -129,9 +132,6 @@ export const EquipmentSet: React.FC<IEquipmentSetProps> = ({
129
132
  onPointerDown={(itemType, ContainerType) => {
130
133
  if (onItemClick) onItemClick(itemType, item, ContainerType);
131
134
  }}
132
- onSelected={(optionId: string) => {
133
- if (onSelected) onSelected(optionId);
134
- }}
135
135
  onDragStart={(item, slotIndex, itemContainerType) => {
136
136
  if (!item) {
137
137
  return;
@@ -161,28 +161,45 @@ export const EquipmentSet: React.FC<IEquipmentSetProps> = ({
161
161
  };
162
162
 
163
163
  return (
164
- <DraggingProvider>
165
- <DraggedItem atlasIMG={atlasIMG} atlasJSON={atlasJSON} scale={scale} />
166
- <DraggableContainer
167
- title={'Equipments'}
168
- type={RPGUIContainerTypes.Framed}
169
- onCloseButton={() => {
170
- if (onClose) onClose();
171
- }}
172
- width="330px"
173
- cancelDrag=".equipment-container-body"
174
- scale={scale}
175
- initialPosition={initialPosition}
176
- onPositionChangeEnd={onPositionChangeEnd}
177
- onPositionChangeStart={onPositionChangeStart}
178
- >
179
- <EquipmentSetContainer className="equipment-container-body">
180
- <EquipmentColumn>{onRenderEquipmentSlotRange(0, 3)}</EquipmentColumn>
181
- <EquipmentColumn>{onRenderEquipmentSlotRange(3, 7)}</EquipmentColumn>
182
- <EquipmentColumn>{onRenderEquipmentSlotRange(7, 10)}</EquipmentColumn>
183
- </EquipmentSetContainer>
184
- </DraggableContainer>
185
- </DraggingProvider>
164
+ <ItemSlotDraggingProvider>
165
+ <ItemSlotDetailsProvider>
166
+ <DraggedItem atlasIMG={atlasIMG} atlasJSON={atlasJSON} scale={scale} />
167
+ <DraggableContainer
168
+ title={'Equipments'}
169
+ type={RPGUIContainerTypes.Framed}
170
+ onCloseButton={() => {
171
+ if (onClose) onClose();
172
+ }}
173
+ width="330px"
174
+ cancelDrag=".equipment-container-body"
175
+ scale={scale}
176
+ initialPosition={initialPosition}
177
+ onPositionChangeEnd={onPositionChangeEnd}
178
+ onPositionChangeStart={onPositionChangeStart}
179
+ >
180
+ <EquipmentSetContainer className="equipment-container-body">
181
+ <EquipmentColumn>
182
+ {onRenderEquipmentSlotRange(0, 3)}
183
+ </EquipmentColumn>
184
+ <EquipmentColumn>
185
+ {onRenderEquipmentSlotRange(3, 7)}
186
+ </EquipmentColumn>
187
+ <EquipmentColumn>
188
+ {onRenderEquipmentSlotRange(7, 10)}
189
+ </EquipmentColumn>
190
+ </EquipmentSetContainer>
191
+ </DraggableContainer>
192
+
193
+ <ItemSlotToolTips
194
+ dragScale={scale}
195
+ atlasIMG={atlasIMG}
196
+ atlasJSON={atlasJSON}
197
+ equipmentSet={equipmentSet}
198
+ onSelected={onSelected}
199
+ isContextMenuDisabled={isMobile()}
200
+ />
201
+ </ItemSlotDetailsProvider>
202
+ </ItemSlotDraggingProvider>
186
203
  );
187
204
  };
188
205
 
@@ -4,7 +4,7 @@ import styled from 'styled-components';
4
4
  import { useCursorPosition } from '../../../hooks/useCursorPosition';
5
5
  import { SpriteFromAtlas } from '../../shared/SpriteFromAtlas';
6
6
  import { onRenderStackInfo } from './ItemSlotQty/ItemSlotQty';
7
- import { useDragging } from './context/DraggingContext';
7
+ import { useItemSlotDragging } from './context/ItemSlotDraggingContext';
8
8
 
9
9
  const CONTAINER_SIZE = 32;
10
10
  const OFFSET = CONTAINER_SIZE / 2;
@@ -20,7 +20,9 @@ export const DraggedItem = ({
20
20
  atlasIMG,
21
21
  scale,
22
22
  }: IProps): JSX.Element | null => {
23
- const { item } = useDragging();
23
+ const { draggingState } = useItemSlotDragging();
24
+
25
+ const { item } = draggingState;
24
26
 
25
27
  const { x, y } = useCursorPosition({
26
28
  scale,
@@ -7,17 +7,19 @@ import {
7
7
  ItemSubType,
8
8
  ItemType,
9
9
  } from '@rpg-engine/shared';
10
- import React, { useCallback, useRef, useState } from 'react';
10
+ import React, { useCallback, useMemo, useRef, useState } from 'react';
11
11
  import styled from 'styled-components';
12
12
  import { SlotsContainer } from '../../Abstractions/SlotsContainer';
13
13
 
14
14
  import { useScrollOnDrag } from '../../../hooks/useScrollOnDrag';
15
15
  import { IPosition } from '../../../types/eventTypes';
16
16
  import { ShortcutsSetter } from '../../Shortcuts/ShortcutsSetter';
17
+ import { ItemSlotDetailsProvider } from './context/ItemSlotDetailsContext';
18
+ import { ItemSlotDraggingProvider } from './context/ItemSlotDraggingContext';
17
19
  import { DraggedItem } from './DraggedItem';
18
20
  import { ItemQuantitySelectorModal } from './ItemQuantitySelectorModal';
19
21
  import { ItemSlot } from './ItemSlot';
20
- import { DraggingProvider } from './context/DraggingContext';
22
+ import { ItemSlotToolTips } from './ItemSlotTooltips';
21
23
 
22
24
  export interface IItemContainerProps {
23
25
  itemContainer: IItemContainer;
@@ -71,207 +73,272 @@ type onDragEnd = ((quantity?: number) => void) | undefined;
71
73
 
72
74
  const MIN_SLOTS_FOR_SCROLL = 20;
73
75
 
74
- export const ItemContainer: React.FC<IItemContainerProps> = ({
75
- itemContainer,
76
- onClose,
77
- onMouseOver,
78
- onSelected,
79
- onItemClick,
80
- type,
81
- atlasJSON,
82
- atlasIMG,
83
- disableContextMenu = false,
84
- onItemDragEnd,
85
- onItemDragStart,
86
- onItemPlaceDrop,
87
- onOutsideDrop,
88
- checkIfItemCanBeMoved,
89
- initialPosition,
90
- checkIfItemShouldDragEnd,
91
- scale,
92
- shortcuts,
93
- setItemShortcut,
94
- removeShortcut,
95
- equipmentSet,
96
- isDepotSystem,
97
- onPositionChangeEnd,
98
- onPositionChangeStart,
99
- isFullScreen,
100
- opacity,
101
- }) => {
102
- const [quantitySelect, setQuantitySelect] = useState({
103
- isOpen: false,
104
- maxQuantity: 1,
105
- callback: (_quantity: number) => {},
106
- });
107
- const [settingShortcutIndex, setSettingShortcutIndex] = useState(-1);
76
+ export const ItemContainer: React.FC<IItemContainerProps> = React.memo(
77
+ ({
78
+ itemContainer,
79
+ onClose,
80
+ onMouseOver,
81
+ onSelected,
82
+ onItemClick,
83
+ type,
84
+ atlasJSON,
85
+ atlasIMG,
86
+ disableContextMenu = false,
87
+ onItemDragEnd,
88
+ onItemDragStart,
89
+ onItemPlaceDrop,
90
+ onOutsideDrop,
91
+ checkIfItemCanBeMoved,
92
+ initialPosition,
93
+ checkIfItemShouldDragEnd,
94
+ scale,
95
+ shortcuts,
96
+ setItemShortcut,
97
+ removeShortcut,
98
+ equipmentSet,
99
+ isDepotSystem,
100
+ onPositionChangeEnd,
101
+ onPositionChangeStart,
102
+ isFullScreen,
103
+ opacity,
104
+ }) => {
105
+ const [quantitySelect, setQuantitySelect] = useState({
106
+ isOpen: false,
107
+ maxQuantity: 1,
108
+ callback: (_quantity: number) => {},
109
+ });
110
+ const [settingShortcutIndex, setSettingShortcutIndex] = useState(-1);
108
111
 
109
- const containerRef = useRef<HTMLDivElement>(null);
112
+ const containerRef = useRef<HTMLDivElement>(null);
110
113
 
111
- const { startScrolling, stopScrolling } = useScrollOnDrag({
112
- containerRef,
113
- threshold: 50,
114
- scrollStep: 4,
115
- });
114
+ const { startScrolling, stopScrolling } = useScrollOnDrag({
115
+ containerRef,
116
+ threshold: 50,
117
+ scrollStep: 4,
118
+ });
116
119
 
117
- const handleSetShortcut = (item: IItem, index: number) => {
118
- if (
119
- item.type === ItemType.Consumable ||
120
- item.type === ItemType.Tool ||
121
- item.subType === ItemSubType.Seed
122
- ) {
123
- setItemShortcut?.(item.key, index);
124
- }
125
- };
120
+ const handleSetShortcut = useCallback(
121
+ (item: IItem, index: number) => {
122
+ if (
123
+ item.type === ItemType.Consumable ||
124
+ item.type === ItemType.Tool ||
125
+ item.subType === ItemSubType.Seed
126
+ ) {
127
+ setItemShortcut?.(item.key, index);
128
+ }
129
+ },
130
+ [setItemShortcut]
131
+ );
126
132
 
127
- const handleMouseMove = useCallback(
128
- (e: MouseEvent) => {
129
- startScrolling(e.clientY);
130
- },
131
- [startScrolling]
132
- );
133
+ const handleMouseMove = useCallback(
134
+ (e: MouseEvent) => {
135
+ startScrolling(e.clientY);
136
+ },
137
+ [startScrolling]
138
+ );
133
139
 
134
- const onDragStartScrollingEvents = useCallback(() => {
135
- document.addEventListener('pointermove', handleMouseMove);
136
- document.addEventListener('pointerup', () => {
140
+ const onDragStartScrollingEvents = useCallback(() => {
141
+ document.addEventListener('pointermove', handleMouseMove);
142
+ document.addEventListener(
143
+ 'pointerup',
144
+ () => {
145
+ stopScrolling();
146
+ document.removeEventListener('pointermove', handleMouseMove);
147
+ },
148
+ { once: true }
149
+ );
150
+ }, [handleMouseMove, stopScrolling]);
151
+
152
+ const onDragEndScrollingEvents = useCallback(() => {
137
153
  stopScrolling();
138
154
  document.removeEventListener('pointermove', handleMouseMove);
139
- });
140
- }, [handleMouseMove, onItemDragStart, stopScrolling]);
155
+ }, [handleMouseMove, stopScrolling]);
141
156
 
142
- const onDragEndScrollingEvents = useCallback(() => {
143
- stopScrolling();
144
- document.removeEventListener('pointermove', handleMouseMove);
145
- }, [handleMouseMove, onItemDragStart, stopScrolling]);
157
+ const onDragStartHandler: onDragStart = useCallback(
158
+ (
159
+ item: IItem,
160
+ slotIndex: number,
161
+ itemContainerType: ItemContainerType | null
162
+ ) => {
163
+ if (onItemDragStart) {
164
+ onItemDragStart(item, slotIndex, itemContainerType);
165
+ }
146
166
 
147
- const onDragStart: onDragStart = (item, slotIndex, itemContainerType) => {
148
- if (onItemDragStart) {
149
- onItemDragStart(item, slotIndex, itemContainerType);
150
- }
167
+ onDragStartScrollingEvents();
168
+ },
169
+ [onItemDragStart, onDragStartScrollingEvents]
170
+ );
151
171
 
152
- onDragStartScrollingEvents();
153
- };
172
+ const onDragEndHandler: onDragEnd = useCallback(
173
+ (quantity: number | undefined) => {
174
+ if (onItemDragEnd) {
175
+ onItemDragEnd(quantity);
176
+ }
177
+ onDragEndScrollingEvents();
178
+ },
179
+ [onItemDragEnd, onDragEndScrollingEvents]
180
+ );
154
181
 
155
- const onDragEnd: onDragEnd = quantity => {
156
- if (onItemDragEnd) {
157
- onItemDragEnd(quantity);
158
- }
159
- onDragEndScrollingEvents();
160
- };
182
+ // Memoize handleSetShortcut to prevent unnecessary re-renders
183
+ const memoizedHandleSetShortcut = useCallback(
184
+ (item: IItem, index: number) => {
185
+ handleSetShortcut(item, index);
186
+ },
187
+ [handleSetShortcut]
188
+ );
161
189
 
162
- const onRenderSlots = () => {
163
- const slots = [];
190
+ // Memoize onRenderSlots to prevent re-creating the slots array on every render
191
+ const onRenderSlots = useMemo(() => {
192
+ const slots = [];
164
193
 
165
- for (let i = 0; i < itemContainer.slotQty; i++) {
166
- slots.push(
167
- <ItemSlot
168
- isContextMenuDisabled={disableContextMenu}
169
- key={i}
170
- slotIndex={i}
171
- item={itemContainer.slots?.[i] || null}
172
- itemContainerType={type}
173
- onMouseOver={(event, slotIndex, item) => {
174
- if (onMouseOver) onMouseOver(event, slotIndex, item);
175
- }}
176
- onPointerDown={(itemType, containerType, item) => {
177
- if (settingShortcutIndex !== -1) {
178
- setSettingShortcutIndex(-1);
194
+ for (let i = 0; i < itemContainer.slotQty; i++) {
195
+ const currentItem = itemContainer.slots?.[i] || null;
179
196
 
180
- handleSetShortcut(item, settingShortcutIndex);
181
- } else if (onItemClick) {
182
- onItemClick(item, itemType, containerType);
183
- }
184
- }}
185
- onSelected={(optionId: string, item: IItem) => {
186
- if (onSelected) onSelected(optionId, item);
187
- }}
188
- onDragStart={onDragStart}
189
- onDragEnd={onDragEnd}
190
- dragScale={scale}
191
- checkIfItemCanBeMoved={checkIfItemCanBeMoved}
192
- checkIfItemShouldDragEnd={checkIfItemShouldDragEnd}
193
- openQuantitySelector={(maxQuantity, callback) => {
194
- setQuantitySelect({
195
- isOpen: true,
196
- maxQuantity,
197
- callback,
198
- });
199
- }}
200
- onPlaceDrop={(item, slotIndex, itemContainerType) => {
201
- if (onItemPlaceDrop) {
202
- onItemPlaceDrop(item, slotIndex, itemContainerType);
197
+ slots.push(
198
+ <ItemSlot
199
+ key={i}
200
+ slotIndex={i}
201
+ item={currentItem}
202
+ itemContainerType={type}
203
+ onMouseOver={
204
+ onMouseOver
205
+ ? (event, slotIndex, item) =>
206
+ onMouseOver(event, slotIndex, item)
207
+ : undefined
203
208
  }
209
+ onMouseOut={undefined} // Assuming you don't need onMouseOut here
210
+ onPointerDown={(itemType, containerType, item) => {
211
+ if (settingShortcutIndex !== -1) {
212
+ setSettingShortcutIndex(-1);
213
+ memoizedHandleSetShortcut(item, settingShortcutIndex);
214
+ } else if (onItemClick) {
215
+ onItemClick(item, itemType, containerType);
216
+ }
217
+ }}
218
+ onDragStart={onDragStartHandler}
219
+ onDragEnd={onDragEndHandler}
220
+ dragScale={scale}
221
+ checkIfItemCanBeMoved={checkIfItemCanBeMoved}
222
+ checkIfItemShouldDragEnd={checkIfItemShouldDragEnd}
223
+ openQuantitySelector={(maxQuantity, callback) => {
224
+ setQuantitySelect({
225
+ isOpen: true,
226
+ maxQuantity,
227
+ callback,
228
+ });
229
+ }}
230
+ onPlaceDrop={(item, slotIndex, itemContainerType) => {
231
+ if (onItemPlaceDrop) {
232
+ onItemPlaceDrop(item, slotIndex, itemContainerType);
233
+ }
204
234
 
205
- console.log('PLACE DROP');
235
+ console.log('PLACE DROP');
206
236
 
207
- onDragEndScrollingEvents();
208
- }}
209
- onOutsideDrop={(item, position) => {
210
- if (onOutsideDrop) {
211
- onOutsideDrop(item, position);
212
- }
237
+ onDragEndScrollingEvents();
238
+ }}
239
+ onOutsideDrop={(item, position) => {
240
+ if (onOutsideDrop) {
241
+ onOutsideDrop(item, position);
242
+ }
213
243
 
214
- console.log('OUTSIDE DROP');
244
+ console.log('OUTSIDE DROP');
215
245
 
216
- onDragEndScrollingEvents();
217
- }}
218
- atlasIMG={atlasIMG}
219
- atlasJSON={atlasJSON}
220
- isSelectingShortcut={settingShortcutIndex !== -1}
221
- equipmentSet={equipmentSet}
222
- setItemShortcut={
223
- type === ItemContainerType.Inventory ? handleSetShortcut : undefined
224
- }
225
- isDepotSystem={isDepotSystem}
226
- />
227
- );
228
- }
229
- return slots;
230
- };
246
+ onDragEndScrollingEvents();
247
+ }}
248
+ atlasIMG={atlasIMG}
249
+ atlasJSON={atlasJSON}
250
+ isSelectingShortcut={settingShortcutIndex !== -1}
251
+ equipmentSet={equipmentSet}
252
+ setItemShortcut={
253
+ type === ItemContainerType.Inventory
254
+ ? memoizedHandleSetShortcut
255
+ : undefined
256
+ }
257
+ isDepotSystem={isDepotSystem}
258
+ />
259
+ );
260
+ }
261
+ return slots;
262
+ }, [
263
+ itemContainer.slotQty,
264
+ itemContainer.slots,
265
+ type,
266
+ onMouseOver,
267
+ settingShortcutIndex,
268
+ memoizedHandleSetShortcut,
269
+ onItemClick,
270
+ onDragStartHandler,
271
+ onDragEndHandler,
272
+ scale,
273
+ checkIfItemCanBeMoved,
274
+ checkIfItemShouldDragEnd,
275
+ onItemPlaceDrop,
276
+ onOutsideDrop,
277
+ atlasIMG,
278
+ atlasJSON,
279
+ equipmentSet,
280
+ isDepotSystem,
281
+ onDragEndScrollingEvents,
282
+ ]);
231
283
 
232
- return (
233
- <DraggingProvider>
234
- <DraggedItem atlasIMG={atlasIMG} atlasJSON={atlasJSON} scale={scale} />
235
- <SlotsContainer
236
- title={itemContainer.name || 'Container'}
237
- onClose={onClose}
238
- initialPosition={initialPosition}
239
- scale={scale}
240
- onPositionChangeEnd={onPositionChangeEnd}
241
- onPositionChangeStart={onPositionChangeStart}
242
- isFullScreen={isFullScreen}
243
- opacity={opacity}
244
- >
245
- {type === ItemContainerType.Inventory &&
246
- shortcuts &&
247
- removeShortcut && (
248
- <ShortcutsSetter
249
- setSettingShortcutIndex={setSettingShortcutIndex}
250
- settingShortcutIndex={settingShortcutIndex}
251
- shortcuts={shortcuts}
252
- removeShortcut={removeShortcut}
253
- atlasIMG={atlasIMG}
254
- atlasJSON={atlasJSON}
284
+ return (
285
+ <ItemSlotDraggingProvider>
286
+ <ItemSlotDetailsProvider>
287
+ <DraggedItem
288
+ atlasIMG={atlasIMG}
289
+ atlasJSON={atlasJSON}
290
+ scale={scale}
291
+ />
292
+ <SlotsContainer
293
+ title={itemContainer.name || 'Container'}
294
+ onClose={onClose}
295
+ initialPosition={initialPosition}
296
+ scale={scale}
297
+ onPositionChangeEnd={onPositionChangeEnd}
298
+ onPositionChangeStart={onPositionChangeStart}
299
+ isFullScreen={isFullScreen}
300
+ opacity={opacity}
301
+ >
302
+ {type === ItemContainerType.Inventory &&
303
+ shortcuts &&
304
+ removeShortcut && (
305
+ <ShortcutsSetter
306
+ setSettingShortcutIndex={setSettingShortcutIndex}
307
+ settingShortcutIndex={settingShortcutIndex}
308
+ shortcuts={shortcuts}
309
+ removeShortcut={removeShortcut}
310
+ atlasIMG={atlasIMG}
311
+ atlasJSON={atlasJSON}
312
+ />
313
+ )}
314
+ <ItemsContainer
315
+ className="item-container-body"
316
+ ref={containerRef}
317
+ isScrollable={itemContainer.slotQty > MIN_SLOTS_FOR_SCROLL}
318
+ isFullScreen={isFullScreen}
319
+ >
320
+ {onRenderSlots}
321
+ </ItemsContainer>
322
+ </SlotsContainer>
323
+ {quantitySelect.isOpen && (
324
+ <ItemQuantitySelectorModal
325
+ quantitySelect={quantitySelect}
326
+ setQuantitySelect={setQuantitySelect}
255
327
  />
256
328
  )}
257
- <ItemsContainer
258
- className="item-container-body"
259
- ref={containerRef}
260
- isScrollable={itemContainer.slotQty > MIN_SLOTS_FOR_SCROLL}
261
- isFullScreen={isFullScreen}
262
- >
263
- {onRenderSlots()}
264
- </ItemsContainer>
265
- </SlotsContainer>
266
- {quantitySelect.isOpen && (
267
- <ItemQuantitySelectorModal
268
- quantitySelect={quantitySelect}
269
- setQuantitySelect={setQuantitySelect}
270
- />
271
- )}
272
- </DraggingProvider>
273
- );
274
- };
329
+ <ItemSlotToolTips
330
+ dragScale={scale}
331
+ atlasIMG={atlasIMG}
332
+ atlasJSON={atlasJSON}
333
+ equipmentSet={equipmentSet}
334
+ isContextMenuDisabled={disableContextMenu}
335
+ onSelected={onSelected}
336
+ />
337
+ </ItemSlotDetailsProvider>
338
+ </ItemSlotDraggingProvider>
339
+ );
340
+ }
341
+ );
275
342
 
276
343
  interface IItemsContainerProps {
277
344
  isScrollable: boolean;