@rpg-engine/long-bow 0.4.7 → 0.4.8

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.
Files changed (177) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +181 -181
  3. package/dist/components/ConfirmModal.d.ts +8 -0
  4. package/dist/components/DraggableContainer.d.ts +1 -0
  5. package/dist/components/Item/Inventory/ItemSlot.d.ts +7 -7
  6. package/dist/components/Marketplace/BuyPanel.d.ts +22 -0
  7. package/dist/components/Marketplace/ManagmentPanel.d.ts +18 -0
  8. package/dist/components/Marketplace/Marketplace.d.ts +22 -9
  9. package/dist/components/Marketplace/MarketplaceRows.d.ts +3 -1
  10. package/dist/components/Marketplace/{__mocks__ → filters}/index.d.ts +1 -3
  11. package/dist/components/Pager.d.ts +9 -0
  12. package/dist/long-bow.cjs.development.js +1540 -1163
  13. package/dist/long-bow.cjs.development.js.map +1 -1
  14. package/dist/long-bow.cjs.production.min.js +1 -1
  15. package/dist/long-bow.cjs.production.min.js.map +1 -1
  16. package/dist/long-bow.esm.js +1594 -1217
  17. package/dist/long-bow.esm.js.map +1 -1
  18. package/package.json +100 -100
  19. package/src/components/Abstractions/ModalPortal.tsx +22 -22
  20. package/src/components/Abstractions/SlotsContainer.tsx +62 -62
  21. package/src/components/Arrow/SelectArrow.tsx +69 -69
  22. package/src/components/Arrow/img/arrow01-left-clicked.png +0 -0
  23. package/src/components/Arrow/img/arrow01-left.png +0 -0
  24. package/src/components/Arrow/img/arrow01-right-clicked.png +0 -0
  25. package/src/components/Arrow/img/arrow01-right.png +0 -0
  26. package/src/components/Arrow/img/arrow02-left-clicked.png +0 -0
  27. package/src/components/Arrow/img/arrow02-left.png +0 -0
  28. package/src/components/Arrow/img/arrow02-right-clicked.png +0 -0
  29. package/src/components/Arrow/img/arrow02-right.png +0 -0
  30. package/src/components/Button.tsx +40 -40
  31. package/src/components/Character/CharacterSelection.tsx +98 -98
  32. package/src/components/CharacterStatus/CharacterStatus.tsx +120 -120
  33. package/src/components/Chat/Chat.tsx +196 -196
  34. package/src/components/Chatdeprecated/ChatDeprecated.tsx +198 -198
  35. package/src/components/CheckButton.tsx +65 -65
  36. package/src/components/CircularController/CircularController.tsx +282 -282
  37. package/src/components/ConfirmModal.tsx +87 -0
  38. package/src/components/CraftBook/CraftBook.tsx +286 -286
  39. package/src/components/CraftBook/CraftingRecipe.tsx +161 -161
  40. package/src/components/CraftBook/MockItems.ts +101 -101
  41. package/src/components/DraggableContainer.tsx +183 -180
  42. package/src/components/Dropdown.tsx +14 -6
  43. package/src/components/DropdownSelectorContainer.tsx +42 -42
  44. package/src/components/Equipment/EquipmentSet.tsx +199 -199
  45. package/src/components/HistoryDialog.tsx +104 -104
  46. package/src/components/Input.tsx +15 -15
  47. package/src/components/InputRadio.tsx +41 -41
  48. package/src/components/Item/Cards/ItemInfo.tsx +298 -298
  49. package/src/components/Item/Cards/ItemInfoDisplay.tsx +135 -135
  50. package/src/components/Item/Cards/ItemInfoWrapper.tsx +62 -62
  51. package/src/components/Item/Cards/ItemTooltip.tsx +83 -83
  52. package/src/components/Item/Cards/MobileItemTooltip.tsx +149 -149
  53. package/src/components/Item/Inventory/ErrorBoundary.tsx +42 -42
  54. package/src/components/Item/Inventory/ItemContainer.tsx +231 -231
  55. package/src/components/Item/Inventory/ItemContainerTypes.ts +6 -6
  56. package/src/components/Item/Inventory/ItemQuantitySelector.tsx +138 -138
  57. package/src/components/Item/Inventory/ItemSlot.tsx +595 -580
  58. package/src/components/Item/Inventory/itemContainerHelper.ts +175 -175
  59. package/src/components/ListMenu.tsx +63 -63
  60. package/src/components/Marketplace/BuyPanel.tsx +296 -0
  61. package/src/components/Marketplace/ManagmentPanel.tsx +247 -0
  62. package/src/components/Marketplace/Marketplace.tsx +106 -132
  63. package/src/components/Marketplace/MarketplaceRows.tsx +161 -171
  64. package/src/components/Marketplace/{__mocks__ → filters}/index.tsx +67 -65
  65. package/src/components/Multitab/Tab.tsx +66 -66
  66. package/src/components/Multitab/TabBody.tsx +13 -13
  67. package/src/components/Multitab/TabsContainer.tsx +97 -97
  68. package/src/components/NPCDialog/NPCDialog.tsx +121 -121
  69. package/src/components/NPCDialog/NPCDialogText.tsx +113 -113
  70. package/src/components/NPCDialog/NPCMultiDialog.tsx +159 -159
  71. package/src/components/NPCDialog/QuestionDialog/QuestionDialog.tsx +237 -237
  72. package/src/components/Pager.tsx +94 -0
  73. package/src/components/ProgressBar.tsx +95 -95
  74. package/src/components/PropertySelect/PropertySelect.tsx +106 -106
  75. package/src/components/QuestInfo/QuestInfo.tsx +233 -233
  76. package/src/components/QuestList.tsx +135 -135
  77. package/src/components/RPGUIContainer.tsx +47 -47
  78. package/src/components/RPGUIForceRenderStart.tsx +45 -45
  79. package/src/components/RPGUIRoot.tsx +14 -14
  80. package/src/components/RadioButton.tsx +53 -53
  81. package/src/components/RadioInput/RadioButton.tsx +96 -96
  82. package/src/components/RadioInput/RadioInput.tsx +102 -102
  83. package/src/components/RadioInput/instruments.ts +15 -15
  84. package/src/components/RangeSlider.tsx +78 -78
  85. package/src/components/RelativeListMenu.tsx +90 -90
  86. package/src/components/ScrollList.tsx +79 -79
  87. package/src/components/Shortcuts/Shortcuts.tsx +193 -193
  88. package/src/components/Shortcuts/ShortcutsSetter.tsx +139 -139
  89. package/src/components/Shortcuts/SingleShortcut.ts +82 -82
  90. package/src/components/Shortcuts/useShortcutCooldown.ts +23 -23
  91. package/src/components/SimpleProgressBar.tsx +62 -62
  92. package/src/components/SkillProgressBar.tsx +198 -198
  93. package/src/components/SkillsContainer.tsx +213 -213
  94. package/src/components/Spellbook/Spell.tsx +235 -236
  95. package/src/components/Spellbook/Spellbook.tsx +145 -161
  96. package/src/components/Spellbook/cards/MobileSpellTooltip.tsx +137 -137
  97. package/src/components/Spellbook/cards/SpellInfo.tsx +134 -134
  98. package/src/components/Spellbook/cards/SpellInfoDisplay.tsx +31 -31
  99. package/src/components/Spellbook/cards/SpellInfoWrapper.tsx +48 -48
  100. package/src/components/Spellbook/cards/SpellTooltip.tsx +70 -70
  101. package/src/components/Spellbook/constants.ts +7 -7
  102. package/src/components/Spellbook/mockSpells.ts +84 -84
  103. package/src/components/StaticBook/StaticBook.tsx +103 -103
  104. package/src/components/TextArea.tsx +11 -11
  105. package/src/components/TimeWidget/DayNightPeriod/DayNightPeriod.tsx +35 -35
  106. package/src/components/TimeWidget/TimeWidget.tsx +65 -65
  107. package/src/components/TradingMenu/TradingItemRow.tsx +199 -199
  108. package/src/components/TradingMenu/TradingMenu.tsx +219 -219
  109. package/src/components/TradingMenu/items.mock.ts +48 -48
  110. package/src/components/Truncate.tsx +25 -25
  111. package/src/components/itemSelector/ItemSelector.tsx +136 -136
  112. package/src/components/shared/Column.tsx +16 -16
  113. package/src/components/shared/Ellipsis.tsx +76 -68
  114. package/src/components/shared/SpriteFromAtlas.tsx +104 -104
  115. package/src/components/typography/DynamicText.tsx +49 -49
  116. package/src/constants/uiColors.ts +20 -20
  117. package/src/constants/uiDevices.ts +3 -3
  118. package/src/constants/uiFonts.ts +12 -12
  119. package/src/hooks/useEventListener.ts +21 -21
  120. package/src/hooks/useOutsideAlerter.ts +25 -25
  121. package/src/index.tsx +42 -42
  122. package/src/libs/CastingTypeHelper.ts +7 -7
  123. package/src/libs/StringHelpers.ts +3 -3
  124. package/src/libs/itemCounter.ts +21 -21
  125. package/src/mocks/atlas/entities/entities.json +20215 -20215
  126. package/src/mocks/atlas/icons/icons.json +735 -735
  127. package/src/mocks/atlas/items/items.json +12086 -12086
  128. package/src/mocks/equipmentSet.mocks.ts +391 -391
  129. package/src/mocks/itemContainer.mocks.ts +605 -605
  130. package/src/mocks/skills.mocks.ts +130 -130
  131. package/src/stories/Arrow.stories.tsx +26 -26
  132. package/src/stories/Button.stories.tsx +36 -36
  133. package/src/stories/CharacterSelection.stories.tsx +44 -44
  134. package/src/stories/CharacterStatus.stories.tsx +29 -29
  135. package/src/stories/Chat.stories.tsx +187 -187
  136. package/src/stories/ChatDeprecated.stories.tsx +170 -170
  137. package/src/stories/CheckButton.stories.tsx +48 -48
  138. package/src/stories/CircullarController.stories.tsx +37 -37
  139. package/src/stories/CraftBook.stories.tsx +42 -42
  140. package/src/stories/DayNightPeriod.stories.tsx +27 -27
  141. package/src/stories/DraggableContainer.stories.tsx +28 -28
  142. package/src/stories/Dropdown.stories.tsx +46 -46
  143. package/src/stories/DropdownSelectorContainer.stories.tsx +41 -41
  144. package/src/stories/EquipmentSet.stories.tsx +65 -65
  145. package/src/stories/HistoryDialog.stories.tsx +61 -61
  146. package/src/stories/ItemContainer.stories.tsx +201 -201
  147. package/src/stories/ItemInfoDisplay.stories.tsx +33 -33
  148. package/src/stories/ItemQuantitySelector.stories.tsx +26 -26
  149. package/src/stories/ItemSelector.stories.tsx +77 -77
  150. package/src/stories/ItemTradingComponent.stories.tsx +35 -35
  151. package/src/stories/ListMenu.stories.tsx +56 -56
  152. package/src/stories/Marketplace.stories.tsx +53 -42
  153. package/src/stories/MarketplaceRows.stories.tsx +28 -28
  154. package/src/stories/Multitab.stories.tsx +51 -51
  155. package/src/stories/NPCDialog.stories.tsx +130 -130
  156. package/src/stories/NPCMultiDialog.stories.tsx +71 -71
  157. package/src/stories/ProgressBar.stories.tsx +23 -23
  158. package/src/stories/PropertySelect.stories.tsx +40 -40
  159. package/src/stories/QuestInfo.stories.tsx +107 -107
  160. package/src/stories/QuestList.stories.tsx +82 -82
  161. package/src/stories/RPGUIContainers.stories.tsx +42 -42
  162. package/src/stories/RadioButton.stories.tsx +49 -49
  163. package/src/stories/RadioInput.stories.tsx +34 -34
  164. package/src/stories/RangeSlider.stories.tsx +64 -64
  165. package/src/stories/ScrollList.stories.tsx +85 -85
  166. package/src/stories/Shortcuts.stories.tsx +39 -39
  167. package/src/stories/SimpleProgressBar.stories.tsx +22 -22
  168. package/src/stories/SkillProgressBar.stories.tsx +34 -34
  169. package/src/stories/SkillsContainer.stories.tsx +35 -35
  170. package/src/stories/SpellInfoDisplay.stories.tsx +27 -27
  171. package/src/stories/Spellbook.stories.tsx +104 -104
  172. package/src/stories/StaticBook.stories.tsx +32 -32
  173. package/src/stories/Text.stories.tsx +42 -42
  174. package/src/stories/TimeWidget.stories.tsx +27 -27
  175. package/src/stories/TradingMenu.stories.tsx +47 -47
  176. package/src/types/eventTypes.ts +4 -4
  177. package/src/types/index.d.ts +2 -2
