@rpg-engine/long-bow 0.5.27 → 0.5.29

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.5.27",
3
+ "version": "0.5.29",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -5,7 +5,8 @@ import { uiColors } from '../../../constants/uiColors';
5
5
  import { uiFonts } from '../../../constants/uiFonts';
6
6
  import { SpriteFromAtlas } from '../../shared/SpriteFromAtlas';
7
7
  import { ErrorBoundary } from '../Inventory/ErrorBoundary';
8
- import { EquipmentSlotSpriteByType, rarityColor } from '../Inventory/ItemSlot';
8
+ import { EquipmentSlotSpriteByType } from '../Inventory/ItemSlot';
9
+ import { rarityColor } from '../Inventory/ItemSlotRarity';
9
10
 
10
11
  interface IItemInfoProps {
11
12
  item: IItem;
@@ -3,14 +3,13 @@ import {
3
3
  IItem,
4
4
  IItemContainer,
5
5
  ItemContainerType,
6
- ItemRarities,
7
6
  ItemSlotType,
8
7
  ItemType,
9
8
  } from '@rpg-engine/shared';
10
9
 
11
10
  import { observer } from 'mobx-react-lite';
12
11
  import React, { useEffect, useRef, useState } from 'react';
13
- import Draggable from 'react-draggable';
12
+ import Draggable, { DraggableEventHandler } from 'react-draggable';
14
13
  import styled from 'styled-components';
15
14
  import { IPosition } from '../../../types/eventTypes';
16
15
  import { ItemSlotRenderer } from './ItemSlotRenderer';
@@ -156,6 +155,107 @@ export const ItemSlot: React.FC<IProps> = observer(
156
155
  }
157
156
  };
158
157
 
