@rpg-engine/long-bow 0.7.39 → 0.7.41

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.39",
3
+ "version": "0.7.41",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -193,6 +193,7 @@ const EquipmentSetContainer = styled.div`
193
193
  flex-wrap: wrap;
194
194
  flex-direction: row;
195
195
  touch-action: none;
196
+ overflow: hidden;
196
197
  `;
197
198
 
198
199
  const EquipmentColumn = styled.div`
@@ -180,7 +180,9 @@ export const ItemContainer: React.FC<IItemContainerProps> = ({
180
180
  setSettingShortcutIndex(-1);
181
181
 
182
182
  handleSetShortcut(item, settingShortcutIndex);
183
- } else if (onItemClick) onItemClick(item, itemType, containerType);
183
+ } else if (onItemClick) {
184
+ onItemClick(item, itemType, containerType);
185
+ }
184
186
  }}
185
187
  onSelected={(optionId: string, item: IItem) => {
186
188
  if (onSelected) onSelected(optionId, item);
@@ -9,7 +9,7 @@ import {
9
9
  } from '@rpg-engine/shared';
10
10
 
11
11
  import { observer } from 'mobx-react-lite';
12
- import React, { useEffect, useRef, useState } from 'react';
12
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
13
13
  import Draggable, { DraggableEventHandler } from 'react-draggable';
14
14
  import styled from 'styled-components';
15
15
  import { IPosition } from '../../../types/eventTypes';
@@ -77,299 +77,361 @@ interface IProps {
77
77
  isDepotSystem?: boolean;
78
78
  }
79
79
 
80
- export const ItemSlot: React.FC<IProps> = observer(
81
- ({
82
- slotIndex,
83
- item,
84
- itemContainerType: containerType,
85
- slotSpriteMask,
86
- onMouseOver,
87
- onMouseOut,
88
- onPointerDown,
89
- onSelected,
90
- atlasJSON,
91
- atlasIMG,
92
- isContextMenuDisabled = false,
93
- onDragEnd,
94
- onDragStart,
95
- onPlaceDrop,
96
- onOutsideDrop: onDrop,
97
- checkIfItemCanBeMoved,
98
- openQuantitySelector,
99
- checkIfItemShouldDragEnd,
100
- dragScale,
101
- isSelectingShortcut,
102
- equipmentSet,
103
- setItemShortcut,
104
- isDepotSystem,
105
- }) => {
106
- const [isTooltipVisible, setTooltipVisible] = useState(false);
107
- const [isTooltipMobileVisible, setIsTooltipMobileVisible] = useState(false);
108
-
109
- const [isContextMenuVisible, setIsContextMenuVisible] = useState(false);
110
- const [contextMenuPosition, setContextMenuPosition] = useState({
111
- x: 0,
112
- y: 0,
113
- });
114
-
115
- const [isFocused, setIsFocused] = useState(false);
116
- const [wasDragged, setWasDragged] = useState(false);
117
- const [dragPosition, setDragPosition] = useState<IPosition>({ x: 0, y: 0 });
118
- const [dropPosition, setDropPosition] = useState<IPosition | null>(null);
119
- const dragContainer = useRef<HTMLDivElement>(null);
120
-
121
- const { item: draggingItem, setDraggingItem } = useDragging();
122
-
123
- const [contextActions, setContextActions] = useState<IContextMenuItem[]>(
124
- []
125
- );
126
-
127
- useEffect(() => {
128
- setDragPosition({ x: 0, y: 0 });
129
- setIsFocused(false);
130
-
131
- if (item && containerType) {
132
- setContextActions(
133
- generateContextMenu(item, containerType, isDepotSystem)
134
- );
135
- }
136
- }, [item, isDepotSystem]);
137
-
138
- useEffect(() => {
139
- if (onDrop && item && dropPosition) {
140
- onDrop(item, dropPosition);
141
- }
142
- }, [dropPosition]);
80
+ export type TooltipState = {
81
+ visible: boolean;
82
+ mobileVisible: boolean;
83
+ };
143
84
 
144
- const resetItem = () => {
145
- setTooltipVisible(false);
146
- setWasDragged(false);
147
- };
85
+ export type ContextMenuState = {
86
+ position: IPosition;
87
+ visible: boolean;
88
+ };
148
89
 
149
- const onSuccessfulDrag = (quantity?: number) => {
150
- resetItem();
90
+ export type DragState = {
91
+ isFocused: boolean;
92
+ wasDragged: boolean;
93
+ position: IPosition;
94
+ dropPosition: IPosition | null;
95
+ };
151
96
 
152
- if (quantity === -1) {
153
- setDragPosition({ x: 0, y: 0 });
154
- setIsFocused(false);
155
- } else if (item) {
156
- onDragEnd?.(quantity);
157
- }
158
- };
97
+ export const ItemSlot = React.memo(
98
+ observer(
99
+ ({
100
+ slotIndex,
101
+ item,
102
+ itemContainerType: containerType,
103
+ slotSpriteMask,
104
+ onMouseOver,
105
+ onMouseOut,
106
+ onPointerDown,
107
+ onSelected,
108
+ atlasJSON,
109
+ atlasIMG,
110
+ isContextMenuDisabled = false,
111
+ onDragEnd,
112
+ onDragStart,
113
+ onPlaceDrop,
114
+ onOutsideDrop: onDrop,
115
+ checkIfItemCanBeMoved,
116
+ openQuantitySelector,
117
+ checkIfItemShouldDragEnd,
118
+ dragScale,
119
+ isSelectingShortcut,
120
+ equipmentSet,
121
+ setItemShortcut,
122
+ isDepotSystem,
123
+ }: IProps): JSX.Element => {
124
+ const [tooltipState, setTooltipState] = useState<TooltipState>({
125
+ visible: false,
126
+ mobileVisible: false,
127
+ });
128
+ const [contextMenuState, setContextMenuState] = useState<
129
+ ContextMenuState
130
+ >({
131
+ visible: false,
132
+ position: { x: 0, y: 0 },
133
+ });
134
+ const [dragState, setDragState] = useState<DragState>({
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();
142
+ const [contextActions, setContextActions] = useState<IContextMenuItem[]>(
143
+ []
144
+ );
145
+
146
+ useEffect(() => {
147
+ setDragState(prev => ({
148
+ ...prev,
149
+ position: { x: 0, y: 0 },
150
+ isFocused: false,
151
+ }));
152
+
153
+ if (item && containerType) {
154
+ setContextActions(
155
+ generateContextMenu(item, containerType, isDepotSystem)
156
+ );
157
+ }
158
+ }, [item, isDepotSystem]);
159
+
160
+ useEffect(() => {
161
+ if (onDrop && item && dragState.dropPosition) {
162
+ onDrop(item, dragState.dropPosition);
163
+ setDragState(prev => ({
164
+ ...prev,
165
+ dropPosition: null,
166
+ }));
167
+ }
168
+ }, [dragState.dropPosition]);
169
+
170
+ const getContainerBounds = useCallback(() => {
171
+ const container = dragContainer.current;
172
+ if (!container) return { left: 0, top: 0, right: 0, bottom: 0 };
173
+ const rect = container.getBoundingClientRect();
174
+ return {
175
+ left: rect.left,
176
+ top: rect.top,
177
+ right: window.innerWidth - rect.right,
178
+ bottom: window.innerHeight - rect.bottom,
179
+ };
180
+ }, [dragContainer]);
181
+
182
+ const resetItem = () => {
183
+ setTooltipState(prev => ({ ...prev, visible: false }));
184
+ setDragState(prev => ({ ...prev, wasDragged: false }));
185
+ };
186
+
187
+ const onSuccessfulDrag = (quantity?: number) => {
188
+ resetItem();
189
+
190
+ if (quantity === -1) {
191
+ setDragState(prev => ({
192
+ ...prev,
193
+ position: { x: 0, y: 0 },
194
+ isFocused: false,
195
+ }));
196
+ } else if (item) {
197
+ onDragEnd?.(quantity);
198
+ }
199
+ };
159
200
 
160
- const onDraggableStop: DraggableEventHandler = (e, data) => {
161
- setDraggingItem(null);
201
+ const onDraggableStop: DraggableEventHandler = (e, data) => {
202
+ setDraggingItem(null);
162
203
 
163
- const target = e.target as HTMLElement;
204
+ const target = e.target as HTMLElement;
205
+ if (!target) return;
164
206
 
165
- if (target?.id.includes('shortcutSetter') && setItemShortcut && item) {
166
- const index = parseInt(target.id.split('_')[1]);
167
- if (!isNaN(index)) {
168
- setItemShortcut(item, index);
207
+ if (target?.id.includes('shortcutSetter') && setItemShortcut && item) {
208
+ const index = parseInt(target.id.split('_')[1]);
209
+ if (!isNaN(index)) {
210
+ setItemShortcut(item, index);
211
+ }
169
212
  }
170
- }
171
213
 
172
- // remove the class react-draggable-dragging from the element
173
- // to prevent the item from being dragged again
214
+ // remove the class react-draggable-dragging from the element
215
+ // to prevent the item from being dragged again
216
+ target.classList.remove('react-draggable-dragging');
174
217
 
175
- target.classList.remove('react-draggable-dragging');
218
+ if (dragState.wasDragged && item && !isSelectingShortcut) {
219
+ //@ts-ignore
220
+ const classes: string[] = Array.from(e.target?.classList);
176
221
 
177
- if (wasDragged && item && !isSelectingShortcut) {
178
- //@ts-ignore
179
- const classes: string[] = Array.from(e.target?.classList);
222
+ const isOutsideDrop =
223
+ classes.some(elm => {
224
+ return elm.includes('rpgui-content');
225
+ }) || classes.length === 0;
180
226
 
181
- const isOutsideDrop =
182
- classes.some(elm => {
183
- return elm.includes('rpgui-content');
184
- }) || classes.length === 0;
185
-
186
- if (isOutsideDrop) {
187
- setDropPosition({
188
- x: data.x,
189
- y: data.y,
190
- });
191
- }
227
+ if (isOutsideDrop) {
228
+ setDragState(prev => ({
229
+ ...prev,
230
+ dropPosition: { x: data.x, y: data.y },
231
+ }));
232
+ }
192
233
 
193
- setWasDragged(false);
234
+ setDragState(prev => ({ ...prev, wasDragged: false }));
235
+
236
+ const target = dragContainer.current;
237
+ if (!target || !dragState.wasDragged) return;
238
+
239
+ const style = window.getComputedStyle(target);
240
+ const matrix = new DOMMatrixReadOnly(style.transform);
241
+ const x = matrix.m41;
242
+ const y = matrix.m42;
243
+
244
+ setDragState(prev => ({
245
+ ...prev,
246
+ position: { x, y },
247
+ }));
248
+
249
+ setTimeout(() => {
250
+ if (checkIfItemCanBeMoved && checkIfItemCanBeMoved()) {
251
+ if (checkIfItemShouldDragEnd && !checkIfItemShouldDragEnd())
252
+ return;
253
+ if (
254
+ item.stackQty &&
255
+ item.stackQty !== 1 &&
256
+ openQuantitySelector
257
+ ) {
258
+ openQuantitySelector(item.stackQty, onSuccessfulDrag);
259
+ } else onSuccessfulDrag(item.stackQty);
260
+ } else {
261
+ resetItem();
262
+ setDragState(prev => ({
263
+ ...prev,
264
+ isFocused: false,
265
+ position: { x: 0, y: 0 },
266
+ }));
267
+ }
268
+ }, 50);
269
+ } else if (item) {
270
+ let isTouch = false;
271
+ if (
272
+ !isContextMenuDisabled &&
273
+ e.type === 'touchend' &&
274
+ !isSelectingShortcut
275
+ ) {
276
+ isTouch = true;
277
+ setTooltipState(prev => ({ ...prev, mobileVisible: true }));
278
+ }
194
279
 
195
- const target = dragContainer.current;
196
- if (!target || !wasDragged) return;
280
+ if (!isContextMenuDisabled && !isSelectingShortcut && !isTouch) {
281
+ setContextMenuState(prev => ({
282
+ ...prev,
283
+ visible: !contextMenuState.visible,
284
+ }));
285
+
286
+ const event = e as MouseEvent;
287
+
288
+ if (event.clientX && event.clientY) {
289
+ setContextMenuState(prev => ({
290
+ ...prev,
291
+ position: {
292
+ x: event.clientX - 10,
293
+ y: event.clientY - 5,
294
+ },
295
+ }));
296
+ }
297
+ }
197
298
 
198
- const style = window.getComputedStyle(target);
199
- const matrix = new DOMMatrixReadOnly(style.transform);
200
- const x = matrix.m41;
201
- const y = matrix.m42;
299
+ onPointerDown(item.type, containerType ?? null, item);
300
+ }
301
+ };
202
302
 
203
- setDragPosition({ x, y });
303
+ const onDraggableStart: DraggableEventHandler = () => {
304
+ if (!item || isSelectingShortcut) {
305
+ return;
306
+ }
204
307
 
205
- setTimeout(() => {
206
- if (checkIfItemCanBeMoved?.()) {
207
- if (checkIfItemShouldDragEnd && !checkIfItemShouldDragEnd()) return;
308
+ if (onDragStart && containerType) {
309
+ onDragStart(item, slotIndex, containerType);
310
+ }
311
+ };
208
312
 
209
- if (item.stackQty && item.stackQty !== 1 && openQuantitySelector)
210
- openQuantitySelector(item.stackQty, onSuccessfulDrag);
211
- else onSuccessfulDrag(item.stackQty);
212
- } else {
213
- resetItem();
214
- setIsFocused(false);
215
- setDragPosition({ x: 0, y: 0 });
216
- }
217
- }, 50);
218
- } else if (item) {
219
- let isTouch = false;
313
+ const onDraggableProgress: DraggableEventHandler = (_e, data) => {
220
314
  if (
221
- !isContextMenuDisabled &&
222
- e.type === 'touchend' &&
223
- !isSelectingShortcut
315
+ Math.abs(data.x - dragState.position.x) > 5 ||
316
+ Math.abs(data.y - dragState.position.y) > 5
224
317
  ) {
225
- isTouch = true;
226
- setIsTooltipMobileVisible(true);
318
+ setDragState(prev => ({
319
+ ...prev,
320
+ wasDragged: true,
321
+ isFocused: true,
322
+ }));
227
323
  }
228
324
 
229
- if (!isContextMenuDisabled && !isSelectingShortcut && !isTouch) {
230
- setIsContextMenuVisible(!isContextMenuVisible);
231
- const event = e as MouseEvent;
232
-
233
- if (event.clientX && event.clientY) {
234
- setContextMenuPosition({
235
- x: event.clientX - 10,
236
- y: event.clientY - 5,
237
- });
238
- }
325
+ if (!draggingItem) {
326
+ setDraggingItem(item);
239
327
  }
328
+ };
240
329
 
241
- onPointerDown(item.type, containerType ?? null, item);
242
- }
243
- };
244
-
245
- const onDraggableStart: DraggableEventHandler = () => {
246
- if (!item || isSelectingShortcut) {
247
- return;
248
- }
249
-
250
- if (onDragStart && containerType) {
251
- onDragStart(item, slotIndex, containerType);
252
- }
253
- };
254
-
255
- const onDraggableProgress: DraggableEventHandler = (_e, data) => {
256
- if (
257
- Math.abs(data.x - dragPosition.x) > 5 ||
258
- Math.abs(data.y - dragPosition.y) > 5
259
- ) {
260
- setWasDragged(true);
261
- setIsFocused(true);
262
- }
330
+ const bounds = getContainerBounds();
331
+ return (
332
+ <Container
333
+ isDraggingItem={!!draggingItem}
334
+ item={item}
335
+ className="rpgui-icon empty-slot"
336
+ onMouseUp={() => {
337
+ const data = item ? item : null;
338
+ if (onPlaceDrop && containerType) {
339
+ onPlaceDrop(data, slotIndex, containerType);
340
+ }
341
+ }}
342
+ onTouchEnd={e => {
343
+ const { clientX, clientY } = e.changedTouches[0];
344
+ const simulatedEvent = new MouseEvent('mouseup', {
345
+ clientX,
346
+ clientY,
347
+ bubbles: true,
348
+ });
263
349
 
264
- if (!draggingItem) {
265
- setDraggingItem(item);
266
- }
267
- };
268
-
269
- return (
270
- <Container
271
- isDraggingItem={!!draggingItem}
272
- item={item}
273
- className="rpgui-icon empty-slot"
274
- onMouseUp={() => {
275
- const data = item ? item : null;
276
- if (onPlaceDrop && containerType)
277
- onPlaceDrop(data, slotIndex, containerType);
278
- }}
279
- onTouchEnd={e => {
280
- const { clientX, clientY } = e.changedTouches[0];
281
- const simulatedEvent = new MouseEvent('mouseup', {
282
- clientX,
283
- clientY,
284
- bubbles: true,
285
- });
286
-
287
- document
288
- .elementFromPoint(clientX, clientY)
289
- ?.dispatchEvent(simulatedEvent);
290
- }}
291
- onPointerDown={
292
- onDragStart !== undefined && onDragEnd !== undefined
293
- ? undefined
294
- : () => {
295
- if (item) onPointerDown(item.type, containerType ?? null, item);
296
- }
297
- }
298
- isSelectingShortcut={
299
- isSelectingShortcut &&
300
- (item?.type === ItemType.Consumable ||
301
- item?.type === ItemType.Tool ||
302
- item?.subType === ItemSubType.Seed)
303
- }
304
- >
305
- <Draggable
306
- axis={isSelectingShortcut ? 'none' : 'both'}
307
- defaultClassName={item ? 'draggable' : 'empty-slot'}
308
- scale={dragScale}
309
- disabled={onDragStart === undefined || onDragEnd === undefined}
310
- onStop={onDraggableStop}
311
- onStart={onDraggableStart}
312
- onDrag={onDraggableProgress}
313
- position={dragPosition}
314
- cancel=".empty-slot"
315
- bounds=".item-container-body, .equipment-container-body"
350
+ document
351
+ .elementFromPoint(clientX, clientY)
352
+ ?.dispatchEvent(simulatedEvent);
353
+ }}
354
+ onPointerDown={
355
+ onDragStart !== undefined && onDragEnd !== undefined
356
+ ? undefined
357
+ : () => {
358
+ if (item)
359
+ onPointerDown(item.type, containerType ?? null, item);
360
+ }
361
+ }
362
+ isSelectingShortcut={
363
+ isSelectingShortcut &&
364
+ (item?.type === ItemType.Consumable ||
365
+ item?.type === ItemType.Tool ||
366
+ item?.subType === ItemSubType.Seed)
367
+ }
316
368
  >
317
- <ItemContainer
318
- ref={dragContainer}
319
- isFocused={isFocused}
320
- onMouseOver={event => {
321
- onMouseOver?.(
322
- event,
323
- slotIndex,
324
- item,
325
- event.clientX,
326
- event.clientY
327
- );
328
- }}
329
- onMouseOut={() => {
330
- if (onMouseOut) onMouseOut();
331
- }}
332
- onMouseEnter={() => {
333
- setTooltipVisible(true);
334
- }}
335
- onMouseLeave={() => {
336
- setTooltipVisible(false);
337
- }}
369
+ <Draggable
370
+ axis={isSelectingShortcut ? 'none' : 'both'}
371
+ defaultClassName={item ? 'draggable' : 'empty-slot'}
372
+ scale={dragScale}
373
+ disabled={onDragStart === undefined || onDragEnd === undefined}
374
+ onStop={onDraggableStop}
375
+ onStart={onDraggableStart}
376
+ onDrag={onDraggableProgress}
377
+ position={dragState.position}
378
+ cancel=".empty-slot"
379
+ bounds={bounds}
338
380
  >
339
- <ItemSlotRenderer
340
- item={item}
341
- slotSpriteMask={slotSpriteMask}
342
- atlasIMG={atlasIMG}
343
- atlasJSON={atlasJSON}
344
- containerType={containerType}
345
- />
346
- </ItemContainer>
347
- </Draggable>
348
-
349
- <ItemSlotToolTips
350
- isTooltipVisible={isTooltipVisible}
351
- isTooltipMobileVisible={isTooltipMobileVisible}
352
- setIsTooltipMobileVisible={setIsTooltipMobileVisible}
353
- isFocused={isFocused}
354
- isContextMenuVisible={isContextMenuVisible}
355
- isContextMenuDisabled={isContextMenuDisabled}
356
- item={item}
357
- contextActions={contextActions}
358
- contextMenuPosition={contextMenuPosition}
359
- dragScale={dragScale}
360
- setIsContextMenuVisible={setIsContextMenuVisible}
361
- onSelected={(optionId: string, item: IItem) => {
362
- setIsContextMenuVisible(false);
363
- if (onSelected) onSelected(optionId, item);
364
- }}
365
- atlasIMG={atlasIMG}
366
- atlasJSON={atlasJSON}
367
- equipmentSet={equipmentSet}
368
- setIsTooltipVisible={setTooltipVisible}
369
- />
370
- </Container>
371
- );
372
- }
381
+ <ItemContainer
382
+ ref={dragContainer}
383
+ isFocused={dragState.isFocused}
384
+ onMouseOver={event => {
385
+ onMouseOver?.(
386
+ event,
387
+ slotIndex,
388
+ item,
389
+ event.clientX,
390
+ event.clientY
391
+ );
392
+ }}
393
+ onMouseOut={() => {
394
+ if (onMouseOut) onMouseOut();
395
+ }}
396
+ onMouseEnter={() => {
397
+ setTooltipState(prev => ({ ...prev, visible: true }));
398
+ }}
399
+ onMouseLeave={() => {
400
+ setTooltipState(prev => ({ ...prev, visible: false }));
401
+ }}
402
+ >
403
+ <ItemSlotRenderer
404
+ item={item}
405
+ slotSpriteMask={slotSpriteMask}
406
+ atlasIMG={atlasIMG}
407
+ atlasJSON={atlasJSON}
408
+ containerType={containerType}
409
+ />
410
+ </ItemContainer>
411
+ </Draggable>
412
+
413
+ <ItemSlotToolTips
414
+ tooltipState={tooltipState}
415
+ setTooltipState={setTooltipState}
416
+ contextMenuState={contextMenuState}
417
+ setContextMenuState={setContextMenuState}
418
+ isFocused={dragState.isFocused}
419
+ isContextMenuDisabled={isContextMenuDisabled}
420
+ item={item}
421
+ contextActions={contextActions}
422
+ dragScale={dragScale}
423
+ onSelected={(optionId: string, item: IItem) => {
424
+ setContextMenuState(prev => ({ ...prev, visible: false }));
425
+ if (onSelected) onSelected(optionId, item);
426
+ }}
427
+ atlasIMG={atlasIMG}
428
+ atlasJSON={atlasJSON}
429
+ equipmentSet={equipmentSet}
430
+ />
431
+ </Container>
432
+ );
433
+ }
434
+ )
373
435
  );
374
436
 
375
437
  interface ContainerTypes {