@@ -1,580 +1,595 @@
1
- import {
2
- getItemTextureKeyPath,
3
- IEquipmentSet,
4
- IItem,
5
- IItemContainer,
6
- ItemContainerType,
7
- ItemRarities,
8
- ItemSlotType,
9
- ItemType,
10
- } from '@rpg-engine/shared';
11
-
12
- import { observer } from 'mobx-react-lite';
13
- import React, { useEffect, useRef, useState } from 'react';
14
- import Draggable from 'react-draggable';
15
- import styled from 'styled-components';
16
- import { v4 as uuidv4 } from 'uuid';
17
- import { uiFonts } from '../../../constants/uiFonts';
18
- import { IPosition } from '../../../types/eventTypes';
19
- import { RelativeListMenu } from '../../RelativeListMenu';
20
- import { Ellipsis } from '../../shared/Ellipsis';
21
- import { SpriteFromAtlas } from '../../shared/SpriteFromAtlas';
22
- import { ItemTooltip } from '../Cards/ItemTooltip';
23
- import { MobileItemTooltip } from '../Cards/MobileItemTooltip';
24
- import { ErrorBoundary } from './ErrorBoundary';
25
- import { generateContextMenu, IContextMenuItem } from './itemContainerHelper';
26
-
27
- export const EquipmentSlotSpriteByType: any = {
28
- Neck: 'accessories/corruption-necklace.png',
29
- LeftHand: 'swords/broad-sword.png',
30
- Ring: 'rings/iron-ring.png',
31
- Head: 'helmets/viking-helmet.png',
32
- Torso: 'armors/iron-armor.png',
33
- Legs: 'legs/studded-legs.png',
34
- Feet: 'boots/iron-boots.png',
35
- Inventory: 'containers/bag.png',
36
- RightHand: 'shields/plate-shield.png',
37
- Accessory: 'ranged-weapons/arrow.png',
38
- };
39
-
40
- interface IProps {
41
- slotIndex: number;
42
- item: IItem | null;
43
- itemContainer?: IItemContainer | null;
44
- itemContainerType: ItemContainerType | null;
45
- slotSpriteMask?: ItemSlotType | null;
46
- onSelected: (selectedOption: string, item: IItem) => void;
47
- onMouseOver: (
48
- event: any,
49
- slotIndex: number,
50
- item: IItem | null,
51
- x: number,
52
- y: number
53
- ) => void;
54
- onMouseOut?: () => void;
55
- onPointerDown: (
56
- ItemType: ItemType,
57
- itemContainerType: ItemContainerType | null,
58
- item: IItem
59
- ) => void;
60
- onDragStart: (
61
- item: IItem,
62
- slotIndex: number,
63
- itemContainerType: ItemContainerType | null
64
- ) => void;
65
- onDragEnd: (quantity?: number) => void;
66
- onOutsideDrop?: (item: IItem, position: IPosition) => void;
67
- dragScale?: number;
68
- checkIfItemCanBeMoved: () => boolean;
69
- checkIfItemShouldDragEnd?: () => boolean;
70
- openQuantitySelector?: (maxQuantity: number, callback: () => void) => void;
71
- onPlaceDrop: (
72
- item: IItem | null,
73
- slotIndex: number,
74
- itemContainerType: ItemContainerType | null
75
- ) => void;
76
- atlasJSON: any;
77
- atlasIMG: any;
78
- isContextMenuDisabled?: boolean;
79
- isSelectingShortcut?: boolean;
80
- equipmentSet?: IEquipmentSet | null;
81
- setItemShortcut?: (item: IItem, shortcutIndex: number) => void;
82
- isDepotSystem?: boolean;
83
- }
84
-
85
- export const ItemSlot: React.FC<IProps> = observer(
86
- ({
87
- slotIndex,
88
- item,
89
- itemContainerType: containerType,
90
- slotSpriteMask,
91
- onMouseOver,
92
- onMouseOut,
93
- onPointerDown,
94
- onSelected,
95
- atlasJSON,
96
- atlasIMG,
97
- isContextMenuDisabled = false,
98
- onDragEnd,
99
- onDragStart,
100
- onPlaceDrop,
101
- onOutsideDrop: onDrop,
102
- checkIfItemCanBeMoved,
103
- openQuantitySelector,
104
- checkIfItemShouldDragEnd,
105
- dragScale,
106
- isSelectingShortcut,
107
- equipmentSet,
108
- setItemShortcut,
109
- isDepotSystem,
110
- }) => {
111
- const [isTooltipVisible, setTooltipVisible] = useState(false);
112
- const [isTooltipMobileVisible, setIsTooltipMobileVisible] = useState(false);
113
-
114
- const [isContextMenuVisible, setIsContextMenuVisible] = useState(false);
115
- const [contextMenuPosition, setContextMenuPosition] = useState({
116
- x: 0,
117
- y: 0,
118
- });
119
-
120
- const [isFocused, setIsFocused] = useState(false);
121
- const [wasDragged, setWasDragged] = useState(false);
122
- const [dragPosition, setDragPosition] = useState<IPosition>({ x: 0, y: 0 });
123
- const [dropPosition, setDropPosition] = useState<IPosition | null>(null);
124
- const dragContainer = useRef<HTMLDivElement>(null);
125
-
126
- const [contextActions, setContextActions] = useState<IContextMenuItem[]>(
127
- []
128
- );
129
-
130
- useEffect(() => {
131
- setDragPosition({ x: 0, y: 0 });
132
- setIsFocused(false);
133
-
134
- if (item) {
135
- setContextActions(
136
- generateContextMenu(item, containerType, isDepotSystem)
137
- );
138
- }
139
- }, [item, isDepotSystem]);
140
-
141
- useEffect(() => {
142
- if (onDrop && item && dropPosition) {
143
- onDrop(item, dropPosition);
144
- }
145
- }, [dropPosition]);
146
-
147
- const getStackInfo = (itemId: string, stackQty: number) => {
148
- const isFractionalStackQty = stackQty % 1 !== 0;
149
- const isLargerThan999 = stackQty > 999;
150
-
151
- let qtyClassName = 'regular';
152
- if (isLargerThan999) qtyClassName = 'small';
153
- if (isFractionalStackQty) qtyClassName = 'xsmall';
154
-
155
- if (stackQty > 1) {
156
- return (
157
- <ItemQtyContainer key={`qty-${itemId}`}>
158
- <Ellipsis maxLines={1} maxWidth="48px">
159
- <ItemQty className={qtyClassName}>
160
- {Math.round(stackQty * 100) / 100}{' '}
161
- </ItemQty>
162
- </Ellipsis>
163
- </ItemQtyContainer>
164
- );
165
- }
166
- return undefined;
167
- };
168
-
169
- const renderItem = (itemToRender: IItem | null) => {
170
- const element = [];
171
-
172
- if (itemToRender?.texturePath) {
173
- element.push(
174
- <ErrorBoundary key={itemToRender._id}>
175
- <SpriteFromAtlas
176
- key={itemToRender._id}
177
- atlasIMG={atlasIMG}
178
- atlasJSON={atlasJSON}
179
- spriteKey={getItemTextureKeyPath(
180
- {
181
- key: itemToRender.texturePath,
182
- texturePath: itemToRender.texturePath,
183
- stackQty: itemToRender.stackQty || 1,
184
- isStackable: itemToRender.isStackable,
185
- },
186
- atlasJSON
187
- )}
188
- imgScale={3}
189
- imgClassname="sprite-from-atlas-img--item"
190
- />
191
- </ErrorBoundary>
192
- );
193
- }
194
- const stackInfo = getStackInfo(
195
- itemToRender?._id ?? '',
196
- itemToRender?.stackQty ?? 0
197
- );
198
- if (stackInfo) {
199
- element.push(stackInfo);
200
- }
201
-
202
- return element;
203
- };
204
-
205
- const renderEquipment = (itemToRender: IItem | null) => {
206
- if (
207
- itemToRender?.texturePath &&
208
- itemToRender.allowedEquipSlotType?.includes(slotSpriteMask!)
209
- ) {
210
- const element = [];
211
-
212
- element.push(
213
- <ErrorBoundary key={itemToRender._id}>
214
- <SpriteFromAtlas
215
- key={itemToRender._id}
216
- atlasIMG={atlasIMG}
217
- atlasJSON={atlasJSON}
218
- spriteKey={getItemTextureKeyPath(
219
- {
220
- key: itemToRender.texturePath,
221
- texturePath: itemToRender.texturePath,
222
- stackQty: itemToRender.stackQty || 1,
223
- isStackable: itemToRender.isStackable,
224
- },
225
- atlasJSON
226
- )}
227
- imgScale={3}
228
- imgClassname="sprite-from-atlas-img--item"
229
- />
230
- </ErrorBoundary>
231
- );
232
- const stackInfo = getStackInfo(
233
- itemToRender?._id ?? '',
234
- itemToRender?.stackQty ?? 0
235
- );
236
- if (stackInfo) {
237
- element.push(stackInfo);
238
- }
239
- return element;
240
- } else {
241
- return (
242
- <ErrorBoundary key={uuidv4()}>
243
- <SpriteFromAtlas
244
- key={uuidv4()}
245
- atlasIMG={atlasIMG}
246
- atlasJSON={atlasJSON}
247
- spriteKey={EquipmentSlotSpriteByType[slotSpriteMask!]}
248
- imgScale={3}
249
- grayScale={true}
250
- opacity={0.4}
251
- imgClassname="sprite-from-atlas-img--item"
252
- />
253
- </ErrorBoundary>
254
- );
255
- }
256
- };
257
-
258
- const onRenderSlot = (itemToRender: IItem | null) => {
259
- switch (containerType) {
260
- case ItemContainerType.Equipment:
261
- return renderEquipment(itemToRender);
262
- case ItemContainerType.Inventory:
263
- return renderItem(itemToRender);
264
- default:
265
- return renderItem(itemToRender);
266
- }
267
- };
268
-
269
- const resetItem = () => {
270
- setTooltipVisible(false);
271
- setWasDragged(false);
272
- };
273
-
274
- const onSuccesfulDrag = (quantity?: number) => {
275
- resetItem();
276
-
277
- if (quantity === -1) {
278
- setDragPosition({ x: 0, y: 0 });
279
- setIsFocused(false);
280
- } else if (item) {
281
- onDragEnd(quantity);
282
- }
283
- };
284
-
285
- return (
286
- <Container
287
- item={item}
288
- className="rpgui-icon empty-slot"
289
- onMouseUp={() => {
290
- const data = item ? item : null;
291
- if (onPlaceDrop) onPlaceDrop(data, slotIndex, containerType);
292
- }}
293
- onTouchEnd={e => {
294
- const { clientX, clientY } = e.changedTouches[0];
295
- const simulatedEvent = new MouseEvent('mouseup', {
296
- clientX,
297
- clientY,
298
- bubbles: true,
299
- });
300
-
301
- document
302
- .elementFromPoint(clientX, clientY)
303
- ?.dispatchEvent(simulatedEvent);
304
- }}
305
- isSelectingShortcut={
306
- isSelectingShortcut &&
307
- (item?.type === ItemType.Consumable || item?.type === ItemType.Tool)
308
- }
309
- >
310
- <Draggable
311
- axis={isSelectingShortcut ? 'none' : 'both'}
312
- defaultClassName={item ? 'draggable' : 'empty-slot'}
313
- scale={dragScale}
314
- onStop={(e, data) => {
315
- const target = e.target as HTMLElement;
316
- if (
317
- target?.id.includes('shortcutSetter') &&
318
- setItemShortcut &&
319
- item
320
- ) {
321
- const index = parseInt(target.id.split('_')[1]);
322
- if (!isNaN(index)) {
323
- setItemShortcut(item, index);
324
- }
325
- }
326
-
327
- if (wasDragged && item && !isSelectingShortcut) {
328
- //@ts-ignore
329
- const classes: string[] = Array.from(e.target?.classList);
330
-
331
- const isOutsideDrop =
332
- classes.some(elm => {
333
- return elm.includes('rpgui-content');
334
- }) || classes.length === 0;
335
-
336
- if (isOutsideDrop) {
337
- setDropPosition({
338
- x: data.x,
339
- y: data.y,
340
- });
341
- }
342
-
343
- setWasDragged(false);
344
-
345
- const target = dragContainer.current;
346
- if (!target || !wasDragged) return;
347
-
348
- const style = window.getComputedStyle(target);
349
- const matrix = new DOMMatrixReadOnly(style.transform);
350
- const x = matrix.m41;
351
- const y = matrix.m42;
352
-
353
- setDragPosition({ x, y });
354
-
355
- setTimeout(() => {
356
- if (checkIfItemCanBeMoved()) {
357
- if (checkIfItemShouldDragEnd && !checkIfItemShouldDragEnd())
358
- return;
359
-
360
- if (
361
- item.stackQty &&
362
- item.stackQty !== 1 &&
363
- openQuantitySelector
364
- )
365
- openQuantitySelector(item.stackQty, onSuccesfulDrag);
366
- else onSuccesfulDrag(item.stackQty);
367
- } else {
368
- resetItem();
369
- setIsFocused(false);
370
- setDragPosition({ x: 0, y: 0 });
371
- }
372
- }, 100);
373
- } else if (item) {
374
- let isTouch = false;
375
- if (
376
- !isContextMenuDisabled &&
377
- e.type === 'touchend' &&
378
- !isSelectingShortcut
379
- ) {
380
- isTouch = true;
381
- setIsTooltipMobileVisible(true);
382
- }
383
-
384
- if (!isContextMenuDisabled && !isSelectingShortcut && !isTouch) {
385
- setIsContextMenuVisible(!isContextMenuVisible);
386
- const event = e as MouseEvent;
387
-
388
- if (event.clientX && event.clientY) {
389
- setContextMenuPosition({
390
- x: event.clientX - 10,
391
- y: event.clientY - 5,
392
- });
393
- }
394
- }
395
-
396
- onPointerDown(item.type, containerType, item);
397
- }
398
- }}
399
- onStart={() => {
400
- if (!item || isSelectingShortcut) {
401
- return;
402
- }
403
-
404
- if (onDragStart) {
405
- onDragStart(item, slotIndex, containerType);
406
- }
407
- }}
408
- onDrag={(_e, data) => {
409
- if (
410
- Math.abs(data.x - dragPosition.x) > 5 ||
411
- Math.abs(data.y - dragPosition.y) > 5
412
- ) {
413
- setWasDragged(true);
414
- setIsFocused(true);
415
- }
416
- }}
417
- position={dragPosition}
418
- cancel=".empty-slot"
419
- >
420
- <ItemContainer
421
- ref={dragContainer}
422
- isFocused={isFocused}
423
- onMouseOver={event => {
424
- onMouseOver(event, slotIndex, item, event.clientX, event.clientY);
425
- }}
426
- onMouseOut={() => {
427
- if (onMouseOut) onMouseOut();
428
- }}
429
- onMouseEnter={() => {
430
- setTooltipVisible(true);
431
- }}
432
- onMouseLeave={() => {
433
- setTooltipVisible(false);
434
- }}
435
- >
436
- {onRenderSlot(item)}
437
- </ItemContainer>
438
- </Draggable>
439
-
440
- {isTooltipVisible && item && !isFocused && (
441
- <ItemTooltip
442
- item={item}
443
- atlasIMG={atlasIMG}
444
- atlasJSON={atlasJSON}
445
- equipmentSet={equipmentSet}
446
- />
447
- )}
448
-
449
- {isTooltipMobileVisible && item && (
450
- <MobileItemTooltip
451
- item={item}
452
- atlasIMG={atlasIMG}
453
- atlasJSON={atlasJSON}
454
- equipmentSet={equipmentSet}
455
- closeTooltip={() => {
456
- setIsTooltipMobileVisible(false);
457
- }}
458
- scale={dragScale}
459
- options={contextActions}
460
- onSelected={(optionId: string) => {
461
- setIsContextMenuVisible(false);
462
- if (item) {
463
- onSelected(optionId, item);
464
- }
465
- }}
466
- />
467
- )}
468
-
469
- {!isContextMenuDisabled && isContextMenuVisible && contextActions && (
470
- <RelativeListMenu
471
- options={contextActions}
472
- onSelected={(optionId: string) => {
473
- setIsContextMenuVisible(false);
474
- if (item) {
475
- onSelected(optionId, item);
476
- }
477
- }}
478
- onOutsideClick={() => {
479
- setIsContextMenuVisible(false);
480
- }}
481
- pos={contextMenuPosition}
482
- />
483
- )}
484
- </Container>
485
- );
486
- }
487
- );
488
-
489
- export const rarityColor = (item: IItem | null) => {
490
- switch (item?.rarity) {
491
- case ItemRarities.Uncommon:
492
- return 'rgba(13, 193, 13, 0.6)';
493
- case ItemRarities.Rare:
494
- return 'rgba(8, 104, 187, 0.6)';
495
- case ItemRarities.Epic:
496
- return 'rgba(191, 0, 255, 0.6)';
497
- case ItemRarities.Legendary:
498
- return 'rgba(255, 191, 0,0.6)';
499
- default:
500
- return null;
501
- }
502
- };
503
-
504
- interface ContainerTypes {
505
- item: IItem | null;
506
- isSelectingShortcut?: boolean;
507
- }
508
-
509
- const Container = styled.div<ContainerTypes>`
510
- margin: 0.1rem;
511
- .sprite-from-atlas-img--item {
512
- position: relative;
513
- top: 1.5rem;
514
- left: 1.5rem;
515
- border-color: ${({ item }) => rarityColor(item)};
516
- box-shadow: ${({ item }) => `0 0 5px 2px ${rarityColor(item)}`} inset, ${({
517
- item,
518
- }) => `0 0 4px 3px ${rarityColor(item)}`};
519
- //background-color: ${({ item }) => rarityColor(item)};
520
- }
521
- position: relative;
522
-
523
- &::before {
524
- content: '';
525
- position: absolute;
526
- top: 0;
527
- left: 0;
528
- width: 100%;
529
- height: 100%;
530
- z-index: 1;
531
- border-radius: 12px;
532
- pointer-events: none;
533
- animation: ${({ isSelectingShortcut }) =>
534
- isSelectingShortcut ? 'bg-color-change 1s infinite' : 'none'};
535
-
536
- @keyframes bg-color-change {
537
- 0% {
538
- background-color: rgba(255 255 255 / 0.5);
539
- }
540
- 50% {
541
- background-color: transparent;
542
- }
543
- 100% {
544
- background-color: rgba(255 255 255 / 0.5);
545
- }
546
- }
547
- }
548
- `;
549
-
550
- const ItemContainer = styled.div<{ isFocused?: boolean }>`
551
- width: 100%;
552
- height: 100%;
553
- position: relative;
554
-
555
- ${props => props.isFocused && 'z-index: 100; pointer-events: none;'}
556
- `;
557
-
558
- const ItemQtyContainer = styled.div`
559
- position: relative;
560
- width: 85%;
561
- height: 16px;
562
- top: 25px;
563
- left: 2px;
564
- pointer-events: none;
565
-
566
- display: flex;
567
- justify-content: flex-end;
568
- `;
569
-
570
- const ItemQty = styled.span`
571
- &.regular {
572
- font-size: ${uiFonts.size.small};
573
- }
574
- &.small {
575
- font-size: ${uiFonts.size.xsmall};
576
- }
577
- &.xsmall {
578
- font-size: ${uiFonts.size.xxsmall};
579
- }
580
- `;
1
+ import {
2
+ getItemTextureKeyPath,
3
+ IEquipmentSet,
4
+ IItem,
5
+ IItemContainer,
6
+ ItemContainerType,
7
+ ItemRarities,
8
+ ItemSlotType,
9
+ ItemType,
10
+ } from '@rpg-engine/shared';
11
+
12
+ import { observer } from 'mobx-react-lite';
13
+ import React, { useEffect, useRef, useState } from 'react';
14
+ import Draggable from 'react-draggable';
15
+ import styled from 'styled-components';
16
+ import { v4 as uuidv4 } from 'uuid';
17
+ import { uiFonts } from '../../../constants/uiFonts';
18
+ import { IPosition } from '../../../types/eventTypes';
19
+ import { RelativeListMenu } from '../../RelativeListMenu';
20
+ import { Ellipsis } from '../../shared/Ellipsis';
21
+ import { SpriteFromAtlas } from '../../shared/SpriteFromAtlas';
22
+ import { ItemTooltip } from '../Cards/ItemTooltip';
23
+ import { MobileItemTooltip } from '../Cards/MobileItemTooltip';
24
+ import { ErrorBoundary } from './ErrorBoundary';
25
+ import { generateContextMenu, IContextMenuItem } from './itemContainerHelper';
26
+
27
+ export const EquipmentSlotSpriteByType: any = {
28
+ Neck: 'accessories/corruption-necklace.png',
29
+ LeftHand: 'swords/broad-sword.png',
30
+ Ring: 'rings/iron-ring.png',
31
+ Head: 'helmets/viking-helmet.png',
32
+ Torso: 'armors/iron-armor.png',
33
+ Legs: 'legs/studded-legs.png',
34
+ Feet: 'boots/iron-boots.png',
35
+ Inventory: 'containers/bag.png',
36
+ RightHand: 'shields/plate-shield.png',
37
+ Accessory: 'ranged-weapons/arrow.png',
38
+ };
39
+
40
+ interface IProps {
41
+ slotIndex: number;
42
+ item: IItem | null;
43
+ itemContainer?: IItemContainer | null;
44
+ itemContainerType?: ItemContainerType | null;
45
+ slotSpriteMask?: ItemSlotType | null;
46
+ onSelected?: (selectedOption: string, item: IItem) => void;
47
+ onMouseOver?: (
48
+ event: any,
49
+ slotIndex: number,
50
+ item: IItem | null,
51
+ x: number,
52
+ y: number
53
+ ) => void;
54
+ onMouseOut?: () => void;
55
+ onPointerDown: (
56
+ ItemType: ItemType,
57
+ itemContainerType: ItemContainerType | null,
58
+ item: IItem
59
+ ) => void;
60
+ onDragStart?: (
61
+ item: IItem,
62
+ slotIndex: number,
63
+ itemContainerType: ItemContainerType | null
64
+ ) => void;
65
+ onDragEnd?: (quantity?: number) => void;
66
+ onOutsideDrop?: (item: IItem, position: IPosition) => void;
67
+ dragScale?: number;
68
+ checkIfItemCanBeMoved?: () => boolean;
69
+ checkIfItemShouldDragEnd?: () => boolean;
70
+ openQuantitySelector?: (maxQuantity: number, callback: () => void) => void;
71
+ onPlaceDrop?: (
72
+ item: IItem | null,
73
+ slotIndex: number,
74
+ itemContainerType: ItemContainerType | null
75
+ ) => void;
76
+ atlasJSON: any;
77
+ atlasIMG: any;
78
+ isContextMenuDisabled?: boolean;
79
+ isSelectingShortcut?: boolean;
80
+ equipmentSet?: IEquipmentSet | null;
81
+ setItemShortcut?: (item: IItem, shortcutIndex: number) => void;
82
+ isDepotSystem?: boolean;
83
+ }
84
+
85
+ export const ItemSlot: React.FC<IProps> = observer(
86
+ ({
87
+ slotIndex,
88
+ item,
89
+ itemContainerType: containerType,
90
+ slotSpriteMask,
91
+ onMouseOver,
92
+ onMouseOut,
93
+ onPointerDown,
94
+ onSelected,
95
+ atlasJSON,
96
+ atlasIMG,
97
+ isContextMenuDisabled = false,
98
+ onDragEnd,
99
+ onDragStart,
100
+ onPlaceDrop,
101
+ onOutsideDrop: onDrop,
102
+ checkIfItemCanBeMoved,
103
+ openQuantitySelector,
104
+ checkIfItemShouldDragEnd,
105
+ dragScale,
106
+ isSelectingShortcut,
107
+ equipmentSet,
108
+ setItemShortcut,
109
+ isDepotSystem,
110
+ }) => {
111
+ const [isTooltipVisible, setTooltipVisible] = useState(false);
112
+ const [isTooltipMobileVisible, setIsTooltipMobileVisible] = useState(false);
113
+
114
+ const [isContextMenuVisible, setIsContextMenuVisible] = useState(false);
115
+ const [contextMenuPosition, setContextMenuPosition] = useState({
116
+ x: 0,
117
+ y: 0,
118
+ });
119
+
120
+ const [isFocused, setIsFocused] = useState(false);
121
+ const [wasDragged, setWasDragged] = useState(false);
122
+ const [dragPosition, setDragPosition] = useState<IPosition>({ x: 0, y: 0 });
123
+ const [dropPosition, setDropPosition] = useState<IPosition | null>(null);
124
+ const dragContainer = useRef<HTMLDivElement>(null);
125
+
126
+ const [contextActions, setContextActions] = useState<IContextMenuItem[]>(
127
+ []
128
+ );
129
+
130
+ useEffect(() => {
131
+ setDragPosition({ x: 0, y: 0 });
132
+ setIsFocused(false);
133
+
134
+ if (item && containerType) {
135
+ setContextActions(
136
+ generateContextMenu(item, containerType, isDepotSystem)
137
+ );
138
+ }
139
+ }, [item, isDepotSystem]);
140
+
141
+ useEffect(() => {
142
+ if (onDrop && item && dropPosition) {
143
+ onDrop(item, dropPosition);
144
+ }
145
+ }, [dropPosition]);
146
+
147
+ const getStackInfo = (itemId: string, stackQty: number) => {
148
+ const isFractionalStackQty = stackQty % 1 !== 0;
149
+ const isLargerThan999 = stackQty > 999;
150
+
151
+ let qtyClassName = 'regular';
152
+ if (isLargerThan999) qtyClassName = 'small';
153
+ if (isFractionalStackQty) qtyClassName = 'xsmall';
154
+
155
+ if (stackQty > 1) {
156
+ return (
157
+ <ItemQtyContainer key={`qty-${itemId}`}>
158
+ <Ellipsis maxLines={1} maxWidth="48px">
159
+ <ItemQty className={qtyClassName}>
160
+ {Math.round(stackQty * 100) / 100}{' '}
161
+ </ItemQty>
162
+ </Ellipsis>
163
+ </ItemQtyContainer>
164
+ );
165
+ }
166
+ return undefined;
167
+ };
168
+
169
+ const renderItem = (itemToRender: IItem | null) => {
170
+ const element = [];
171
+
172
+ if (itemToRender?.texturePath) {
173
+ element.push(
174
+ <ErrorBoundary key={itemToRender._id}>
175
+ <SpriteFromAtlas
176
+ key={itemToRender._id}
177
+ atlasIMG={atlasIMG}
178
+ atlasJSON={atlasJSON}
179
+ spriteKey={getItemTextureKeyPath(
180
+ {
181
+ key: itemToRender.texturePath,
182
+ texturePath: itemToRender.texturePath,
183
+ stackQty: itemToRender.stackQty || 1,
184
+ isStackable: itemToRender.isStackable,
185
+ },
186
+ atlasJSON
187
+ )}
188
+ imgScale={3}
189
+ imgClassname="sprite-from-atlas-img--item"
190
+ />
191
+ </ErrorBoundary>
192
+ );
193
+ }
194
+ const stackInfo = getStackInfo(
195
+ itemToRender?._id ?? '',
196
+ itemToRender?.stackQty ?? 0
197
+ );
198
+ if (stackInfo) {
199
+ element.push(stackInfo);
200
+ }
201
+
202
+ return element;
203
+ };
204
+
205
+ const renderEquipment = (itemToRender: IItem | null) => {
206
+ if (
207
+ itemToRender?.texturePath &&
208
+ itemToRender.allowedEquipSlotType?.includes(slotSpriteMask!)
209
+ ) {
210
+ const element = [];
211
+
212
+ element.push(
213
+ <ErrorBoundary key={itemToRender._id}>
214
+ <SpriteFromAtlas
215
+ key={itemToRender._id}
216
+ atlasIMG={atlasIMG}
217
+ atlasJSON={atlasJSON}
218
+ spriteKey={getItemTextureKeyPath(
219
+ {
220
+ key: itemToRender.texturePath,
221
+ texturePath: itemToRender.texturePath,
222
+ stackQty: itemToRender.stackQty || 1,
223
+ isStackable: itemToRender.isStackable,
224
+ },
225
+ atlasJSON
226
+ )}
227
+ imgScale={3}
228
+ imgClassname="sprite-from-atlas-img--item"
229
+ />
230
+ </ErrorBoundary>
231
+ );
232
+ const stackInfo = getStackInfo(
233
+ itemToRender?._id ?? '',
234
+ itemToRender?.stackQty ?? 0
235
+ );
236
+ if (stackInfo) {
237
+ element.push(stackInfo);
238
+ }
239
+ return element;
240
+ } else {
241
+ return (
242
+ <ErrorBoundary key={uuidv4()}>
243
+ <SpriteFromAtlas
244
+ key={uuidv4()}
245
+ atlasIMG={atlasIMG}
246
+ atlasJSON={atlasJSON}
247
+ spriteKey={EquipmentSlotSpriteByType[slotSpriteMask!]}
248
+ imgScale={3}
249
+ grayScale={true}
250
+ opacity={0.4}
251
+ imgClassname="sprite-from-atlas-img--item"
252
+ />
253
+ </ErrorBoundary>
254
+ );
255
+ }
256
+ };
257
+
258
+ const onRenderSlot = (itemToRender: IItem | null) => {
259
+ switch (containerType) {
260
+ case ItemContainerType.Equipment:
261
+ return renderEquipment(itemToRender);
262
+ case ItemContainerType.Inventory:
263
+ return renderItem(itemToRender);
264
+ default:
265
+ return renderItem(itemToRender);
266
+ }
267
+ };
268
+
269
+ const resetItem = () => {
270
+ setTooltipVisible(false);
271
+ setWasDragged(false);
272
+ };
273
+
274
+ const onSuccesfulDrag = (quantity?: number) => {
275
+ resetItem();
276
+
277
+ if (quantity === -1) {
278
+ setDragPosition({ x: 0, y: 0 });
279
+ setIsFocused(false);
280
+ } else if (item) {
281
+ onDragEnd?.(quantity);
282
+ }
283
+ };
284
+
285
+ return (
286
+ <Container
287
+ item={item}
288
+ className="rpgui-icon empty-slot"
289
+ onMouseUp={() => {
290
+ const data = item ? item : null;
291
+ if (onPlaceDrop && containerType)
292
+ onPlaceDrop(data, slotIndex, containerType);
293
+ }}
294
+ onTouchEnd={e => {
295
+ const { clientX, clientY } = e.changedTouches[0];
296
+ const simulatedEvent = new MouseEvent('mouseup', {
297
+ clientX,
298
+ clientY,
299
+ bubbles: true,
300
+ });
301
+
302
+ document
303
+ .elementFromPoint(clientX, clientY)
304
+ ?.dispatchEvent(simulatedEvent);
305
+ }}
306
+ onPointerDown={
307
+ onDragStart !== undefined && onDragEnd !== undefined
308
+ ? undefined
309
+ : () => {
310
+ if (item) onPointerDown(item.type, containerType ?? null, item);
311
+ }
312
+ }
313
+ isSelectingShortcut={
314
+ isSelectingShortcut &&
315
+ (item?.type === ItemType.Consumable || item?.type === ItemType.Tool)
316
+ }
317
+ >
318
+ <Draggable
319
+ axis={isSelectingShortcut ? 'none' : 'both'}
320
+ defaultClassName={item ? 'draggable' : 'empty-slot'}
321
+ scale={dragScale}
322
+ disabled={onDragStart === undefined || onDragEnd === undefined}
323
+ onStop={(e, data) => {
324
+ const target = e.target as HTMLElement;
325
+ if (
326
+ target?.id.includes('shortcutSetter') &&
327
+ setItemShortcut &&
328
+ item
329
+ ) {
330
+ const index = parseInt(target.id.split('_')[1]);
331
+ if (!isNaN(index)) {
332
+ setItemShortcut(item, index);
333
+ }
334
+ }
335
+
336
+ if (wasDragged && item && !isSelectingShortcut) {
337
+ //@ts-ignore
338
+ const classes: string[] = Array.from(e.target?.classList);
339
+
340
+ const isOutsideDrop =
341
+ classes.some(elm => {
342
+ return elm.includes('rpgui-content');
343
+ }) || classes.length === 0;
344
+
345
+ if (isOutsideDrop) {
346
+ setDropPosition({
347
+ x: data.x,
348
+ y: data.y,
349
+ });
350
+ }
351
+
352
+ setWasDragged(false);
353
+
354
+ const target = dragContainer.current;
355
+ if (!target || !wasDragged) return;
356
+
357
+ const style = window.getComputedStyle(target);
358
+ const matrix = new DOMMatrixReadOnly(style.transform);
359
+ const x = matrix.m41;
360
+ const y = matrix.m42;
361
+
362
+ setDragPosition({ x, y });
363
+
364
+ setTimeout(() => {
365
+ if (checkIfItemCanBeMoved?.()) {
366
+ if (checkIfItemShouldDragEnd && !checkIfItemShouldDragEnd())
367
+ return;
368
+
369
+ if (
370
+ item.stackQty &&
371
+ item.stackQty !== 1 &&
372
+ openQuantitySelector
373
+ )
374
+ openQuantitySelector(item.stackQty, onSuccesfulDrag);
375
+ else onSuccesfulDrag(item.stackQty);
376
+ } else {
377
+ resetItem();
378
+ setIsFocused(false);
379
+ setDragPosition({ x: 0, y: 0 });
380
+ }
381
+ }, 100);
382
+ } else if (item) {
383
+ let isTouch = false;
384
+ if (
385
+ !isContextMenuDisabled &&
386
+ e.type === 'touchend' &&
387
+ !isSelectingShortcut
388
+ ) {
389
+ isTouch = true;
390
+ setIsTooltipMobileVisible(true);
391
+ }
392
+
393
+ if (!isContextMenuDisabled && !isSelectingShortcut && !isTouch) {
394
+ setIsContextMenuVisible(!isContextMenuVisible);
395
+ const event = e as MouseEvent;
396
+
397
+ if (event.clientX && event.clientY) {
398
+ setContextMenuPosition({
399
+ x: event.clientX - 10,
400
+ y: event.clientY - 5,
401
+ });
402
+ }
403
+ }
404
+
405
+ onPointerDown(item.type, containerType ?? null, item);
406
+ }
407
+ }}
408
+ onStart={() => {
409
+ if (!item || isSelectingShortcut) {
410
+ return;
411
+ }
412
+
413
+ if (onDragStart && containerType) {
414
+ onDragStart(item, slotIndex, containerType);
415
+ }
416
+ }}
417
+ onDrag={(_e, data) => {
418
+ if (
419
+ Math.abs(data.x - dragPosition.x) > 5 ||
420
+ Math.abs(data.y - dragPosition.y) > 5
421
+ ) {
422
+ setWasDragged(true);
423
+ setIsFocused(true);
424
+ }
425
+ }}
426
+ position={dragPosition}
427
+ cancel=".empty-slot"
428
+ >
429
+ <ItemContainer
430
+ ref={dragContainer}
431
+ isFocused={isFocused}
432
+ onMouseOver={event => {
433
+ onMouseOver?.(
434
+ event,
435
+ slotIndex,
436
+ item,
437
+ event.clientX,
438
+ event.clientY
439
+ );
440
+ }}
441
+ onMouseOut={() => {
442
+ if (onMouseOut) onMouseOut();
443
+ }}
444
+ onMouseEnter={() => {
445
+ setTooltipVisible(true);
446
+ }}
447
+ onMouseLeave={() => {
448
+ setTooltipVisible(false);
449
+ }}
450
+ >
451
+ {onRenderSlot(item)}
452
+ </ItemContainer>
453
+ </Draggable>
454
+
455
+ {isTooltipVisible && item && !isFocused && (
456
+ <ItemTooltip
457
+ item={item}
458
+ atlasIMG={atlasIMG}
459
+ atlasJSON={atlasJSON}
460
+ equipmentSet={equipmentSet}
461
+ />
462
+ )}
463
+
464
+ {isTooltipMobileVisible && item && (
465
+ <MobileItemTooltip
466
+ item={item}
467
+ atlasIMG={atlasIMG}
468
+ atlasJSON={atlasJSON}
469
+ equipmentSet={equipmentSet}
470
+ closeTooltip={() => {
471
+ setIsTooltipMobileVisible(false);
472
+ }}
473
+ scale={dragScale}
474
+ options={contextActions}
475
+ onSelected={(optionId: string) => {
476
+ setIsContextMenuVisible(false);
477
+ if (item) {
478
+ onSelected?.(optionId, item);
479
+ }
480
+ }}
481
+ />
482
+ )}
483
+
484
+ {!isContextMenuDisabled && isContextMenuVisible && contextActions && (
485
+ <RelativeListMenu
486
+ options={contextActions}
487
+ onSelected={(optionId: string) => {
488
+ setIsContextMenuVisible(false);
489
+ if (item) {
490
+ onSelected?.(optionId, item);
491
+ }
492
+ }}
493
+ onOutsideClick={() => {
494
+ setIsContextMenuVisible(false);
495
+ }}
496
+ pos={contextMenuPosition}
497
+ />
498
+ )}
499
+ </Container>
500
+ );
501
+ }
502
+ );
503
+
504
+ export const rarityColor = (item: IItem | null) => {
505
+ switch (item?.rarity) {
506
+ case ItemRarities.Uncommon:
507
+ return 'rgba(13, 193, 13, 0.6)';
508
+ case ItemRarities.Rare:
509
+ return 'rgba(8, 104, 187, 0.6)';
510
+ case ItemRarities.Epic:
511
+ return 'rgba(191, 0, 255, 0.6)';
512
+ case ItemRarities.Legendary:
513
+ return 'rgba(255, 191, 0,0.6)';
514
+ default:
515
+ return null;
516
+ }
517
+ };
518
+
519
+ interface ContainerTypes {
520
+ item: IItem | null;
521
+ isSelectingShortcut?: boolean;
522
+ }
523
+
524
+ const Container = styled.div<ContainerTypes>`
525
+ margin: 0.1rem;
526
+ .sprite-from-atlas-img--item {
527
+ position: relative;
528
+ top: 1.5rem;
529
+ left: 1.5rem;
530
+ border-color: ${({ item }) => rarityColor(item)};
531
+ box-shadow: ${({ item }) => `0 0 5px 2px ${rarityColor(item)}`} inset, ${({
532
+ item,
533
+ }) => `0 0 4px 3px ${rarityColor(item)}`};
534
+ //background-color: ${({ item }) => rarityColor(item)};
535
+ }
536
+ position: relative;
537
+
538
+ &::before {
539
+ content: '';
540
+ position: absolute;
541
+ top: 0;
542
+ left: 0;
543
+ width: 100%;
544
+ height: 100%;
545
+ z-index: 1;
546
+ border-radius: 12px;
547
+ pointer-events: none;
548
+ animation: ${({ isSelectingShortcut }) =>
549
+ isSelectingShortcut ? 'bg-color-change 1s infinite' : 'none'};
550
+
551
+ @keyframes bg-color-change {
552
+ 0% {
553
+ background-color: rgba(255 255 255 / 0.5);
554
+ }
555
+ 50% {
556
+ background-color: transparent;
557
+ }
558
+ 100% {
559
+ background-color: rgba(255 255 255 / 0.5);
560
+ }
561
+ }
562
+ }
563
+ `;
564
+
565
+ const ItemContainer = styled.div<{ isFocused?: boolean }>`
566
+ width: 100%;
567
+ height: 100%;
568
+ position: relative;
569
+
570
+ ${props => props.isFocused && 'z-index: 100; pointer-events: none;'}
571
+ `;
572
+
573
+ const ItemQtyContainer = styled.div`
574
+ position: relative;
575
+ width: 85%;
576
+ height: 16px;
577
+ top: 25px;
578
+ left: 2px;
579
+ pointer-events: none;
580
+
581
+ display: flex;
582
+ justify-content: flex-end;
583
+ `;
584
+
585
+ const ItemQty = styled.span`
586
+ &.regular {
587
+ font-size: ${uiFonts.size.small};
588
+ }
589
+ &.small {
590
+ font-size: ${uiFonts.size.xsmall};
591
+ }
592
+ &.xsmall {
593
+ font-size: ${uiFonts.size.xxsmall};
594
+ }
595
+ `;