158
+ const onDraggableStop: DraggableEventHandler = (e, data) => {
159
+ setDraggingItem(null);
160
+
161
+ const target = e.target as HTMLElement;
162
+ if (target?.id.includes('shortcutSetter') && setItemShortcut && item) {
163
+ const index = parseInt(target.id.split('_')[1]);
164
+ if (!isNaN(index)) {
165
+ setItemShortcut(item, index);
166
+ }
167
+ }
168
+
169
+ if (wasDragged && item && !isSelectingShortcut) {
170
+ //@ts-ignore
171
+ const classes: string[] = Array.from(e.target?.classList);
172
+
173
+ const isOutsideDrop =
174
+ classes.some(elm => {
175
+ return elm.includes('rpgui-content');
176
+ }) || classes.length === 0;
177
+
178
+ if (isOutsideDrop) {
179
+ setDropPosition({
180
+ x: data.x,
181
+ y: data.y,
182
+ });
183
+ }
184
+
185
+ setWasDragged(false);
186
+
187
+ const target = dragContainer.current;
188
+ if (!target || !wasDragged) return;
189
+
190
+ const style = window.getComputedStyle(target);
191
+ const matrix = new DOMMatrixReadOnly(style.transform);
192
+ const x = matrix.m41;
193
+ const y = matrix.m42;
194
+
195
+ setDragPosition({ x, y });
196
+
197
+ setTimeout(() => {
198
+ if (checkIfItemCanBeMoved?.()) {
199
+ if (checkIfItemShouldDragEnd && !checkIfItemShouldDragEnd()) return;
200
+
201
+ if (item.stackQty && item.stackQty !== 1 && openQuantitySelector)
202
+ openQuantitySelector(item.stackQty, onSuccessfulDrag);
203
+ else onSuccessfulDrag(item.stackQty);
204
+ } else {
205
+ resetItem();
206
+ setIsFocused(false);
207
+ setDragPosition({ x: 0, y: 0 });
208
+ }
209
+ }, 50);
210
+ } else if (item) {
211
+ let isTouch = false;
212
+ if (
213
+ !isContextMenuDisabled &&
214
+ e.type === 'touchend' &&
215
+ !isSelectingShortcut
216
+ ) {
217
+ isTouch = true;
218
+ setIsTooltipMobileVisible(true);
219
+ }
220
+
221
+ if (!isContextMenuDisabled && !isSelectingShortcut && !isTouch) {
222
+ setIsContextMenuVisible(!isContextMenuVisible);
223
+ const event = e as MouseEvent;
224
+
225
+ if (event.clientX && event.clientY) {
226
+ setContextMenuPosition({
227
+ x: event.clientX - 10,
228
+ y: event.clientY - 5,
229
+ });
230
+ }
231
+ }
232
+
233
+ onPointerDown(item.type, containerType ?? null, item);
234
+ }
235
+ };
236
+
237
+ const onDraggableStart: DraggableEventHandler = () => {
238
+ if (!item || isSelectingShortcut) {
239
+ return;
240
+ }
241
+
242
+ if (onDragStart && containerType) {
243
+ setDraggingItem(item);
244
+
245
+ onDragStart(item, slotIndex, containerType);
246
+ }
247
+ };
248
+
249
+ const onDraggableProgress: DraggableEventHandler = (_e, data) => {
250
+ if (
251
+ Math.abs(data.x - dragPosition.x) > 5 ||
252
+ Math.abs(data.y - dragPosition.y) > 5
253
+ ) {
254
+ setWasDragged(true);
255
+ setIsFocused(true);
256
+ }
257
+ };
258
+
159
259
  return (
160
260
  <Container
161
261
  item={item}
@@ -194,113 +294,9 @@ export const ItemSlot: React.FC<IProps> = observer(
194
294
  defaultClassName={item ? 'draggable' : 'empty-slot'}
195
295
  scale={dragScale}
196
296
  disabled={onDragStart === undefined || onDragEnd === undefined}
197
- onStop={(e, data) => {
198
- setDraggingItem(null);
199
-
200
- const target = e.target as HTMLElement;
201
- if (
202
- target?.id.includes('shortcutSetter') &&
203
- setItemShortcut &&
204
- item
205
- ) {
206
- const index = parseInt(target.id.split('_')[1]);
207
- if (!isNaN(index)) {
208
- setItemShortcut(item, index);
209
- }
210
- }
211
-
212
- if (wasDragged && item && !isSelectingShortcut) {
213
- //@ts-ignore
214
- const classes: string[] = Array.from(e.target?.classList);
215
-
216
- const isOutsideDrop =
217
- classes.some(elm => {
218
- return elm.includes('rpgui-content');
219
- }) || classes.length === 0;
220
-
221
- if (isOutsideDrop) {
222
- setDropPosition({
223
- x: data.x,
224
- y: data.y,
225
- });
226
- }
227
-
228
- setWasDragged(false);
229
-
230
- const target = dragContainer.current;
231
- if (!target || !wasDragged) return;
232
-
233
- const style = window.getComputedStyle(target);
234
- const matrix = new DOMMatrixReadOnly(style.transform);
235
- const x = matrix.m41;
236
- const y = matrix.m42;
237
-
238
- setDragPosition({ x, y });
239
-
240
- setTimeout(() => {
241
- if (checkIfItemCanBeMoved?.()) {
242
- if (checkIfItemShouldDragEnd && !checkIfItemShouldDragEnd())
243
- return;
244
-
245
- if (
246
- item.stackQty &&
247
- item.stackQty !== 1 &&
248
- openQuantitySelector
249
- )
250
- openQuantitySelector(item.stackQty, onSuccessfulDrag);
251
- else onSuccessfulDrag(item.stackQty);
252
- } else {
253
- resetItem();
254
- setIsFocused(false);
255
- setDragPosition({ x: 0, y: 0 });
256
- }
257
- }, 50);
258
- } else if (item) {
259
- let isTouch = false;
260
- if (
261
- !isContextMenuDisabled &&
262
- e.type === 'touchend' &&
263
- !isSelectingShortcut
264
- ) {
265
- isTouch = true;
266
- setIsTooltipMobileVisible(true);
267
- }
268
-
269
- if (!isContextMenuDisabled && !isSelectingShortcut && !isTouch) {
270
- setIsContextMenuVisible(!isContextMenuVisible);
271
- const event = e as MouseEvent;
272
-
273
- if (event.clientX && event.clientY) {
274
- setContextMenuPosition({
275
- x: event.clientX - 10,
276
- y: event.clientY - 5,
277
- });
278
- }
279
- }
280
-
281
- onPointerDown(item.type, containerType ?? null, item);
282
- }
283
- }}
284
- onStart={() => {
285
- if (!item || isSelectingShortcut) {
286
- return;
287
- }
288
-
289
- if (onDragStart && containerType) {
290
- setDraggingItem(item);
291
-
292
- onDragStart(item, slotIndex, containerType);
293
- }
294
- }}
295
- onDrag={(_e, data) => {
296
- if (
297
- Math.abs(data.x - dragPosition.x) > 5 ||
298
- Math.abs(data.y - dragPosition.y) > 5
299
- ) {
300
- setWasDragged(true);
301
- setIsFocused(true);
302
- }
303
- }}
297
+ onStop={onDraggableStop}
298
+ onStart={onDraggableStart}
299
+ onDrag={onDraggableProgress}
304
300
  position={dragPosition}
305
301
  cancel=".empty-slot"
306
302
  >
@@ -362,21 +358,6 @@ export const ItemSlot: React.FC<IProps> = observer(
362
358
  }
363
359
  );
364
360
 
365
- export const rarityColor = (item: IItem | null) => {
366
- switch (item?.rarity) {
367
- case ItemRarities.Uncommon:
368
- return 'rgba(13, 193, 13, 0.6)';
369
- case ItemRarities.Rare:
370
- return 'rgba(8, 104, 187, 0.6)';
371
- case ItemRarities.Epic:
372
- return 'rgba(191, 0, 255, 0.6)';
373
- case ItemRarities.Legendary:
374
- return 'rgba(255, 191, 0,0.6)';
375
- default:
376
- return null;
377
- }
378
- };
379
-
380
361
  interface ContainerTypes {
381
362
  item: IItem | null;
382
363
  containerType?: ItemContainerType | null;
@@ -387,20 +368,9 @@ const Container = styled.div<ContainerTypes>`
387
368
  margin: 0.1rem;
388
369
 
389
370
  .react-draggable-dragging {
390
- display: none;
391
- }
392
-
393
-
394
- .sprite-from-atlas-img--item {
395
- position: relative;
396
- top: 1.5rem;
397
- left: 1.5rem;
398
- border-color: ${({ item }) => rarityColor(item)};
399
- box-shadow: ${({ item }) => `0 0 5px 2px ${rarityColor(item)}`} inset, ${({
400
- item,
401
- }) => `0 0 4px 3px ${rarityColor(item)}`};
402
- //background-color: ${({ item }) => rarityColor(item)};
371
+ opacity: 0;
403
372
  }
373
+
404
374
  position: relative;
405
375
 
406
376
  &::before {
@@ -433,7 +403,7 @@ const Container = styled.div<ContainerTypes>`
433
403
  const ItemContainer = styled.div<{ isFocused?: boolean }>`
434
404
  width: 64px;
435
405
  height: 64px;
436
- // This fixes an issue where if you drag an item inside of a scrollable container (overflow), its cut out
406
+
437
407
  position: relative;
438
408
  ${props => props.isFocused && 'z-index: 100; pointer-events: none;'};
439
409
  `;
@@ -0,0 +1,46 @@
1
+ import { IItem, ItemRarities } from '@rpg-engine/shared';
2
+ import React from 'react';
3
+ import styled from 'styled-components';
4
+
5
+ interface IProps {
6
+ item: IItem | null;
7
+ children: React.ReactNode;
8
+ }
9
+
10
+ export const ItemSlotRarity = ({ children, item }: IProps) => {
11
+ if (!item) return <>{children}</>;
12
+
13
+ return <Container item={item}>{children}</Container>;
14
+ };
15
+
16
+ interface IContainer {
17
+ item: IItem | null;
18
+ }
19
+
20
+ export const rarityColor = (item: IItem | null) => {
21
+ switch (item?.rarity) {
22
+ case ItemRarities.Uncommon:
23
+ return 'rgba(13, 193, 13, 0.6)';
24
+ case ItemRarities.Rare:
25
+ return 'rgba(8, 104, 187, 0.6)';
26
+ case ItemRarities.Epic:
27
+ return 'rgba(191, 0, 255, 0.6)';
28
+ case ItemRarities.Legendary:
29
+ return 'rgba(255, 191, 0,0.6)';
30
+ default:
31
+ return null;
32
+ }
33
+ };
34
+ const Container = styled.div<IContainer>`
35
+ .sprite-from-atlas-img--item {
36
+ position: relative;
37
+ top: 1.5rem;
38
+ left: 1.5rem;
39
+ border-color: ${({ item }) => rarityColor(item)};
40
+ box-shadow: ${({ item }) => `0 0 5px 2px ${rarityColor(item)}`} inset, ${({
41
+ item,
42
+ }) => `0 0 4px 3px ${rarityColor(item)}`};
43
+ //background-color: ${({ item }) => rarityColor(item)};
44
+ }
45
+
46
+ `;
@@ -10,6 +10,7 @@ import { SpriteFromAtlas } from '../../shared/SpriteFromAtlas';
10
10
  import { ErrorBoundary } from './ErrorBoundary';
11
11
  import { EquipmentSlotSpriteByType } from './ItemSlot';
12
12
  import { onRenderStackInfo } from './ItemSlotQty/ItemSlotQty';
13
+ import { ItemSlotRarity } from './ItemSlotRarity';
13
14
 
14
15
  interface IProps {
15
16
  containerType: ItemContainerType | null | undefined;
@@ -26,29 +27,31 @@ export const ItemSlotRenderer: React.FC<IProps> = ({
26
27
  slotSpriteMask,
27
28
  item,
28
29
  }) => {
29
- const renderItem = (itemToRender: IItem | null) => {
30
- if (!itemToRender?.texturePath) {
30
+ const renderItem = (item: IItem | null) => {
31
+ if (!item?.texturePath) {
31
32
  return null;
32
33
  }
33
34
 
34
35
  return (
35
- <ErrorBoundary key={itemToRender._id}>
36
- <SpriteFromAtlas
37
- atlasIMG={atlasIMG}
38
- atlasJSON={atlasJSON}
39
- spriteKey={getItemTextureKeyPath(
40
- {
41
- key: itemToRender.texturePath,
42
- texturePath: itemToRender.texturePath,
43
- stackQty: itemToRender.stackQty || 1,
44
- isStackable: itemToRender.isStackable,
45
- },
46
- atlasJSON
47
- )}
48
- imgScale={3}
49
- imgClassname="sprite-from-atlas-img--item"
50
- />
51
- {onRenderStackInfo(itemToRender._id, itemToRender.stackQty ?? 0)}
36
+ <ErrorBoundary key={item._id}>
37
+ <ItemSlotRarity item={item}>
38
+ <SpriteFromAtlas
39
+ atlasIMG={atlasIMG}
40
+ atlasJSON={atlasJSON}
41
+ spriteKey={getItemTextureKeyPath(
42
+ {
43
+ key: item.texturePath,
44
+ texturePath: item.texturePath,
45
+ stackQty: item.stackQty || 1,
46
+ isStackable: item.isStackable,
47
+ },
48
+ atlasJSON
49
+ )}
50
+ imgScale={3}
51
+ imgClassname="sprite-from-atlas-img--item"
52
+ />
53
+ {onRenderStackInfo(item._id, item.stackQty ?? 0)}
54
+ </ItemSlotRarity>
52
55
  </ErrorBoundary>
53
56
  );
54
57
  };
@@ -81,12 +84,8 @@ export const ItemSlotRenderer: React.FC<IProps> = ({
81
84
  switch (containerType) {
82
85
  case ItemContainerType.Equipment:
83
86
  return renderEquipment(itemToRender);
84
- case ItemContainerType.Inventory:
85
- return renderItem(itemToRender);
86
- case ItemContainerType.Depot:
87
- return renderItem(itemToRender);
88
87
  default:
89
- return null;
88
+ return renderItem(itemToRender);
90
89
  }
91
90
  };
92
91
 
@@ -9,7 +9,7 @@ import { uiColors } from '../../constants/uiColors';
9
9
  import { uiFonts } from '../../constants/uiFonts';
10
10
  import { Button, ButtonTypes } from '../Button';
11
11
  import { ItemInfoWrapper } from '../Item/Cards/ItemInfoWrapper';
12
- import { rarityColor } from '../Item/Inventory/ItemSlot';
12
+ import { rarityColor } from '../Item/Inventory/ItemSlotRarity';
13
13
  import { Ellipsis } from '../shared/Ellipsis';
14
14
  import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
15
15
 
@@ -0,0 +1,30 @@
1
+ import { useCallback, useState } from 'react';
2
+
3
+ interface ITapAndHoldProps {
4
+ onHoldFn: (...args: any[]) => void;
5
+ holdTime?: number;
6
+ }
7
+
8
+ export const useTapAndHold = ({ onHoldFn: onHold, holdTime = 500 }: ITapAndHoldProps) => {
9
+ const [timer, setTimer] = useState<NodeJS.Timeout | null>(null);
10
+
11
+ const start = useCallback(() => {
12
+ const timeout = setTimeout(onHold, holdTime);
13
+ setTimer(timeout);
14
+ }, [onHold, holdTime]);
15
+
16
+ const clear = useCallback(() => {
17
+ if (timer) {
18
+ clearTimeout(timer);
19
+ setTimer(null);
20
+ }
21
+ }, [timer]);
22
+
23
+ const bind = {
24
+ onTouchStart: start,
25
+ onTouchEnd: clear,
26
+ onTouchCancel: clear,
27
+ };
28
+
29
+ return bind;
30
+ };
@@ -31,7 +31,7 @@ Default.args = {
31
31
  onActionClick: () => console.log('action'),
32
32
  onCancelClick: () => console.log('cancel attack'),
33
33
  mana: 100,
34
- inventory: itemContainerMock,
34
+ inventory: itemContainerMock(),
35
35
  atlasJSON,
36
36
  atlasIMG,
37
37
  };
@@ -35,5 +35,5 @@ Default.args = {
35
35
  mana: 100,
36
36
  atlasIMG,
37
37
  atlasJSON,
38
- inventory: itemContainerMock,
38
+ inventory: itemContainerMock(),
39
39
  };